Grammalecte  Check-in [2ce6101a65]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:[tb] merge tbme
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | tb
Files: files | file ages | folders
SHA3-256:2ce6101a653334fafc4ba61fca20d72f9095dcfa17195ab837e7d5d18b184a47
User & Date: olr 2019-11-26 15:19:50
Context
2019-11-26
17:19
[fr] faux positif check-in: e724bfa216 user: olr tags: fr, trunk, v1.6.0
15:19
[tb] merge tbme check-in: 2ce6101a65 user: olr tags: tb, trunk
15:05
[fr] data rebuild check-in: 688d963eb0 user: olr tags: fr, trunk
2019-09-30
07:26
merge trunk Leaf check-in: fb270e0f3b user: olr tags: tbme
Changes

Changes to gc_lang/fr/build.py.

4
5
6
7
8
9
10
11
12
13
14

15
16
17
18
19
20
21
..
36
37
38
39
40
41
42

















43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import platform
import zipfile
from distutils import dir_util, file_util

import helpers


def build (sLang, dVars, spLangPack):
    "complementary build launched from make.py"
    createWebExtension(sLang, dVars)
    createThunderbirdExtension(sLang, dVars, spLangPack)

    createNodeJSPackage(sLang)


def createWebExtension (sLang, dVars):
    "create Web-extension"
    print("Building WebExtension")
    helpers.createCleanFolder("_build/webext/"+sLang)
................................................................................
        for lLineOpt in lOpt:
            for sOpt in lLineOpt:
                sHTML += f'  <p><input type="checkbox" id="option_{sOpt}" class="gc_option" data-option="{sOpt}"/><label id="option_label_{sOpt}" for="option_{sOpt}" data-l10n-id="option_{sOpt}">{dVars["dOptLabel"][sLang][sOpt][0]}</label></p>\n'
        sHTML += '</div>\n'
    return sHTML



















def createThunderbirdExtension (sLang, dVars, spLangPack):
    "create extension for Thunderbird"
    print("Building extension for Thunderbird")
    sExtensionName = dVars['tb_identifier'] + "-v" + dVars['version'] + '.xpi'
    spfZip = "_build/" + sExtensionName
    hZip = zipfile.ZipFile(spfZip, mode='w', compression=zipfile.ZIP_DEFLATED)
    _copyGrammalecteJSPackageInZipFile(hZip, spLangPack)
    for spf in ["LICENSE.txt", "LICENSE.fr.txt"]:
        hZip.write(spf)
    dVars = _createOptionsForThunderbird(dVars)
    helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/tb", "", dVars, True)
    hZip.close()
    spDebugProfile = dVars['win_tb_debug_extension_path']  if platform.system() == "Windows"  else dVars['linux_tb_debug_extension_path']
    helpers.unzip(spfZip, spDebugProfile)
    spfBetaExtension = dVars['win_tb_beta_extension_filepath']  if platform.system() == "Windows"  else dVars['linux_tb_beta_extension_filepath']
    #helpers.unzip(spfZip, spBetaProfile)
    file_util.copy_file(spfZip, spfBetaExtension)


def _createOptionsForThunderbird (dVars):
    dVars['sXULTabs'] = ""
    dVars['sXULTabPanels'] = ""
    # dialog options
    for sSection, lOpt in dVars['lStructOpt']:
................................................................................
        dVars['sXULTabPanels'] += '    </tabpanel>\n'
    # translation data
    for sLang in dVars['dOptLabel'].keys():
        dVars['gc_options_labels_'+sLang] = "\n".join( [ "<!ENTITY option.label." + sOpt + ' "' + dVars['dOptLabel'][sLang][sOpt][0] + '">'  for sOpt in dVars['dOptLabel'][sLang] ] )
    return dVars


def _copyGrammalecteJSPackageInZipFile (hZip, spLangPack, sAddPath=""):
    for sf in os.listdir("grammalecte-js"):
        if not os.path.isdir("grammalecte-js/"+sf):
            hZip.write("grammalecte-js/"+sf, sAddPath+"grammalecte-js/"+sf)
    for sf in os.listdir("grammalecte-js/graphspell"):
        if not os.path.isdir("grammalecte-js/graphspell/"+sf):
            hZip.write("grammalecte-js/graphspell/"+sf, sAddPath+"grammalecte-js/graphspell/"+sf)
    for sf in os.listdir("grammalecte-js/graphspell/_dictionaries"):
        if not os.path.isdir("grammalecte-js/graphspell/_dictionaries/"+sf):
            hZip.write("grammalecte-js/graphspell/_dictionaries/"+sf, sAddPath+"grammalecte-js/graphspell/_dictionaries/"+sf)
    for sf in os.listdir(spLangPack):
        if not os.path.isdir(spLangPack+"/"+sf):
            hZip.write(spLangPack+"/"+sf, sAddPath+spLangPack+"/"+sf)


def createNodeJSPackage (sLang):
    helpers.createCleanFolder("_build/nodejs/"+sLang)
    dir_util.copy_tree("gc_lang/"+sLang+"/nodejs/", "_build/nodejs/"+sLang)
    dir_util.copy_tree("grammalecte-js", "_build/nodejs/"+sLang+"/core/grammalecte")







|


|
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|

|
<

|





|
|
<
<
<







 







|


|


|


|
|
|
|






4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73



74
75
76
77
78
79
80
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import platform
import zipfile
from distutils import dir_util, file_util

import helpers


def build (sLang, dVars):
    "complementary build launched from make.py"
    createWebExtension(sLang, dVars)
    createThunderbirdExtension(sLang, dVars)
    createMailExtension(sLang, dVars)
    createNodeJSPackage(sLang)


def createWebExtension (sLang, dVars):
    "create Web-extension"
    print("Building WebExtension")
    helpers.createCleanFolder("_build/webext/"+sLang)
................................................................................
        for lLineOpt in lOpt:
            for sOpt in lLineOpt:
                sHTML += f'  <p><input type="checkbox" id="option_{sOpt}" class="gc_option" data-option="{sOpt}"/><label id="option_label_{sOpt}" for="option_{sOpt}" data-l10n-id="option_{sOpt}">{dVars["dOptLabel"][sLang][sOpt][0]}</label></p>\n'
        sHTML += '</div>\n'
    return sHTML


def createMailExtension (sLang, dVars):
    "create extension for Thunderbird (as MailExtension)"
    print("Building extension for Thunderbird (MailExtension)")
    spfZip = "_build/" + dVars['tb_identifier'] + "-v" + dVars['version'] + '.mailext.xpi'
    hZip = zipfile.ZipFile(spfZip, mode='w', compression=zipfile.ZIP_DEFLATED)
    _copyGrammalecteJSPackageInZipFile(hZip, sLang)
    for spf in ["LICENSE.txt", "LICENSE.fr.txt"]:
        hZip.write(spf)
    dVars = _createOptionsForThunderbird(dVars)
    helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/mailext", "", dVars, True)
    hZip.close()
    spExtension = dVars['win_tb_debug_extension_path']  if platform.system() == "Windows"  else dVars['linux_tb_debug_extension_path']
    file_util.copy_file(spfZip, spExtension + "/" + dVars['tb_identifier']+ ".xpi")  # Filename for TB is just <identifier.xpi>
    spExtension = dVars['win_tb_beta_extension_path']  if platform.system() == "Windows"  else dVars['linux_tb_beta_extension_path']
    file_util.copy_file(spfZip, spExtension + "/" + dVars['tb_identifier']+ ".xpi")  # Filename for TB is just <identifier.xpi>


def createThunderbirdExtension (sLang, dVars):
    "create extension for Thunderbird (as XUL addon)"
    print("Building extension for Thunderbird")
    spfZip = "_build/" + dVars['tb_identifier'] + "-v" + dVars['version'] + '.xpi'

    hZip = zipfile.ZipFile(spfZip, mode='w', compression=zipfile.ZIP_DEFLATED)
    _copyGrammalecteJSPackageInZipFile(hZip, sLang)
    for spf in ["LICENSE.txt", "LICENSE.fr.txt"]:
        hZip.write(spf)
    dVars = _createOptionsForThunderbird(dVars)
    helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/tb", "", dVars, True)
    hZip.close()
    #spDebugProfile = dVars['win_tb_debug_extension_path']  if platform.system() == "Windows"  else dVars['linux_tb_debug_extension_path']
    #helpers.unzip(spfZip, spDebugProfile)





def _createOptionsForThunderbird (dVars):
    dVars['sXULTabs'] = ""
    dVars['sXULTabPanels'] = ""
    # dialog options
    for sSection, lOpt in dVars['lStructOpt']:
................................................................................
        dVars['sXULTabPanels'] += '    </tabpanel>\n'
    # translation data
    for sLang in dVars['dOptLabel'].keys():
        dVars['gc_options_labels_'+sLang] = "\n".join( [ "<!ENTITY option.label." + sOpt + ' "' + dVars['dOptLabel'][sLang][sOpt][0] + '">'  for sOpt in dVars['dOptLabel'][sLang] ] )
    return dVars


def _copyGrammalecteJSPackageInZipFile (hZip, sLang, sAddPath=""):
    for sf in os.listdir("grammalecte-js"):
        if not os.path.isdir("grammalecte-js/"+sf):
            hZip.write("grammalecte-js/"+sf, sAddPath+"grammalecte/"+sf)
    for sf in os.listdir("grammalecte-js/graphspell"):
        if not os.path.isdir("grammalecte-js/graphspell/"+sf):
            hZip.write("grammalecte-js/graphspell/"+sf, sAddPath+"grammalecte/graphspell/"+sf)
    for sf in os.listdir("grammalecte-js/graphspell/_dictionaries"):
        if not os.path.isdir("grammalecte-js/graphspell/_dictionaries/"+sf):
            hZip.write("grammalecte-js/graphspell/_dictionaries/"+sf, sAddPath+"grammalecte/graphspell/_dictionaries/"+sf)
    for sf in os.listdir("grammalecte-js/"+sLang):
        if not os.path.isdir("grammalecte-js/"+sLang+"/"+sf):
            hZip.write("grammalecte-js/"+sLang+"/"+sf, sAddPath+"grammalecte/"+sLang+"/"+sf)


def createNodeJSPackage (sLang):
    helpers.createCleanFolder("_build/nodejs/"+sLang)
    dir_util.copy_tree("gc_lang/"+sLang+"/nodejs/", "_build/nodejs/"+sLang)
    dir_util.copy_tree("grammalecte-js", "_build/nodejs/"+sLang+"/core/grammalecte")

Changes to gc_lang/fr/config.ini.

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
..
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
lang = fr
lang_name = French
locales = fr_FR fr_BE fr_CA fr_CH fr_LU fr_BF fr_BJ fr_CD fr_CI fr_CM fr_MA fr_ML fr_MU fr_NE fr_RE fr_SN fr_TG
country_default = FR
name = Grammalecte
implname = grammalecte
# always use 3 numbers for version: x.y.z
version = 1.5.0
author = Olivier R.
provider = Grammalecte.net
link = https://grammalecte.net
description = Correcteur grammatical pour le français.
extras = README_fr.txt
logo = logo.png

# main dictionary
lexicon_src = lexicons/French.lex
dic_filenames = fr-allvars,fr-classic,fr-reform
dic_name = fr-allvars,fr-classic,fr-reform
................................................................................
# Finite state automaton compression: 1, 2 (experimental) or 3 (experimental)
fsa_method = 1
# stemming method: S for suffixes only, A for prefixes and suffixes
stemming_method = S

# LibreOffice
unopkg = C:/Program Files/LibreOffice/program/unopkg.com
oxt_version = 6.5
oxt_identifier = French.linguistic.resources.from.Dicollecte.by.OlivierR
oxt_update_info_URL = https://grammalecte.net/grammalecte/oxt/grammalecte.update.xml

# Firefox
fx_identifier = French-GC@grammalecte.net
fx_name = Grammalecte [fr]

................................................................................
linux_fx_dev_path = /usr/bin/firefox
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 (x86)\Mozilla Thunderbird (Beta)\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_filepath = D:\_temp\tb-beta.profile\extensions\French-GC-TB@grammalecte.net.xpi
linux_tb_beta_extension_filepath = ~/tb-beta.profile/extensions/French-GC-TB@grammalecte.net.xpi
# 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
# To launch Thunderbird with the profile debug, type:
#     	thunderbird -P debug







|



|







 







|







 







<
|


|
|
|
|







2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
..
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67
68
69
70
71
72
lang = fr
lang_name = French
locales = fr_FR fr_BE fr_CA fr_CH fr_LU fr_BF fr_BJ fr_CD fr_CI fr_CM fr_MA fr_ML fr_MU fr_NE fr_RE fr_SN fr_TG
country_default = FR
name = Grammalecte
implname = grammalecte
# always use 3 numbers for version: x.y.z
version = 1.6.0
author = Olivier R.
provider = Grammalecte.net
link = https://grammalecte.net
description = Correcteur grammatical, orthographique et typographique pour le français.
extras = README_fr.txt
logo = logo.png

# main dictionary
lexicon_src = lexicons/French.lex
dic_filenames = fr-allvars,fr-classic,fr-reform
dic_name = fr-allvars,fr-classic,fr-reform
................................................................................
# Finite state automaton compression: 1, 2 (experimental) or 3 (experimental)
fsa_method = 1
# stemming method: S for suffixes only, A for prefixes and suffixes
stemming_method = S

# LibreOffice
unopkg = C:/Program Files/LibreOffice/program/unopkg.com
oxt_version = 6.5.1
oxt_identifier = French.linguistic.resources.from.Dicollecte.by.OlivierR
oxt_update_info_URL = https://grammalecte.net/grammalecte/oxt/grammalecte.update.xml

# Firefox
fx_identifier = French-GC@grammalecte.net
fx_name = Grammalecte [fr]

................................................................................
linux_fx_dev_path = /usr/bin/firefox
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\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
linux_tb_debug_extension_path = ~/tb-debug.profile/extensions
win_tb_beta_extension_path = D:\_temp\tb-beta.profile\extensions
linux_tb_beta_extension_path = ~/tb-beta.profile/extensions
# 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
# To launch Thunderbird with the profile debug, type:
#     	thunderbird -P debug

Added gc_lang/fr/mailext/README.txt.





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

= GRAMMALECTE =

French grammar checker
By Olivier R. (olivier /at/ grammalecte /dot/ net)

Website: https://grammalecte.net/

License: GPL 3 -- http://www.gnu.org/copyleft/gpl.html

Grammalecte for Firefox is a derivative tool born from the version
for LibreOffice written in Python.

Written in JavaScript ES6/ES7.

Added gc_lang/fr/mailext/background.js.





















































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Background

"use strict";

// Draft for later

const oWorkerHandler = {
    xGCEWorker: null,

    nLastTimeWorkerResponse: 0,  // milliseconds since 1970-01-01

    oTask: {},

    start: function () {
        this.xGCEWorker = new Worker("gce_worker.js");
        this.xGCEWorker.onmessage = function (e) {
            // Messages received from the Worker
            // https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent
            try {
                this.nLastTimeWorkerResponse = Date.now();
                let {sActionDone, result, dInfo, bEnd, bError} = e.data;
                if (bError) {
                    console.log(result);
                    console.log(dInfo);
                    return;
                }
                switch (sActionDone) {
                    case "init":
                        storeGCOptions(result);
                        break;
                    case "parse":
                    case "parseAndSpellcheck":
                    case "parseAndSpellcheck1":
                    case "parseFull":
                    case "getListOfTokens":
                    case "getSpellSuggestions":
                    case "getVerb":
                        // send result to content script
                        if (typeof(dInfo.iReturnPort) === "number") {
                            let xPort = dConnx.get(dInfo.iReturnPort);
                            xPort.postMessage(e.data);
                        } else {
                            console.log("[background] don’t know where to send results");
                            console.log(e.data);
                        }
                        break;
                    case "textToTest":
                    case "fullTests":
                        // send result to panel
                        browser.runtime.sendMessage(e.data);
                        break;
                    case "getOptions":
                    case "getDefaultOptions":
                    case "resetOptions":
                        // send result to panel
                        storeGCOptions(result);
                        browser.runtime.sendMessage(e.data);
                        break;
                    case "setOptions":
                    case "setOption":
                        storeGCOptions(result);
                        break;
                    case "setDictionary":
                    case "setDictionaryOnOff":
                        //console.log("[background] " + sActionDone + ": " + result);
                        break;
                    default:
                        console.log("[background] Unknown command: " + sActionDone);
                        console.log(e.data);
                }
            }
            catch (error) {
                showError(error);
                console.log(e.data);
            }
        };
    },

    getTimeSinceLastResponse: function () {
        // result in seconds
        return Math.floor((Date.now() - this.nLastTimeWorkerResponse) / 1000);
    },

    restart: function (nDelay=5) {
        if (this.getTimeSinceLastResponse() <= nDelay) {
            console.log("Worker not restarted. Worked ", nDelay, " seconds ago.");
            return false;
        }
        if (this.xGCEWorker) {
            this.xGCEWorker.terminate();
        }
        this.start();
        oInitHandler.initGrammarChecker();
        sendCommandToAllTabs("workerRestarted");
        console.log("Worker restarted.");
        return true;
    },

    addTask: function () {
        //
    },

    closeTask: function () {
        //
    }
}


const oInitHandler = {

    initUIOptions: function () {
        browser.storage.local.get("ui_options").then(this._initUIOptions, showError);
        browser.storage.local.get("autorefresh_option").then(this._initUIOptions, showError);
    },

    initGrammarChecker: function () {
        browser.storage.local.get("gc_options").then(this._initGrammarChecker, showError);
        browser.storage.local.get("personal_dictionary").then(this._setSpellingDictionaries, showError);
        browser.storage.local.get("community_dictionary").then(this._setSpellingDictionaries, showError);
        browser.storage.local.get("sc_options").then(this._initSCOptions, showError);
    },

    _initUIOptions: function (oSavedOptions) {
        if (!oSavedOptions.hasOwnProperty("ui_options")) {
            browser.storage.local.set({"ui_options": {
                textarea: true,
                editablenode: true
            }});
        }
        if (!oSavedOptions.hasOwnProperty("autorefresh_option")) {
            browser.storage.local.set({"autorefresh_option": true});
        }
    },

    _initGrammarChecker: function (oSavedOptions) {
        try {
            let dOptions = (oSavedOptions.hasOwnProperty("gc_options")) ? oSavedOptions.gc_options : null;
            if (dOptions !== null && Object.getOwnPropertyNames(dOptions).length == 0) {
                console.log("# Error: the saved options was an empty object.");
                dOptions = null;
            }
            oWorkerHandler.xGCEWorker.postMessage({
                sCommand: "init",
                dParam: {sExtensionPath: browser.extension.getURL(""), dOptions: dOptions, sContext: "Firefox"},
                dInfo: {}
            });
        }
        catch (e) {
            console.log("initGrammarChecker failed");
            showError(e);
        }
    },

    _setSpellingDictionaries: function (oData) {
        if (oData.hasOwnProperty("community_dictionary")) {
            oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionary", dParam: { sDictionary: "community", oDict: oData["community_dictionary"] }, dInfo: {} });
        }
        if (oData.hasOwnProperty("personal_dictionary")) {
            oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionary", dParam: { sDictionary: "personal", oDict: oData["personal_dictionary"] }, dInfo: {} });
        }
    },

    _initSCOptions: function (oData) {
        if (!oData.hasOwnProperty("sc_options")) {
            browser.storage.local.set({"sc_options": {
                community: true,
                personal: true
            }});
            oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionaryOnOff", dParam: { sDictionary: "community", bActivate: true }, dInfo: {} });
            oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionaryOnOff", dParam: { sDictionary: "personal", bActivate: true }, dInfo: {} });
        } else {
            oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionaryOnOff", dParam: { sDictionary: "community", bActivate: oData.sc_options["community"] }, dInfo: {} });
            oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionaryOnOff", dParam: { sDictionary: "personal", bActivate: oData.sc_options["personal"] }, dInfo: {} });
        }
    }
}

// start the Worker for the GC
oWorkerHandler.start();

// init the options stuff and start the GC
oInitHandler.initUIOptions();
oInitHandler.initGrammarChecker();



/*
    Actions
*/

function storeGCOptions (dOptions) {
    if (dOptions instanceof Map) {
        dOptions = helpers.mapToObject(dOptions);
    }
    browser.storage.local.set({"gc_options": dOptions});
}


function showError (e) {
    console.error(e);
    //console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message);
}

Added gc_lang/fr/mailext/chrome.manifest.



















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
# https://developer.mozilla.org/en-US/docs/Chrome_Registration
content	grammarchecker	content/
content promiseworker ./worker/
resource grammalecte ./grammalecte/
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://messenger/content/customizeToolbar.xul	chrome://grammarchecker/content/overlay.css

Added gc_lang/fr/mailext/content/about.css.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* CSS */

.descr {
	font-size: 18px;
	font-weight: bold;
	text-align: center;
}

.stdlabel {
	font-size: 16px;
	text-align: center;
}

#website {
	font-size: 16px;
	font-weight: bold;
	color: hsl(210, 50%, 50%);
	text-align: center;
	cursor: pointer;
}

#contrib {
	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;
}

Added gc_lang/fr/mailext/content/about.js.







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// JavaScript

const Cc = Components.classes;
const Ci = Components.interfaces;
//const Cu = Components.utils;


function openInBrowserURL (sURL) {
    // method found in S3.Google.Translator
    try {
        openURL(sURL);
        // Works in overlay.js, but not here… Seems there is no documentation available about this feature on Mozilla.org
    }
    catch (e) {
        console.error(e);
        //Cu.reportError(e);
    }
}

function openInTabURL (sURL) {
    // method found in S3.Google.Translator
    try {
        let xWM = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
        let xWin = xWM.getMostRecentWindow("mail:3pane");
        let xTabmail = xWin.document.getElementById('tabmail');
        xWin.focus();
        if (xTabmail) {
            xTabmail.openTab('contentTab', { contentPage: sURL });
        }
    }
    catch (e) {
        console.error(e);
        //Cu.reportError(e);
    }
}

Added gc_lang/fr/mailext/content/about.xul.













































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://grammarchecker/content/about.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://grammarchecker/locale/about.dtd">

<dialog
  id="grammalecte-about-window"
  title="&windowtitle;"
  orient="vertical"
  buttons="accept"
  width="300"
  onload="document.getElementById('grammalecte-about-window').centerWindowOnScreen();"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <!-- Other elements go here -->
  <description  style="text-align: center;">
    <image src="chrome://grammarchecker/skin/logo120_text.png" />
  </description>
  <label class="descr">&description1;</label>
  <label class="descr">&description2;</label>
  <label class="stdlabel" value="&version; ${version}" />
  <label class="stdlabel" value="&license; GPL 3" />
  <label id="website" value="Site web" onclick="openInTabURL('https://grammalecte.net/?from=grammalecte-tb');" />

  <separator class="groove" orient="horizontal" style="margin: 10px;"/>

  <label class="stdlabel" value="&thanks;" />
  <image src="chrome://grammarchecker/skin/LaMouette_small.png" style="cursor: pointer;"
    onclick="openInTabURL('http://lamouette.org/?from=grammalecte-tb');" />
  <label class="stdlabel" value="&amp;" />
  <image src="chrome://grammarchecker/skin/Algoo_logo.png" style="cursor: pointer;"
    onclick="openInTabURL('https://www.algoo.fr/?from=grammalecte-tb');" />
  <label id="contrib" value="&contrib;"
    onclick="openInTabURL('https://grammalecte.net/#thanks');" />

  <script type="application/javascript" src="about.js"/>
</dialog>

Added gc_lang/fr/mailext/content/conjugueur.css.

































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* CSS */

#verb {
    width: 200px;
}

#verb_title {
    font-size: 24px;
    font-weight: bold;
    text-align: center;
    color: hsl(210, 50%, 50%);
}

#info {
    font-size: 16px;
    text-align: center;
    vertical-align: bottom;
}


.groupbox {
    margin-left: 5px;
    margin-bottom: 5px;
    width: 275px;
    border: 1px solid hsl(0, 0%, 90%);
    border-radius: 5px;
}

#smallnote {
    font-size: 10px;
    width: 250px;
}

.sub_header {
    color: hsl(0, 50%, 50%);
    font-size: 16px;
    font-weight: bold;
    border-bottom: 1px solid hsl(0, 0%, 80%);
}

label.temps {
    color: hsl(210, 50%, 50%);
    font-size: 13px;
    font-weight: bold;
}
label.cj {
    font-size: 11px;
}

Added gc_lang/fr/mailext/content/conjugueur.js.





















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// JavaScript

//const Cu = Components.utils;
//const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});
//const conj = require("resource://grammalecte/fr/conj.js");


let oConj = {
    init: function () {
        console.log("Init conjugueur");
        try {
            // button
            document.getElementById('conjugate').addEventListener("click", (xEvent) => {
                this.getVerbAndConjugate();
            });
            // text field
            document.getElementById('verb').addEventListener("change", (xEvent) => {
                this.getVerbAndConjugate();
            });
            // options
            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) {
            console.error(e);
        }
        this.conjugate("être");
    },

    oVerb: null,

    getVerbAndConjugate: function () {
        this.conjugate(document.getElementById('verb').value);
    },

    conjugate: function (sVerb) {
        try {
            document.getElementById('oneg').checked = false;
            document.getElementById('opro').checked = false;
            document.getElementById('oint').checked = false;
            document.getElementById('otco').checked = false;
            document.getElementById('ofem').checked = false;
            document.getElementById('smallnote').hidden = true;

            // request analyzing
            sVerb = sVerb.trim().toLowerCase().replace(/’/g, "'").replace(/  +/g, " ");
            if (sVerb) {
                if (sVerb.startsWith("ne pas ")) {
                    document.getElementById('oneg').checked = true;
                    sVerb = sVerb.slice(7);
                }
                if (sVerb.startsWith("se ")) {
                    document.getElementById('opro').checked = true;
                    sVerb = sVerb.slice(3);
                } else if (sVerb.startsWith("s'")) {
                    document.getElementById('opro').checked = true;
                    sVerb = sVerb.slice(2);
                }
                if (sVerb.endsWith("?")) {
                    document.getElementById('oint').checked = true;
                    sVerb = sVerb.slice(0,-1).trim();
                }

                if (!conj.isVerb(sVerb)) {
                    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 Verb(sVerb);
                    document.getElementById('info').textContent = this.oVerb.sInfo;
                    document.getElementById('opro').label = this.oVerb.sProLabel;
                    if (this.oVerb.bUncomplete) {
                        document.getElementById('opro').checked = false;
                        document.getElementById('opro').disabled = true;
                        document.getElementById('opro').style = "color: #CCC;";
                        document.getElementById('otco').checked = false;
                        document.getElementById('otco').disabled = true;
                        document.getElementById('otco').style = "color: #CCC;";
                        document.getElementById('smallnote').hidden = false;
                    } else {
                        document.getElementById('otco').disabled = false;
                        document.getElementById('otco').style = "color: #000;";
                        if (this.oVerb.nPronominable == 0) {
                            document.getElementById('opro').checked = false;
                            document.getElementById('opro').disabled = false;
                            document.getElementById('opro').style = "color: #000;";
                        } else if (this.oVerb.nPronominable == 1) {
                            document.getElementById('opro').checked = true;
                            document.getElementById('opro').disabled = true;
                            document.getElementById('opro').style = "color: #CCC;";
                        } else { // -1 or 1 or error
                            document.getElementById('opro').checked = false;
                            document.getElementById('opro').disabled = true;
                            document.getElementById('opro').style = "color: #CCC;";
                        }
                        document.getElementById('smallnote').hidden = true;
                    }
                    this._displayResults();
                }
            }
        }
        catch (e) {
            console.error(e);
        }
    },

    _displayResults: function () {
        if (this.oVerb === null) {
            return;
        }
        try {
            let bPro = document.getElementById('opro').checked;
            let bNeg = document.getElementById('oneg').checked;
            let bTCo = document.getElementById('otco').checked;
            let bInt = document.getElementById('oint').checked;
            let bFem = document.getElementById('ofem').checked;
            let oConjTable = this.oVerb.createConjTable(bPro, bNeg, bTCo, bInt, bFem);
            document.getElementById('verb').Text = "";
            // infinitif
            document.getElementById('infi').textContent = oConjTable["infi"] || " "; // something or nbsp
            // participe présent
            document.getElementById('ppre').textContent = oConjTable["ppre"] || " ";
            // participes passés
            document.getElementById('ppas1').textContent = oConjTable["ppas1"] || " ";
            document.getElementById('ppas2').textContent = oConjTable["ppas2"] || " ";
            document.getElementById('ppas3').textContent = oConjTable["ppas3"] || " ";
            document.getElementById('ppas4').textContent = oConjTable["ppas4"] || " ";
            // impératif
            document.getElementById('impe_temps').textContent = oConjTable["t_impe"] || " ";
            document.getElementById('impe1').textContent = oConjTable["impe1"] || " ";
            document.getElementById('impe2').textContent = oConjTable["impe2"] || " ";
            document.getElementById('impe3').textContent = oConjTable["impe3"] || " ";
            // présent
            document.getElementById('ipre_temps').textContent = oConjTable["t_ipre"] || " ";
            document.getElementById('ipre1').textContent = oConjTable["ipre1"] || " ";
            document.getElementById('ipre2').textContent = oConjTable["ipre2"] || " ";
            document.getElementById('ipre3').textContent = oConjTable["ipre3"] || " ";
            document.getElementById('ipre4').textContent = oConjTable["ipre4"] || " ";
            document.getElementById('ipre5').textContent = oConjTable["ipre5"] || " ";
            document.getElementById('ipre6').textContent = oConjTable["ipre6"] || " ";
            // imparfait
            document.getElementById('iimp_temps').textContent = oConjTable["t_iimp"] || " ";
            document.getElementById('iimp1').textContent = oConjTable["iimp1"] || " ";
            document.getElementById('iimp2').textContent = oConjTable["iimp2"] || " ";
            document.getElementById('iimp3').textContent = oConjTable["iimp3"] || " ";
            document.getElementById('iimp4').textContent = oConjTable["iimp4"] || " ";
            document.getElementById('iimp5').textContent = oConjTable["iimp5"] || " ";
            document.getElementById('iimp6').textContent = oConjTable["iimp6"] || " ";
            // passé simple
            document.getElementById('ipsi_temps').textContent = oConjTable["t_ipsi"] || " ";
            document.getElementById('ipsi1').textContent = oConjTable["ipsi1"] || " ";
            document.getElementById('ipsi2').textContent = oConjTable["ipsi2"] || " ";
            document.getElementById('ipsi3').textContent = oConjTable["ipsi3"] || " ";
            document.getElementById('ipsi4').textContent = oConjTable["ipsi4"] || " ";
            document.getElementById('ipsi5').textContent = oConjTable["ipsi5"] || " ";
            document.getElementById('ipsi6').textContent = oConjTable["ipsi6"] || " ";
            // futur
            document.getElementById('ifut_temps').textContent = oConjTable["t_ifut"] || " ";
            document.getElementById('ifut1').textContent = oConjTable["ifut1"] || " ";
            document.getElementById('ifut2').textContent = oConjTable["ifut2"] || " ";
            document.getElementById('ifut3').textContent = oConjTable["ifut3"] || " ";
            document.getElementById('ifut4').textContent = oConjTable["ifut4"] || " ";
            document.getElementById('ifut5').textContent = oConjTable["ifut5"] || " ";
            document.getElementById('ifut6').textContent = oConjTable["ifut6"] || " ";
            // Conditionnel
            document.getElementById('conda_temps').textContent = oConjTable["t_conda"] || " ";
            document.getElementById('conda1').textContent = oConjTable["conda1"] || " ";
            document.getElementById('conda2').textContent = oConjTable["conda2"] || " ";
            document.getElementById('conda3').textContent = oConjTable["conda3"] || " ";
            document.getElementById('conda4').textContent = oConjTable["conda4"] || " ";
            document.getElementById('conda5').textContent = oConjTable["conda5"] || " ";
            document.getElementById('conda6').textContent = oConjTable["conda6"] || " ";
            document.getElementById('condb_temps').textContent = oConjTable["t_condb"] || " ";
            document.getElementById('condb1').textContent = oConjTable["condb1"] || " ";
            document.getElementById('condb2').textContent = oConjTable["condb2"] || " ";
            document.getElementById('condb3').textContent = oConjTable["condb3"] || " ";
            document.getElementById('condb4').textContent = oConjTable["condb4"] || " ";
            document.getElementById('condb5').textContent = oConjTable["condb5"] || " ";
            document.getElementById('condb6').textContent = oConjTable["condb6"] || " ";
            // subjonctif présent
            document.getElementById('spre_temps').textContent = oConjTable["t_spre"] || " ";
            document.getElementById('spre1').textContent = oConjTable["spre1"] || " ";
            document.getElementById('spre2').textContent = oConjTable["spre2"] || " ";
            document.getElementById('spre3').textContent = oConjTable["spre3"] || " ";
            document.getElementById('spre4').textContent = oConjTable["spre4"] || " ";
            document.getElementById('spre5').textContent = oConjTable["spre5"] || " ";
            document.getElementById('spre6').textContent = oConjTable["spre6"] || " ";
            // subjonctif imparfait
            document.getElementById('simp_temps').textContent = oConjTable["t_simp"] || " ";
            document.getElementById('simp1').textContent = oConjTable["simp1"] || " ";
            document.getElementById('simp2').textContent = oConjTable["simp2"] || " ";
            document.getElementById('simp3').textContent = oConjTable["simp3"] || " ";
            document.getElementById('simp4').textContent = oConjTable["simp4"] || " ";
            document.getElementById('simp5').textContent = oConjTable["simp5"] || " ";
            document.getElementById('simp6').textContent = oConjTable["simp6"] || " ";
        }
        catch (e) {
            console.error(e);
        }
    }
};

conj.init(helpers.loadFile("resource://grammalecte/fr/conj_data.json"));
oConj.init();

Added gc_lang/fr/mailext/content/conjugueur.xul.





































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://grammarchecker/content/conjugueur.css" type="text/css"?>


<dialog
  id="grammalecte-conjugueur-window"
  title="Grammalecte · Conjugueur…"
  orient="vertical"
  width="580"
  buttons="extra1"
  buttonlabelextra1="Conjuguer"
  ondialogextra1="oConj.getVerbAndConjugate();"
  defaultButton="extra1"
  onload="document.getElementById('grammalecte-conjugueur-window').centerWindowOnScreen();"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">


  <!-- Other elements go here -->
  <box>
    <spacer flex="1"/>
    <image src="chrome://grammarchecker/skin/grammarcheck.png" />
    <textbox id="verb" value="" />
    <button id="conjugate" label="Conjuguer" />
    <spacer flex="1"/>
  </box>

  <box>
    <spacer flex="1"/>
    <checkbox id="oneg" label="Négation" />
    <checkbox id="opro" label="Pronominal" />
    <checkbox id="ofem" label="Féminin" />
    <checkbox id="oint" label="Interrogatif" />
    <checkbox id="otco" label="Temps composés" />
    <spacer flex="1"/>
  </box>

  <box>
    <spacer flex="1"/>
    <box orient="vertical">
      <label id="verb_title">.</label>
      <label id="info">.</label>
    </box>
    <spacer flex="1"/>
  </box>

  <columns>
    <column>
      <groupbox class="groupbox" id="infinitif">
        <label class="sub_header">Infinitif</label>
        <label id="infi" class="cj">.</label>
      </groupbox>

      <groupbox class="groupbox" id="imperatif">
        <label class="sub_header">Impératif</label>
        <label id="impe_temps" class="temps">Présent</label>
        <label id="impe1" class="cj">.</label>
        <label id="impe2" class="cj">.</label>
        <label id="impe3" class="cj">.</label>
      </groupbox>
    </column>

    <column>
      <groupbox class="groupbox" id="partpre">
        <label class="sub_header">Participe présent</label>
        <label id="ppre" class="cj">.</label>
      </groupbox>

      <groupbox class="groupbox" id="partpas">
        <label class="sub_header">Participes passés</label>
        <label id="ppas1" class="cj">.</label>
        <label id="ppas2" class="cj">.</label>
        <label id="ppas3" class="cj">.</label>
        <label id="ppas4" class="cj">.</label>
      </groupbox>
    </column>
  </columns>

  <columns>
    <column>
      <groupbox class="groupbox" id="indicatif">
        <label class="sub_header">Indicatif</label>
        <label id="ipre_temps" class="temps">Présent</label>
        <label id="ipre1" class="cj">.</label>
        <label id="ipre2" class="cj">.</label>
        <label id="ipre3" class="cj">.</label>
        <label id="ipre4" class="cj">.</label>
        <label id="ipre5" class="cj">.</label>
        <label id="ipre6" class="cj">.</label>

        <label id="iimp_temps" class="temps">Imparfait</label>
        <label id="iimp1" class="cj">.</label>
        <label id="iimp2" class="cj">.</label>
        <label id="iimp3" class="cj">.</label>
        <label id="iimp4" class="cj">.</label>
        <label id="iimp5" class="cj">.</label>
        <label id="iimp6" class="cj">.</label>

        <label id="ipsi_temps" class="temps">Passé simple</label>
        <label id="ipsi1" class="cj">.</label>
        <label id="ipsi2" class="cj">.</label>
        <label id="ipsi3" class="cj">.</label>
        <label id="ipsi4" class="cj">.</label>
        <label id="ipsi5" class="cj">.</label>
        <label id="ipsi6" class="cj">.</label>

        <label id="ifut_temps" class="temps">Futur</label>
        <label id="ifut1" class="cj">.</label>
        <label id="ifut2" class="cj">.</label>
        <label id="ifut3" class="cj">.</label>
        <label id="ifut4" class="cj">.</label>
        <label id="ifut5" class="cj">.</label>
        <label id="ifut6" class="cj">.</label>
      </groupbox>
      <description id="smallnote">Ce verbe n’a pas encore été vérifié. C’est pourquoi les options “pronominal” et “temps composés” sont désactivées.</description>
    </column>

    <column>
      <groupbox class="groupbox" id="subjonctif">
        <label class="sub_header">Subjonctif</label>
        <label id="spre_temps" class="temps">Présent</label>
        <label id="spre1" class="cj">.</label>
        <label id="spre2" class="cj">.</label>
        <label id="spre3" class="cj">.</label>
        <label id="spre4" class="cj">.</label>
        <label id="spre5" class="cj">.</label>
        <label id="spre6" class="cj">.</label>

        <label id="simp_temps" class="temps">Imparfait</label>
        <label id="simp1" class="cj">.</label>
        <label id="simp2" class="cj">.</label>
        <label id="simp3" class="cj">.</label>
        <label id="simp4" class="cj">.</label>
        <label id="simp5" class="cj">.</label>
        <label id="simp6" class="cj">.</label>
      </groupbox>

      <groupbox class="groupbox" id="conditionnel">
        <label class="sub_header">Conditionnel</label>
        <label id="conda_temps" class="temps">Présent</label>
        <label id="conda1" class="cj">.</label>
        <label id="conda2" class="cj">.</label>
        <label id="conda3" class="cj">.</label>
        <label id="conda4" class="cj">.</label>
        <label id="conda5" class="cj">.</label>
        <label id="conda6" class="cj">.</label>

        <label id="condb_temps" class="temps">.</label>
        <label id="condb1" class="cj">.</label>
        <label id="condb2" class="cj">.</label>
        <label id="condb3" class="cj">.</label>
        <label id="condb4" class="cj">.</label>
        <label id="condb5" class="cj">.</label>
        <label id="condb6" class="cj">.</label>
      </groupbox>
    </column>
  </columns>

  <script type="application/javascript" src="resource://grammalecte/graphspell/helpers.js"/>
  <script type="application/javascript" src="resource://grammalecte/fr/conj.js"/>
  <script type="application/javascript" src="conjugueur.js"/>
</dialog>

Added gc_lang/fr/mailext/content/editor.js.



































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// JavaScript

class Editor {

    constructor (sLang) {
        this.xEditor = GetCurrentEditor();
        this.lNode = [];
        this.lParsableNodes = ["P", "LI"];
        this.lRootNodes = ["DIV", "UL", "OL"];
    };

    _getTextFromNode (xNode) {
        if ("innerHTML" in xNode) {
            return xNode.innerHTML;
        } else {
            return xNode.textContent;
        }
    };

    * _getParsableNodes (xRootNode=this.xEditor.rootElement) {
        // recursive function
        try {
            for (let xNode of xRootNode.childNodes) {
                if (xNode.className !== "moz-cite-prefix" && xNode.tagName !== "BLOCKQUOTE"
                    && (xNode.nodeType == Node.TEXT_NODE || (xNode.nodeType == Node.ELEMENT_NODE && !xNode.textContent.startsWith(">")))
                    && xNode.textContent !== "") {
                    if (xNode.tagName === undefined) {
                        if (!prefs.getBoolPref("bCheckSignature") && xNode.textContent.startsWith("-- ")) {
                            break;
                        }
                        yield xNode;
                    } else if (this.lParsableNodes.includes(xNode.tagName)) {
                        yield xNode;
                    } else if (this.lRootNodes.includes(xNode.tagName)) {
                        yield* this._getParsableNodes(xNode);
                    }
                }
            }
        } catch (e) {
            console.error(e);
            // Cu.reportError(e);
        }
    };

    * getParagraphs () {
        try {
            let i = 0;
            for (let xNode of this._getParsableNodes()) {
                this.lNode.push(xNode);
                yield [i, this._getTextFromNode(xNode)];
                i += 1;
            }
        } catch (e) {
            console.error(e);
            // Cu.reportError(e);
        }
    };

    getContent () {
        try {
            let sContent = "";
            for (let [i, sHTML] of this.getParagraphs()) {
                if (sContent.length > 0) {
                    sContent += "\n";
                }
                sContent += sHTML;
            }
            return sContent;
        } catch (e) {
            console.error(e);
            // Cu.reportError(e);
        }
    };

    getParagraph (iPara) {
        try {
            return this._getTextFromNode(this.lNode[iPara]);
        } catch (e) {
            console.error(e);
            // Cu.reportError(e);
        }
    };

    writeParagraph (iPara, sText) {
        try {
            let xNode = this.lNode[iPara];
            if ("innerHTML" in xNode) {
                xNode.innerHTML = sText;
            } else {
                xNode.textContent = sText;
            }
        } catch (e) {
            console.error(e);
            // Cu.reportError(e);
        }
    };

    changeParagraph (iPara, sModif, iStart, iEnd) {
        let sText = this.getParagraph(iPara);
        this.writeParagraph(iPara, sText.slice(0, iStart) + sModif + sText.slice(iEnd));
    };

    getLangFromSpellChecker () {
        try {
            let gSpellChecker = this.xEditor.getInlineSpellChecker(true);
            let sLang = gSpellChecker.spellChecker.GetCurrentDictionary();
            return sLang.slice(0, 2);
        } catch (e) {
            console.error(e);
            // Cu.reportError(e);
        }
    };
}

Added gc_lang/fr/mailext/content/file_handler.js.









































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// JavaScript

"use strict";

// Assuming that Cc, Ci and Cu are already loaded

const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm", {});

//ChromeUtils.import("resource://gre/modules/osfile.jsm")

const oFileHandler = {
    // https://developer.mozilla.org/fr/docs/Mozilla/JavaScript_code_modules/OSFile.jsm/OS.File_for_the_main_thread

    xDataFolder: null,

    prepareDataFolder: function () {
        let xDirectoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
        // this is a reference to the profile dir (ProfD) now.
        let xExtFolder = xDirectoryService.get("ProfD", Ci.nsIFile);
        xExtFolder.append("grammalecte-data");
        if (!xExtFolder.exists() || !xExtFolder.isDirectory()) {
            // read and write permissions to owner and group, read-only for others.
            xExtFolder.create(Ci.nsIFile.DIRECTORY_TYPE, 774);
        }
        this.xDataFolder = xExtFolder;
    },

    createPathFileName: function (sFilename) {
        let spfDest = this.xDataFolder.path;
        spfDest += (/^[A-Z]:/.test(this.xDataFolder.path)) ? "\\" + sFilename : "/" + sFilename;
        return spfDest;
    },

    loadFile: async function (sFilename) {
        if (!this.xDataFolder) {
            this.prepareDataFolder();
        }
        try {
            let array = await OS.File.read(this.createPathFileName(sFilename));
            let xDecoder = new TextDecoder();
            return xDecoder.decode(array);
        }
        catch (e) {
            console.error(e);
            return null;
        }
    },

    loadAs: function (callback) {
        let xFilePicker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
        xFilePicker.init(window, "Charger fichier", Ci.nsIFilePicker.modeOpen);
        xFilePicker.appendFilters(Ci.nsIFilePicker.filterAll | Ci.nsIFilePicker.filterText);
        xFilePicker.open(async function (nReturnValue) {
            if (nReturnValue == Ci.nsIFilePicker.returnOK || nReturnValue == Ci.nsIFilePicker.returnReplace) {
                console.log(xFilePicker.file.path);
                try {
                    let array = await OS.File.read(xFilePicker.file.path);
                    let xDecoder = new TextDecoder();
                    callback(xDecoder.decode(array));
                }
                catch (e) {
                    console.error(e);
                    callback(null);
                }
            }
        });
    },

    saveFile: function (sFilename, sData) {
        if (!this.xDataFolder) {
            this.prepareDataFolder();
        }
        let xEncoder = new TextEncoder();
        let xEncodedRes = xEncoder.encode(sData);
        console.log("save dictionary: " + this.createPathFileName(sFilename));
        OS.File.writeAtomic(this.createPathFileName(sFilename), xEncodedRes);
        //OS.File.writeAtomic(this.createPathFileName(sFilename), xEncodedRes, {tmpPath: "file.txt.tmp"}); // error with a temporary file (can’t move it)
    },

    deleteFile: function (sFilename) {
        if (!this.xDataFolder) {
            this.prepareDataFolder();
        }
        OS.File.remove(this.createPathFileName(sFilename), {ignoreAbsent: true});
    },

    saveAs: function (sData) {
        // save anywhere with file picker
        let xFilePicker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
        xFilePicker.init(window, "Enregistrer sous", Ci.nsIFilePicker.modeSave);
        xFilePicker.appendFilters(Ci.nsIFilePicker.filterAll | Ci.nsIFilePicker.filterText);
        xFilePicker.open(function (nReturnValue) {
            if (nReturnValue == Ci.nsIFilePicker.returnOK || nReturnValue == Ci.nsIFilePicker.returnReplace) {
                let xEncoder = new TextEncoder();
                let xEncodedRes = xEncoder.encode(sData);
                OS.File.writeAtomic(xFilePicker.file.path, xEncodedRes, {tmpPath: "file.txt.tmp"});
            }
        });
    }
}

Added gc_lang/fr/mailext/content/gc_options.css.





































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* CSS */

.section {
	font-size: 16px;
	font-weight: bold;
	color: hsl(210, 50%, 50%);
}

.dialogheader-title {
  margin: 5px;
  padding: 5px 8px;
  border: 1px solid hsl(210, 50%, 80%);
  background-color: hsl(210, 50%, 50%);
  color: hsl(210, 10%, 90%);
  font-size: larger;
  font-weight: bold;
}

Added gc_lang/fr/mailext/content/gc_options.js.











































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// JavaScript

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 = {
    oOptions: null,
    load: function () {
        this._setDialogOptions(false);
        this.listen();
    },
    listen: function () {
        document.addEventListener("dialogaccept", (event) => {
            this.save();
        });
        document.addEventListener("dialogextra1", (event) => {
            this.reset();
        });
    },
    _setDialogOptions: function (bDefaultOptions=false) {
        try {
            sOptions = bDefaultOptions ? prefs.getCharPref("sGCDefaultOptions") : prefs.getCharPref("sGCOptions");
            this.oOptions = JSON.parse(sOptions);
            for (let sParam in this.oOptions) {
                if (document.getElementById("option_"+sParam) !== null) {
                    document.getElementById("option_"+sParam).checked = this.oOptions[sParam];
                }
            }
        }
        catch (e) {
            console.error(e);
        }
    },
    save: function () {
        try {
            for (let xNode of document.getElementsByClassName("option")) {
                this.oOptions[xNode.id.slice(7)] = xNode.checked;
            }
            prefs.setCharPref("sGCOptions", JSON.stringify(this.oOptions));
        }
        catch (e) {
            console.error(e);
        }
    },
    reset: function () {
        this._setDialogOptions(true);
    }
}

oOptControl.load();

Added gc_lang/fr/mailext/content/gc_options.xul.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://grammarchecker/content/gc_options.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://grammarchecker/locale/gc_options.dtd">

<dialog
  id="grammalecte-gcoptions-window"
  title="&window.title;"
  orient="vertical"
  buttons="accept, cancel, extra1"
  buttonlabelextra1="&defaultbutton.label;"
  defaultButton="accept"
  width="400"
  onload="document.getElementById('grammalecte-gcoptions-window').centerWindowOnScreen();"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <!-- Other elements go here -->

  <div class="dialogheader-title">&dialogheader.label;</div>

  <tabbox id="tabs_options" selectedIndex="0">
    <tabs>
${sXULTabs}
    </tabs>
    <tabpanels>
${sXULTabPanels}
    </tabpanels>
  </tabbox>

  <script type="application/javascript" src="gc_options.js" />

</dialog>

Added gc_lang/fr/mailext/content/lex_editor.css.













































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* CSS */

button {
    font-weight: bold;
}

.dialogheader-title {
  margin: 5px;
  padding: 5px 8px;
  border: 1px solid hsl(210, 50%, 80%);
  background-color: hsl(210, 50%, 50%);
  color: hsl(210, 10%, 90%);
  font-size: larger;
  font-weight: bold;
}

/*
    Dictionary
*/
.dictionary {
    font-size: 18px;
    font-weight: bold;
    color: hsl(210, 50%, 50%);
}
.dic_text {
    font-size: 18px;
    color: hsl(210, 50%, 50%);
}
.align_right {
    text-align: right;
}


/*
    Add new words
*/
.section {
    font-size: 16px;
    font-weight: bold;
    color: hsl(210, 50%, 50%);
}

.subsection {
    font-size: 14px;
    font-weight: bold;
    color: hsl(210, 50%, 50%);
}

.option {
    width: 120px;
    font-size: 12px;
}

.m_left {
    margin-left: 20px;
}

.m_left2 {
    margin-left: 40px;
}

.v_usage {
    width: 150px;
}
.v_textbox {
    width: 200px;
}

.other_label {
    width: 80px;
}
.other_textbox {
    width: 250px;
}

#generated_words_table {
    width: 400px;
    height: 600px;
}

.delete_entry {
    color: hsl(0, 100%, 50%);
    font-weight: bold;
}

/*
    Lexicon tab
*/

#lexicon_table {
    width: 600px;
    height: 650px;
}

.listheader {
    padding: 2px 0;
    font-weight: bold;
    background-color: hsl(0, 0%, 95%);
    border-bottom: 1px solid hsl(0, 0%, 50%);
}
.info_label {
    width: 120px;
}
.data {
    width: 90px;
}

/*
    Search tab
*/

#search_table {
    width: 600px;
    height: 650px;
}

.bold {
    font-weight: bold;
}
description {
    width: 200px;
}

/*
    Informations tab
*/

#tags_table {
    width: 600px;
    height: 650px;
    font-family: "Courier new", "Ubuntu Mono", Courier, Consolas, monospace;
}


Added gc_lang/fr/mailext/content/lex_editor.js.





























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
// 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.");


/*
    Common functions
*/

function showError (e) {
    Cu.reportError(e);
    console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message);
}

function createNode  (sType, oAttr) {
    try {
        let xNode = document.createElement(sType);
        for (let sParam in oAttr) {
            xNode.setAttribute(sParam, oAttr[sParam]);
        }
        return xNode;
    }
    catch (e) {
        showError(e);
    }
}

function enableElement (sElemId) {
    if (document.getElementById(sElemId)) {
        document.getElementById(sElemId).disabled = false;
    } else {
        console.log("HTML node named <" + sElemId + "> not found.")
    }
}

function disableElement (sElemId) {
    if (document.getElementById(sElemId)) {
        document.getElementById(sElemId).disabled = true;
    } else {
        console.log("HTML node named <" + sElemId + "> not found.")
    }
}



class Table {

    constructor (sNodeId, lColumn, lColumnWidth, sProgressBarId, sResultId="") {
        this.sNodeId = sNodeId;
        this.xTable = document.getElementById(sNodeId);
        this.nColumn = lColumn.length;
        this.lColumn = lColumn;
        this.lColumnWidth = lColumnWidth;
        this.xProgressBar = document.getElementById(sProgressBarId);
        this.xNumEntry = sResultId ? document.getElementById(sResultId) : null;
        this.iEntryIndex = 0;
        this.lEntry = [];
        this.nEntry = 0
        this._createHeader();
    }

    _createHeader () {
        let xListheadNode = createNode("richlistitem", { class: "listheader" });
        for (let i=0;  i < this.lColumn.length;  i++) {
            xListheadNode.appendChild(createNode("label", { value: this.lColumn[i], width: this.lColumnWidth[i] }));
        }
        this.xTable.appendChild(xListheadNode);
    }

    clear () {
        while (this.xTable.firstChild) {
            this.xTable.removeChild(this.xTable.firstChild);
        }
        this.iEntryIndex = 0;
        this._createHeader();
    }

    fill (lFlex) {
        this.clear();
        if (lFlex.length > 0) {
            this.xProgressBar.max = lFlex.length;
            this.xProgressBar.value = 1;
            for (let lData of lFlex) {
                this._addRow(lData);
                this.xProgressBar.value += 1;
            }
            this.xProgressBar.value = this.xProgressBar.max;
            window.setTimeout(() => { this.xProgressBar.value = 0; }, 3000);
        }
        this.lEntry = lFlex;
        this.nEntry = lFlex.length;
        this.showEntryNumber();
    }

    addEntries (lFlex) {
        this.lEntry.push(...lFlex);
        for (let lData of lFlex) {
            this._addRow(lData);
        }
        this.nEntry += lFlex.length;
        this.showEntryNumber();
    }

    showEntryNumber () {
        if (this.xNumEntry) {
            this.xNumEntry.value = this.nEntry;
        }
    }

    _addRow (lData) {
        let xRowNode = createNode("richlistitem", { id: this.sNodeId + "_item_" + this.iEntryIndex, value: this.iEntryIndex });
        for (let i=0;  i < lData.length;  i++) {
            xRowNode.appendChild(createNode("label", { class: "listcell", value: lData[i], width: this.lColumnWidth[i] }));
        }
        this.xTable.appendChild(xRowNode);
        this.iEntryIndex += 1;
    }

    deleteSelection () {
        for (let xItem of this.xTable.selectedItems) {
            this.lEntry[parseInt(xItem.value)] = null;
            xItem.style.display = "none";
            this.nEntry -= 1;
        }
        this.showEntryNumber();
    }

    getEntries () {
        return this.lEntry.filter((e) => e !== null);
    }
}


const oGenerator = {

    sLemma: "",

    cMainTag: "",

    lFlexion: [],

    listen: function () {
        document.getElementById("lemma").addEventListener("keyup", () => { this.update(); }, false);
        // nom commun
        document.getElementById("tag_N").addEventListener("click", () => { this.update("N"); }, false);
        document.getElementById("nom_adj").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("nom").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("adj").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("N_epi").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("N_mas").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("N_fem").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("N_s").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("N_x").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("N_inv").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("lemma2").addEventListener("keyup", () => { this.update(); }, false);
        // nom propre
        document.getElementById("tag_M").addEventListener("click", () => { this.update("M"); }, false);
        document.getElementById("M1").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("M2").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("MP").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("M_epi").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("M_mas").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("M_fem").addEventListener("click", () => { this.update(); }, false);
        // verbe
        document.getElementById("tag_V").addEventListener("click", () => { this.update("V"); }, false);
        document.getElementById("v_i").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("v_t").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("v_n").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("v_p").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("v_m").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("v_ae").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("v_aa").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("v_ppas").addEventListener("click", () => { this.update(); }, false);
        document.getElementById("verbe_modele").addEventListener("keyup", () => { this.update(); }, false);
        // adverbe
        document.getElementById("tag_W").addEventListener("click", () => { this.update("W"); }, false);
        // autre
        document.getElementById("tag_X").addEventListener("click", () => { this.update("X"); }, false);
        document.getElementById("flexion").addEventListener("keyup", () => { this.update(); }, false);
        document.getElementById("tags").addEventListener("keyup", () => { this.update(); }, false);
        // ajout
        document.getElementById("add_to_lexicon").addEventListener("click", () => { this.addToLexicon(); }, false);
        document.getElementById("delete_selection").addEventListener("click", () => { oGenWordsTable.deleteSelection(); }, false);
    },

    clear: function () {
        try {
            // nom commun
            document.getElementById("tag_N").checked = false;
            document.getElementById("nom_adj").checked = false;
            document.getElementById("nom").checked = false;
            document.getElementById("adj").checked = false;
            document.getElementById("N_epi").checked = false;
            document.getElementById("N_mas").checked = false;
            document.getElementById("N_fem").checked = false;
            document.getElementById("N_s").checked = false;
            document.getElementById("N_x").checked = false;
            document.getElementById("N_inv").checked = false;
            document.getElementById("lemma2").value = "";
            // nom propre
            document.getElementById("tag_M").checked = false;
            document.getElementById("M1").checked = false;
            document.getElementById("M2").checked = false;
            document.getElementById("MP").checked = false;
            document.getElementById("M_epi").checked = false;
            document.getElementById("M_mas").checked = false;
            document.getElementById("M_fem").checked = false;
            // verbe
            document.getElementById("tag_V").checked = false;
            document.getElementById("v_i").checked = false;
            document.getElementById("v_t").checked = false;
            document.getElementById("v_n").checked = false;
            document.getElementById("v_p").checked = false;
            document.getElementById("v_m").checked = false;
            document.getElementById("v_ae").checked = false;
            document.getElementById("v_aa").checked = false;
            document.getElementById("v_ppas").checked = false;
            document.getElementById("verbe_modele").value = "";
            // adverbe
            document.getElementById("tag_W").checked = false;
            // autre
            document.getElementById("tag_X").checked = false;
            document.getElementById("flexion").value = "";
            document.getElementById("tags").value = "";
        }
        catch (e) {
            showError(e);
        }
    },

    lTag: ["N", "M", "V", "W", "X"],

    setMainTag: function (cTag) {
        this.cMainTag = cTag;
        for (let c of this.lTag) {
            if (c !== cTag) {
                document.getElementById("tag_"+c).checked = false;
            }
        }
    },

    update: function (cTag=null) {
        if (cTag !== null) {
            this.setMainTag(cTag);
        }
        try {
            this.lFlexion = [];
            this.sLemma = document.getElementById("lemma").value.trim();
            if (this.sLemma.length > 0) {
                switch (this.cMainTag) {
                    case "N":
                        if (!this.getRadioValue("pos_nom_commun") || !this.getRadioValue("genre_nom_commun")) {
                            break;
                        }
                        let sTag = this.getRadioValue("pos_nom_commun") + this.getRadioValue("genre_nom_commun");
                        switch (this.getRadioValue("pluriel_nom_commun")) {
                            case "s":
                                this.lFlexion.push([this.sLemma, sTag+":s/*"]);
                                this.lFlexion.push([this.sLemma+"s", sTag+":p/*"]);
                                break;
                            case "x":
                                this.lFlexion.push([this.sLemma, sTag+":s/*"]);
                                this.lFlexion.push([this.sLemma+"x", sTag+":p/*"]);
                                break;
                            case "i":
                                this.lFlexion.push([this.sLemma, sTag+":i/*"]);
                                break;
                        }
                        let sLemma2 = document.getElementById("lemma2").value.trim();
                        if (sLemma2.length > 0  &&  this.getRadioValue("pos_nom_commun2")  &&  this.getRadioValue("genre_nom_commun2")) {
                            let sTag2 = this.getRadioValue("pos_nom_commun2") + this.getRadioValue("genre_nom_commun2");
                            switch (this.getRadioValue("pluriel_nom_commun2")) {
                                case "s":
                                    this.lFlexion.push([sLemma2, sTag2+":s/*"]);
                                    this.lFlexion.push([sLemma2+"s", sTag2+":p/*"]);
                                    break;
                                case "x":
                                    this.lFlexion.push([sLemma2, sTag2+":s/*"]);
                                    this.lFlexion.push([sLemma2+"x", sTag2+":p/*"]);
                                    break;
                                case "i":
                                    this.lFlexion.push([sLemma2, sTag2+":i/*"]);
                                    break;
                            }
                        }
                        break;
                    case "V": {
                        if (!this.sLemma.endsWith("er") && !this.sLemma.endsWith("ir") && !this.sLemma.endsWith("re")) {
                            break;
                        }
                        this.sLemma = this.sLemma.toLowerCase();
                        let cGroup = "";
                        let c_i = (document.getElementById("v_i").checked) ? "i" : "_";
                        let c_t = (document.getElementById("v_t").checked) ? "t" : "_";
                        let c_n = (document.getElementById("v_n").checked) ? "n" : "_";
                        let c_p = (document.getElementById("v_p").checked) ? "p" : "_";
                        let c_m = (document.getElementById("v_m").checked) ? "m" : "_";
                        let c_ae = (document.getElementById("v_ae").checked) ? "e" : "_";
                        let c_aa = (document.getElementById("v_aa").checked) ? "a" : "_";
                        let sVerbTag = c_i + c_t + c_n + c_p + c_m + c_ae + c_aa;
                        if (sVerbTag.includes("p") && !sVerbTag.startsWith("___p_")) {
                            sVerbTag = sVerbTag.replace("p", "q");
                        }
                        if (!sVerbTag.endsWith("__") && !sVerbTag.startsWith("____")) {
                            let sVerbPattern = document.getElementById("verbe_modele").value.trim();
                            if (sVerbPattern.length == 0) {
                                // utilisation du générateur de conjugaison
                                let bVarPpas = !document.getElementById("v_ppas").checked;
                                for (let [sFlexion, sFlexTags] of conj_generator.conjugate(this.sLemma, sVerbTag, bVarPpas)) {
                                    this.lFlexion.push([sFlexion, sFlexTags]);
                                }
                            } else {
                                // copie du motif d’un autre verbe : utilisation du conjugueur
                                if (conj.isVerb(sVerbPattern)) {
                                    let oVerb = new Verb(this.sLemma, sVerbPattern);
                                    for (let [sTag1, dFlex] of oVerb.dConj.entries()) {
                                        if (sTag1 !== ":Q") {
                                            for (let [sTag2, sConj] of dFlex.entries()) {
                                                if (sTag2.startsWith(":") && sConj !== "") {
                                                    this.lFlexion.push([sConj, ":V" + oVerb.cGroup + "_" + sVerbTag + sTag1 + sTag2]);
                                                }
                                            }
                                        } else {
                                            // participes passés
                                            if (dFlex.get(":Q3") !== "") {
                                                if (dFlex.get(":Q2") !== "") {
                                                    this.lFlexion.push([dFlex.get(":Q1"), ":V" + oVerb.cGroup + "_" + sVerbTag + ":Q:A:m:s/*"]);
                                                    this.lFlexion.push([dFlex.get(":Q2"), ":V" + oVerb.cGroup + "_" + sVerbTag + ":Q:A:m:p/*"]);
                                                } else {
                                                    this.lFlexion.push([dFlex.get(":Q1"), ":V" + oVerb.cGroup + "_" + sVerbTag + ":Q:A:m:i/*"]);
                                                }
                                                this.lFlexion.push([dFlex.get(":Q3"), ":V" + oVerb.cGroup + "_" + sVerbTag + ":Q:A:f:s/*"]);
                                                this.lFlexion.push([dFlex.get(":Q4"), ":V" + oVerb.cGroup + "_" + sVerbTag + ":Q:A:f:p/*"]);
                                            } else {
                                                this.lFlexion.push([dFlex.get(":Q1"), ":V" + oVerb.cGroup + "_" + sVerbTag + ":Q:e:i/*"]);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    }
                    case "W":
                        this.sLemma = this.sLemma.toLowerCase();
                        this.lFlexion.push([this.sLemma, ":W/*"]);
                        break;
                    case "M":
                        this.sLemma = this.sLemma.slice(0,1).toUpperCase() + this.sLemma.slice(1);
                        let sPOSTag = this.getRadioValue("pos_nom_propre");
                        let sGenderTag = this.getRadioValue("genre_nom_propre");
                        if (sGenderTag) {
                            this.lFlexion.push([this.sLemma, sPOSTag+sGenderTag+":i/*"]);
                        }
                        break;
                    case "X":
                        let sFlexion = document.getElementById("flexion").value.trim();
                        let sTags = document.getElementById("tags").value.trim();
                        if (sFlexion.length > 0 && sTags.startsWith(":")) {
                            this.lFlexion.push([sFlexion, sTags]);
                        }
                        break;
                }
            }
            oGenWordsTable.fill(this.lFlexion);
        }
        catch (e) {
            showError(e);
        }
    },

    getRadioValue: function (sName) {
        if (document.getElementById(sName)) {
            for (let xNode of document.getElementById(sName).children) {
                if (xNode.selected) {
                    return xNode.value;
                }
            }
        }
        return null;
    },

    createFlexLemmaTagArray: function () {
        let lEntry = [];
        for (let [sFlex, sTags] of oGenWordsTable.getEntries()) {
            lEntry.push([sFlex, this.sLemma, sTags]);
        }
        return lEntry;
    },

    addToLexicon: function () {
        try {
            oLexiconTable.addEntries(this.createFlexLemmaTagArray());
            oGenWordsTable.clear();
            document.getElementById("lemma").value = "";
            document.getElementById("lemma").focus();
            enableElement("save_button");
            this.clear();
            this.cMainTag = "";
        }
        catch (e) {
            showError(e);
        }
    }
}


const oBinaryDict = {

    oIBDAWG: null,

    load: async function () {
        let sJSON = await oFileHandler.loadFile("fr.personal.json");
        this._load(sJSON);
    },

    _load: function (sJSON, bSave=false) {
        //console.log("_load");
        if (sJSON) {
            try {
                let oJSON = JSON.parse(sJSON);
                this.oIBDAWG = new IBDAWG(oJSON);
            }
            catch (e) {
                this.setDictData(0, "#Erreur. Voir la console.");
                console.error(e);
                return;
            }
            if (bSave) {
                oFileHandler.saveFile("fr.personal.json", sJSON);
            }
            let lEntry = [];
            for (let aRes of this.oIBDAWG.select()) {
                lEntry.push(aRes);
            }
            oLexiconTable.fill(lEntry);
            this.setDictData(this.oIBDAWG.nEntry, this.oIBDAWG.sDate);
            enableElement("export_button");
        } else {
            this.setDictData(0, "[néant]");
            disableElement("export_button");
        }
    },

    import: function () {
        oFileHandler.loadAs(this._import.bind(this));
    },

    _import: function (sJSON) {
        this._load(sJSON, true);
    },

    setDictData: function (nEntries, sDate) {
        document.getElementById("dic_num_entries").value = nEntries;
        document.getElementById("dic_save_date").value = sDate;
    },

    listen: function () {
        document.getElementById("delete_button").addEventListener("click", () => { oLexiconTable.deleteSelection(); }, false);
        document.getElementById("save_button").addEventListener("click", () => { this.build(); }, false);
        document.getElementById("export_button").addEventListener("click", () => { this.export(); }, false);
        document.getElementById("import_button").addEventListener("click", () => { this.import(); }, false);
    },

    build: function () {
        let xProgressNode = document.getElementById("wait_progress");
        let lEntry = oLexiconTable.getEntries();
        if (lEntry.length > 0) {
            let oDAWG = new DAWG(lEntry, "S", "fr", "Français", "fr.personal", "Dictionnaire personnel", xProgressNode);
            let oJSON = oDAWG.createBinaryJSON(1);
            oFileHandler.saveFile("fr.personal.json", JSON.stringify(oJSON));
            this.oIBDAWG = new IBDAWG(oJSON);
            this.setDictData(this.oIBDAWG.nEntry, this.oIBDAWG.sDate);
            //browser.runtime.sendMessage({ sCommand: "setDictionary", dParam: {sType: "personal", oDict: oJSON}, dInfo: {} });
            enableElement("export_button");
        } else {
            oFileHandler.deleteFile("fr.personal.json");
            this.setDictData(0, "[néant]");
            disableElement("export_button");
        }
    },

    export: function () {
        let sJSON = JSON.stringify(this.oIBDAWG.getJSON());
        oFileHandler.saveAs(sJSON);
    }
}


const oSearch = {

    oSpellChecker: null,

    load: function () {
        this.oSpellChecker = new SpellChecker("fr", "", "fr-allvars.json");
    },

    listen: function () {
        document.getElementById("search_similar_button").addEventListener("click", () => { this.searchSimilar(); }, false);
        document.getElementById("search_regex_button").addEventListener("click", () => { this.searchRegex() }, false);
    },

    searchSimilar: function () {
        oSearchTable.clear();
        let sWord = document.getElementById("search_similar").value;
        if (sWord !== "") {
            let lResult = this.oSpellChecker.getSimilarEntries(sWord, 20);
            oSearchTable.fill(lResult);
        }
    },

    searchRegex: function () {
        let sFlexPattern = document.getElementById("search_flexion_pattern").value.trim();
        let sTagsPattern = document.getElementById("search_tags_pattern").value.trim();
        let lEntry = [];
        let i = 0;
        for (let aRes of this.oSpellChecker.select(sFlexPattern, sTagsPattern)) {
            lEntry.push(aRes);
            i++;
            if (i >= 2000) {
                break;
            }
        }
        oSearchTable.fill(lEntry);
    }
}


const oTagsInfo = {
    load: function () {
        let lEntry = [];
        for (let [sTag, [_, sLabel]] of _dTag) {
            lEntry.push([sTag, sLabel.trim()]);
        }
        oTagsTable.fill(lEntry);
    }
}


const oGenWordsTable = new Table("generated_words_table", ["Flexions", "Étiquettes"], ["125px", "225px"], "progress_new_words");
const oLexiconTable = new Table("lexicon_table", ["Flexions", "Lemmes", "Étiquettes"], ["190px", "150px", "200px"], "progress_lexicon", "num_entries");
const oSearchTable = new Table("search_table", ["Flexions", "Lemmes", "Étiquettes"], ["190px", "150px", "200px"], "progress_search", "search_num_entries");
const oTagsTable = new Table("tags_table", ["Étiquette", "Signification"], ["75px", "475px"], "progress_lexicon");

conj.init(helpers.loadFile("resource://grammalecte/fr/conj_data.json"));


oTagsInfo.load();
oSearch.load();
oBinaryDict.load();
oBinaryDict.listen();
oGenerator.listen();
oSearch.listen();

Added gc_lang/fr/mailext/content/lex_editor.xul.































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://grammarchecker/content/lex_editor.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://grammarchecker/locale/lex_editor.dtd">

<dialog
  id="grammalecte-lexicon-editor-window"
  title="&window.title;"
  orient="vertical"
  buttons="cancel"
  buttonlabelcancel="&button.cancel.label;"
  ondialogcancel="return;"
  width="500"
  onload="document.getElementById('grammalecte-lexicon-editor-window').centerWindowOnScreen();"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <!-- Other elements go here -->
  <hbox>
    <label class="dictionary" value="Dictionnaire personnel" />
    <spacer flex="1" />
    <label class="dic_text align_right" value="Enregistré le :" />
    <label id="dic_save_date" class="dic_text" value="[néant]" />
    <spacer flex="1" />
    <label id="dic_num_entries" class="dic_text align_right" value="0" />
    <label class="dic_text" value="entrées" />
    <spacer flex="1" />
    <button id="import_button" label="Importer" />
    <button id="export_button" label="Exporter" />
  </hbox>

  <tabbox id="tabs" selectedIndex="0">
    <tabs>
      <tab label="&tab.editor.label;"/>
      <tab label="&tab.lexicon.label;"/>
      <tab label="&tab.search.label;"/>
      <tab label="&tab.info.label;"/>
    </tabs>

    <tabpanels>

      <!-- NEW WORD -->
      <tabpanel orient="vertical">

        <hbox>

          <vbox>
            <div class="dialogheader-title">&dialogheader.newword.label;</div>
            <textbox id="lemma" value="" />

            <checkbox id="tag_N" class="subsection" label="Nom et adjectif" />
            <hbox class="m_left">
              <radiogroup id="pos_nom_commun" orient="vertical">
                <radio id="nom_adj" class="option" label="Nom et adjectif" value=":N:A" />
                <radio id="nom" class="option" label="Nom" value=":N" />
                <radio id="adj" class="option" label="Adjectif" value=":A" />
              </radiogroup>
              <radiogroup id="genre_nom_commun" orient="vertical">
                <radio id="N_epi" class="option" label="épicène" value=":e" />
                <radio id="N_mas" class="option" label="masculin" value=":m" />
                <radio id="N_fem" class="option" label="féminin" value=":f" />
              </radiogroup>
              <radiogroup id="pluriel_nom_commun" orient="vertical">
                <radio id="N_s" class="option" label="pluriel en ·s" value="s" />
                <radio id="N_x" class="option" label="pluriel en ·x" value="x" />
                <radio id="N_inv" class="option" label="invariable" value="i" />
              </radiogroup>
            </hbox>
            <vbox class="m_left2">
              <label class="subsection" value="[optionnel] Autre forme (masculine, féminine, variante…)" />
              <textbox id="lemma2" value="" />
              <hbox>
                <radiogroup id="pos_nom_commun2" orient="vertical">
                  <radio id="nom_adj2" class="option" label="Nom et adjectif" value=":N:A" />
                  <radio id="nom2" class="option" label="Nom" value=":N" />
                  <radio id="adj2" class="option" label="Adjectif" value=":A" />
                </radiogroup>
                <radiogroup id="genre_nom_commun2" orient="vertical">
                  <radio id="N_epi2" class="option" label="épicène" value=":e" />
                  <radio id="N_mas2" class="option" label="masculin" value=":m" />
                  <radio id="N_fem2" class="option" label="féminin" value=":f" />
                </radiogroup>
                <radiogroup id="pluriel_nom_commun2" orient="vertical">
                  <radio id="N_s2" class="option" label="pluriel en ·s" value="s" />
                  <radio id="N_x2" class="option" label="pluriel en ·x" value="x" />
                  <radio id="N_inv2" class="option" label="invariable" value="inv" />
                </radiogroup>
              </hbox>
            </vbox>

            <checkbox id="tag_M" class="subsection" label="Nom propre" />
            <hbox class="m_left">
              <radiogroup id="pos_nom_propre" orient="vertical">
                <radio id="M1" class="option" label="Prénom" value=":M1" />
                <radio id="M2" class="option" label="Patronyme" value=":M2" />
                <radio id="MP" class="option" label="Autre" value=":MP" />
              </radiogroup>
              <radiogroup id="genre_nom_propre" orient="vertical">
                <radio id="M_epi" class="option" label="épicène" value=":e" />
                <radio id="M_mas" class="option" label="masculin" value=":m" />
                <radio id="M_fem" class="option" label="féminin" value=":f" />
              </radiogroup>
            </hbox>

            <checkbox id="tag_V" class="subsection" label="Verbe" />
            <hbox class="m_left">
              <vbox>
                <checkbox id="v_i" class="v_usage" label="intransitif" />
                <checkbox id="v_t" class="v_usage" label="transitif" />
                <checkbox id="v_n" class="v_usage" label="transitif indirect" />
                <checkbox id="v_p" class="v_usage" label="pronominal" />
                <checkbox id="v_m" class="v_usage" label="impersonnel" />
              </vbox>
              <vbox>
                <label value="Auxilaire avec le passé composé" />
                <checkbox id="v_ae" class="v_usage" label="être" />
                <checkbox id="v_aa" class="v_usage" label="avoir" />
                <label value="" />
                <checkbox id="v_ppas" class="v_usage" label="Participe passé invariable" />
              </vbox>
            </hbox>
            <hbox class="m_left">
              <label class="subsection" value="Verbe modèle [optionnel]" />
              <textbox id="verbe_modele" class="v_textbox" value="" />
            </hbox>

            <checkbox id="tag_W" class="subsection" label="Adverbe" />

            <checkbox id="tag_X" class="subsection" label="Autre" />
            <vbox class="m_left">
              <hbox>
                <label class="subsection other_label" value="Flexion" />
                <textbox id="flexion" class="other_textbox" value="" />
              </hbox>
              <hbox>
                <label class="subsection other_label" value="Étiquettes" />
                <textbox id="tags" class="other_textbox" value="" />
              </hbox>
            </vbox>
          </vbox>

          <vbox>
            <div class="dialogheader-title">&dialogheader.generated_words.label;</div>
            <richlistbox id="generated_words_table" seltype="multiple">
              <!--
              <listhead>
                <listheader label="Flexions"/>
                <listheader label="Étiquettes"/>
              </listhead>

              <listcols>
                <listcol flex="1"/>
                <listcol flex="1"/>
              </listcols>

              <listitem>
                <listcell label="George"/>
                <listcell label="House"/>
              </listitem>
              -->
            </richlistbox>
            <progressmeter id="progress_new_words" value="0"/>
            <!--<html:progress id="progress_new_words" max="100" value="0">-->
            <hbox>
              <button id="delete_selection" label="Effacer la sélection" />
              <spacer flex="1" />
              <button id="add_to_lexicon" label="Ajouter au lexique" />
            </hbox>
          </vbox>
        </hbox>
      </tabpanel>

      <!-- LEXICON -->
      <tabpanel orient="vertical">
        <div class="dialogheader-title">&dialogheader.lexicon.label;</div>
        <hbox>
          <vbox>
            <hbox>
              <label class="info_label" value="Nombre d’entrées : " />
              <label id="num_entries" class="data" value="0" />
            </hbox>
            <spacer flex="1" />
            <button id="delete_button" label="Supprimer la sélection" />
            <spacer flex="20" />
            <progressmeter id="progress_lexicon" value="0"/>
            <!--<html:progress id="progress_lexicon" max="100" value="0">-->
            <button id="save_button" label="Enregistrer" />
          </vbox>

          <vbox>
            <richlistbox id="lexicon_table" seltype="multiple">
              <!--
              <listhead>
                <listheader label="Flexions"/>
                <listheader label="Lemmes"/>
                <listheader label="Étiquettes"/>
              </listhead>

              <listcols>
                <listcol flex="4"/>
                <listcol flex="3"/>
                <listcol flex="4"/>
              </listcols>

              <listitem>
                <listcell label="George"/>
                <listcell label="House Painter"/>
                <listcell label="House"/>
              </listitem>
              -->
            </richlistbox>
          </vbox>
        </hbox>
      </tabpanel>

      <tabpanel orient="vertical">
        <hbox>
          <vbox>
            <div class="dialogheader-title">&dialogheader.search.similar.label;</div>
            <hbox>
              <textbox id="search_similar" value="" />
              <button id="search_similar_button" label="Chercher" />
            </hbox>

            <spacer flex="1" />

            <div class="dialogheader-title">&dialogheader.search.regex.label;</div>
            <hbox>
              <label value="Flexion" />
              <spacer flex="1" />
              <textbox id="search_flexion_pattern" value="" />
            </hbox>
            <hbox>
              <label value="Étiquettes" />
              <spacer flex="1" />
              <textbox id="search_tags_pattern" value="" />
            </hbox>
            <button id="search_regex_button" label="Chercher" />
            <label value="" />
            <description>La recherche par expressions régulières peut générer un nombre gigantesque de résultats. Seules les 2000 premières occurrences trouvées seront affichées. La recherche peut être longue, parce tout le graphe de mots, qui contient 500 000 flexions, sera parcouru si besoin.</description>

            <spacer flex="1" />

            <progressmeter id="progress_search" value="0"/>
            <!--<html:progress id="progress_search" max="100" value="0">-->
            <hbox>
              <spacer flex="1" />
              <label id="search_num_entries" class="align_right" value="0" />
              <label value="entrées" />
            </hbox>
          </vbox>
          <vbox>
            <div class="dialogheader-title">&dialogheader.search.result.label;</div>
            <richlistbox id="search_table">
              <!--
              <listhead>
                <listheader label="Flexions"/>
                <listheader label="Lemmes"/>
                <listheader label="Étiquettes"/>
              </listhead>

              <listcols>
                <listcol flex="4"/>
                <listcol flex="3"/>
                <listcol flex="4"/>
              </listcols>

              <listitem>
                <listcell label="George"/>
                <listcell label="House Painter"/>
                <listcell label="House"/>
              </listitem>
              -->
            </richlistbox>
          </vbox>
        </hbox>
      </tabpanel>

      <tabpanel orient="vertical">
        <hbox>
          <vbox>
            <div class="dialogheader-title">&dialogheader.info.label;</div>
            <label class="bold" value="Enregistrement" />
            <description>Les modifications apportées au lexique ne sont enregistrées dans le dictionnaire qu’au moment où vous cliquez sur ‹Enregistrer› dans l’onglet ‹Lexique›.</description>
            <spacer flex="1" />
            <label class="bold" value="Doublons" />
            <description>Il est inutile de purger votre lexique des doublons éventuels. Les doublons sont automatiquement supprimés lors de la création du dictionnaire.</description>
            <spacer flex="1" />
            <label class="bold" value="Compilation du dictionnaire" />
            <description>Le dictionnaire est compilé comme un graphe de mots sous la forme d’une chaîne binaire dans un fichier JSON. Cette opération peut prendre du temps et consommer beaucoup de mémoire si votre lexique contient plusieurs dizaines de milliers d’entrées.</description>
            <spacer flex="1" />
            <label class="bold" value="Avertissement" />
            <description>Il est déconseillé d’utiliser la catégorie ‹Autre› pour générer autre chose que des noms, des adjectifs, des noms propres, des verbes et des adverbes. Il n’y a aucune garantie que les étiquettes pour les autres catégories, notamment les mots grammaticaux, ne changeront pas.</description>
          </vbox>
          <vbox>
            <div class="dialogheader-title">&dialogheader.tags.label;</div>
            <richlistbox id="tags_table">
              <!--
              <listhead>
                <listheader label="Étiquettes"/>
                <listheader label="Signification"/>
              </listhead>

              <listcols>
                <listcol flex="1"/>
                <listcol flex="10"/>
              </listcols>

              <listitem>
                <listcell label="House Painter"/>
                <listcell label="House"/>
              </listitem>
              -->
            </richlistbox>
          </vbox>
        </hbox>
      </tabpanel>

    </tabpanels>

  </tabbox>

  <script type="application/x-javascript" src="resource://grammalecte/graphspell/helpers.js" />
  <script type="application/x-javascript" src="resource://grammalecte/graphspell/str_transform.js" />
  <script type="application/x-javascript" src="resource://grammalecte/graphspell/char_player.js" />
  <script type="application/x-javascript" src="resource://grammalecte/graphspell/dawg.js" />
  <script type="application/x-javascript" src="resource://grammalecte/graphspell/ibdawg.js" />
  <script type="application/x-javascript" src="resource://grammalecte/graphspell/spellchecker.js" />
  <script type="application/x-javascript" src="resource://grammalecte/fr/conj.js" />
  <script type="application/x-javascript" src="resource://grammalecte/fr/conj_generator.js" />
  <script type="application/x-javascript" src="resource://grammalecte/fr/lexicographe.js" />
  <script type="application/x-javascript" src="file_handler.js" />
  <script type="application/x-javascript" src="lex_editor.js" />

</dialog>

Added gc_lang/fr/mailext/content/options.css.

























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
/* CSS */


.dialogheader-title {
  margin: 5px;
  padding: 5px 8px;
  border: 1px solid hsl(210, 50%, 80%);
  background-color: hsl(210, 50%, 50%);
  color: hsl(210, 10%, 90%);
  font-size: larger;
  font-weight: bold;
}

Added gc_lang/fr/mailext/content/options.js.

















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 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) {
            console.error(e);
        }
        this.listen();
    },

    listen: function () {
        document.addEventListener("dialogaccept", (event) => {
            this.save();
        });
    },

    save: function () {
        try {
            prefs.setBoolPref('bCheckSignature', document.getElementById('check_signature').checked);
        }
        catch (e) {
            console.error(e);
        }
    }
}

oOptControl.load();

Added gc_lang/fr/mailext/content/options.xul.



















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://grammarchecker/content/options.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://grammarchecker/locale/options.dtd">

<dialog
  id="grammalecte-options-window"
  title="&window.title;"
  orient="vertical"
  buttons="accept, cancel"
  defaultButton="accept"
  width="400"
  onload="document.getElementById('grammalecte-options-window').centerWindowOnScreen();"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <!-- Other elements go here -->

  <div class="dialogheader-title">&dialogheader.label;</div>

  <checkbox id="check_signature" label="&check_signature.label;" accesskey="&check_signature.accesskey;" />

  <script type="application/javascript" src="options.js" />

</dialog>

Added gc_lang/fr/mailext/content/overlay.css.





































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#grammarchecker-toolbar-button {
    list-style-image: url("chrome://grammarchecker/skin/grammarcheck.png");
}
[iconsize="small"] #grammarchecker-toolbar-button {
    list-style-image: url("chrome://grammarchecker/skin/grammarcheck_small.png");
}
#grammarchecker-toolbar-button[disabled="true"] {
    list-style-image: url("chrome://grammarchecker/skin/grammarcheck_disabled.png");
}
[iconsize="small"] #grammarchecker-toolbar-button[disabled="true"] {
    list-style-image: url("chrome://grammarchecker/skin/grammarcheck_small_disabled.png");
}

#grammalecte-menu {
    list-style-image: url("chrome://grammarchecker/skin/grammarcheck.png");
}
menuitem#gl-item-analyze {
    list-style-image: url('chrome://grammarchecker/skin/grammarcheck.png');
}

#textformatter-panel {
    overflow: auto;
    height: 500px;
    max-height: 650px;
}
#textformatter-title {
    margin-top: 10px;
}
#textformatter-commands {
    margin: 10px 0;
}
#textformatter-infomsg {
    padding: 5px;
    font-size: 10px;
}
.optiongroup {
    color: hsl(210, 50%, 40%);
    font-size: 14px;
    font-weight: bold;
}
#textformatter-progressbar {
    padding: 5px;
}
#textformatter-timer {
    padding: 5px;
}


#grammarchecker-panel {
    overflow: auto;
    margin-left: 0.5em;
    height: 400px;
    max-height: 600px;
}

#grammalecte-title {
    margin-top: 10px;
}

#grammalecte-infobox {
    display: block;
    margin: 20px 10px 5px 10px;
    padding: 10px;
    background-color: hsl(210, 20%, 40%);
    border-radius: 3px;
    color: hsl(210, 50%, 96%);
    font-weight: bold;
}
#grammalecte-info {
    padding: 1px 5px;
}
#closebutton {
    padding: 1px 5px;
    background-color: hsl(0, 50%, 50%);
    color: hsl(0, 10%, 96%);
    border-radius: 2px;
    cursor: pointer;
}

#grammalecte-errors div {
    display: block;
    /*display: list-item;
    list-style-position: inside;*/
    margin-top: 10px;
}
#grammalecte-errors p {
    display: block;
    margin-left: 10px;
}
#grammalecte-errors p.paragraph {
    margin-top: 20px;
    font-size: 16px;
    font-family: "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace;
}
#grammalecte-errors p.message {
    margin-left: 20px;
}
#grammalecte-errors p.moreinfo {
    margin-left: 40px;
}
#grammalecte-errors p.suggestions {
    margin-left: 40px;
    color: hsl(120, 5%, 33%);
}
#grammalecte-errors span.suggestions_button {
    padding: 1px 6px;
    background-color: hsl(120, 40%, 60%);
    color: hsl(120, 10%, 96%);
    border-radius: 2px;
    cursor: pointer;
}
#grammalecte-errors {
    display: block;
}

a {
    font-weight: bold;
    color: hsl(30, 50%, 50%);
    text-decoration: underline;
    cursor: pointer;
}
.error {
    /* default color */
    background-color: hsl(210, 50%, 50%);
    color: hsl(210, 10%, 96%);
    font-weight: bold;
    border-radius: 3px;
    /*border-bottom: 2px solid hsl(210, 50%, 50%);*/
}
.errornum {
    color: hsl(210, 10%, 30%);
    font-weight: bold;
}
.sugg {
    padding: 1px 6px;
    background-color: hsl(120, 50%, 30%);
    color: hsl(210, 10%, 90%);
    border-radius: 2px;
    font-weight: bold;
    cursor: pointer;
}
.debug_info {
    color: hsl(0, 50%, 50%);
    font-style: italic;
}

.spell {
    background-color: hsl(0, 50%, 50%);
    color: hsl(0, 0%, 96%);
    border-radius: 3px;
}


.dialogheader-title {
    margin: 5px;
    padding: 5px 8px;
    border: 1px solid hsl(210, 50%, 80%);
    background-color: hsl(210, 50%, 50%);
    color: hsl(210, 10%, 90%);
    font-size: larger;
    font-weight: bold;
}

Added gc_lang/fr/mailext/content/overlay.js.



















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
// JavaScript

"use strict";


const Cc = Components.classes;
const Ci = Components.interfaces;
//const Cu = Components.utils;
//const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});

const { BasePromiseWorker } = ChromeUtils.import('resource://gre/modules/PromiseWorker.jsm', {});
const prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("extensions.grammarchecker.");

//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", "⁵"],
        ["6", "⁶"], ["7", "⁷"], ["8", "⁸"], ["9", "⁹"], ["0", "⁰"]
    ]),
    convert: function (sText) {
        let sRes = "";
        for (let c of sText) {
            sRes += (this.dNumbers.has(c)) ? this.dNumbers.get(c) : "⁻";
        }
        return sRes;
    }
};


var oGrammarChecker = {
    // you must use var to be able to call this object from elsewhere
    xGCEWorker: null,
    loadGC: function () {
        if (this.xGCEWorker === null) {
            // Grammar checker
            console.log('Loading Grammalecte');
            this.xGCEWorker = new BasePromiseWorker('chrome://promiseworker/content/gce_worker.js');
            let that = this;
            let xPromise = this.xGCEWorker.post('loadGrammarChecker', [prefs.getCharPref("sGCOptions"), "Thunderbird"]);
            xPromise.then(
                function (aVal) {
                    console.log(aVal);
                    prefs.setCharPref("sGCOptions", aVal);
                    if (prefs.getBoolPref("bPersonalDictionary")) {
                        let sDicJSON = oFileHandler.loadFile("fr.personal.json");
                        if (sDicJSON) {
                            that.xGCEWorker.post('setDictionary', ["personal", sDicJSON]);
                        }
                    }
                },
                function (aReason) { console.log('Promise rejected - ', aReason); }
            ).catch(
                function (aCaught) { console.log('Promise Error - ', aCaught); }
            );
        }
    },
    fullTests: function () {
        console.log('Performing tests... Wait...');
        let xPromise = this.xGCEWorker.post('fullTests', ['{"nbsp":true, "esp":true, "unit":true, "num":true}']);
        xPromise.then(
            function (aVal) {
                console.log('Done.');
                console.log(aVal);
            },
            function (aReason) { console.log('Promise rejected', aReason); }
        ).catch(
            function (aCaught) { console.log('Promise Error', aCaught); }
        );
    },
    test: function (sText) {
        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) {
                        console.log(text.getReadableError(dErr));
                    }
                } else {
                    console.log("no error found");
                }
            },
            function (aReason) { console.log('Promise rejected', aReason); }
        ).catch(
            function (aCaught) { console.log('Promise Error', aCaught); }
        );
    },
    setOptions: function () {
        console.log('Set options');
        let xPromise = this.xGCEWorker.post('setOptions', [prefs.getCharPref("sGCOptions")]);
        xPromise.then(
            function (aVal) {
                console.log(aVal);
                prefs.setCharPref("sGCOptions", aVal);
            },
            function (aReason) { console.log('Promise rejected', aReason); }
        ).catch(
            function (aCaught) { console.log('Promise Error', aCaught); }
        );
    },
    resetOptions: function () {
        let xPromise = this.xGCEWorker.post('resetOptions');
        xPromise.then(
            function (aVal) {
                console.log(aVal);
                prefs.setCharPref("sGCOptions", aVal);
            },
            function (aReason) { console.log('Promise rejected', aReason); }
        ).catch(
            function (aCaught) { console.log('Promise Error', aCaught); }
        );
    },
    parse: async function () {
        this.clearPreview();
        this.openPanel();
        this.setInfo("Analyse en cours…");
        try {
            let xEditor = new Editor();
            let nParagraph = 0;
            let bIsError = false;
            for (let [iParagraph, sParagraph] of xEditor.getParagraphs()) {
                if (sParagraph.trim() !== "") {
                    let sRes = await this.xGCEWorker.post('parseAndSpellcheck', [sParagraph, "FR", false, false]);
                    let oRes = JSON.parse(sRes);
                    if (oRes.aGrammErr.length > 0 || oRes.aSpellErr.length > 0) {
                        document.getElementById("grammalecte-errors").appendChild(this.createResultNode(xEditor, sParagraph, iParagraph, oRes.aGrammErr, oRes.aSpellErr));
                        bIsError = true;
                    }
                    nParagraph += 1;
                }
            }
            if (bIsError === false) {
                let xNodeP = document.createElement("p");
                xNodeP.setAttribute("class", "message");
                xNodeP.textContent = "Aucune erreur détectée…";
                document.getElementById("grammalecte-errors").appendChild(xNodeP);
            }
            this.setInfo("Nombre de paragraphes analysés : " + nParagraph);
        }
        catch (e) {
            this.setInfo("Erreur : " + e.message);
            console.error(e);
        }
    },
    createResultNode: function (xEditor, sParagraph, iParagraph, aGrammErr, aSpellErr) {
        let xResultNode = document.createElement("div");
        xResultNode.setAttribute("id", "resnode" + iParagraph);
        this.fillResultNode(xResultNode, xEditor, sParagraph, iParagraph, aGrammErr, aSpellErr);
        return xResultNode;
    },
    reparseParagraph: function (xEditor, iParagraph) {
        try {
            let that = this;
            let xResultNode = document.getElementById("resnode"+iParagraph);
            xResultNode.textContent = "…………… réanalyse en cours ……………";
            let sParagraph = xEditor.getParagraph(iParagraph);
            let xPromise = this.xGCEWorker.post('parseAndSpellcheck', [sParagraph, "FR", false, false]);
            xPromise.then(function (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);
                }
            }, function (res) {
                xResultNode.textContent = "Erreur: " + res;
            });
        }
        catch (e) {
            console.error(e);
        }
    },
    fillResultNode: function (xResultNode, xEditor, sParagraph, iParagraph, aGrammErr, aSpellErr) {
        try {
            if (aGrammErr.length === 0  &&  aSpellErr.length === 0) {
                return null;
            }
            aGrammErr.push(...aSpellErr);
            aGrammErr.sort(function (a, b) {
                if (a["nStart"] < b["nStart"])
                    return -1;
                if (a["nStart"] > b["nStart"])
                    return 1;
                return 0;
            });
            let xParagraphNode = document.createElement("p");
            let lNodeError = [];
            let nEndLastErr = 0;
            let nError = 1;
            xParagraphNode.setAttribute("class", "paragraph");
            for (let dErr of aGrammErr) {
                let nStart = dErr["nStart"];
                let nEnd = dErr["nEnd"];
                if (nStart >= nEndLastErr) {
                    xParagraphNode.appendChild(document.createTextNode(this._purgeTags(sParagraph.slice(nEndLastErr, nStart))));
                    let xNodeError = document.createElement("b");
                    if (dErr['sType'] !== 'WORD') {
                        xNodeError.setAttribute("class", "error");
                        xNodeError.textContent = oConverterToExponent.convert(nError.toString()) + sParagraph.slice(nStart, nEnd);
                        xNodeError.style.backgroundColor = dErr["aColor"];
                        xParagraphNode.appendChild(xNodeError);
                        lNodeError.push(this._createNodeGCErrorDescription(xEditor, nError, dErr, iParagraph));
                    }
                    else {
                        xNodeError.setAttribute("class", "error spell");
                        xNodeError.textContent = oConverterToExponent.convert(nError.toString()) + sParagraph.slice(nStart, nEnd);
                        xParagraphNode.appendChild(xNodeError);
                        lNodeError.push(this._createNodeSpellErrorDescription(xEditor, nError, dErr, iParagraph));
                    }
                    nEndLastErr = nEnd;
                    nError += 1;
                }
            }
            xParagraphNode.appendChild(document.createTextNode(this._purgeTags(sParagraph.slice(nEndLastErr))));
            xResultNode.appendChild(xParagraphNode);
            for (let xNode of lNodeError) {
                xResultNode.appendChild(xNode);
            }
        }
        catch (e) {
            console.error(e);
            xResultNode.textContent = "# Error: " + e.message;
        }
    },
    _createNodeGCErrorDescription: function (xEditor, nError, dErr, iParagraph) {
        let xNodeDiv = document.createElement("div");
        let that = this;
        // message
        let xNodeMessage = document.createElement("p");
        xNodeMessage.setAttribute("class", "message");
        let xNodeErrorNumber = document.createElement("b");
        xNodeErrorNumber.setAttribute("class", "errornum");
        xNodeErrorNumber.textContent = "[" + nError + "] ";
        xNodeMessage.appendChild(xNodeErrorNumber);
        xNodeMessage.appendChild(document.createTextNode(" " + dErr["sMessage"].replace(/&nbsp;/g, " ") + " "));
        if (false) {
            // debug info
            let xNodeDebug = document.createElement("span");
            xNodeDebug.setAttribute("class", "debug_info");
            xNodeDebug.textContent = " #" + dErr["sRuleId"] + " #" + dErr["sLineId"];
            xNodeMessage.appendChild(xNodeDebug);
        }
        xNodeDiv.appendChild(xNodeMessage);
        // URL
        if (dErr["URL"]) {
            let xNodeP = document.createElement("p");
            xNodeP.setAttribute("class", "moreinfo");
            xNodeP.appendChild(document.createTextNode("→ "));
            let xNodeURL = document.createElement("a");
            xNodeURL.setAttribute("href", dErr["URL"]);
            xNodeURL.textContent = "Plus d’informations…";
            xNodeURL.addEventListener("click", function (e) {
                that.openInTabURL(dErr["URL"]);
            });
            xNodeP.appendChild(xNodeURL);
            xNodeDiv.appendChild(xNodeP);
        }
        // suggestions
        if (dErr["aSuggestions"].length > 0) {
            let xNodeSuggLine = document.createElement("p");
            xNodeSuggLine.setAttribute("class", "suggestions");
            xNodeSuggLine.textContent = "Suggestions : ";
            let n = 0;
            for (let sSugg of dErr["aSuggestions"]) {
                if (n > 0) {
                    xNodeSuggLine.appendChild(document.createTextNode(" "));
                }
                let xNodeSugg = document.createElement("span");
                xNodeSugg.setAttribute("class", "sugg");
                xNodeSugg.textContent = sSugg.replace(" ", " "); // use nnbsp
                xNodeSugg.addEventListener("click", function (e) {
                    xEditor.changeParagraph(iParagraph, sSugg, dErr["nStart"], dErr["nEnd"]);
                    xNodeDiv.textContent = "";
                    that.reparseParagraph(xEditor, iParagraph);
                });
                xNodeSuggLine.appendChild(xNodeSugg);
                n += 1;
            }
            xNodeDiv.appendChild(xNodeSuggLine);
        }
        return xNodeDiv;
    },
    _purgeTags: function (sText) {
        sText = sText.replace(/<br ?\/?>/ig, " ");
        sText = sText.replace(/<font size="[+-]\d+">/g, "");
        return sText.replace(/<\/? ?[a-zA-Z]+ ?>/g, "");
    },
    _createNodeSpellErrorDescription: function (xEditor, nError, dErr, iParagraph) {
        let xNodeDiv = document.createElement("div");
        let that = this;
        // message
        let xNodeMessage = document.createElement("p");
        xNodeMessage.setAttribute("class", "message");
        let xNodeErrorNumber = document.createElement("b");
        xNodeErrorNumber.setAttribute("class", "errornum");
        xNodeErrorNumber.textContent = "[" + nError + "] ";
        xNodeMessage.appendChild(xNodeErrorNumber);
        xNodeMessage.appendChild(document.createTextNode(" Mot inconnu du dictionnaire. "));
        xNodeDiv.appendChild(xNodeMessage);
        // suggestions
        let xNodeSuggLine = document.createElement("p");
        xNodeSuggLine.setAttribute("class", "suggestions");
        let xNodeSuggButton = document.createElement("span");
        xNodeSuggButton.setAttribute("class", "suggestions_button");
        xNodeSuggButton.textContent = "Suggestions : ";
        xNodeSuggButton.addEventListener("click", (e) => {
            let xPromise = this.xGCEWorker.post('suggest', [dErr['sValue'], 10]);
            xPromise.then(
                function (sVal) {
                    if (sVal != "") {
                        let lSugg = sVal.split("|");
                        let n = 0;
                        for (let sSugg of lSugg) {
                            xNodeSuggLine.appendChild(document.createTextNode(" "));
                            let xNodeSugg = document.createElement("span");
                            xNodeSugg.setAttribute("class", "sugg");
                            xNodeSugg.textContent = sSugg;
                            xNodeSugg.addEventListener("click", function (e) {
                                xEditor.changeParagraph(iParagraph, xNodeSugg.textContent, dErr["nStart"], dErr["nEnd"]);
                                xNodeDiv.textContent = "";
                                that.reparseParagraph(xEditor, iParagraph);
                            });
                            xNodeSuggLine.appendChild(xNodeSugg);
                            n += 1;
                        }
                    } else {
                        xNodeSuggLine.appendChild(document.createTextNode("Aucune suggestion."));
                    }
                },
                function (aReason) { console.error('Promise rejected - ', aReason); }
            ).catch(
                function (aCaught) { console.error('Promise Error - ', aCaught); }
            );
        });
        xNodeSuggLine.appendChild(xNodeSuggButton);
        xNodeDiv.appendChild(xNodeSuggLine);
        return xNodeDiv;
    },
    loadUI: function () {
        this._strings = document.getElementById("grammarchecker-strings");
        let that = this;
        let nsGrammarCommand = {
            isCommandEnabled: function (aCommand, dummy) {
                return (IsDocumentEditable() && !IsInHTMLSourceMode());
            },
            getCommandStateParams: function (aCommand, aParams, aRefCon) {},
            doCommandParams: function (aCommand, aParams, aRefCon) {},
            doCommand: function (aCommand) {
                that.onParseText(aCommand);
            }
        };
        let xCommandTable = GetComposerCommandTable();
        xCommandTable.registerCommand("cmd_grammar", nsGrammarCommand);
        let sButtonId = "grammarchecker-toolbar-button";
        let sButtonId2 = "grammalecte-menu";
        let xNavBar  = document.getElementById("composeToolbar2");
        let lCurSet  = xNavBar.currentSet.split(",");
        if (lCurSet.indexOf(sButtonId) == -1) {
            let iPos = lCurSet.indexOf("spellingButton") + 1 || lCurSet.length;
            let aSet = lCurSet.slice(0, iPos).concat(sButtonId).concat(sButtonId2).concat(lCurSet.slice(iPos));
            xNavBar.setAttribute("currentset", aSet.join(","));
            //xNavBar.currentSet = aSet.join(",");
            Services.xulStore.persist(xNavBar, "currentset");
            Ci.BrowserToolboxCustomizeDone(true);
        }
    },
    clearPreview: function () {
        let xPreview = document.getElementById("grammalecte-errors");
        while (xPreview.firstChild) {
            xPreview.removeChild(xPreview.firstChild);
        };
        let xEditor = GetCurrentEditor();
        if (xEditor != null) {
            try {
                xEditor.QueryInterface(Ci.nsIEditorStyleSheets);
                xEditor.addOverrideStyleSheet("chrome://grammarchecker/content/overlay.css");
            }
            catch (e) {
                console.error(e);
            }
        }
        this.setInfo("[vide]");
    },
    setInfo: function (sText) {
        document.getElementById("grammalecte-info").textContent = sText;
    },
    openPanel: function () {
        document.getElementById("textformatter-splitter").setAttribute("state", "collapsed");
        document.getElementById("grammarchecker-splitter").setAttribute("state", "open");
    },
    closePanel: function () {
        document.getElementById("grammarchecker-splitter").setAttribute("state", "collapsed");
    },
    openDialog: function (sWhat, sName="", sOptions="") {
        try {
            window.openDialog(sWhat, sName, sOptions);
        }
        catch (e) {
            console.error(e);
        }
    },
    openInTabURL: function (sURL) {
        // method found in S3.Google.Translator
        try {
            let xWM = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
            let xWin = xWM.getMostRecentWindow("mail:3pane");
            let xTabmail = xWin.document.getElementById('tabmail');
            xWin.focus();
            if (xTabmail) {
                xTabmail.openTab('contentTab', { contentPage: sURL });
            }
        }
        catch (e) {
            console.error(e);
        }
    },
    openInBrowserURL: function (sURL) {
        // method found in S3.Google.Translator
        try {
            openURL(sURL);
        }
        catch (e) {
            console.error(e);
        }
    },
    onParseText: function (e) {
        this.parse();
    },
    onClosePanel: function (e) {
        this.closePanel();
    },
    onOpenGCOptions: function (e) {
        let that = this;
        let xPromise = this.xGCEWorker.post('getDefaultOptions');
        xPromise.then(
            function (aVal) {
                console.log(aVal);
                prefs.setCharPref("sGCDefaultOptions", aVal);
            },
            function (aReason) { console.log('Promise rejected', aReason); }
        ).catch(
            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) { console.log('Error options dialog', aReason); }
        ).catch(
            function (aCaught) { console.log('Error', aCaught); }
        );
    },
    onOpenSpellOptions: function (e) {
        this.openDialog("chrome://grammarchecker/content/spell_options.xul", "", "chrome, dialog, modal, resizable=no");
    },
    onOpenOptions: function (e) {
        this.openDialog("chrome://grammarchecker/content/options.xul", "", "chrome, dialog, modal, resizable=no");
    },
    onOpenTextFormatter: function (e) {
        oTextFormatter.openPanel();
    },
    onOpenConjugueur: function (e) {
        this.openDialog("chrome://grammarchecker/content/conjugueur.xul", "", "chrome, resizable=no");
    },
    onOpenLexiconEditor: function (e) {
        this.openDialog("chrome://grammarchecker/content/lex_editor.xul", "", "chrome, resizable=no");
    },
    onAbout: function (e) {
        this.openDialog("chrome://grammarchecker/content/about.xul", "", "chrome, dialog, modal, resizable=no");
    }
};


var oTextFormatter = {
    init: function () {
        try {
            this.closePanel();
            this.listen();
            let sTFOptions = prefs.getCharPref("sTFOptions");
            if (sTFOptions !== "") {
                this.setOptionsInPanel(JSON.parse(sTFOptions));
                this.resetProgressBar();
            } else {
                this.reset();
            }
        }
        catch (e) {
            console.error(e);
        }
    },
    listen: function () {
        window.addEventListener("click", (xEvent) => {
            let xElem = xEvent.target;
            if (xElem.id && xElem.id.startsWith("o_group_")) {
                this.switchGroup(xElem.id);
                this.resetProgressBar();
            }
        }, false);
    },
    apply: function () {
        try {
            this.saveOptions();
            this.resetProgressBar();
            let xEditor = new Editor();
            let sText = xEditor.getContent();
            let iParagraph = 0;
            sText = this.applyOptions(sText);
            for (let sParagraph of text.getParagraph(sText)) {
                xEditor.writeParagraph(iParagraph, sParagraph);
                iParagraph += 1;
            }
        }
        catch (e) {
            console.error(e);
        }
    },
    saveOptions: function () {
        let oOptions = {};
        for (let xNode of document.getElementsByClassName("option")) {
            oOptions[xNode.id] = xNode.checked;
        }
        //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]);
            if (document.getElementById(sOptName) !== null) {
                document.getElementById(sOptName).checked = oOptions[sOptName];
                if (sOptName.startsWith("o_group_")) {
                    this.switchGroup(sOptName);
                }
                if (document.getElementById("res_"+sOptName) !== null) {
                    document.getElementById("res_"+sOptName).textContent = "";
                }
            }
        }
    },
    switchGroup: function (sOptName) {
        if (document.getElementById(sOptName).checked) {
            document.getElementById(sOptName.slice(2)).style.opacity = 1;
        } else {
            document.getElementById(sOptName.slice(2)).style.opacity = 0.3;
        }
    },
    reset: function () {
        try {
            this.resetProgressBar();
            for (let xNode of document.getElementsByClassName('option')) {
                xNode.checked = (xNode.getAttribute('data-default') === "true");
                if (xNode.id.startsWith("o_group_")) {
                    this.switchGroup(xNode.id);
                }
            }
        }
        catch (e) {
            console.error(e);
        }
    },
    resetProgressBar: function () {
        document.getElementById('textformatter-progressbar').value = 0;
        document.getElementById('textformatter-timer').textContent = "";
    },
    getTimeRes: function (n) {
        // returns duration in seconds as string
        if (n < 10) {
            return n.toFixed(3).toString() + " s";
        }
        if (n < 100) {
            return n.toFixed(2).toString() + " s";
        }
        if (n < 1000) {
            return n.toFixed(1).toString() + " s";
        }
        return n.toFixed().toString() + " s";
    },
    openPanel: function () {
        document.getElementById("grammarchecker-splitter").setAttribute("state", "collapsed");
        document.getElementById("textformatter-splitter").setAttribute("state", "open");
    },
    closePanel: function () {
        document.getElementById("textformatter-splitter").setAttribute("state", "collapsed");
    },
    onOpenPanel: function (e) {
        this.openPanel();
    },
    onClosePanel: function (e) {
        this.closePanel();
    },
    onApply: function (e) {
        this.apply();
    },
    onReset: function (e) {
        this.reset();
    },
    //
    applyOptions: function (sText) {
        try {
            const t0 = Date.now();
            //window.setCursor("wait"); // change pointer
            document.getElementById('textformatter-progressbar').value = 0;
            document.getElementById('textformatter-progressbar').max = 6;
            let n1 = 0, n2 = 0, n3 = 0, n4 = 0, n5 = 0, n6 = 0, n7 = 0;

            // espaces surnuméraires
            if (document.getElementById("o_group_ssp").checked) {
                if (document.getElementById("o_end_of_paragraph").checked) {
                    [sText, n1] = this.formatText(sText, "end_of_paragraph");
                    document.getElementById('res_o_end_of_paragraph').textContent = n1;
                }
                if (document.getElementById("o_between_words").checked) {
                    [sText, n1] = this.formatText(sText, "between_words");
                    document.getElementById('res_o_between_words').textContent = n1;
                }
                if (document.getElementById("o_start_of_paragraph").checked) {
                    [sText, n1] = this.formatText(sText, "start_of_paragraph");
                    document.getElementById('res_o_start_of_paragraph').textContent = n1;
                }
                if (document.getElementById("o_before_punctuation").checked) {
                    [sText, n1] = this.formatText(sText, "before_punctuation");
                    document.getElementById('res_o_before_punctuation').textContent = n1;
                }
                if (document.getElementById("o_within_parenthesis").checked) {
                    [sText, n1] = this.formatText(sText, "within_parenthesis");
                    document.getElementById('res_o_within_parenthesis').textContent = n1;
                }
                if (document.getElementById("o_within_square_brackets").checked) {
                    [sText, n1] = this.formatText(sText, "within_square_brackets");
                    document.getElementById('res_o_within_square_brackets').textContent = n1;
                }
                if (document.getElementById("o_within_quotation_marks").checked) {
                    [sText, n1] = this.formatText(sText, "within_quotation_marks");
                    document.getElementById('res_o_within_quotation_marks').textContent = n1;
                }
                document.getElementById("o_group_ssp").checked = false;
                this.switchGroup("o_group_ssp");
            }
            document.getElementById('textformatter-progressbar').value = 1;

            // espaces insécables
            if (document.getElementById("o_group_nbsp").checked) {
                if (document.getElementById("o_nbsp_before_punctuation").checked) {
                    [sText, n1] = this.formatText(sText, "nbsp_before_punctuation");
                    [sText, n2] = this.formatText(sText, "nbsp_repair");
                    document.getElementById('res_o_nbsp_before_punctuation').textContent = n1 - n2;
                }
                if (document.getElementById("o_nbsp_within_quotation_marks").checked) {
                    [sText, n1] = this.formatText(sText, "nbsp_within_quotation_marks");
                    document.getElementById('res_o_nbsp_within_quotation_marks').textContent = n1;
                }
                if (document.getElementById("o_nbsp_before_symbol").checked) {
                    [sText, n1] = this.formatText(sText, "nbsp_before_symbol");
                    document.getElementById('res_o_nbsp_before_symbol').textContent = n1;
                }
                if (document.getElementById("o_nbsp_within_numbers").checked) {
                    [sText, n1] = this.formatText(sText, "nbsp_within_numbers");
                    document.getElementById('res_o_nbsp_within_numbers').textContent = n1;
                }
                if (document.getElementById("o_nbsp_before_units").checked) {
                    [sText, n1] = this.formatText(sText, "nbsp_before_units");
                    document.getElementById('res_o_nbsp_before_units').textContent = n1;
                }
                if (document.getElementById("o_nbsp_titles").checked) {
                    [sText, n1] = this.formatText(sText, "nbsp_titles");
                    document.getElementById('res_o_nbsp_titles').textContent = n1;
                }
                document.getElementById("o_group_nbsp").checked = false;
                this.switchGroup("o_group_nbsp");
            }
            document.getElementById('textformatter-progressbar').value = 2;

            // espaces manquants
            if (document.getElementById("o_group_typo").checked) {
                if (document.getElementById("o_ts_units").checked) {
                    [sText, n1] = this.formatText(sText, "ts_units");
                    document.getElementById('res_o_ts_units').textContent = n1;
                }
            }
            if (document.getElementById("o_group_space").checked) {
                if (document.getElementById("o_add_space_after_punctuation").checked) {
                    [sText, n1] = this.formatText(sText, "add_space_after_punctuation");
                    [sText, n2] = this.formatText(sText, "add_space_repair");
                    document.getElementById('res_o_add_space_after_punctuation').textContent = n1 - n2;
                }
                if (document.getElementById("o_add_space_around_hyphens").checked) {
                    [sText, n1] = this.formatText(sText, "add_space_around_hyphens");
                    document.getElementById('res_o_add_space_around_hyphens').textContent = n1;
                }
                document.getElementById("o_group_space").checked = false;
                this.switchGroup("o_group_space");
            }
            document.getElementById('textformatter-progressbar').value = 3;

            // suppression
            if (document.getElementById("o_group_delete").checked) {
                if (document.getElementById("o_erase_non_breaking_hyphens").checked) {
                    [sText, n1] = this.formatText(sText, "erase_non_breaking_hyphens");
                    document.getElementById('res_o_erase_non_breaking_hyphens').textContent = n1;
                }
                document.getElementById("o_group_delete").checked = false;
                this.switchGroup("o_group_delete");
            }
            document.getElementById('textformatter-progressbar').value = 4;

            // signes typographiques
            if (document.getElementById("o_group_typo").checked) {
                if (document.getElementById("o_ts_apostrophe").checked) {
                    [sText, n1] = this.formatText(sText, "ts_apostrophe");
                    document.getElementById('res_o_ts_apostrophe').textContent = n1;
                }
                if (document.getElementById("o_ts_ellipsis").checked) {
                    [sText, n1] = this.formatText(sText, "ts_ellipsis");
                    document.getElementById('res_o_ts_ellipsis').textContent = n1;
                }
                if (document.getElementById("o_ts_dash_start").checked) {
                    if (document.getElementById("o_ts_m_dash_start").checked) {
                        [sText, n1] = this.formatText(sText, "ts_m_dash_start");
                    } else {
                        [sText, n1] = this.formatText(sText, "ts_n_dash_start");
                    }
                    document.getElementById('res_o_ts_dash_start').textContent = n1;
                }
                if (document.getElementById("o_ts_dash_middle").checked) {
                    if (document.getElementById("o_ts_m_dash_middle").checked) {
                        [sText, n1] = this.formatText(sText, "ts_m_dash_middle");
                    } else {
                        [sText, n1] = this.formatText(sText, "ts_n_dash_middle");
                    }
                    document.getElementById('res_o_ts_dash_middle').textContent = n1;
                }
                if (document.getElementById("o_ts_quotation_marks").checked) {
                    [sText, n1] = this.formatText(sText, "ts_quotation_marks");
                    document.getElementById('res_o_ts_quotation_marks').textContent = n1;
                }
                if (document.getElementById("o_ts_spell").checked) {
                    [sText, n1] = this.formatText(sText, "ts_spell");
                    document.getElementById('res_o_ts_spell').textContent = n1;
                }
                if (document.getElementById("o_ts_ligature").checked) {
                    // ligatures typographiques : fi, fl, ff, ffi, ffl, ft, st
                    if (document.getElementById("o_ts_ligature_do").checked) {
                        if (document.getElementById("o_ts_ligature_ffi").checked) {
                            [sText, n1] = this.formatText(sText, "ts_ligature_ffi_do");
                        }
                        if (document.getElementById("o_ts_ligature_ffl").checked) {
                            [sText, n2] = this.formatText(sText, "ts_ligature_ffl_do");
                        }
                        if (document.getElementById("o_ts_ligature_fi").checked) {
                            [sText, n3] = this.formatText(sText, "ts_ligature_fi_do");
                        }
                        if (document.getElementById("o_ts_ligature_fl").checked) {
                            [sText, n4] = this.formatText(sText, "ts_ligature_fl_do");
                        }
                        if (document.getElementById("o_ts_ligature_ff").checked) {
                            [sText, n5] = this.formatText(sText, "ts_ligature_ff_do");
                        }
                        if (document.getElementById("o_ts_ligature_ft").checked) {
                            [sText, n6] = this.formatText(sText, "ts_ligature_ft_do");
                        }
                        if (document.getElementById("o_ts_ligature_st").checked) {
                            [sText, n7] = this.formatText(sText, "ts_ligature_st_do");
                        }
                    }
                    if (document.getElementById("o_ts_ligature_undo").checked) {
                        if (document.getElementById("o_ts_ligature_ffi").checked) {
                            [sText, n1] = this.formatText(sText, "ts_ligature_ffi_undo");
                        }
                        if (document.getElementById("o_ts_ligature_ffl").checked) {
                            [sText, n2] = this.formatText(sText, "ts_ligature_ffl_undo");
                        }
                        if (document.getElementById("o_ts_ligature_fi").checked) {
                            [sText, n3] = this.formatText(sText, "ts_ligature_fi_undo");
                        }
                        if (document.getElementById("o_ts_ligature_fl").checked) {
                            [sText, n4] = this.formatText(sText, "ts_ligature_fl_undo");
                        }
                        if (document.getElementById("o_ts_ligature_ff").checked) {
                            [sText, n5] = this.formatText(sText, "ts_ligature_ff_undo");
                        }
                        if (document.getElementById("o_ts_ligature_ft").checked) {
                            [sText, n6] = this.formatText(sText, "ts_ligature_ft_undo");
                        }
                        if (document.getElementById("o_ts_ligature_st").checked) {
                            [sText, n7] = this.formatText(sText, "ts_ligature_st_undo");
                        }
                    }
                    document.getElementById('res_o_ts_ligature').textContent = n1 + n2 + n3 + n4 + n5 + n6 + n7;
                }
                document.getElementById("o_group_typo").checked = false;
                this.switchGroup("o_group_typo");
            }
            document.getElementById('textformatter-progressbar').value = 5;

            // divers
            if (document.getElementById("o_group_misc").checked) {
                if (document.getElementById("o_ordinals_no_exponant").checked) {
                    if (document.getElementById("o_ordinals_exponant").checked) {
                        [sText, n1] = this.formatText(sText, "ordinals_exponant");
                    } else {
                        [sText, n1] = this.formatText(sText, "ordinals_no_exponant");
                    }
                    document.getElementById('res_o_ordinals_no_exponant').textContent = n1;
                }
                if (document.getElementById("o_etc").checked) {
                    [sText, n1] = this.formatText(sText, "etc");
                    document.getElementById('res_o_etc').textContent = n1;
                }
                if (document.getElementById("o_missing_hyphens").checked) {
                    [sText, n1] = this.formatText(sText, "missing_hyphens");
                    document.getElementById('res_o_missing_hyphens').textContent = n1;
                }
                if (document.getElementById("o_ma_word").checked) {
                    [sText, n1] = this.formatText(sText, "ma_word");
                    if (document.getElementById("o_ma_1letter_lowercase").checked) {
                        [sText, n1] = this.formatText(sText, "ma_1letter_lowercase");
                        if (document.getElementById("o_ma_1letter_uppercase").checked) {
                            [sText, n1] = this.formatText(sText, "ma_1letter_uppercase");
                        }
                    }
                    document.getElementById('res_o_ma_word').textContent = n1;
                }
                document.getElementById("o_group_misc").checked = false;
                this.switchGroup("o_group_misc");
            }
            document.getElementById('textformatter-progressbar').value = document.getElementById('textformatter-progressbar').max;
            document.getElementById('textformatter-progressbar').value = "Formatage terminé.";
            // end of processing

            //window.setCursor("auto"); // restore pointer
            const t1 = Date.now();
            document.getElementById('textformatter-timer').textContent = this.getTimeRes((t1-t0)/1000);
        }
        catch (e) {
            console.error(e);
        }
        return sText;
    },
    formatText: function (sText, sOptName) {
        let nCount = 0;
        try {
            if (!oReplTable.hasOwnProperty(sOptName)) {
                console.log("# Error. TF: there is no option “" + sOptName+ "”.");
                return [sText, nCount];
            }
            for (let [zRgx, sRep] of oReplTable[sOptName]) {
                nCount += (sText.match(zRgx) || []).length;
                sText = sText.replace(zRgx, sRep);
            }
        }
        catch (e) {
            console.error(e);
        }
        return [sText, nCount];
    }
}


/* EVENTS */

window.addEventListener("load", function (xEvent) {
    oGrammarChecker.loadGC();
    //oGrammarChecker.fullTests();
}, false);

window.addEventListener("compose-window-init", function (xEvent) {
    oGrammarChecker.loadUI();
    oGrammarChecker.closePanel();
    oGrammarChecker.clearPreview();
    oTextFormatter.init();
}, true);

Added gc_lang/fr/mailext/content/overlay.xul.



















































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://grammarchecker/content/overlay.css" type="text/css"?>
<?xml-stylesheet type="text/css" href="chrome://messenger/skin/messenger.css"?>

<!DOCTYPE overlay SYSTEM "chrome://grammarchecker/locale/overlay.dtd">

<overlay id="grammarchecker-overlay"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
  <script type="application/javascript" src="chrome://messenger/content/customElements.js"/>
  <script type="application/javascript" src="resource://grammalecte/text.js"/>
  <script type="application/javascript" src="resource://grammalecte/fr/textformatter.js"/>
  <script type="application/javascript" src="overlay.js"/>
  <script type="application/javascript" src="file_handler.js"/>
  <script type="application/javascript" src="spellchecker.js"/>
  <script type="application/javascript" src="editor.js"/>


  <!--<stringbundleset id="stringbundleset">
    <stringbundle id="grammarchecker-strings" src="chrome://grammarchecker/locale/grammarchecker.properties"/>
  </stringbundleset>-->

  <commandset id="composerEditMenuItems" commandupdater="true"
              events="focus"
              oncommandupdate="goUpdateCommand('cmd_grammar')">
    <command id="cmd_grammar" label="&grammarchecker.button.label;" oncommand="goDoCommand('cmd_grammar')"/>
  </commandset>

  <menupopup id="taskPopup">
    <!-- menu tools -->
    <menuitem id="grammarchecker-hello" command="cmd_grammar"/>
  </menupopup>

  <popup id="msgComposeContext">
    <!-- contextual menu -->
    <menuitem id="context-grammarchecker" command="cmd_grammar"
        accesskey="&grammarchecker.accesskey;"/>
  </popup>

  <toolbarpalette id="MsgComposeToolbarPalette">
    <!-- toolbar palette -->
    <toolbarbutton id="grammarchecker-toolbar-button" command="cmd_grammar"
                   tooltiptext="&grammarchecker.tooltip;"
                   class="toolbarbutton-1 chromeclass-toolbar-additional" />

    <toolbarbutton id="grammalecte-menu" type="menu-button" label="Grammalecte" class="toolbarbutton-1" tooltip="&grammarchecker.tooltip;" oncommand="">
      <menupopup id="gl-toolbar-popup">
        <menuitem id="gl-item-conj" class="menuitem-iconic" label="&grammalectemenu.textformatter;" oncommand="oTextFormatter.onOpenPanel(event);"/>
        <menuitem id="gl-item-conj" class="menuitem-iconic" label="&grammalectemenu.conjugueur;" oncommand="oGrammarChecker.onOpenConjugueur(event);"/>
        <menuseparator/>
        <menuitem id="gl-item-analyze" class="menuitem-iconic" label="&grammalectemenu.start;" oncommand="oGrammarChecker.onParseText(event);"/>
        <menuitem id="gl-item-options" class="menuitem-iconic" label="&grammalectemenu.gc_options;" onclick="oGrammarChecker.onOpenGCOptions(event);"/>
        <menuitem id="gl-item-options" class="menuitem-iconic" label="&grammalectemenu.spell_options;" onclick="oGrammarChecker.onOpenSpellOptions(event);"/>
        <menuitem id="gl-item-options" class="menuitem-iconic" label="&grammalectemenu.other_options;" onclick="oGrammarChecker.onOpenOptions(event);"/>
        <menuitem id="gl-item-options" class="menuitem-iconic" label="&grammalectemenu.lexicon_editor;" onclick="oGrammarChecker.onOpenLexiconEditor(event);"/>
        <menuseparator/>
        <menuitem id="gl-item-about" class="menuitem-iconic" label="&grammalectemenu.about;" oncommand="oGrammarChecker.onAbout(event);"/>
      </menupopup>
    </toolbarbutton>
  </toolbarpalette>


  <vbox id="appcontent">

    <!--

      TEXT FORMATTER PANEL

    -->
    <splitter id="textformatter-splitter" state="collapsed" collapse="after"><grippy /></splitter>

    <vbox id="textformatter-panel">

      <div class="dialogheader-title">Grammalecte · &textformatter; · &automated_replacements;</div>

      <hbox id="tf-columns">
        <vbox class="column" width="400">
          <!-- Supernumerary spaces -->
          <groupbox>
            <hbox class="groupbox-title">
              <label class="header"><checkbox id="o_group_ssp" class="option optiongroup" data-default="true" label="&tf_ssp;" /></label>
            </hbox>
            <vbox id="group_ssp" class="groupblock">
              <hbox class="blockopt underline">
                <checkbox id="o_start_of_paragraph" class="option" data-default="true" label="&tf_start_of_paragraph;" />
                <spacer flex="1" />
                <label id="res_o_start_of_paragraph" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_end_of_paragraph" class="option" data-default="true" label="&tf_end_of_paragraph;" />
                <spacer flex="1" />
                <label id="res_o_end_of_paragraph" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_between_words" class="option" data-default="true" label="&tf_between_words;" />
                <spacer flex="1" />
                <label id="res_o_between_words" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_before_punctuation" class="option" data-default="true" label="&tf_before_punctuation;" />
                <spacer flex="1" />
                <label id="res_o_before_punctuation" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_within_parenthesis" class="option" data-default="true" label="&tf_within_parenthesis;" />
                <spacer flex="1" />
                <label id="res_o_within_parenthesis" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_within_square_brackets" class="option" data-default="true" label="&tf_within_square_brackets;" />
                <spacer flex="1" />
                <label id="res_o_within_square_brackets" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_within_quotation_marks" class="option" data-default="true" label="&tf_within_quotation_marks;" />
                <spacer flex="1" />
                <label id="res_o_within_quotation_marks" class="result" />
              </hbox>
            </vbox>
          </groupbox>

          <!-- Missing spaces -->
          <groupbox>
            <hbox class="groupbox-title">
              <label class="header"><checkbox id="o_group_space" class="option optiongroup" data-default="true" label="&tf_space;" /></label>
            </hbox>
            <vbox id="group_space" class="groupblock">
              <hbox class="blockopt underline">
                <checkbox id="o_add_space_after_punctuation" class="option" data-default="true" label="&tf_add_space_after_punctuation;" />
                <spacer flex="1" />
                <label id="res_o_add_space_after_punctuation" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_add_space_around_hyphens" class="option" data-default="true" label="&tf_add_space_around_hyphens;" />
                <spacer flex="1" />
                <label id="res_o_add_space_around_hyphens" class="result" />
              </hbox>
            </vbox>
          </groupbox>

          <!-- Non breaking spaces -->
          <groupbox>
            <hbox class="groupbox-title">
              <label class="header"><checkbox id="o_group_nbsp" class="option optiongroup" data-default="true" label="&tf_nbsp;" /></label>
            </hbox>
            <vbox id="group_nbsp" class="groupblock">
              <hbox class="blockopt underline">
                <checkbox id="o_nbsp_before_punctuation" class="option" data-default="true" label="&tf_nbsp_before_punctuation;" />
                <!--<div class="secondoption">
                  <checkbox id="o_nnbsp_before_punctuation" class="option" />
                  <label="o_nnbsp_before_punctuation" class="opt_lbl smallw">fines<span>sauf avec “:”</span></label>
                </div>-->
                <spacer flex="1" />
                <label id="res_o_nbsp_before_punctuation" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_nbsp_within_quotation_marks" class="option" data-default="true" label="&tf_nbsp_within_quotation_marks;" />
                <!--<div class="secondoption">
                  <checkbox id="o_nnbsp_within_quotation_marks" class="option" />
                  <label="o_nnbsp_within_quotation_marks" class="opt_lbl smallw">fines</label>
                </div>-->
                <spacer flex="1" />
                <label id="res_o_nbsp_within_quotation_marks" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                  <checkbox id="o_nbsp_before_symbol" class="option" data-default="true" label="&tf_nbsp_before_symbol;" />
                  <spacer flex="1" />
                  <label id="res_o_nbsp_before_symbol" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_nbsp_within_numbers" class="option" data-default="true" label="&tf_nbsp_within_numbers;" />
                <!--<div class="secondoption">
                  <checkbox id="o_nnbsp_within_numbers" class="option" />
                  <label="o_nnbsp_within_numbers" class="opt_lbl smallw">fines</label>
                </div>-->
                <spacer flex="1" />
                <label id="res_o_nbsp_within_numbers" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_nbsp_before_units" class="option" data-default="true" label="&tf_nbsp_before_units;" />
                <spacer flex="1" />
                <label id="res_o_nbsp_before_units" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_nbsp_titles" class="option" data-default="true" label="&tf_nbsp_titles;" />
                <spacer flex="1" />
                <label id="res_o_nbsp_titles" class="result" />
              </hbox>
            </vbox>
          </groupbox>

          <!-- Deletions -->
          <groupbox>
            <hbox class="groupbox-title">
              <label class="header"><checkbox id="o_group_delete" class="option optiongroup" data-default="true" label="&tf_delete;" /></label>
            </hbox>
            <vbox id="group_delete" class="groupblock">
              <hbox class="blockopt underline">
                <checkbox id="o_erase_non_breaking_hyphens" class="option" data-default="true" label="&tf_erase_non_breaking_hyphens;" />
                <spacer flex="1" />
                <label id="res_o_erase_non_breaking_hyphens" class="result" />
              </hbox>
            </vbox>
          </groupbox>
        </vbox>

        <vbox class="column" width="400">
          <!-- Typographical signs -->
          <groupbox>
            <hbox class="groupbox-title">
              <label class="header"><checkbox id="o_group_typo" class="option optiongroup" data-default="true" label="&tf_typo;" /></label>
            </hbox>
            <vbox id="group_typo" class="groupblock">
              <hbox class="blockopt underline">
                <checkbox id="o_ts_apostrophe" class="option" data-default="true" label="&tf_ts_apostrophe;" />
                <spacer flex="1" />
                <label id="res_o_ts_apostrophe" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_ts_ellipsis" class="option" data-default="true" label="&tf_ts_ellipsis;" />
                <spacer flex="1" />
                <label id="res_o_ts_ellipsis" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_ts_dash_middle" class="option" data-default="true" label="&tf_ts_dash_middle;" />
                <spacer flex="1" />
                <label id="res_o_ts_dash_middle" class="result" />
              </hbox>
              <hbox class="blockopt">
                <spacer flex="1" />
                <radiogroup orient="horizontal">
                  <radio id="o_ts_m_dash_middle" class="option" data-default="false" label="&tf_emdash;" />
                  <radio id="o_ts_n_dash_middle" class="option" data-default="true" label="&tf_endash;" selected="true" />
                </radiogroup>
                <spacer flex="3" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_ts_dash_start" class="option" data-default="true" label="&tf_ts_dash_start;" />
                <spacer flex="1" />
                <label id="res_o_ts_dash_start" class="result" />
              </hbox>
              <hbox class="blockopt">
                <spacer flex="1" />
                <radiogroup orient="horizontal">
                  <radio id="o_ts_m_dash_start" class="option"  data-default="true" label="&tf_emdash;" selected="true" />
                  <radio id="o_ts_n_dash_start" class="option" data-default="false" label="&tf_endash;" />
                </radiogroup>
                <spacer flex="3" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_ts_quotation_marks" class="option" data-default="true" label="&tf_ts_quotation_marks;" />
                <spacer flex="1" />
                <label id="res_o_ts_quotation_marks" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_ts_units" class="option" data-default="true" label="&tf_ts_units;" />
                <spacer flex="1" />
                <label id="res_o_ts_units" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_ts_spell" class="option" data-default="true" label="&tf_ts_spell;" />
                <spacer flex="1" />
                <label id="res_o_ts_spell" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_ts_ligature" class="option" data-default="false" label="&tf_ts_ligature;" />
                <input type="radio" id="o_ts_ligature_do" name="liga" class="option" data-default="false" label="&tf_ts_ligature_do;" />
                <input type="radio" id="o_ts_ligature_undo" name="liga" class="option" data-default="true" label="&tf_ts_ligature_undo;" />
                <spacer flex="1" />
                <label id="res_o_ts_ligature" class="result" />
              </hbox>
              <hbox class="blockopt">
                <spacer flex="1" />
                <checkbox id="o_ts_ligature_ff" class="option" data-default="true" label="ff" />
                <checkbox id="o_ts_ligature_fi" class="option" data-default="true" label="fi" />
                <checkbox id="o_ts_ligature_ffi" class="option" data-default="true" label="ffi" />
                <checkbox id="o_ts_ligature_fl" class="option" data-default="true" label="fl" />
                <checkbox id="o_ts_ligature_ffl" class="option" data-default="true" label="ffl" />
                <checkbox id="o_ts_ligature_ft" class="option" data-default="true" label="ft" />
                <checkbox id="o_ts_ligature_st" class="option" data-default="false" label="st" />
                <spacer flex="2" />
              </hbox>
            </vbox>
          </groupbox>

          <!-- Misc -->
          <groupbox>
            <hbox class="groupbox-title">
              <label class="header"><checkbox id="o_group_misc" class="option optiongroup" data-default="true" label="&tf_misc;" /></label>
            </hbox>
            <vbox id="group_misc" class="groupblock">
              <hbox class="blockopt underline">
                <checkbox id="o_ordinals_no_exponant" class="option" data-default="true" label="&tf_ordinals_no_exponant;" />
                <checkbox id="o_ordinals_exponant" class="option" data-default="true" label="&tf_ordinals_exponant;" />
                <spacer flex="1" />
                <label id="res_o_ordinals_no_exponant" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_etc" class="option" data-default="true" label="&tf_etc;" />
                <spacer flex="1" />
                <label id="res_o_etc" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_missing_hyphens" class="option" data-default="true" label="&tf_missing_hyphens;" />
                <spacer flex="1" />
                <label id="res_o_missing_hyphens" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_ma_word" class="option" data-default="true" label="&tf_ma_word;" />
                <spacer flex="1" />
                <label id="res_o_ma_word" class="result" />
              </hbox>
              <hbox class="blockopt">
                <spacer flex="1" />
                <checkbox id="o_ma_1letter_lowercase" class="option" label="&tf_ma_1letter_lowercase;" />
                <checkbox id="o_ma_1letter_uppercase" class="option" label="&tf_ma_1letter_uppercase;" />
                <spacer flex="3" />
              </hbox>
            </vbox>
          </groupbox>

          <!-- Restructuration -->
          <!--<groupbox>
            <hbox class="groupbox-title">
              <label class="header"><checkbox id="o_group_struct" class="option optiongroup" data-default="false" label="&tf_struct;" /></label>
            </hbox>
            <vbox id="group_struct" class="groupblock">
              <hbox class="blockopt underline">
                <checkbox id="o_remove_hyphens_at_end_of_paragraphs" class="option" data-default="false" label="&tf_remove_hyphens_at_end_of_paragraphs;" />
                <spacer flex="1" />
                <label id="res_o_remove_hyphens_at_end_of_paragraphs" class="result" />
              </hbox>
              <hbox class="blockopt underline">
                <checkbox id="o_merge_contiguous_paragraphs" class="option" data-default="false" label="&tf_merge_contiguous_paragraphs;" />
                <spacer flex="1" />
                <label id="res_o_merge_contiguous_paragraphs" class="result" />
              </hbox>
            </vbox>
          </groupbox>-->

          <description id="textformatter-infomsg">&tf_infomsg;</description>
        </vbox>
      </hbox>

      <hbox id="textformatter-commands">
        <button id="reset" label="&tf.button.default;" oncommand="oTextFormatter.onReset(event);" />
        <!-- <html:progress id="textformatter-progressbar" max="470">-->
        <label id="textformatter-progressbar" width="470">...</label>
        <label id="textformatter-timer" width="50"></label>
        <button id="apply" label="&tf.button.apply;" oncommand="oTextFormatter.onApply(event);" />
        <button id="close" label="&tf.button.close;" oncommand="oTextFormatter.onClosePanel(event);" />
      </hbox>

    </vbox>


    <!--

      GRAMMAR CHECKING PANEL

    -->
    <splitter id="grammarchecker-splitter" state="collapsed" collapse="after"><grippy /></splitter>

    <vbox id="grammarchecker-panel">
      <div class="dialogheader-title">Grammalecte · &detected_mistakes;</div>
      <div id="grammalecte-errors"/>
      <hbox id="grammalecte-infobox">
        <label id="grammalecte-info" />
        <spacer flex="1" />
        <label id="closebutton" value="Fermer" onclick="oGrammarChecker.onClosePanel(event);" />
      </hbox>
    </vbox>

  </vbox>

</overlay>

Added gc_lang/fr/mailext/content/spell_options.css.









































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* CSS */

#grouptitle {
	font-size: 16px;
	font-weight: bold;
	color: hsl(0, 50%, 50%);
}

.option {
	font-size: 16px;
	font-weight: bold;
	color: hsl(210, 50%, 50%);
}

description {
	width: 340px;
}

.dicdescr {
	margin-left: 27px;
}

.disabled {
  opacity: .25;
}


.dialogheader-title {
  margin: 5px;
  padding: 5px 8px;
  border: 1px solid hsl(210, 50%, 80%);
  background-color: hsl(210, 50%, 50%);
  color: hsl(210, 10%, 90%);
  font-size: larger;
  font-weight: bold;
}

Added gc_lang/fr/mailext/content/spell_options.js.













































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 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 oDialogControl = {
    load: function () {
        try {
            // center window
            document.getElementById('grammalecte-spelloptions-window').centerWindowOnScreen();
            // Graphspell dictionaries
            document.getElementById('personal_dic').checked = prefs.getBoolPref('bPersonalDictionary');
            this.listen();
        }
        catch (e) {
            console.error(e);
        }
    },
    listen: function () {
        document.addEventListener("dialogaccept", (event) => {
            oDialogControl.setDictionaries();
        });
    },
    setDictionaries: function () {
        oSpellControl.init();
        this._setGraphspellDictionaries();
    },
    _setGraphspellDictionaries: function () {
        let bActivate = document.getElementById('personal_dic').checked;
        prefs.setBoolPref("bPersonalDictionary", bActivate);
    }
};

Added gc_lang/fr/mailext/content/spell_options.xul.

















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://grammarchecker/content/spell_options.css" type="text/css"?>

<!DOCTYPE dialog SYSTEM "chrome://grammarchecker/locale/spell_options.dtd">

<dialog
  id="grammalecte-spelloptions-window"
  title="&window.title;"
  orient="vertical"
  buttons="accept,cancel"
  width="400"
  height="300"
  onload="oDialogControl.load();"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <!-- Other elements go here -->

  <div class="dialogheader-title">&dialogheader.label;</div>

  <hbox>
    <!--
    <groupbox with="380">
      <caption id="grouptitle" label="&grouptitle_hunspell;" />
      <description id="warning">&warning_hunspell;</description>

      <checkbox id="fr-FR-modern" class="option" label="&option.modern.label;" />
      <description class="dicdescr">&option.modern.descr;</description>
      <checkbox id="fr-FR-classic" class="option" label="&option.classic.label;" />
      <description class="dicdescr">&option.classic.descr;</description>
      <checkbox id="fr-FR-reform" class="option" label="&option.reform.label;" />
      <description class="dicdescr">&option.reform.descr;</description>
      <checkbox id="fr-FR-classic-reform" class="option" label="&option.allvar.label;" />
      <description class="dicdescr">&option.allvar.descr;</description>
    </groupbox>
    -->

    <groupbox with="380">
      <caption id="grouptitle" label="&grouptitle_graphspell;" />
      <description id="warning">&warning_graphspell;</description>

      <checkbox id="main_dic" class="option" label="&option.main_dic.label;" disabled="true" checked="true" />
      <description class="dicdescr">&option.main_dic.descr;</description>

      <checkbox id="community_dic" class="option disabled" label="&option.community_dic.label;" disabled="true" />
      <description class="dicdescr disabled">&option.community_dic.descr;</description>

      <checkbox id="personal_dic" class="option" label="&option.personal_dic.label;" />
      <description class="dicdescr">&option.personal_dic.descr;</description>
    </groupbox>
  </hbox>

  <script type="application/javascript" src="spell_options.js"/>
  <script type="application/javascript" src="spellchecker.js"/>

</dialog>

Added gc_lang/fr/mailext/content/spellchecker.js.

































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 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
*/

"use strict";


const { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");


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) {
                console.log("Can’t initiate the spellchecker.");
                console.error(e);
            }
        }
    },
    getDictionariesList: function () {
        this.init();
        try {
            let l = {};
            let c = {};
            this.xSCEngine.getDictionaryList(l, c);
            return l.value;
        }
        catch (e) {
            console.error(e);
            return [];
        }
    },
    setDictionary: function (sLocale) {
        if (this.getDictionariesList().includes(sLocale)) {
            try {
                this.xSCEngine.dictionary = sLocale; // en-US, fr, etc.
                return true;
            }
            catch (e) {
                console.error(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) {
            console.error(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) {
            console.error(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);
            console.error(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);
            console.error(e);
        }
    },
    setExtensionDictFolder: function (sDictName, bActivate) {
        try {
            let that = this;
            let sPath = "/content/dictionaries/" + sDictName;
            AddonManager.getAddonByID("French-GC-TB@grammalecte.net")
            .then(function (xAddon) {
                let xURI = xAddon.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");
            console.error(e);
        }
    }
};

Added gc_lang/fr/mailext/defaults/preferences/grammarchecker.js.





















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
pref("extensions.grammarchecker.sGCOptions", "");
pref("extensions.grammarchecker.sTFOptions", "");
pref("extensions.grammarchecker.bDictModern", false);
pref("extensions.grammarchecker.bDictClassic", true);
pref("extensions.grammarchecker.bDictReform", false);
pref("extensions.grammarchecker.bDictClassicReform", false);
pref("extensions.grammarchecker.bCheckSignature", true);
pref("extensions.grammarchecker.bExtendedDictionary", false);
pref("extensions.grammarchecker.bCommunityDictionary", false);
pref("extensions.grammarchecker.bPersonalDictionary", true);

Added gc_lang/fr/mailext/gce_worker.js.

























































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
/*
    WORKER:
    https://developer.mozilla.org/en-US/docs/Web/API/Worker
    https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope


    JavaScript sucks.
    No module available in WebExtension at the moment! :(
    No require, no import/export.

    In Worker, we have importScripts() which imports everything in this scope.

    In order to use the same base of code with XUL-addon for Thunderbird and SDK-addon for Firefox,
    all modules have been “objectified”. And while they are still imported via “require”
    in the previous extensions, they are loaded as background scripts in WebExtension sharing
    the same memory space…

    When JavaScript become a modern language, “deobjectify” the modules…

    ATM, import/export are not available by default:
    — Chrome 60 – behind the Experimental Web Platform flag in chrome:flags.
    — Firefox 54 – behind the dom.moduleScripts.enabled setting in about:config.
    — Edge 15 – behind the Experimental JavaScript Features setting in about:flags.

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
*/

"use strict";


//console.log("[Worker] GC Engine Worker [start]");
//console.log(self);

importScripts("grammalecte/graphspell/helpers.js");
importScripts("grammalecte/graphspell/str_transform.js");
importScripts("grammalecte/graphspell/char_player.js");
importScripts("grammalecte/graphspell/suggest.js");
importScripts("grammalecte/graphspell/ibdawg.js");
importScripts("grammalecte/graphspell/spellchecker.js");
importScripts("grammalecte/text.js");
importScripts("grammalecte/graphspell/tokenizer.js");
importScripts("grammalecte/fr/conj.js");
importScripts("grammalecte/fr/mfsp.js");
importScripts("grammalecte/fr/phonet.js");
importScripts("grammalecte/fr/cregex.js");
importScripts("grammalecte/fr/gc_options.js");
importScripts("grammalecte/fr/gc_rules.js");
importScripts("grammalecte/fr/gc_rules_graph.js");
importScripts("grammalecte/fr/gc_engine.js");
importScripts("grammalecte/fr/lexicographe.js");
importScripts("grammalecte/tests.js");
/*
    Warning.
    Initialization can’t be completed at startup of the worker,
    for we need the path of the extension to load data stored in JSON files.
    This path is retrieved in background.js and passed with the event “init”.
*/


function createResponse (sActionDone, result, dInfo, bEnd, bError=false) {
    return {
        "sActionDone": sActionDone,
        "result": result, // can be of any type
        "dInfo": dInfo,
        "bEnd": bEnd,
        "bError": bError
    };
}

function createErrorResult (e, sDescr="no description") {
    return {
        "sType": "error",
        "sDescription": sDescr,
        "sMessage": e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message
    };
}

function showData (e) {
    for (let sParam in e) {
        console.log(sParam);
        console.log(e[sParam]);
    }
}


/*
    Message Event Object
    https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent
*/
onmessage = function (e) {
    let {sCommand, dParam, dInfo} = e.data;
    switch (sCommand) {
        case "init":
            init(dParam.sExtensionPath, dParam.dOptions, dParam.sContext, dInfo);
            break;
        case "parse":
            parse(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo);
            break;
        case "parseAndSpellcheck":
            parseAndSpellcheck(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo);
            break;
        case "parseAndSpellcheck1":
            parseAndSpellcheck1(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo);
            break;
        case "parseFull":
            parseFull(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo);
            break;
        case "getListOfTokens":
            getListOfTokens(dParam.sText, dInfo);
            break;
        case "getOptions":
            getOptions(dInfo);
            break;
        case "getDefaultOptions":
            getDefaultOptions(dInfo);
            break;
        case "setOptions":
            setOptions(dParam.sOptions, dInfo);
            break;
        case "setOption":
            setOption(dParam.sOptName, dParam.bValue, dInfo);
            break;
        case "resetOptions":
            resetOptions(dInfo);
            break;
        case "textToTest":
            textToTest(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo);
            break;
        case "fullTests":
            fullTests(dInfo);
            break;
        case "setDictionary":
            setDictionary(dParam.sDictionary, dParam.oDict, dInfo);
            break;
        case "setDictionaryOnOff":
            setDictionaryOnOff(dParam.sDictionary, dParam.bActivate, dInfo);
            break;
        case "getSpellSuggestions":
            getSpellSuggestions(dParam.sWord, dInfo);
            break;
        case "getVerb":
            getVerb(dParam.sVerb, dParam.bPro, dParam.bNeg, dParam.bTpsCo, dParam.bInt, dParam.bFem, dInfo);
            break;
        default:
            console.log("[Worker] Unknown command: " + sCommand);
            showData(e.data);
    }
}



let bInitDone = false;

let oSpellChecker = null;
let oTokenizer = null;
let oLxg = null;
let oTest = null;
let oLocution = null;


/*
    Technical note:
    This worker don’t work as a PromiseWorker (which returns a promise),  so when we send request
    to this worker, we can’t wait the return of the answer just after the request made.
    The answer is received by the background in another function (onmessage).
    That’s why the full text to analyze is send in one block, but analyse is returned paragraph
    by paragraph.
*/

function init (sExtensionPath, dOptions=null, sContext="JavaScript", dInfo={}) {
    try {
        if (!bInitDone) {
            console.log("[Worker] Loading… Extension path: " + sExtensionPath);
            conj.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/conj_data.json"));
            phonet.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/phonet_data.json"));
            mfsp.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/mfsp_data.json"));
            //console.log("[Worker] Modules have been initialized…");
            gc_engine.load(sContext, "sCSS", sExtensionPath+"grammalecte/graphspell/_dictionaries");
            oSpellChecker = gc_engine.getSpellChecker();
            oTest = new TestGrammarChecking(gc_engine, sExtensionPath+"/grammalecte/fr/tests_data.json");
            oTokenizer = new Tokenizer("fr");
            oLocution =  helpers.loadFile(sExtensionPath + "/grammalecte/fr/locutions_data.json");
            oLxg = new Lexicographe(oSpellChecker, oTokenizer, oLocution);
            if (dOptions !== null) {
                if (!(dOptions instanceof Map)) {
                    dOptions = helpers.objectToMap(dOptions);
                }
                gc_engine.setOptions(dOptions);
            }
            //tests();
            bInitDone = true;
        } else {
            console.log("[Worker] Already initialized…")
        }
        // we always retrieve options from the gc_engine, for setOptions filters obsolete options
        dOptions = helpers.mapToObject(gc_engine.getOptions());
        postMessage(createResponse("init", dOptions, dInfo, true));
    }
    catch (e) {
        console.error(e);
        postMessage(createResponse("init", createErrorResult(e, "init failed"), dInfo, true, true));
    }
}


function parse (sText, sCountry, bDebug, bContext, dInfo={}) {
    sText = sText.replace(/­/g, "").normalize("NFC");
    for (let sParagraph of text.getParagraph(sText)) {
        let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, bContext);
        postMessage(createResponse("parse", aGrammErr, dInfo, false));
    }
    postMessage(createResponse("parse", null, dInfo, true));
}

function parseAndSpellcheck (sText, sCountry, bDebug, bContext, dInfo={}) {
    let i = 0;
    sText = sText.replace(/­/g, "").normalize("NFC");
    for (let sParagraph of text.getParagraph(sText)) {
        let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, null, bContext);
        let aSpellErr = oSpellChecker.parseParagraph(sParagraph);
        postMessage(createResponse("parseAndSpellcheck", {sParagraph: sParagraph, iParaNum: i, aGrammErr: aGrammErr, aSpellErr: aSpellErr}, dInfo, false));
        i += 1;
    }
    postMessage(createResponse("parseAndSpellcheck", null, dInfo, true));
}

function parseAndSpellcheck1 (sParagraph, sCountry, bDebug, bContext, dInfo={}) {
    sParagraph = sParagraph.replace(/­/g, "").normalize("NFC");
    let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, null, bContext);
    let aSpellErr = oSpellChecker.parseParagraph(sParagraph);
    postMessage(createResponse("parseAndSpellcheck1", {sParagraph: sParagraph, aGrammErr: aGrammErr, aSpellErr: aSpellErr}, dInfo, true));
}

function parseFull (sText, sCountry, bDebug, bContext, dInfo={}) {
    let i = 0;
    sText = sText.replace(/­/g, "").normalize("NFC");
    for (let sParagraph of text.getParagraph(sText)) {
        let lSentence = gc_engine.parse(sParagraph, sCountry, bDebug, null, bContext, true);
        console.log("*", lSentence);
        postMessage(createResponse("parseFull", {sParagraph: sParagraph, iParaNum: i, lSentence: lSentence}, dInfo, false));
        i += 1;
    }
    postMessage(createResponse("parseFull", null, dInfo, true));
}

function getListOfTokens (sText, dInfo={}) {
    // lexicographer
    try {
        sText = sText.replace(/­/g, "").normalize("NFC");
        for (let sParagraph of text.getParagraph(sText)) {
            if (sParagraph.trim() !== "") {
                postMessage(createResponse("getListOfTokens", oLxg.getListOfTokensReduc(sParagraph, true), dInfo, false));
            }
        }
        postMessage(createResponse("getListOfTokens", null, dInfo, true));
    }
    catch (e) {
        console.error(e);
        postMessage(createResponse("getListOfTokens", createErrorResult(e, "no tokens"), dInfo, true, true));
    }
}

function getOptions (dInfo={}) {
    let dOptions = helpers.mapToObject(gc_engine.getOptions());
    postMessage(createResponse("getOptions", dOptions, dInfo, true));
}

function getDefaultOptions (dInfo={}) {
    let dOptions = helpers.mapToObject(gc_engine.getDefaultOptions());
    postMessage(createResponse("getDefaultOptions", dOptions, dInfo, true));
}

function setOptions (dOptions, dInfo={}) {
    if (!(dOptions instanceof Map)) {
        dOptions = helpers.objectToMap(dOptions);
    }
    gc_engine.setOptions(dOptions);
    dOptions = helpers.mapToObject(gc_engine.getOptions());
    postMessage(createResponse("setOptions", dOptions, dInfo, true));
}

function setOption (sOptName, bValue, dInfo={}) {
    console.log(sOptName+": "+bValue);
    if (sOptName) {
        gc_engine.setOption(sOptName, bValue);
        let dOptions = helpers.mapToObject(gc_engine.getOptions());
        postMessage(createResponse("setOption", dOptions, dInfo, true));
    }
}

function resetOptions (dInfo={}) {
    gc_engine.resetOptions();
    let dOptions = helpers.mapToObject(gc_engine.getOptions());
    postMessage(createResponse("resetOptions", dOptions, dInfo, true));
}

function tests () {
    console.log(conj.getConj("devenir", ":E", ":2s"));
    console.log(mfsp.getMasForm("emmerdeuse", true));
    console.log(mfsp.getMasForm("pointilleuse", false));
    console.log(phonet.getSimil("est"));
    let aRes = gc_engine.parse("Je suit...");
    for (let oErr of aRes) {
        console.log(text.getReadableError(oErr));
    }
}

function textToTest (sText, sCountry, bDebug, bContext, dInfo={}) {
    if (!gc_engine) {
        postMessage(createResponse("textToTest", "# Grammar checker not loaded.", dInfo, true));
        return;
    }
    sText = sText.replace(/­/g, "").normalize("NFC");
    let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext);
    let sMsg = "";
    for (let oErr of aGrammErr) {
        sMsg += text.getReadableError(oErr) + "\n";
    }
    if (sMsg == "") {
        sMsg =  "Aucune erreur détectée.";
    }
    postMessage(createResponse("textToTest", sMsg, dInfo, true));
}

function fullTests (dInfo={}) {
    if (!gc_engine) {
        postMessage(createResponse("fullTests", "# Grammar checker not loaded.", dInfo, true));
        return;
    }
    let dMemoOptions = gc_engine.getOptions();
    let dTestOptions = gc_engine.getDefaultOptions();
    dTestOptions.set("nbsp", true);
    dTestOptions.set("esp", true);
    dTestOptions.set("unit", true);
    dTestOptions.set("num", true);
    gc_engine.setOptions(dTestOptions);
    let sMsg = "";
    for (let sRes of oTest.testParse()) {
        sMsg += sRes + "\n";
        console.log(sRes);
    }
    gc_engine.setOptions(dMemoOptions);
    postMessage(createResponse("fullTests", sMsg, dInfo, true));
}


// SpellChecker

function setDictionary (sDictionary, oDict, dInfo) {
    if (!oSpellChecker) {
        postMessage(createResponse("setDictionary", "# Error. SpellChecker not loaded.", dInfo, true));
        return;
    }
    //console.log("setDictionary", sDictionary);
    switch (sDictionary) {
        case "main":
            oSpellChecker.setMainDictionary(oDict);
            break;
        case "community":
            oSpellChecker.setCommunityDictionary(oDict);
            break;
        case "personal":
            oSpellChecker.setPersonalDictionary(oDict);
            break;
        default:
            console.log("[worker] setDictionary: Unknown dictionary <"+sDictionary+">");
    }
    postMessage(createResponse("setDictionary", true, dInfo, true));
}

function setDictionaryOnOff (sDictionary, bActivate, dInfo) {
    if (!oSpellChecker) {
        postMessage(createResponse("setDictionary", "# Error. SpellChecker not loaded.", dInfo, true));
        return;
    }
    //console.log("setDictionaryOnOff", sDictionary, bActivate);
    switch (sDictionary) {
        case "community":
            if (bActivate) {
                oSpellChecker.activateCommunityDictionary();
            } else {
                oSpellChecker.deactivateCommunityDictionary();
            }
            break;
        case "personal":
            if (bActivate) {
                oSpellChecker.activatePersonalDictionary();
            } else {
                oSpellChecker.deactivatePersonalDictionary();
            }
            break;
        default:
            console.log("[worker] setDictionaryOnOff: Unknown dictionary <"+sDictionary+">");
    }
    postMessage(createResponse("setDictionaryOnOff", true, dInfo, true));
}

function getSpellSuggestions (sWord, dInfo) {
    if (!oSpellChecker) {
        postMessage(createResponse("getSpellSuggestions", "# Error. SpellChecker not loaded.", dInfo, true));
        return;
    }
    let i = 0;
    for (let aSugg of oSpellChecker.suggest(sWord)) {
        postMessage(createResponse("getSpellSuggestions", {sWord: sWord, aSugg: aSugg, iSuggBlock: i}, dInfo, true));
        i += 1;
    }
}


// Conjugueur

function getVerb (sWord, bPro, bNeg, bTpsCo, bInt, bFem, dInfo) {
    try {
        let oVerb = null;
        let oConjTable = null;
        if (conj.isVerb(sWord)) {
            oVerb = new Verb(sWord);
            oConjTable = oVerb.createConjTable(bPro, bNeg, bTpsCo, bInt, bFem);
        }
        postMessage(createResponse("getVerb", { oVerb: oVerb, oConjTable: oConjTable }, dInfo, true));
    }
    catch (e) {
        console.error(e);
        postMessage(createResponse("getVerb", createErrorResult(e, "no verb"), dInfo, true, true));
    }
}

Added gc_lang/fr/mailext/icon.png.

cannot compute difference between binary files

Added gc_lang/fr/mailext/locale/en/about.dtd.















>
>
>
>
>
>
>
1
2
3
4
5
6
7
<!ENTITY windowtitle "About Grammalecte">
<!ENTITY description1 "French">
<!ENTITY description2 "Grammar Checker">
<!ENTITY version "Version:">
<!ENTITY license "License:">
<!ENTITY thanks "Thanks to">
<!ENTITY contrib "and those who supported us…">

Added gc_lang/fr/mailext/locale/en/gc_options.dtd.









>
>
>
>
1
2
3
4
<!ENTITY window.title "Grammalecte · Grammar options">
<!ENTITY defaultbutton.label "Default">
<!ENTITY dialogheader.label "Grammar options">
${gc_options_labels_en}

Added gc_lang/fr/mailext/locale/en/grammarchecker.properties.







>
>
>
1
2
3
processingMessage=Parsing in progress…
errorMessage=Error…
noErrorMessage=No error detected.

Added gc_lang/fr/mailext/locale/en/lex_editor.dtd.



































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!ENTITY window.title "Grammalecte · Lexical editor lexical">

<!ENTITY button.cancel.label "Close">

<!ENTITY tab.editor.label "Add">
<!ENTITY tab.lexicon.label "Lexicon">
<!ENTITY tab.search.label "Search">
<!ENTITY tab.info.label "Informations">

<!ENTITY dialogheader.newword.label "New word (lemma)">
<!ENTITY dialogheader.generated_words.label "Generated words">
<!ENTITY dialogheader.lexicon.label "Your lexicon">
<!ENTITY dialogheader.search.similar.label "Similar spellings">
<!ENTITY dialogheader.search.regex.label "Regular expressions">
<!ENTITY dialogheader.search.result.label "Result">
<!ENTITY dialogheader.info.label "Informations">
<!ENTITY dialogheader.tags.label "Meaning of tags">

Added gc_lang/fr/mailext/locale/en/options.dtd.













>
>
>
>
>
>
1
2
3
4
5
6
<!ENTITY window.title "Grammalecte · Options">
<!ENTITY dialogheader.label "Other options">
<!ENTITY check_signature.label "Check signature text (in text mode)">
<!ENTITY check_signature.accesskey "C">


Added gc_lang/fr/mailext/locale/en/overlay.dtd.













































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<!ENTITY grammarchecker.button.label "Grammar">
<!ENTITY grammarchecker.accesskey "G">
<!ENTITY grammarchecker.tooltip "Grammar checking">

<!ENTITY grammalectemenu.textformatter "Text formatter…">
<!ENTITY grammalectemenu.conjugueur "Conjugation tool…">
<!ENTITY grammalectemenu.start "Proofread the text…">
<!ENTITY grammalectemenu.spell_options "Spelling options…">
<!ENTITY grammalectemenu.gc_options "Grammar options…">
<!ENTITY grammalectemenu.lexicon_editor "Lexical editor…">
<!ENTITY grammalectemenu.other_options "Other options…">
<!ENTITY grammalectemenu.about "About Grammalecte…">

<!ENTITY detected_mistakes "Detected mistakes">

<!ENTITY textformatter "Text Formatter">
<!ENTITY automated_replacements "Automated replacements">
<!ENTITY tf.button.default "Default">
<!ENTITY tf.button.apply "Apply">
<!ENTITY tf.button.close "Close">

<!ENTITY tf_title "Text Formatter">
<!ENTITY tf_ssp "Supernumerary spaces">
<!ENTITY tf_start_of_paragraph "At the beginning of paragraph">
<!ENTITY tf_between_words "Between words">
<!ENTITY tf_end_of_paragraph "At the end of paragraph">
<!ENTITY tf_before_punctuation "Before dots (.), commas (,)">
<!ENTITY tf_within_parenthesis "Within parenthesis">
<!ENTITY tf_within_square_brackets "Within square brackets">
<!ENTITY tf_within_quotation_marks "Within “ and ”">
<!ENTITY tf_space "Missing spaces">
<!ENTITY tf_add_space_after_punctuation "After , ; : ? ! . …">
<!ENTITY tf_add_space_around_hyphens "Surrounding dashes">
<!ENTITY tf_nbsp "Non breaking spaces">
<!ENTITY tf_nbsp_before_punctuation "Before : ; ? and !">
<!ENTITY tf_nbsp_within_quotation_marks "With quoting marks « and »">
<!ENTITY tf_nbsp_before_symbol "Before &#x0025; ‰ € $ £ ¥ ˚C">
<!ENTITY tf_nbsp_within_numbers "Within numbers">
<!ENTITY tf_nbsp_before_units "Before units of measurement">
<!ENTITY tf_nbsp_titles "After titles">
<!ENTITY tf_delete "Deletions">
<!ENTITY tf_erase_non_breaking_hyphens "Soft hyphens">
<!ENTITY tf_typo "Typographical signs">
<!ENTITY tf_ts_apostrophe "Apostrophe (’)">
<!ENTITY tf_ts_ellipsis "Ellipsis (…)">
<!ENTITY tf_ts_dash_middle "Dashes:">
<!ENTITY tf_ts_dash_start "Dashes at beginning of paragraph:">
<!ENTITY tf_emdash "em dash (—)">
<!ENTITY tf_endash "en dash (–)">
<!ENTITY tf_ts_quotation_marks "Change quotation marks  (&quot; and ')">
<!ENTITY tf_ts_units "Interpuncts in units (N·m, Ω·m…)">
<!ENTITY tf_ts_spell "Ligatures (cœur…) and diacritics (ça, État…)">
<!ENTITY tf_ts_ligature "Ligatures">
<!ENTITY tf_ts_ligature_do "Set">
<!ENTITY tf_ts_ligature_undo "Unset">
<!ENTITY tf_misc "Miscellaneous">
<!ENTITY tf_ordinals_no_exponant "Ordinals (15e, XXIe…)">
<!ENTITY tf_ordinals_exponant "e → ᵉ">
<!ENTITY tf_etc "Et cætera, etc.">
<!ENTITY tf_missing_hyphens "Missing hyphens">
<!ENTITY tf_ma_word "Missing apostrophes">
<!ENTITY tf_ma_1letter_lowercase "single letters (j’ n’ m’ t’ s’ c’ d’ l’)">
<!ENTITY tf_ma_1letter_uppercase "Cap.">
<!ENTITY tf_struct "Restructuration [!]">
<!ENTITY tf_remove_hyphens_at_end_of_paragraphs "Remove syllabification hyphens at EOL/EOP">
<!ENTITY tf_merge_contiguous_paragraphs "Merge contiguous paragraphs [!]">
<!ENTITY tf_apply "Apply">
<!ENTITY tf_default "Default">
<!ENTITY tf_close "Close">
<!ENTITY tf_infomsg "The text formatter is a tool which automates correction of typographical errors. Use this tool with caution. Check your text after use. If you write in HTML mode, the use of this module is at your own risk.">

Added gc_lang/fr/mailext/locale/en/spell_options.dtd.

















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!ENTITY window.title "Grammalecte · Spelling options">
<!ENTITY dialogheader.label "Spelling options">

<!ENTITY grouptitle_graphspell "Graphspell Dictionaries (Grammalecte)">
<!ENTITY warning_graphspell "These dictionaries are used only when analyzing texts.">
<!ENTITY option.main_dic.label "Main dictionary">
<!ENTITY option.main_dic.descr "About 83 000 entries, 500 000 flexions. Not editable, not deactivable.">
<!ENTITY option.community_dic.label "Community dictionary">
<!ENTITY option.community_dic.descr "Feature to come..">
<!ENTITY option.personal_dic.label "Personal dictionary">
<!ENTITY option.personal_dic.descr "The personal dictionary is created and edited via the lexicon editor.">

<!ENTITY grouptitle_hunspell "Hunspell Dictionaries (Thunderbird)">
<!ENTITY warning_hunspell "These dictionaries are only used when you are writing texts (red underlining). Select dictionaries you want to see in textareas list. Selected dictionaries are added instantly. Whereas unselected dictionnaries will be removed only at Thunderbird’s restart.">
<!ENTITY option.modern.label "“Modern”">
<!ENTITY option.modern.descr "This dictionary offers the French spelling as it is written nowadays most often. This is the recommended dictionary if French is not your mother tongue or if you want only one correct spelling per word.">
<!ENTITY option.classic.label "“Classic” (recommanded)">
<!ENTITY option.classic.descr "This is the “Modern” dictionary plus classical spellings, some of them still widely used, others obsolete. This is the recommended dictionary if French is your native language.">
<!ENTITY option.reform.label "“Reform 1990”">
<!ENTITY option.reform.descr "With this dictionary, only the reformed spelling is recognized. As many of reformed spellings are considered erroneous for many people, this dictionary is unadvised. Reformed spellings commonly used are already included in the “Modern” dictionary.">
<!ENTITY option.allvar.label "“All variants”">
<!ENTITY option.allvar.descr "This dictionary contains all spelling variants, classical and reformed, and some others even rarer. This dictionary is unadvised for those who don’t know very well the French language.">


Added gc_lang/fr/mailext/locale/fr/about.dtd.















>
>
>
>
>
>
>
1
2
3
4
5
6
7
<!ENTITY windowtitle "À propos de Grammalecte">
<!ENTITY description1 "Correcteur grammatical">
<!ENTITY description2 "pour le français">
<!ENTITY version "Version :">
<!ENTITY license "Licence :">
<!ENTITY thanks "Avec le soutien de">
<!ENTITY contrib "et de nombreux contributeurs…">

Added gc_lang/fr/mailext/locale/fr/gc_options.dtd.









>
>
>
>
1
2
3
4
<!ENTITY window.title "Grammalecte · Options grammaticales">
<!ENTITY defaultbutton.label "Par défaut">
<!ENTITY dialogheader.label "Options grammaticales">
${gc_options_labels_fr}

Added gc_lang/fr/mailext/locale/fr/grammarchecker.properties.







>
>
>
1
2
3
processingMessage=Analyse en cours
errorMessage=Erreur…
noErrorMessage=Aucune erreur détectée.

Added gc_lang/fr/mailext/locale/fr/lex_editor.dtd.





































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!ENTITY window.title "Grammalecte · Éditeur lexical">

<!ENTITY button.cancel.label "Fermer">

<!ENTITY tab.editor.label "Ajout">
<!ENTITY tab.lexicon.label "Lexique">
<!ENTITY tab.search.label "Recherche">
<!ENTITY tab.info.label "Informations">

<!ENTITY dialogheader.newword.label "Nouveau mot (lemme)">
<!ENTITY dialogheader.generated_words.label "Mots générés">
<!ENTITY dialogheader.lexicon.label "Votre lexique">
<!ENTITY dialogheader.search.similar.label "Graphies similaires">
<!ENTITY dialogheader.search.regex.label "Expressions régulières">
<!ENTITY dialogheader.search.result.label "Résultat">
<!ENTITY dialogheader.info.label "Informations">
<!ENTITY dialogheader.tags.label "Signification des étiquettes">

Added gc_lang/fr/mailext/locale/fr/options.dtd.









>
>
>
>
1
2
3
4
<!ENTITY window.title "Grammalecte · Options">
<!ENTITY dialogheader.label "Autres options">
<!ENTITY check_signature.label "Vérifier le texte de la signature (en mode texte)">
<!ENTITY check_signature.accesskey "V">

Added gc_lang/fr/mailext/locale/fr/overlay.dtd.













































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<!ENTITY grammarchecker.button.label "Grammaire">
<!ENTITY grammarchecker.accesskey "G">
<!ENTITY grammarchecker.tooltip "Correction grammaticale">

<!ENTITY grammalectemenu.textformatter "Formateur de texte…">
<!ENTITY grammalectemenu.conjugueur "Conjugueur…">
<!ENTITY grammalectemenu.start "Analyser le texte…">
<!ENTITY grammalectemenu.spell_options "Options orthographiques…">
<!ENTITY grammalectemenu.gc_options "Options grammaticales…">
<!ENTITY grammalectemenu.lexicon_editor "Éditeur lexical…">
<!ENTITY grammalectemenu.other_options "Autres options…">
<!ENTITY grammalectemenu.about "À propos de Grammalecte…">

<!ENTITY detected_mistakes "Erreurs détectées">

<!ENTITY textformatter "Formateur de texte">
<!ENTITY automated_replacements "Remplacements automatisés">
<!ENTITY tf.button.default "Par défaut">
<!ENTITY tf.button.apply "Appliquer">
<!ENTITY tf.button.close "Fermer">

<!ENTITY tf_title "Formateur de texte">
<!ENTITY tf_ssp "Espaces surnuméraires">
<!ENTITY tf_start_of_paragraph "En début de paragraphe">
<!ENTITY tf_between_words "Entre les mots">
<!ENTITY tf_end_of_paragraph "En fin de paragraphe">
<!ENTITY tf_before_punctuation "Avant les points (.), les virgules (,)">
<!ENTITY tf_within_parenthesis "À l’intérieur des parenthèses">
<!ENTITY tf_within_square_brackets "À l’intérieur des crochets">
<!ENTITY tf_within_quotation_marks "À l’intérieur des guillemets “ et ”">
<!ENTITY tf_space "Espaces manquants">
<!ENTITY tf_add_space_after_punctuation "Après , ; : ? ! . …">
<!ENTITY tf_add_space_around_hyphens "Autour des tirets d’incise">
<!ENTITY tf_nbsp "Espaces insécables">
<!ENTITY tf_nbsp_before_punctuation "Avant : ; ? et !">
<!ENTITY tf_nbsp_within_quotation_marks "Avec les guillemets « et »">
<!ENTITY tf_nbsp_before_symbol "Avant &#x0025; ‰ € $ £ ¥ ˚C">
<!ENTITY tf_nbsp_within_numbers "À l’intérieur des nombres">
<!ENTITY tf_nbsp_before_units "Avant les unités de mesure">
<!ENTITY tf_nbsp_titles "Après les titres de civilité">
<!ENTITY tf_delete "Suppressions">
<!ENTITY tf_erase_non_breaking_hyphens "Tirets conditionnels">
<!ENTITY tf_typo "Signes typographiques">
<!ENTITY tf_ts_apostrophe "Apostrophe (’)">
<!ENTITY tf_ts_ellipsis "Points de suspension (…)">
<!ENTITY tf_ts_dash_middle "Tirets d’incise :">
<!ENTITY tf_ts_dash_start "Tirets en début de paragraphe :">
<!ENTITY tf_emdash "cadratin (—)">
<!ENTITY tf_endash "demi-cadratin (–)">
<!ENTITY tf_ts_quotation_marks "Modifier les guillemets droits (&quot; et ')">
<!ENTITY tf_ts_units "Points médians des unités (N·m, Ω·m…)">
<!ENTITY tf_ts_spell "Ligatures (cœur…) et diacritiques (ça, État…)">
<!ENTITY tf_ts_ligature "Ligatures">
<!ENTITY tf_ts_ligature_do "Faire">
<!ENTITY tf_ts_ligature_undo "Défaire">
<!ENTITY tf_misc "Divers">
<!ENTITY tf_ordinals_no_exponant "Ordinaux (15e, XXIe…)">
<!ENTITY tf_ordinals_exponant "e → ᵉ">
<!ENTITY tf_etc "Et cætera, etc.">
<!ENTITY tf_missing_hyphens "Traits d’union manquants">
<!ENTITY tf_ma_word "Apostrophes manquantes">
<!ENTITY tf_ma_1letter_lowercase "lettres isolées (j’ n’ m’ t’ s’ c’ d’ l’)">
<!ENTITY tf_ma_1letter_uppercase "Maj.">
<!ENTITY tf_struct "Restructuration [!]">
<!ENTITY tf_remove_hyphens_at_end_of_paragraphs "Enlever césures en fin de ligne/paragraphe [!]">
<!ENTITY tf_merge_contiguous_paragraphs "Fusionner les paragraphes contigus [!]">
<!ENTITY tf_apply "Appliquer">
<!ENTITY tf_default "Par défaut">
<!ENTITY tf_close "Fermer">
<!ENTITY tf_infomsg "Le formateur de texte est un outil qui automatise la correction d’erreurs typographiques. Utilisez l’outil avec prudence. Vérifiez votre texte après emploi. Si vous écrivez en mode HTML, l’emploi de ce module est à vos risques et périls.">

Added gc_lang/fr/mailext/locale/fr/spell_options.dtd.













































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!ENTITY window.title "Grammalecte · Options orthographiques">
<!ENTITY dialogheader.label "Options orthographiques">

<!ENTITY grouptitle_graphspell "Dictionnaires de Grammalecte">
<!ENTITY warning_graphspell "Ces dictionnaires ne sont utilisés que lors de l’analyse grammaticale.">
<!ENTITY option.main_dic.label "Dictionnaire principal">
<!ENTITY option.main_dic.descr "Environ 83 000 entrées, 500 000 flexions. Ni éditable, ni désactivable.">
<!ENTITY option.community_dic.label "Dictionnaire communautaire">
<!ENTITY option.community_dic.descr "Fonctionnalité à venir.">
<!ENTITY option.personal_dic.label "Dictionnaire personnel">
<!ENTITY option.personal_dic.descr "Le dictionnaire personnel est créé et édité via l’éditeur lexical.">

<!ENTITY grouptitle_hunspell "Dictionnaires Hunspell (Thunderbird)">
<!ENTITY warning_hunspell "Ces dictionnaires ne sont utilisés que lors de l’écriture de texte (soulignement rouge). Cochez les dictionnaires que vous voulez voir apparaître dans la liste des dictionnaires utilisables. L’ajout des dictionnaires se fait instantanément. En revanche, les dictionnaires ôtés ne disparaîtront qu’au redémarrage de Thunderbird.">
<!ENTITY option.modern.label "“Moderne”">
<!ENTITY option.modern.descr "Ce dictionnaire propose l’orthographe telle qu’elle est écrite aujourd’hui le plus couramment. C’est le dictionnaire recommandé si le français n’est pas votre langue maternelle ou si vous ne désirez qu’une seule graphie correcte par mot.">
<!ENTITY option.classic.label "“Classique” (recommandé)">
<!ENTITY option.classic.descr "Il s’agit du dictionnaire “Moderne”, avec des graphies classiques en sus, certaines encore communément utilisées, d’autres désuètes. C’est le dictionnaire recommandé si le français est votre langue maternelle.">
<!ENTITY option.reform.label "“Réforme 1990”">
<!ENTITY option.reform.descr "Avec ce dictionnaire, seule l’orthographe réformée est reconnue. Attendu que bon nombre de graphies réformées sont considérées comme erronées par beaucoup, ce dictionnaire est déconseillé. Les graphies passées dans l’usage sont déjà incluses dans le dictionnaire “Moderne”.">
<!ENTITY option.allvar.label "“Toutes variantes”">
<!ENTITY option.allvar.descr "Ce dictionnaire contient les variantes graphiques, classiques, réformées, ainsi que d’autres plus rares encore. Ce dictionnaire est déconseillé à ceux qui ne connaissent pas très bien la langue française.">

Added gc_lang/fr/mailext/manifest.json.











































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
  "manifest_version": 2,
  "applications": {
    "gecko": {
      "id": "${tb_identifier}",
      "strict_min_version": "68.0a1"
    }
  },
  "name": "${tb_name}",
  "description": "${description}",
  "version": "${version}",

  "author": "${author}",
  "homepage_url": "${link}",



  "legacy":  {
    "type": "xul"
  }
}

Added gc_lang/fr/mailext/skin/Algoo_logo.png.

cannot compute difference between binary files

Added gc_lang/fr/mailext/skin/LaMouette_small.png.

cannot compute difference between binary files

Added gc_lang/fr/mailext/skin/grammarcheck.png.

cannot compute difference between binary files

Added gc_lang/fr/mailext/skin/grammarcheck_disabled.png.

cannot compute difference between binary files

Added gc_lang/fr/mailext/skin/grammarcheck_small.png.

cannot compute difference between binary files

Added gc_lang/fr/mailext/skin/grammarcheck_small_disabled.png.

cannot compute difference between binary files

Added gc_lang/fr/mailext/skin/logo100.png.

cannot compute difference between binary files

Added gc_lang/fr/mailext/skin/logo120_text.png.

cannot compute difference between binary files

Added gc_lang/fr/mailext/worker/gce_worker.js.

































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// JavaScript

// Grammar checker engine
// PromiseWorker
// This code is executed in a separate thread (×20 faster too!!!)

// Firefox WTF: it’s impossible to use require as in the main thread here,
// so it is required to declare a resource in the file “chrome.manifest”.


"use strict";

// copy/paste
// https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/PromiseWorker.jsm

importScripts("resource://gre/modules/workers/require.js");
let PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js");

// Instantiate AbstractWorker (see below).
let worker = new PromiseWorker.AbstractWorker();

worker.dispatch = function(method, args = []) {
  // Dispatch a call to method `method` with args `args`
  return self[method](...args);
};
worker.postMessage = function(...args) {
  // Post a message to the main thread
  self.postMessage(...args);
};
worker.close = function() {
  // Close the worker
  self.close();
};
worker.log = function(...args) {
  // Log (or discard) messages (optional)
  dump("Worker: " + args.join(" ") + "\n");
};

// Connect it to message port.
self.addEventListener("message", msg => worker.handleMessage(msg));

// end of copy/paste


// no console here, use “dump”

let gce = null; // module: grammar checker engine
let text = null;
let tkz = null; // module: tokenizer
let lxg = null; // module: lexicographer
let helpers = null;

let oTokenizer = null;
let oSpellChecker = null;
let oLxg = null;

function loadGrammarChecker (sGCOptions="", sContext="JavaScript") {
    if (gce === null) {
        try {
            gce = require("resource://grammalecte/fr/gc_engine.js");
            helpers = require("resource://grammalecte/graphspell/helpers.js");
            text = require("resource://grammalecte/text.js");
            tkz = require("resource://grammalecte/graphspell/tokenizer.js");
            //lxg = require("resource://grammalecte/fr/lexicographe.js");
            oTokenizer = new tkz.Tokenizer("fr");
            gce.load(sContext, "sCSS");
            oSpellChecker = gce.getSpellChecker();
            if (sGCOptions !== "") {
                gce.setOptions(helpers.objectToMap(JSON.parse(sGCOptions)));
            }
            // we always retrieve options from the gce, for setOptions filters obsolete options
            return gce.getOptions().gl_toString();
        }
        catch (e) {
            console.log("# Error: " + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message);
        }
    }
}

function setDictionary (sTypeDic, sDicJSON) {
    try {
        console.log("set dictionary: " + sTypeDic);
        let oJSON = JSON.parse(sDicJSON);
        switch (sTypeDic) {
            case "extended":
                break;
            case "community":
                break;
            case "personal":
                oSpellChecker.setPersonalDictionary(oJSON);
                break;
            default:
                console.log("[GCE worker] unknown dictionary type");
        }
    }
    catch (e) {
        console.error(e);
    }
}

function parse (sText, sCountry, bDebug, bContext) {
    let aGrammErr = gce.parse(sText, sCountry, bDebug, bContext);
    return JSON.stringify(aGrammErr);
}

function parseAndSpellcheck (sText, sCountry, bDebug, bContext) {
    let aGrammErr = gce.parse(sText, sCountry, bDebug, null, bContext);
    let aSpellErr = oSpellChecker.parseParagraph(sText);
    return JSON.stringify({ aGrammErr: aGrammErr, aSpellErr: aSpellErr });
}

function suggest (sWord, nSuggLimit=10) {
    let lSugg = []
    for (let aSugg of oSpellChecker.suggest(sWord, nSuggLimit)) {
        lSugg.push(...aSugg);
    }
    return lSugg.join("|");
}

function getOptions () {
    return gce.getOptions().gl_toString();
}

function getDefaultOptions () {
    return gce.getDefaultOptions().gl_toString();
}

function setOptions (sGCOptions) {
    gce.setOptions(helpers.objectToMap(JSON.parse(sGCOptions)));
    return gce.getOptions().gl_toString();
}

function setOption (sOptName, bValue) {
    gce.setOptions(new Map([ [sOptName, bValue] ]));
    return gce.getOptions().gl_toString();
}

function resetOptions () {
    gce.resetOptions();
    return gce.getOptions().gl_toString();
}

function fullTests (sGCOptions="") {
    if (!gce || !oSpellChecker) {
        return "# Error: grammar checker or dictionary not loaded."
    }
    let dMemoOptions = gce.getOptions();
    if (sGCOptions) {
        gce.setOptions(helpers.objectToMap(JSON.parse(sGCOptions)));
    }
    let tests = require("resource://grammalecte/tests.js");
    let oTest = new tests.TestGrammarChecking(gce);
    let sAllRes = "";
    for (let sRes of oTest.testParse()) {
        console.log(sRes+"\n");
        sAllRes += sRes+"\n";
    }
    gce.setOptions(dMemoOptions);
    return sAllRes;
}

Changes to helpers.py.

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
..
92
93
94
95
96
97
98
99
100
101
102
        print("> unzip in: "+ spDest)
        spInstall = os.path.abspath(spDest)
        if os.path.isdir(spInstall):
            eraseFolder(spInstall)
            with zipfile.ZipFile(spfZip) as hZip:
                hZip.extractall(spDest)
        else:
            print("# folder not found")
    else:
        print("path destination is empty")


def eraseFolder (sp):
    "erase content of a folder"
    for sf in os.listdir(sp):
................................................................................
    for sf in os.listdir(spSrc):
        spfSrc = (spSrc + "/" + sf).strip("/ ")
        spfDst = (spDst + "/" + sf).strip("/ ")
        if os.path.isdir(spfSrc):
            if bRecursive:
                addFolderToZipAndFileFile(hZip, spfSrc, spfDst, dVars, bRecursive)
        else:
            if spfSrc.endswith((".py", ".js", ".css", ".xcu", ".xul", ".rdf", ".dtd", ".properties")):
                hZip.writestr(spfDst, fileFile(spfSrc, dVars))
            else:
                hZip.write(spfSrc, spfDst)







|







 







|



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
..
92
93
94
95
96
97
98
99
100
101
102
        print("> unzip in: "+ spDest)
        spInstall = os.path.abspath(spDest)
        if os.path.isdir(spInstall):
            eraseFolder(spInstall)
            with zipfile.ZipFile(spfZip) as hZip:
                hZip.extractall(spDest)
        else:
            print("# folder <" + spDest + "> not found")
    else:
        print("path destination is empty")


def eraseFolder (sp):
    "erase content of a folder"
    for sf in os.listdir(sp):
................................................................................
    for sf in os.listdir(spSrc):
        spfSrc = (spSrc + "/" + sf).strip("/ ")
        spfDst = (spDst + "/" + sf).strip("/ ")
        if os.path.isdir(spfSrc):
            if bRecursive:
                addFolderToZipAndFileFile(hZip, spfSrc, spfDst, dVars, bRecursive)
        else:
            if spfSrc.endswith((".py", ".js", ".json", ".css", ".xcu", ".xul", ".rdf", ".dtd", ".properties")):
                hZip.writestr(spfDst, fileFile(spfSrc, dVars))
            else:
                hZip.write(spfSrc, spfDst)

Changes to make.py.

286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
        print()

        try:
            buildjs = importlib.import_module("gc_lang."+sLang+".build")
        except ImportError:
            print("# No complementary builder <build.py> in folder gc_lang/"+sLang)
        else:
            buildjs.build(sLang, dVars, spLangPack)

    return dVars['version']


def copyGraphspellCore (bJavaScript=False):
    "copy Graphspell package in Grammalecte package"
    helpers.createCleanFolder("grammalecte/graphspell")







|







286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
        print()

        try:
            buildjs = importlib.import_module("gc_lang."+sLang+".build")
        except ImportError:
            print("# No complementary builder <build.py> in folder gc_lang/"+sLang)
        else:
            buildjs.build(sLang, dVars)

    return dVars['version']


def copyGraphspellCore (bJavaScript=False):
    "copy Graphspell package in Grammalecte package"
    helpers.createCleanFolder("grammalecte/graphspell")