Grammalecte  Changes On Branch a7c123cfd6a3c4b2

Changes In Branch comdic Through [a7c123cfd6] Excluding Merge-Ins

This is equivalent to a diff from 261187c93e to a7c123cfd6

2019-03-15
14:00
merge trunk check-in: 8ca82c9a7e user: olr tags: comdic
2019-02-25
12:20
[fr] faux positif, euphonie “vieil” check-in: b13291e1dc user: olr tags: trunk, fr
12:11
[fx] init: observePage, set this check-in: a7c123cfd6 user: olr tags: fx, comdic
09:32
merge trunk check-in: a6af007243 user: olr tags: comdic
2019-02-24
21:24
[fr] conjugaison: tout +être, + faux positif confusion a/à check-in: 261187c93e user: olr tags: trunk, fr
10:06
[fr] faux positifs check-in: 28fc480186 user: olr tags: trunk, fr

Modified gc_core/js/lang_core/gc_engine.js from [8e4be3285b] to [65c6c687b8].

40
41
42
43
44
45
46





47
48
49
50
51
52
53
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58







+
+
+
+
+







let _sAppContext = "";                                  // what software is running
let _dOptions = null;
let _dOptionsColors = null;
let _oSpellChecker = null;
let _oTokenizer = null;
let _aIgnoredRules = new Set();


function echo (x) {
    console.log(x);
    return true;
}


var gc_engine = {

    //// Informations

    lang: "${lang}",

Modified gc_lang/fr/config.ini from [7da1c762b3] to [f13ee44744].

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
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







+
-
+






-
+
+



-
+
+



-
+
+







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
dic_name = Français,Français (Classique/Moderne),Français (Réforme 1990)
dic_description = Français (Toutes variantes),Français (Classique),Français (Réforme 1990)
dic_filter = ,[*CMPX]$,[*RPX]$
dic_default_filename_py = fr-allvars
dic_default_filename_js = fr-allvars
# extended dictionary
lexicon_extended_src = lexicons/French.extended.lex
dic_extended_filename = fr.extended
dic_extended_name = Français - dictionnaire étendu
dic_extended_name = fr.extended
dic_extended_description = Français - dictionnaire étendu
# community dictionary
lexicon_community_src = lexicons/French.community.lex
dic_community_filename = fr.community
dic_community_name = Français - dictionnaire communautaire
dic_community_name = fr.community
dic_community_description = Français - dictionnaire communautaire
# personal dictionary
lexicon_personal_src = lexicons/French.personal.lex
dic_personal_filename = fr.personal
dic_personal_name = Français - dictionnaire personnel
dic_personal_name = fr.personal
dic_personal_description = Français - dictionnaire personnel
# 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

Modified gc_lang/fr/dictionnaire/genfrdic.py from [321fa8c26f] to [fef905a8c3].

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
522
523
524
525
526
527
528





529
530
531
532
533
534
535







-
-
-
-
-







                hDst.write(oFlex.__str__(oStatsLex))

    def writeGrammarCheckerLexicon (self, spfDst, version):
        echo(' * Lexique simplifié >> [ {} ] '.format(spfDst))
        with open(spfDst[:-4]+".lex", 'w', encoding='utf-8', newline="\n") as hDst:
            hDst.write(MPLHEADER)
            hDst.write("# Lexique simplifié pour Grammalecte v{}\n# Licence : MPL v2.0\n\n".format(version))
            hDst.write("## LangCode: fr\n")
            hDst.write("## LangName: Français\n")
            hDst.write("## DicName: fr.commun\n")
            hDst.write("## Description: Français commun (toutes variantes)\n")
            hDst.write("## Author: Olivier R.\n\n")
            hDst.write(Flexion.simpleHeader())
            for oFlex in self.lFlexions:
                hDst.write(oFlex.getGrammarCheckerRepr())

    def createFiles (self, spDst, lDictVars, nMode, bSimplified):
        sDicName = PREFIX_DICT_PATH + self.sVersion
        spDic = spDst + '/' + sDicName

Modified gc_lang/fr/modules-js/lexicographe.js from [50f4dc02f6] to [7979205e02].

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
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







-
-
+
+

+
+
+

-
+



-
+


-
-
+
+



-
+









+







            if (aRes) {
                yield aRes;
            }
        }
    }

    getListOfTokensReduc (sText, bInfo=true) {
        let aTokenList = this.getListOfTokens(sText.replace("'", "’").trim(), false);
        let iKey = 0;
        let lToken = this.getListOfTokens(sText.replace("'", "’").trim(), false);
        let iToken = 0;
        let aElem = [];
        if (lToken.length == 0) {
            return aElem;
        }
        do {
            let oToken = aTokenList[iKey];
            let oToken = lToken[iToken];
            let sMorphLoc = '';
            let aTokenTempList = [oToken];
            if (oToken.sType == "WORD" || oToken.sType == "WORD_ELIDED"){
                let iKeyTree = iKey + 1;
                let iLocEnd = iToken + 1;
                let oLocNode = this.oLocGraph[oToken.sValue.toLowerCase()];
                while (oLocNode) {
                    let oTokenNext = aTokenList[iKeyTree];
                    iKeyTree++;
                    let oTokenNext = lToken[iLocEnd];
                    iLocEnd++;
                    if (oTokenNext) {
                        oLocNode = oLocNode[oTokenNext.sValue.toLowerCase()];
                    }
                    if (oLocNode && iKeyTree <= aTokenList.length) {
                    if (oLocNode && iLocEnd <= lToken.length) {
                        sMorphLoc = oLocNode["_:_"];
                        aTokenTempList.push(oTokenNext);
                    } else {
                        break;
                    }
                }
            }

            if (sMorphLoc) {
                // we have a locution
                let sValue = '';
                for (let oTokenWord of aTokenTempList) {
                    sValue += oTokenWord.sValue+' ';
                }
                let oTokenLocution = {
                    'nStart': aTokenTempList[0].nStart,
                    'nEnd': aTokenTempList[aTokenTempList.length-1].nEnd,
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
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







-
+

+








-
+

-
+








                        sValue: oTokenLocution.sValue,
                        aLabel: aFormatedTag,
                        aSubElem: aSubElem
                    });
                } else {
                    aElem.push(oTokenLocution);
                }
                iKey = iKey + aTokenTempList.length;
                iToken = iToken + aTokenTempList.length;
            } else {
                // No locution, we just add information
                if (bInfo) {
                    let aRes = this.getInfoForToken(oToken);
                    if (aRes) {
                        aElem.push(aRes);
                    }
                } else {
                    aElem.push(oToken);
                }
                iKey++;
                iToken++;
            }
        } while (iKey < aTokenList.length);
        } while (iToken < lToken.length);
        return aElem;
    }
}


if (typeof(exports) !== 'undefined') {
    exports.Lexicographe = Lexicographe;
}

Modified gc_lang/fr/oxt/DictOptions/LexiconEditor.py from [30177fb38e] to [4e150dc058].

407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
407
408
409
410
411
412
413

414
415
416
417
418
419
420
421







-
+







    @_waitPointer
    def saveLexicon (self):
        xGridDataModel = self.xGridModelLex.GridDataModel
        lEntry = []
        for i in range(xGridDataModel.RowCount):
            lEntry.append(xGridDataModel.getRowData(i))
        if lEntry:
            oDAWG = dawg.DAWG(lEntry, "S", "fr", "Français", "fr.personal")
            oDAWG = dawg.DAWG(lEntry, "S", "fr", "Français", "fr.personal", "Dictionnaire personnel")
            self.oPersonalDicJSON = oDAWG.getBinaryAsJSON()
            self.xOptionNode.setPropertyValue("personal_dic", json.dumps(self.oPersonalDicJSON, ensure_ascii=False))
            self.xSettingNode.commitChanges()
            self.xNumDic.Label = str(self.oPersonalDicJSON["nEntry"])
            self.xDateDic.Label = self.oPersonalDicJSON["sDate"]
        else:
            self.xOptionNode.setPropertyValue("personal_dic", "")

Modified gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-classique.aff from [a9c2422fab] to [5be819083a].

1
2
3
4
5
6
7

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

7
8
9
10
11
12
13
14






-
+







# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# AFFIXES DU DICTIONNAIRE ORTHOGRAPHIQUE FRANÇAIS “CLASSIQUE” v7.0
# par Olivier R. -- licence MPL 2.0
# Généré le 09-01-2019 à 18:08
# Généré le 11-01-2019 à 17:14
# Pour améliorer le dictionnaire, allez sur https://grammalecte.net/



SET UTF-8

WORDCHARS -’'1234567890.

Modified gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-moderne.aff from [cf1eb7064c] to [93c2cdd8ee].

1
2
3
4
5
6
7

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

7
8
9
10
11
12
13
14






-
+







# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# AFFIXES DU DICTIONNAIRE ORTHOGRAPHIQUE FRANÇAIS “MODERNE” v7.0
# par Olivier R. -- licence MPL 2.0
# Généré le 09-01-2019 à 18:08
# Généré le 11-01-2019 à 17:14
# Pour améliorer le dictionnaire, allez sur https://grammalecte.net/



SET UTF-8

WORDCHARS -’'1234567890.

Modified gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-reforme1990.aff from [b9b9132d08] to [84824df4fa].

1
2
3
4
5
6
7

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

7
8
9
10
11
12
13
14






-
+







# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# AFFIXES DU DICTIONNAIRE ORTHOGRAPHIQUE FRANÇAIS “RÉFORME 1990” v7.0
# par Olivier R. -- licence MPL 2.0
# Généré le 09-01-2019 à 18:08
# Généré le 11-01-2019 à 17:14
# Pour améliorer le dictionnaire, allez sur https://grammalecte.net/



SET UTF-8

WORDCHARS -’'1234567890.

Modified gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-toutesvariantes.aff from [b23332b58c] to [3297933a4a].

1
2
3
4
5
6
7

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

7
8
9
10
11
12
13
14






-
+







# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# AFFIXES DU DICTIONNAIRE ORTHOGRAPHIQUE FRANÇAIS “TOUTES VARIANTES” v7.0
# par Olivier R. -- licence MPL 2.0
# Généré le 09-01-2019 à 18:08
# Généré le 11-01-2019 à 17:14
# Pour améliorer le dictionnaire, allez sur https://grammalecte.net/



SET UTF-8

WORDCHARS -’'1234567890.

Modified gc_lang/fr/rules.grx from [210aa28377] to [99443fada4].

503
504
505
506
507
508
509
510

511
512
513
514
515
516
517
503
504
505
506
507
508
509

510
511
512
513
514
515
516
517







-
+







__[s>(p_MM_point)__
    MM[.] <<- ~>> "MM "
__[s>(p_Mr_Mgr_Mme_point)__
    M(?:r|gr|me) [A-ZÉ]([.])(?=\W+[a-zéèêâîïû]) @@$ <<- ~1>> *

# Patronyme réduit à une seule lettre
__[s](p_prénom_lettre_point_patronyme)__
    ([A-ZÉÈÂÎ][\w-]+)[  ][A-ZÉÈÂ]([.])[  ]([A-ZÉÈÂ][\w-]+) @@0,$,$
    ([A-ZÉÈÂÎ][\w-]+)[  ][A-ZÉÈÂ]([.])[  ]([A-ZÉÈÂ][\w-]+) @@0,*,$
    <<- morph(\1, ":M[12]") and (morph(\3, ":(?:M[12]|V)") or not spell(\3)) ~2>> *
__[s>(p_prénom_lettre_point)__
    ([A-ZÉÈÂÎ][\w-]+)[  ][A-ZÉÈÂ]([.]) @@0,$
    <<- morph(\1, ":M[12]") and after("^\\W+[a-zéèêîïâ]") ~2>> _

# Patronymes composés avec Le/La/Les
__[s](p_patronyme_composé_avec_le_la_les)__
739
740
741
742
743
744
745
746

747
748
749
750
751
752
753
739
740
741
742
743
744
745

746
747
748
749
750
751
752
753







-
+







!!
!!!! Ponctuation de fin de paragraphe                                                             !!
!!
!!

__[i>/poncfin__
    ({w_1}) *$  @@0
    <<- before("\\w+(?:[.]|[   ][!?]) +(?:[A-ZÉÈÎ]\\w+|[ÀÔ])") -1>> \1.|\1 !|\1 ?                   # Il semble manquer une ponctuation finale (s’il s’agit d’un titre, le point final n’est pas requis).
    <<- before("\\w+(?:\\.|[   ][!?]) +(?:[A-ZÉÈÎ]\\w+|[ÀÔ])") -1>> \1.|\1 !|\1 ?                   # Il semble manquer une ponctuation finale (s’il s’agit d’un titre, le point final n’est pas requis).

TEST: __poncfin__ Vraiment. Quel {{ennui}}
TEST: Internet : le nouvel eldorado
TEST: OMC-FMI : Les nouveaux maîtres du monde


!!
20028
20029
20030
20031
20032
20033
20034
20035
20036
20037
20038
20039
20040
20041
20042
20028
20029
20030
20031
20032
20033
20034

20035
20036
20037
20038
20039
20040
20041







-







TEST: En 1944, au sortir des deux guerres ayant ravagé l’Europe, le CNR (conseil national de la résistance) avait bien mesuré les tenants et les aboutissants, c’est-à-dire les causes et les conséquences du ravage des inégalités et du jusqu’au boutisme de l’auto-défense d’une caste pour préserver ses acquis.
TEST: Ceux de l’Université sont plus perfectionnés
TEST: Celles de l’Institut sont plus perfectionnées
TEST: Judith A. Allan a aussi constaté que les études sur les crises de la masculinité ne proposent pas ou que très rarement d’indicateurs pour déterminer si une société est (trop) féminisée et si les hommes sont (réellement) en crise.
TEST: une procédure abusive pour fermer la porte qu’elles ont essayé d’ouvrir
TEST: En France, les hauts fonctionnaires gagnent en moyenne 7850 euros net
TEST: Il faut avoir du pouvoir et des ressources



!!! Tests historiques                                                                             !!

## Version 0.5.14
TEST: par le léger tissu de rayonne qui les protégeait en ce moment.

Modified gc_lang/fr/tb/content/lex_editor.js from [0814cc49a1] to [52568b3e47].

474
475
476
477
478
479
480
481

482
483
484
485
486
487
488
474
475
476
477
478
479
480

481
482
483
484
485
486
487
488







-
+







        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", xProgressNode);
            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 {

Modified gc_lang/fr/webext/background.js from [52ec35f9bc] to [5137139ec8].

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
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







-
-
+
+





-



-
-
-
-
+
+











-
-
-
+
+
+
+
+
+
+

-
-
+
+

-
-
+
+







-
-
-
+
+
+





-
-
-
+
+
+







    xGCEWorker.postMessage({
        sCommand: "setDictionaryOnOff",
        dParam: { sDictionary: sDictionary, bActivate: bActivate },
        dInfo: {}
    });
}

function initSCOptions (dSavedOptions) {
    if (!dSavedOptions.hasOwnProperty("sc_options")) {
function initSCOptions (oData) {
    if (!oData.hasOwnProperty("sc_options")) {
        browser.storage.local.set({"sc_options": {
            extended: true,
            community: true,
            personal: true
        }});
        setDictionaryOnOff("extended", true);
        setDictionaryOnOff("community", true);
        setDictionaryOnOff("personal", true);
    } else {
        let dOptions = dSavedOptions.sc_options;
        setDictionaryOnOff("extended", dOptions["extended"]);
        setDictionaryOnOff("community", dOptions["community"]);
        setDictionaryOnOff("personal", dOptions["personal"]);
        setDictionaryOnOff("community", oData.sc_options["community"]);
        setDictionaryOnOff("personal", oData.sc_options["personal"]);
    }
}

function setDictionary (sDictionary, oDictionary) {
    xGCEWorker.postMessage({
        sCommand: "setDictionary",
        dParam: { sDictionary: sDictionary, oDict: oDictionary },
        dInfo: {}
    });
}

function setSpellingDictionary (dSavedDictionary) {
    if (dSavedDictionary.hasOwnProperty("oExtendedDictionary")) {
        setDictionary("extended", dSavedDictionary["oExtendedDictionary"]);
function setSpellingDictionaries (oData) {
    if (oData.hasOwnProperty("oPersonalDictionary")) {
        // deprecated (to be removed in 2020)
        console.log("personal dictionary migration");
        browser.storage.local.set({ "personal_dictionary": oData["oPersonalDictionary"] });
        setDictionary("personal", oData["oPersonalDictionary"]);
        browser.storage.local.remove("oPersonalDictionary");
    }
    if (dSavedDictionary.hasOwnProperty("oCommunityDictionary")) {
        setDictionary("community", dSavedDictionary["oCommunityDictionary"]);
    if (oData.hasOwnProperty("personal_dictionary")) {
        setDictionary("personal", oData["personal_dictionary"]);
    }
    if (dSavedDictionary.hasOwnProperty("oPersonalDictionary")) {
        setDictionary("personal", dSavedDictionary["oPersonalDictionary"]);
    if (oData.hasOwnProperty("community_dictionary")) {
        setDictionary("community", oData["community_dictionary"]);
    }
}

function init () {
    if (bChrome) {
        browser.storage.local.get("gc_options", initGrammarChecker);
        browser.storage.local.get("ui_options", initUIOptions);
        browser.storage.local.get("oExtendedDictionary", setSpellingDictionary);
        browser.storage.local.get("oCommunityDictionary", setSpellingDictionary);
        browser.storage.local.get("oPersonalDictionary", setSpellingDictionary);
        browser.storage.local.get("personal_dictionary", setSpellingDictionaries);
        browser.storage.local.get("community_dictionary", setSpellingDictionaries);
        browser.storage.local.get("oPersonalDictionary", setSpellingDictionaries); // deprecated
        browser.storage.local.get("sc_options", initSCOptions);
        return;
    }
    browser.storage.local.get("gc_options").then(initGrammarChecker, showError);
    browser.storage.local.get("ui_options").then(initUIOptions, showError);
    browser.storage.local.get("oExtendedDictionary").then(setSpellingDictionary, showError);
    browser.storage.local.get("oCommunityDictionary").then(setSpellingDictionary, showError);
    browser.storage.local.get("oPersonalDictionary").then(setSpellingDictionary, showError);
    browser.storage.local.get("personal_dictionary").then(setSpellingDictionaries, showError);
    browser.storage.local.get("community_dictionary").then(setSpellingDictionaries, showError);
    browser.storage.local.get("oPersonalDictionary").then(setSpellingDictionaries, showError); // deprecated
    browser.storage.local.get("sc_options").then(initSCOptions, showError);
}

init();


browser.runtime.onInstalled.addListener(function (oDetails) {
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
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







+
+
+
+
+
+
+
+
+











+







        case "setDictionary":
        case "setDictionaryOnOff":
            xGCEWorker.postMessage(oRequest);
            break;
        case "openURL":
            browser.tabs.create({url: dParam.sURL});
            break;
        case "openConjugueurTab":
            openConjugueurTab();
            break;
        case "openLexiconEditor":
            openLexiconEditor(dParam["dictionary"]);
            break;
        case "openDictionaries":
            openDictionaries();
            break;
        default:
            console.log("[background] Unknown command: " + sCommand);
            console.log(oRequest);
    }
    //sendResponse({response: "response from background script"});
}

browser.runtime.onMessage.addListener(handleMessage);


function handleConnexion (xPort) {
    // Messages from tabs
    let iPortId = xPort.sender.tab.id; // identifier for the port: each port can be found at dConnx[iPortId]
    dConnx.set(iPortId, xPort);
    xPort.onMessage.addListener(function (oRequest) {
        let {sCommand, dParam, dInfo} = oRequest;
        switch (sCommand) {
            case "parse":
            case "parseAndSpellcheck":
275
276
277
278
279
280
281
282

283
284


285
286
287
288
289
290
291
286
287
288
289
290
291
292

293
294
295
296
297
298
299
300
301
302
303
304







-
+


+
+







browser.contextMenus.create({ id: "rightClickLxgEditableNode",  title: "Lexicographe (zone de texte)",              contexts: ["editable"] });
browser.contextMenus.create({ id: "rightClickGCEditableNode",   title: "Correction grammaticale (zone de texte)",   contexts: ["editable"] });
browser.contextMenus.create({ id: "separator_editable",         type: "separator",                                  contexts: ["editable"] });
// Page
browser.contextMenus.create({ id: "rightClickLxgPage",          title: "Lexicographe (page)",                       contexts: ["all"] }); // on all parts, due to unwanted selection
browser.contextMenus.create({ id: "rightClickGCPage",           title: "Correction grammaticale (page)",            contexts: ["all"] });
browser.contextMenus.create({ id: "separator_page",             type: "separator",                                  contexts: ["all"] });
// Conjugueur
// Tools
browser.contextMenus.create({ id: "conjugueur_window",          title: "Conjugueur [fenêtre]",                      contexts: ["all"] });
browser.contextMenus.create({ id: "conjugueur_tab",             title: "Conjugueur [onglet]",                       contexts: ["all"] });
browser.contextMenus.create({ id: "dictionaries",               title: "Dictionnaires",                             contexts: ["all"] });
browser.contextMenus.create({ id: "lexicon_editor",             title: "Éditeur lexical",                           contexts: ["all"] });
// Rescan page
browser.contextMenus.create({ id: "separator_rescan",           type: "separator",                                  contexts: ["editable"] });
browser.contextMenus.create({ id: "rescanPage",                 title: "Rechercher à nouveau les zones de texte",   contexts: ["editable"] });


browser.contextMenus.onClicked.addListener(function (xInfo, xTab) {
    // xInfo = https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/contextMenus/OnClickData
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
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







+
+
+
+
+
+


















+
+
+
+
+
+
+
+
+






-
-
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















-



-
-
-
-
-
-
+
+

        // conjugueur
        case "conjugueur_window":
            openConjugueurWindow();
            break;
        case "conjugueur_tab":
            openConjugueurTab();
            break;
        case "lexicon_editor":
            openLexiconEditor();
            break;
        case "dictionaries":
            openDictionaries();
            break;
        // rescan page
        case "rescanPage":
            let xPort = dConnx.get(xTab.id);
            xPort.postMessage({sActionDone: "rescanPage"});
            break;
        default:
            console.log("[Background] Unknown menu id: " + xInfo.menuItemId);
            console.log(xInfo);
            console.log(xTab);
    }
});


/*
    Keyboard shortcuts
*/
browser.commands.onCommand.addListener(function (sCommand) {
    switch (sCommand) {
        case "lexicographer":
            sendCommandToCurrentTab("shortcutLexicographer");
            break;
        case "text_formatter":
            sendCommandToCurrentTab("shortcutTextFormatter");
            break;
        case "grammar_checker":
            sendCommandToCurrentTab("shortcutGrammarChecker");
            break;
        case "conjugueur_tab":
            openConjugueurTab();
            break;
        case "conjugueur_window":
            openConjugueurWindow();
            break;
        case "lex_editor":
            openLexEditor();
        case "lexicon_editor":
            openLexiconEditor();
            break;
        case "dictionaries":
            openDictionaries();
            break;
    }
});


/*
    Tabs
*/
let nTabLexiconEditor = null;
let nTabDictionaries = null;
let nTabConjugueur = null;

browser.tabs.onRemoved.addListener(function (nTabId, xRemoveInfo) {
    switch (nTabId) {
        case nTabLexiconEditor: nTabLexiconEditor = null; break;
        case nTabDictionaries:  nTabDictionaries = null; break;
        case nTabConjugueur:    nTabConjugueur = null; break;
    }
});


/*
    Actions
*/

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

function sendCommandToTab (sCommand, iTab) {
    let xTabPort = dConnx.get(iTab);
    xTabPort.postMessage({sActionDone: sCommand, result: null, dInfo: null, bEnd: false, bError: false});
}

function sendCommandToCurrentTab (sCommand) {
    console.log(sCommand);
    if (bChrome) {
        browser.tabs.query({ currentWindow: true, active: true }, (lTabs) => {
            for (let xTab of lTabs) {
                console.log(xTab);
                browser.tabs.sendMessage(xTab.id, {sActionRequest: sCommand});
            }
        });
        return;
    }
    browser.tabs.query({ currentWindow: true, active: true }).then((lTabs) => {
        for (let xTab of lTabs) {
            console.log(xTab);
            browser.tabs.sendMessage(xTab.id, {sActionRequest: sCommand});
        }
    }, onError);
}

function openLexEditor () {
    if (bChrome) {
        browser.tabs.create({
            url: browser.extension.getURL("panel/lex_editor.html")
function openLexiconEditor (sName="__personal__") {
    if (nTabLexiconEditor === null) {
        if (bChrome) {
            browser.tabs.create({
                url: browser.extension.getURL("panel/lex_editor.html")
        });
        return;
    }
    let xLexEditor = browser.tabs.create({
        url: browser.extension.getURL("panel/lex_editor.html")
    });
    xLexEditor.then(onCreated, onError);
}

function openConjugueurTab () {
    if (bChrome) {
        browser.tabs.create({
            url: browser.extension.getURL("panel/conjugueur.html")
            }, onLexiconEditorOpened);
            return;
        }
        let xLexEditor = browser.tabs.create({
            url: browser.extension.getURL("panel/lex_editor.html")
        });
        xLexEditor.then(onLexiconEditorOpened, onError);
    }
    else {
        browser.tabs.update(nTabLexiconEditor, {active: true});
    }
}

function onLexiconEditorOpened (xTab) {
    nTabLexiconEditor = xTab.id;
}

function openDictionaries () {
    if (nTabDictionaries === null) {
        if (bChrome) {
            browser.tabs.create({
                url: browser.extension.getURL("panel/dictionaries.html")
            }, onDictionariesOpened);
            return;
        }
        let xLexEditor = browser.tabs.create({
            url: browser.extension.getURL("panel/dictionaries.html")
        });
        xLexEditor.then(onDictionariesOpened, onError);
    }
    else {
        browser.tabs.update(nTabDictionaries, {active: true});
    }
}

function onDictionariesOpened (xTab) {
    nTabDictionaries = xTab.id;
}

function openConjugueurTab () {
    if (nTabDictionaries === null) {
        if (bChrome) {
            browser.tabs.create({
                url: browser.extension.getURL("panel/conjugueur.html")
            }, onConjugueurOpened);
        return;
    }
    let xConjTab = browser.tabs.create({
        url: browser.extension.getURL("panel/conjugueur.html")
    });
    xConjTab.then(onCreated, onError);
            return;
        }
        let xConjTab = browser.tabs.create({
            url: browser.extension.getURL("panel/conjugueur.html")
        });
        xConjTab.then(onConjugueurOpened, onError);
    }
    else {
        browser.tabs.update(nTabConjugueur, {active: true});
    }
}

function onConjugueurOpened (xTab) {
    nTabConjugueur = xTab.id;
}

function openConjugueurWindow () {
    if (bChrome) {
        browser.windows.create({
            url: browser.extension.getURL("panel/conjugueur.html"),
            type: "popup",
            width: 710,
            height: 980
        });
        return;
    }
    let xConjWindow = browser.windows.create({
        url: browser.extension.getURL("panel/conjugueur.html"),
        type: "popup",
        width: 710,
        height: 980
    });
    xConjWindow.then(onCreated, onError);
}


function onCreated (xWindowInfo) {
    //console.log(`Created window: ${xWindowInfo.id}`);
}

function onError (error) {
    console.log(`Error: ${error}`);
function onError (e) {
    console.error(e);
}

Modified gc_lang/fr/webext/content_scripts/init.js from [9009a7100d] to [6e32e0cbb4].

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
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







-
-
+
-
+




-
+




-
+







                    this.nMenu += 1;
                }
            }
        }
    },

    observePage: function () {
        /*
            When a textarea is added via jascript we add the menu :)
        //    When a textarea is added via jascript we add the menu
        */
        let that = this;
        this.xObserver = new MutationObserver(function (mutations) {
            mutations.forEach(function (mutation) {
                for (let i = 0;  i < mutation.addedNodes.length;  i++){
                    if (mutation.addedNodes[i].tagName == "TEXTAREA") {
                        if (this.oOptions === null || this.oOptions.textarea) {
                        if (that.oOptions === null || that.oOptions.textarea) {
                            oGrammalecte.lMenu.push(new GrammalecteMenu(oGrammalecte.nMenu, mutation.addedNodes[i]));
                            oGrammalecte.nMenu += 1;
                        }
                    } else if (mutation.addedNodes[i].getElementsByTagName) {
                        if (this.oOptions === null || this.oOptions.textarea) {
                        if (that.oOptions === null || that.oOptions.textarea) {
                            for (let xNode of mutation.addedNodes[i].getElementsByTagName("textarea")) {
                                oGrammalecte.lMenu.push(new GrammalecteMenu(oGrammalecte.nMenu, xNode));
                                oGrammalecte.nMenu += 1;
                            }
                        }
                    }
                }
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
251
252
253
254
255
256
257

258
259
260
261
262
263
264







-







/*
    Connexion to the background
*/
let xGrammalectePort = browser.runtime.connect({name: "content-script port"});

xGrammalectePort.onMessage.addListener(function (oMessage) {
    let {sActionDone, result, dInfo, bEnd, bError} = oMessage;
    let sText = "";
    switch (sActionDone) {
        case "init":
            oGrammalecte.sExtensionUrl = oMessage.sUrl;
            // Start
            oGrammalecte.listenRightClick();
            oGrammalecte.createMenus();
            oGrammalecte.observePage();
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











































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







-
+
-
-
-
-
-
-





-
-
-
+
-
-
-








-
-
-
-
-
-
+
-





-
-
-
-
-
-
+








-
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
        /*
            Commands received from the context menu
            (Context menu are initialized in background)
        */
        // Grammar checker commands
        case "rightClickGCEditableNode":
            if (oGrammalecte.xRightClickedNode !== null) {
                oGrammalecte.startGCPanel(oGrammalecte.xRightClickedNode);
                parseAndSpellcheckEditableNode(oGrammalecte.xRightClickedNode);
                sText = (oGrammalecte.xRightClickedNode.tagName == "TEXTAREA") ? oGrammalecte.xRightClickedNode.value : oGrammalecte.xRightClickedNode.innerText;
                xGrammalectePort.postMessage({
                    sCommand: "parseAndSpellcheck",
                    dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false},
                    dInfo: {sTextAreaId: oGrammalecte.xRightClickedNode.id}
                });
            } else {
                oGrammalecte.showMessage("Erreur. Le node sur lequel vous avez cliqué n’a pas pu être identifié. Sélectionnez le texte à corriger et relancez le correcteur via le menu contextuel.");
            }
            break;
        case "rightClickGCPage":
            oGrammalecte.startGCPanel();
            xGrammalectePort.postMessage({
                sCommand: "parseAndSpellcheck",
            parseAndSpellcheckPage();
                dParam: {sText: oGrammalecte.getPageText(), sCountry: "FR", bDebug: false, bContext: false},
                dInfo: {}
            });
            break;
        case "rightClickGCSelectedText":
            oGrammalecte.startGCPanel();
            // selected text is sent to the GC worker in the background script.
            break;
        // Lexicographer commands
        case "rightClickLxgEditableNode":
            if (oGrammalecte.xRightClickedNode !== null) {
                oGrammalecte.startLxgPanel();
                sText = (oGrammalecte.xRightClickedNode.tagName == "TEXTAREA") ? oGrammalecte.xRightClickedNode.value : oGrammalecte.xRightClickedNode.innerText;
                xGrammalectePort.postMessage({
                    sCommand: "getListOfTokens",
                    dParam: {sText: sText},
                    dInfo: {sTextAreaId: oGrammalecte.xRightClickedNode.id}
                lexicographerEditableNode(oGrammalecte.xRightClickedNode);
                });
            } else {
                oGrammalecte.showMessage("Erreur. Le node sur lequel vous avez cliqué n’a pas pu être identifié. Sélectionnez le texte à analyser et relancez le lexicographe via le menu contextuel.");
            }
            break;
        case "rightClickLxgPage":
            oGrammalecte.startLxgPanel();
            xGrammalectePort.postMessage({
                sCommand: "getListOfTokens",
                dParam: {sText: oGrammalecte.getPageText()},
                dInfo: {}
            });
            lexicographerPage();
            break;
        case "rightClickLxgSelectedText":
            oGrammalecte.startLxgPanel();
            // selected text is sent to the GC worker in the background script.
            break;
        // Text formatter command
        case "rightClickTFEditableNode":
            if (oGrammalecte.xRightClickedNode !== null) {
                if (oGrammalecte.xRightClickedNode.tagName == "TEXTAREA") {
                if (oGrammalecte.xRightClickedNode.tagName == "TEXTAREA" || oGrammalecte.xRightClickedNode.tagName == "INPUT") {
                    oGrammalecte.startFTPanel(oGrammalecte.xRightClickedNode);
                } else {
                    oGrammalecte.showMessage("Cette zone de texte n’est pas réellement un champ de formulaire, mais un node HTML éditable. Le formateur de texte n’est pas disponible pour ce type de champ de saisie.");
                }
            } else {
                oGrammalecte.showMessage("Erreur. Le node sur lequel vous avez cliqué n’a pas pu être identifié.");
            }
            break;
        // rescan page command
        case "rescanPage":
            oGrammalecte.rescanPage();
            break;

    }
});


/*
    Other messages from background
*/
browser.runtime.onMessage.addListener(function (oMessage) {
    let {sActionRequest} = oMessage;
    let xActiveNode = document.activeElement;
    switch (sActionRequest) {
        /*
            Commands received from the keyboard (shortcuts)
        */
        case "shortcutLexicographer":
            if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT")) {
                lexicographerEditableNode(xActiveNode);
            } else {
                lexicographerPage();
            }
            break;
        case "shortcutTextFormatter":
            if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT")) {
                oGrammalecte.startFTPanel(xActiveNode);
            }
            break;
        case "shortcutGrammarChecker":
            if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT")) {
                parseAndSpellcheckEditableNode(xActiveNode);
            } else {
                parseAndSpellcheckPage();
            }
            break;
        default:
            console.log("[Content script] Unknown command: " + sActionDone);
    }
});


/*
    Actions
*/

function parseAndSpellcheckPage () {
    oGrammalecte.startGCPanel();
    xGrammalectePort.postMessage({
        sCommand: "parseAndSpellcheck",
        dParam: {sText: oGrammalecte.getPageText(), sCountry: "FR", bDebug: false, bContext: false},
        dInfo: {}
    });
}

function parseAndSpellcheckEditableNode (xNode) {
    oGrammalecte.startGCPanel(xNode);
    let sText = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT") ? xNode.value : xNode.innerText;
    xGrammalectePort.postMessage({
        sCommand: "parseAndSpellcheck",
        dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false},
        dInfo: {sTextAreaId: xNode.id}
    });
}

function lexicographerPage () {
    oGrammalecte.startLxgPanel();
    xGrammalectePort.postMessage({
        sCommand: "getListOfTokens",
        dParam: {sText: oGrammalecte.getPageText()},
        dInfo: {}
    });
}

function lexicographerEditableNode (xNode) {
    oGrammalecte.startLxgPanel();
    let sText = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT") ? xNode.value : xNode.innerText;
    xGrammalectePort.postMessage({
        sCommand: "getListOfTokens",
        dParam: {sText: sText},
        dInfo: {sTextAreaId: xNode.id}
    });
}

Modified gc_lang/fr/webext/content_scripts/panel_gc.js from [04fddfc5c0] to [d5e6934557].

60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74







-
+







    }

    start (xNode=null) {
        this.oTooltip.hide();
        this.clear();
        if (xNode) {
            this.oNodeControl.setNode(xNode);
            if (xNode.tagName != "TEXTAREA") {
            if (!(xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT")) {
                this.addMessage("Note : cette zone de texte n’est pas un champ de formulaire “textarea” mais un node HTML éditable. Une telle zone de texte est susceptible de contenir des éléments non textuels qui seront effacés lors de la correction.");
            }
        }
    }

    clear () {
        while (this.xParagraphList.firstChild) {
461
462
463
464
465
466
467
468

469
470
471
472
473
474
475
461
462
463
464
465
466
467

468
469
470
471
472
473
474
475







-
+







        this.dParagraph = new Map();
        this.bTextArea = null;
    }

    setNode (xNode) {
        this.clear();
        this.xNode = xNode;
        this.bTextArea = (xNode.tagName == "TEXTAREA");
        this.bTextArea = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT");
        this.xNode.disabled = true;
        this._loadText();
    }

    clear () {
        if (this.xNode !== null) {
            this.xNode.disabled = false;

Modified gc_lang/fr/webext/gce_worker.js from [f9f8b2256b] to [08f4a4604e].

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
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







-
-
-



















-
-
-
-
-
-
-







        return;
    }
    //console.log("setDictionary", sDictionary);
    switch (sDictionary) {
        case "main":
            oSpellChecker.setMainDictionary(oDict);
            break;
        case "extended":
            oSpellChecker.setExtendedDictionary(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 "extended":
            if (bActivate) {
                oSpellChecker.activateExtendedDictionary();
            } else {
                oSpellChecker.deactivateExtendedDictionary();
            }
            break;
        case "community":
            if (bActivate) {
                oSpellChecker.activateCommunityDictionary();
            } else {
                oSpellChecker.deactivateCommunityDictionary();
            }
            break;

Modified gc_lang/fr/webext/manifest.json from [756d794e19] to [f83d1e1e3e].

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
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







+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+



-
+
-
-


-
-
+
+
-
-
-
+
+
+
+
+







        "content_scripts/init.js"
      ],
      "run_at": "document_idle"
    }
  ],

  "commands": {
    "lexicographer": {
      "suggested_key": { "default": "Ctrl+Shift+L" },
      "description": "Ouvre le lexicographe"
    },
    "conjugueur_tab": {
      "suggested_key": {
        "default": "Ctrl+Shift+6"
      },
    "text_formatter": {
      "suggested_key": { "default": "Ctrl+Shift+F" },
      "description": "Ouvre le formateur de texte"
    },
    "grammar_checker": {
      "suggested_key": { "default": "Ctrl+Shift+V" },
      "description": "Ouvre le correcteur grammatical"
    },
    "conjugueur_tab": {
      "suggested_key": { "default": "Ctrl+Shift+6" },
      "description": "Ouvre le conjugueur dans un onglet"
    },
    "conjugueur_window": {
      "suggested_key": {
      "suggested_key": { "default": "Ctrl+Shift+7" },
        "default": "Ctrl+Shift+7"
      },
      "description": "Ouvre le conjugueur dans une fenêtre"
    },
    "lex_editor": {
      "suggested_key": {
    "lexicon_editor": {
      "suggested_key": { "default": "Ctrl+Shift+8" },
        "default": "Ctrl+Shift+8"
      },
      "description": "Ouvre l’éditeur lexical"
      "description": "Ouvre l’éditeur lexical"
    },
    "dictionaries": {
      "suggested_key": { "default": "Ctrl+Shift+9" },
      "description": "Ouvre le gestionnaire de dictionnaires communautaires"
    }
  },

  "web_accessible_resources": [
    "content_scripts/panel.css",
    "content_scripts/panel_tf.css",
    "content_scripts/panel_gc.css",
114
115
116
117
118
119
120


121
122

123
124
125
126
127
128
129
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142







+
+


+







    "grammalecte/fr/mfsp_data.json",
    "grammalecte/fr/phonet_data.json",
    "grammalecte/fr/tests_data.json",
    "img/logo-16.png"
  ],

  "permissions": [
    "*://localhost/*",
    "*://dic.grammalecte.net/*",
    "activeTab",
    "contextMenus",
    "cookies",
    "downloads",
    "storage"
  ],

  "chrome_settings_overrides": {
    "search_provider": {
      "name": "Grammalecte",

Added gc_lang/fr/webext/panel/dictionaries.css version [da94401062].


































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
   CSS Document
   White
   Design par Olivier R.
*/

* { margin: 0; padding: 0; }
img { border: none; }


/* Generic classes */

.fleft { float: left; }
.fright { float: right; }
.center { text-align: center; }
.right { text-align: right; }
.left { text-align: left; }
.justify { text-align: justify; }
.hidden { display: none; }
.clearer { clear: both; font-size: 0; height: 0; }

body {
    background: hsl(0, 0%, 100%) url(../img/lines.png);
    font: normal 16px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif;
    color: #505050;
}

.inbox {
    width: 800px;
    margin: 20px auto 10px auto;
    padding: 10px 30px 30px 30px;
    background: hsl(0, 0%, 100%);
    border: 2px solid hsl(210, 0%, 90%);
    border-radius: 20px;
}


#message_box {
    display: none;
    position: fixed;
    top: 33%;
    left: calc(50% - 325px);
}
#message {
    display: inline-block;
    padding: 5px 20px;
    width: 600px;
    background-color: hsl(0, 50%, 50%);
    color: hsl(0, 50%, 98%);
    border-style: solid;
    border-width: 3px 0 3px 3px;
    border-color: hsla(0, 50%, 40%, .5);
    border-radius: 5px 0 0 5px;
}
#message_close_button {
    display: inline-block;
    padding: 5px 10px;
    background-color: hsl(0, 50%, 40%);
    color: hsl(0, 90%, 90%);
    border-style: solid;
    border-width: 3px 3px 3px 0;
    border-color: hsla(0, 50%, 30%, .5);
    border-radius: 0 5px 5px 0;
    cursor: pointer;
}


h1 {
    margin: 5px 0 5px 0;
    color: hsl(210, 50%, 50%);
    font: bold 24px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif;
}
h2 {
    margin: 10px 0 2px 0;
    color: hsl(0, 50%, 50%);
    font: bold 20px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif;
}


input[type=text].large {
    display: inline-block;
    width: 250px;
    padding: 5px 10px;
    border: 2px solid hsl(0, 0%, 80%);
    border-radius: 3px;
    height: 24px;
    background: transparent;
    font: normal 20px Tahoma, "Ubuntu Condensed";
    color: hsl(0, 0%, 20%);
}

input[type=text].medium {
    display: inline-block;
    width: 175px;
    padding: 2px 5px;
    border: 2px solid hsl(0, 0%, 80%);
    border-radius: 3px;
    height: 20px;
    background: transparent;
    font: normal 18px Tahoma, "Ubuntu Condensed";
    color: hsl(0, 0%, 20%);
}

input[placeholder] {
    color: hsl(0, 0%, 50%);
}


#connect_panel {
    background-color: hsl(210, 50%, 90%);
    border-radius: 5px;
    padding: 3px 10px;
}
#submit_button {
    display: inline-block;
    padding: 1px 5px;
    background-color: hsl(210, 50%, 30%);
    color: hsl(210, 0%, 100%);
    border-radius: 3px;
    cursor: pointer;
}


.dic_button {
    margin: 2px;
    display: inline-block;
}
.dic_button_close {
    display: inline-block;
    padding: 1px 5px;
    background-color: hsl(0, 50%, 50%);
    color: hsl(0, 90%, 90%);
    border-style: solid;
    border-width: 1px 0 1px 1px;
    border-color: hsl(0, 50%, 45%);
    border-radius: 3px 0 0 3px;
    cursor: pointer;
}
.dic_button_label {
    display: inline-block;
    padding: 1px 10px;
    background-color: hsl(210, 50%, 94%);
    border-style: solid;
    border-width: 1px 1px 1px 0;
    border-color: hsl(210, 50%, 70%);
    border-radius: 0 3px 3px 0;
}

.apply {
    display: none;
    float: right;
    padding: 2px 10px;
    background: hsl(120, 50%, 30%);
    color: hsl(120, 50%, 96%);
    cursor: pointer;
    border-radius: 3px;
}


/*
    Table
*/
#wait_progress {
    width: 100%;
    height: 4px;
}

table {
    border: 1px solid hsl(210, 10%, 50%);
    width: 100%;
    font-size: 14px;
}
th {
    padding: 5px 10px;
    border-left: 1px solid hsl(210, 10%, 90%);
    text-align: left;
}
td {
    padding: 0 10px;
    vertical-align: top;
}
.delete_entry {
    cursor: pointer;
    font-weight: bold;
    color: hsl(0, 100%, 50%);
}
.select_entry {
    cursor: pointer;
    background-color: hsl(210, 50%, 30%);
    color: hsl(210, 50%, 100%);
    border-radius: 3px;
    text-align: center;
}

Added gc_lang/fr/webext/panel/dictionaries.html version [7b8437c981].


























































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<!DOCTYPE HTML>
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="dictionaries.css" />
    <title>Grammalecte · Dictionnaires communautaires</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  </head>

  <body>

    <div id="message_box">
      <div id="message">[néant]</div><div id="message_close_button">×</div>
    </div>

    <div class="inbox">
      <h1>Grammalecte · Dictionnaires communautaires</h1>

      <div id="connect_panel">
        <form id="connect_form" method="post" enctype="multipart/form-data">
          <p>
            Non connecté.
            <label for="email">E-mail : </label> <input type="email" id="email" name="email" />
            <label for="password">Mot de passe : </label> <input type="password" id="pw" name="pw" />
            <span id="submit_button">Se connecter</span>
            |
            <a href="http://dic.grammalecte.net" target="_blank">Inscription</a>
          </p>
        </form>
        <div id="connect_info">
          <p>
            Connecté. Identifiants : <span id="login_label"> </span>, <span id="email_label"> </span>
          </p>
        </div>
      </div>

      <h2>Dictionnaires sélectionnés</h2>
      <p id="dictionaries_list">[Aucun]<p>

      <div id="apply" class="apply">Appliquer les modifications</div>
      <h2><span id="num_dic">0</span> dictionnaires disponibles</h2>
      <progress id="wait_progress" value="0"></progress>
      <table id="dictionaries_table">
      </table>
    </div> <!-- inbox -->


    <script src="../grammalecte/graphspell/helpers.js"></script>
    <script src="../grammalecte/graphspell/char_player.js"></script>
    <script src="../grammalecte/graphspell/str_transform.js"></script>
    <script src="../grammalecte/graphspell/dawg.js"></script>
    <script src="../grammalecte/graphspell/ibdawg.js"></script>
    <script src="../grammalecte/graphspell/dic_merger.js"></script>
    <script src="../grammalecte/graphspell/spellchecker.js"></script>
    <script src="dictionaries.js"></script>
  </body>

</html>

Added gc_lang/fr/webext/panel/dictionaries.js version [185528ef06].


























































































































































































































































































































































































































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// JavaScript

"use strict";


// Chrome don’t follow the W3C specification:
// https://browserext.github.io/browserext/
let bChrome = false;
if (typeof(browser) !== "object") {
    var browser = chrome;
    bChrome = true;
}


/*
    Common functions
*/

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

function createNode  (sType, oAttr, oDataset=null) {
    try {
        let xNode = document.createElement(sType);
        Object.assign(xNode, oAttr);
        if (oDataset) {
            Object.assign(xNode.dataset, oDataset);
        }
        return xNode;
    }
    catch (e) {
        showError(e);
    }
}

function showElement (sElemId, sDisplay="block") {
    if (document.getElementById(sElemId)) {
        document.getElementById(sElemId).style.display = sDisplay;
    } else {
        console.log("HTML node named <" + sElemId + "> not found.")
    }
}

function hideElement (sElemId) {
    if (document.getElementById(sElemId)) {
        document.getElementById(sElemId).style.display = "none";
    } else {
        console.log("HTML node named <" + sElemId + "> not found.")
    }
}

async function hashText (sText, sAlgorithm = 'SHA-256') {
    let msgBuffer = new TextEncoder('utf-8').encode(sText);
    let hashBuffer = await crypto.subtle.digest(sAlgorithm, msgBuffer);
    let hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
}


const oMessage = {
    show: function (sMessage, nDelay=10000) {
        document.getElementById("message").textContent = sMessage;
        showElement("message_box");
        window.setTimeout(this.close, nDelay);
    },

    listen: function () {
        document.getElementById("message_close_button").addEventListener("click", (xEvent) => { this.close(); });
    },

    close: function () {
        hideElement("message_box");
    }
}


const oConnect = {
    bConnected: false,

    init: function () {
        if (bChrome) {
            browser.cookies.getAll({ domain: "localhost" }, this._init.bind(this));
            return;
        }
        let xPromise = browser.cookies.getAll({ domain: "localhost" });
        xPromise.then(this._init.bind(this), showError);
    },

    _init: function (lData) {
        for (let xCookie of lData) {
            console.log(xCookie.name, xCookie.value);
            this.bConnected = true;
        }
        if (this.bConnected) {
            hideElement("connect_form");
            showElement("connect_info");
        }
        else {
            showElement("connect_form");
            hideElement("connect_info");
        }
    },

    listen: function () {
        document.getElementById("submit_button").addEventListener("click", (xEvent) => { this.connect(); });
    },

    connect: function () {
        if (!this.checkValues()) {
            oMessage.show("Les valeurs des champs du formulaire ne sont pas conformes.");
            return;
        }
        let xForm = new FormData(document.getElementById('connect_form'));
        for (let [k, v] of xForm.entries()) {
            console.log("* ", k, v);
        }
        fetch("http://localhost/connect/", {
            method: "POST", // *GET, POST, PUT, DELETE, etc.
            //mode: "cors", // no-cors, cors, *same-origin
            //cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
            headers: {
                "Accept-Charset": "utf-8"
                //"Content-Type": "multipart/form-data",  // text/plain, application/json
            },
            credentials: "omit", // include, *same-origin, omit
            body: xForm
        })
        .then((response) => {
            if (response.ok) {
                if (response.status == 204) {
                    oMessage.show("Échec d’identification. Vérifiez l’e-mail et le mot de passe envoyés…");
                    return null;
                }
                for (let param in response) {
                    console.log(param, response[param]);
                }
                console.log(response.body);
                return response.json();
            } else {
                oMessage.show("Erreur. Le serveur ne semble pas en état de répondre. Veuillez réessayer ultérieurement.");
                return null;
            }
        })
        .then((response) => {
            if (response) {
                console.log("response: ", response);
            }
        })
        .catch((e) => {
            showError(e);
        });
    },

    checkValues () {
        if (document.getElementById("email").value === "") {
            return false;
        }
        let sEmail = document.getElementById("email").value;
        if (sEmail.search(/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/) === -1) {
            return false;
        }
        return true;
    },

    showId (sLogin, sEmail) {
        document.getElementById("login_label").textContent = sLogin;
        document.getElementById("email_label").textContent = sEmail;
        hideElement("connect_form");
        showElement("connect_info");
    }
}


class Table {

    constructor (sNodeId, lColumn, sProgressBarId, sResultId="", bDeleteButtons=true, bActionButtons) {
        this.sNodeId = sNodeId;
        this.xTable = document.getElementById(sNodeId);
        this.xApply = document.getElementById("apply");
        this.nColumn = lColumn.length;
        this.lColumn = lColumn;
        this.xProgressBar = document.getElementById(sProgressBarId);
        this.xNumEntry = document.getElementById(sResultId);
        this.lEntry = [];
        this.nEntry = 0;
        this.dSelectedDictionaries = new Map();
        this.lSelectedDictionaries = [];
        this.dDict = new Map();
        this.bDeleteButtons = bDeleteButtons;
        this.bActionButtons = bActionButtons;
        this._createHeader();
        this.listen();
    }

    _createHeader () {
        let xRowNode = createNode("tr");
        if (this.bDeleteButtons) {
            xRowNode.appendChild(createNode("th", { textContent: "·", width: "12px" }));
        }
        for (let sColumn of this.lColumn) {
            xRowNode.appendChild(createNode("th", { textContent: sColumn }));
        }
        this.xTable.appendChild(xRowNode);
    }

    clear () {
        while (this.xTable.firstChild) {
            this.xTable.removeChild(this.xTable.firstChild);
        }
        this.lEntry = [];
        this.nEntry = 0;
        this._createHeader();
        this.showEntryNumber();
    }

    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;
        }
        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();
    }

    init () {
        if (bChrome) {
            browser.storage.local.get("selected_dictionaries_list", this._init.bind(this));
            return;
        }
        let xPromise = browser.storage.local.get("selected_dictionaries_list");
        xPromise.then(this._init.bind(this), showError);
    }

    _init (oResult) {
        if (oResult.hasOwnProperty("selected_dictionaries_list")) {
            this.lSelectedDictionaries = oResult.selected_dictionaries_list;
            console.log(this.lSelectedDictionaries);
        }
        this.getDictionarieslist();
    }

    getDictionarieslist () {
        fetch("http://localhost/dictionaries/")
        .then((response) => {
            if (response.ok) {
                return response.json();
            } else {
                return null;
            }
        })
        .then((response) => {
            if (response) {
                this.fill(response);
                this.showSelectedDictionaries(true);
            } else {
                // todo
            }
        })
        .catch((e) => {
            showError(e);
        });
    }

    getDictionary (sId, sName) {
        console.log("get: "+sName);
        fetch("http://localhost/download/"+sName)
        .then((response) => {
            if (response.ok) {
                return response.json();
            } else {
                console.log("dictionary not loaded: " + sName);
                return null;
            }
        })
        .then((response) => {
            if (response) {
                this.selectEntry(sId, sName);
                this.dDict.set(sName, response);
                browser.storage.local.set({ "stored_dictionaries": this.dDict });
            } else {
                //
            }
        })
        .catch((e) => {
            showError(e);
        });
    }

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

    _addRow (lData) {
        let [nDicId, sName, nEntry, sDescription, sLastUpdate, ...data] = lData;
        let xRowNode = createNode("tr", { id: this.sNodeId + "_row_" + nDicId });
        if (this.bDeleteButtons) {
            xRowNode.appendChild(createNode("td", { textContent: "×", className: "delete_entry", title: "Effacer cette entrée" }, { id_entry: nDicId }));
        }
        xRowNode.appendChild(createNode("td", { textContent: sName }));
        xRowNode.appendChild(createNode("td", { textContent: nEntry }));
        xRowNode.appendChild(createNode("td", { textContent: sDescription }));
        xRowNode.appendChild(createNode("td", { textContent: sLastUpdate }));
        if (this.bActionButtons) {
            xRowNode.appendChild(createNode("td", { textContent: "+", className: "select_entry", title: "Sélectionner/Désélectionner cette entrée" }, { id_entry: nDicId, dict_name: sName }));
        }
        this.xTable.appendChild(xRowNode);
        if (this.lSelectedDictionaries.includes(sName)) {
            this.dSelectedDictionaries.set(sName, nDicId);
        }
    }

    listen () {
        if (this.bDeleteButtons || this.bActionButtons) {
            this.xTable.addEventListener("click", (xEvent) => { this.onTableClick(xEvent); }, false);
        }
        this.xApply.addEventListener("click", (xEvent) => { this.generateCommunityDictionary(xEvent); }, false);
    }

    onTableClick (xEvent) {
        try {
            let xElem = xEvent.target;
            if (xElem.className) {
                switch (xElem.className) {
                    case "delete_entry":
                        this.deleteRow(xElem.dataset.id_entry, xElem.dataset.dict_name);
                        break;
                    case "select_entry":
                        this.getDictionary(xElem.dataset.id_entry, xElem.dataset.dict_name);
                        break;
                }
            }
        }
        catch (e) {
            showError(e);
        }
    }

    deleteRow (iEntry) {
        this.lEntry[parseInt(iEntry)] = null;
        if (document.getElementById(this.sNodeId + "_row_" + iEntry)) {
            document.getElementById(this.sNodeId + "_row_" + iEntry).style.display = "none";
        }
        this.nEntry -= 1;
        this.showEntryNumber();
        if (this.sNodeId == "lexicon_table") {
            showElement("save_button", "inline-block");
        }
        showElement("apply");
    }

    selectEntry (nEntryId, sDicName) {
        let sRowId = this.sNodeId + "_row_" + nEntryId;
        if (!this.dSelectedDictionaries.has(sDicName)) {
            this.dSelectedDictionaries.set(sDicName, nEntryId);
            document.getElementById(sRowId).style.backgroundColor = "hsl(210, 50%, 90%)";
        }
        else {
            this.dSelectedDictionaries.delete(sDicName);
            document.getElementById(sRowId).style.backgroundColor = "";
        }
        showElement("apply");
        this.showSelectedDictionaries();
    }

    clearSelectedDict () {
        let xDicList = document.getElementById("dictionaries_list");
        while (xDicList.firstChild) {
            xDicList.removeChild(xDicList.firstChild);
        }
    }

    showSelectedDictionaries (bUpdateTable=false) {
        this.clearSelectedDict();
        let xDicList = document.getElementById("dictionaries_list");
        if (this.dSelectedDictionaries.size === 0) {
            xDicList.textContent = "[Aucun]";
            return;
        }
        for (let [sName, nDicId] of this.dSelectedDictionaries) {
            xDicList.appendChild(this._createDictLabel(nDicId, sName));
            if (bUpdateTable) {
                document.getElementById(this.sNodeId + "_row_" + nDicId).style.backgroundColor = "hsl(210, 50%, 90%)";
            }
        }
    }

    _createDictLabel (nDicId, sLabel) {
        let xLabel = createNode("div", {className: "dic_button"});
        let xCloseButton = createNode("div", {className: "dic_button_close", textContent: "×"}, {id_entry: nDicId});
        xCloseButton.addEventListener("click", () => {
            this.dSelectedDictionaries.delete(sLabel);
            document.getElementById(this.sNodeId+"_row_"+nDicId).style.backgroundColor = "";
            xLabel.style.display = "none";
            showElement("apply");
        });
        xLabel.appendChild(xCloseButton);
        xLabel.appendChild(createNode("div", {className: "dic_button_label", textContent: sLabel}));
        return xLabel;
    }

    generateCommunityDictionary (xEvent) {
        hideElement("apply");
        let lDict = [];
        for (let sName of this.dSelectedDictionaries.keys()) {
            lDict.push(this.dDict.get(sName));
        }
        let oDict = dic_merger.merge(lDict, "S", "fr", "Français", "fr.community", "Dictionnaire communautaire (personnalisé)", this.xProgressBar);
        console.log(oDict);
        browser.storage.local.set({ "community_dictionary": oDict });
        browser.storage.local.set({ "selected_dictionaries_list": Array.from(this.dSelectedDictionaries.keys()) });
        browser.runtime.sendMessage({ sCommand: "setDictionary", dParam: {sDictionary: "community", oDict: oDict}, dInfo: {} });
    }
}

const oDicTable = new Table("dictionaries_table", ["Nom", "Entrées", "Description", "Date"], "wait_progress", "num_dic", false, true);

oDicTable.init();

oMessage.listen();

oConnect.init();
oConnect.listen();

Modified gc_lang/fr/webext/panel/lex_editor.css from [62f94b1b3b] to [0f7a2051f7].

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
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







+
+
+
+
+


















+










-
+







    font: bold 20px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif;
}
h3 {
    margin: 3px 0 2px 0;
    color: hsl(210, 50%, 50%);
    font: bold 16px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif;
}

#dic_selector {
    color: hsl(210, 50%, 50%);
    font: bold 16px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif;
}

details {
    font-size: 11px;
    font-variant: small-caps;
    color: hsl(210, 50%, 50%);
    cursor: pointer;
}
details > * {
    font-size: 12px;
    font-variant: normal;
    color: hsl(210, 0%, 0%);
    cursor: auto;
}
details.inline {
    padding: 3px;
    width: 260px;
}



/*
    Main buttons
*/
#buttons {
    padding: 10px 0;
    justify-content: center;
}
.main_button {
    margin: 0 5px;
    width: 100px;
    min-width: 100px;
    padding: 10px 20px;
    background-color: hsl(210, 10%, 95%);
    border-radius: 5px;
    text-align: center;
    cursor: pointer;
}
#lexicon_button {
245
246
247
248
249
250
251
252

253
254
255
256
257
258
259
251
252
253
254
255
256
257

258
259
260
261
262
263
264
265







-
+







    background-color: hsl(150, 60%, 40%);
    color: hsl(150, 60%, 90%);
    box-shadow: 0 0 2px hsl(150, 60%, 50%);
}


#lexicon_page {
    

}


/*
    Search page
*/
#search_page {
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

-
-
+
+








+
+


+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    width: 500px;
}


/*
    Dictionary
*/
#new_dictionary_button, #delete_dictionary_button {
    margin-left: 5px;
    padding: 1px 5px;
    background-color: hsl(150, 50%, 50%);
    color: hsl(150, 0%, 100%);
    border-radius: 3px;
    cursor: pointer;
}
#delete_dictionary_button {
    background-color: hsl(0, 50%, 50%);
    color: hsl(0, 0%, 100%);
}

#save_button, #export_button, #import_button {
#save_button {
    display: none;
    float: right;
    padding: 2px 10px;
    margin-left: 5px;
    padding: 1px 5px;
    background-color: hsl(150, 50%, 50%);
    color: hsl(150, 0%, 100%);
    border-radius: 3px;
    cursor: pointer;
}
#export_button, #import_button {
    display: block;
    margin-left: 5px;
    float: right;
    padding: 2px 10px;
    background-color: hsl(210, 50%, 50%);
    color: hsl(210, 0%, 100%);
    border-radius: 3px;
    cursor: pointer;
}

#new_dictionary_section {
    margin: 5px 0;
    padding: 5px 10px;
    background-color: hsl(210, 10%, 90%);
    border-radius: 3px;
}
#create_dictionary_button {
    padding: 1px 5px;
    background-color: hsl(120, 50%, 30%);
    color: hsl(120, 10%, 100%);
    border-radius: 3px;
    cursor: pointer;
}

#wait_progress {
    width: 100%;
    height: 4px;
}


/*

Modified gc_lang/fr/webext/panel/lex_editor.html from [66bbff43be] to [8e3bc0ab95].

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
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







-
+








-
-
+
+
+
+
+
+
+
+
+
+

-
-
+



-
-
+
+
+
+
+
+
+
+





-
+
+
+







<!DOCTYPE HTML>
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="lex_editor.css" />
    <title>Grammalecte · Éditeur lexical</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  </head>
  

  <body>

    <div class="inbox">

      <h1>Éditeur lexical</h1>

      <div class="big_block">
        <div class="dic_container">
          <div><h3>Dictionnaire personnel</h3></div>
          <div>Enregistré le : <span id="dic_save_date">—</span></div>
          <div><h3>Dictionnaire</h3></div>
          <div class="dic_container">
            <select id="dic_selector">
              <option value="__personal__">__personnel__</option>
            </select>
            <div id="new_dictionary_button" title="Créer un dictionnaire partagé">+</div>
            <div id="delete_dictionary_button" title="Supprimer ce dictionnaire">−</div>
          </div>

          <div>[<span id="dic_save_date">—</span>]</div>
          <div><span id="dic_num_entries">0</span> entrées</div>
          <!--<div id="import_button" class="fright">Importer</div>-->
          <div class="fright">
          <div>
            <input type="file" id="import_input" accept=".json" style="display: none;">
            <label id="import_button" for="import_input">Importer</label>
          </div>
          <div id="export_button" class="fright">Exporter</div>

          <div id="export_button">Exporter</div>
        </div>
        <div id="new_dictionary_section" style="display: none;">
          <div class="dic_container">
            <div>Nouveau dictionnaire partagé</div>
            <div>Nom : fr.x.<input type="text" id="new_dictionary_name" required> · Description : <input type="text" id="new_dictionary_description" required></div>
            <div id="create_dictionary_button">Créer</div>
          </div>
        </div>
        <progress id="wait_progress" value="0"></progress>
      </div>

      <div id="buttons" class="container">
        <div id="lexicon_button" class="main_button">Lexique</div>
        <div id="lexicon_button" class="main_button">
          Lexique · <span id="num_entries">0</span> entrées <span id="save_button">Enregistrer</span>
        </div>
        <div id="add_button" class="main_button">Ajout</div>
        <div id="search_button" class="main_button">Recherche</div>
        <div id="info_button" class="main_button">Informations</div>
      </div>

      <div id="add_page">
        <div class="columns">
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







-
+







                      <details class="inline">
                        <div class="container">
                          <div class="examples_true">Je suis venu.<br/>Je suis parti.</div>
                          <div class="examples_false">J’ai venu.<br/>J’ai parti.</div>
                        </div>
                      </details>
                    </div>
                      

                    <div class="space_container">
                      <div>
                        <input type="checkbox" id="up_v_aa" value="a" /><label for="up_v_aa"> avoir</label>
                      </div>
                      <details class="inline">
                        <div class="container">
                          <div class="examples_true">J’ai mangé.<br/>J’ai prié.</div>
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
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







-
+



-
+











-
-
-
-
-
-
-

-
+







                  <p><input type="text" id="flexion" name="flexion" class="large" maxlength="20" value="" pattern="^[a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ._-]+$" placeholder="flexion" /></p>
                  <h3>Étiquettes</h3>
                  <p><input type="text" id="tags" name="tags" class="large" maxlength="20" value="" pattern="^:[a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ:._-]+$" placeholder="étiquettes" /></p>
                </div>
              </div>
            </div>
          </div> <!-- #new_word_generator -->
          

          <div id="generated_words">
            <h2>Mots générés</h2>
            <table id="generated_words_table">
              

            </table>
            <div id="buttonline">
              <div id="add_to_lexicon">Ajouter au lexique</div>
            </div>
          </div> <!-- #generated_words -->
        </div> <!-- .columns -->
      </div> <!-- #add_page -->


      <div id="lexicon_page">
        <h2>Votre lexique</h2>
        <div class="big_block">
          <div id="save_button" class="fright">
            Enregistrer
          </div>
          <p>Nombre d’entrées : <span id="num_entries">0</span>.</p>
        </div>

        <table id="lexicon_table">
              

        </table>
      </div> <!-- #lexicon_page -->


      <div id="search_page">
        <div class="columns">
          <div style="margin-right: 20px;">
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293
287
288
289
290
291
292
293

294
295
296
297
298
299
300
301







-
+








            <p style="margin-top: 50px;">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.</p>
          </div>

          <div>
            <h2>Résultats</h2>
            <table id="search_table">
                  

            </table>
          </div>
        </div>
      </div> <!-- #search_page -->


      <div id="info_page">
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
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







-
+



















-
+


            <h3>Étiquettes rares</h3>
            <p>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.</p>
          </div>

          <div>
            <table id="tags_table">
              

            </table>
          </div>
        </div>
      </div> <!-- #info_page -->

    </div>


    <script src="../grammalecte/graphspell/helpers.js"></script>
    <script src="../grammalecte/graphspell/char_player.js"></script>
    <script src="../grammalecte/graphspell/str_transform.js"></script>
    <script src="../grammalecte/graphspell/dawg.js"></script>
    <script src="../grammalecte/graphspell/ibdawg.js"></script>
    <script src="../grammalecte/graphspell/spellchecker.js"></script>
    <script src="../grammalecte/fr/conj.js"></script>
    <script src="../grammalecte/fr/conj_generator.js"></script>
    <script src="../grammalecte/fr/lexicographe.js"></script>
    <script src="lex_editor.js"></script>
  </body>
  

</html>

Modified gc_lang/fr/webext/panel/lex_editor.js from [1df07baa4c] to [b98659c88b].

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
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







-
+













-
+

-
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+







    Common functions
*/

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

function createNode  (sType, oAttr, oDataset=null) {
function createNode (sType, oAttr, oDataset=null) {
    try {
        let xNode = document.createElement(sType);
        Object.assign(xNode, oAttr);
        if (oDataset) {
            Object.assign(xNode.dataset, oDataset);
        }
        return xNode;
    }
    catch (e) {
        showError(e);
    }
}

function showElement (sElemId) {
function showElement (sElemId, sDisplay="block") {
    if (document.getElementById(sElemId)) {
        document.getElementById(sElemId).style.display = "block";
        document.getElementById(sElemId).style.display = sDisplay;
    } else {
        console.log("HTML node named <" + sElemId + "> not found.")
    }
}

function hideElement (sElemId) {
    if (document.getElementById(sElemId)) {
        document.getElementById(sElemId).style.display = "none";
    } else {
        console.log("HTML node named <" + sElemId + "> not found.")
    }
}

function switchDisplay (sElemId, sDisplay="block") {
    if (document.getElementById(sElemId)) {
        if (document.getElementById(sElemId).style.display != "none") {
            document.getElementById(sElemId).style.display = "none";
        } else {
            document.getElementById(sElemId).style.display = sDisplay;
        }
    }
}

function showMessage (sMessage) {
    console.log(sMessage);
}


const oTabulations = {

    lPage: ["lexicon_page", "add_page", "search_page", "info_page"],

    showPage: function (sRequestedPage) {
123
124
125
126
127
128
129


130
131

132
133
134
135
136
137
138
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155







+
+


+







        this.xTable.appendChild(xRowNode);
    }

    clear () {
        while (this.xTable.firstChild) {
            this.xTable.removeChild(this.xTable.firstChild);
        }
        this.lEntry = [];
        this.nEntry = 0;
        this.iEntryIndex = 0;
        this._createHeader();
        this.showEntryNumber();
    }

    fill (lFlex) {
        this.clear();
        if (lFlex.length > 0) {
            this.xProgressBar.max = lFlex.length;
            this.xProgressBar.value = 1;
198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229







-
+







        this.lEntry[parseInt(iEntry)] = null;
        if (document.getElementById(this.sNodeId + "_row_" + iEntry)) {
            document.getElementById(this.sNodeId + "_row_" + iEntry).style.display = "none";
        }
        this.nEntry -= 1;
        this.showEntryNumber();
        if (this.sNodeId == "lexicon_table") {
            showElement("save_button");
            showElement("save_button", "inline-block");
        }
    }

    getEntries () {
        return this.lEntry.filter((e) => e !== null);
    }
}
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
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







-
+









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+

-
-
+
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+

-
+



+



+
+
+
+
+
+
+
+
+
+
+
+
+
-
+














-
-
+
-
-
-










-
-
+
+








-
+














+



+
+
+






-
+

-
+



+

-



+
+
+
+
+
+
+
+




-
+







        try {
            oLexiconTable.addEntries(this.createFlexLemmaTagArray());
            oGenWordsTable.clear();
            document.getElementById("lemma").value = "";
            document.getElementById("lemma").focus();
            this.hideAllSections();
            hideElement("editor");
            showElement("save_button");
            showElement("save_button", "inline-block");
            this.clear();
            this.cMainTag = "";
        }
        catch (e) {
            showError(e);
        }
    }
}


const oDictHandler = {
    oDictionaries: {},
    oPersonalDictionary: null,
    lCreatedDictionaries: [],
    xDicSelector: document.getElementById("dic_selector"),

    loadDictionaries: function () {
        if (bChrome) {
            browser.storage.local.get("shared_dictionaries", this._loadDictionaries.bind(this));
            browser.storage.local.get("personal_dictionary", this._loadDictionaries.bind(this));
            browser.storage.local.get("created_dictionaries_list", this._loadDictionaries.bind(this));
            return;
        }
        let xPromise = browser.storage.local.get("shared_dictionaries");
        xPromise.then(this._loadDictionaries.bind(this), showError);
        let xPromise2 = browser.storage.local.get("personal_dictionary");
        xPromise2.then(this._loadDictionaries.bind(this), showError);
        let xPromise3 = browser.storage.local.get("created_dictionaries_list");
        xPromise3.then(this._loadDictionaries.bind(this), showError);
    },

    _loadDictionaries: function (oResult) {
        if (oResult.hasOwnProperty("shared_dictionaries")) {
            this.oDictionaries = oResult.shared_dictionaries;
        }
        if (oResult.hasOwnProperty("personal_dictionary")) {
            this.oPersonalDictionary = oResult.personal_dictionary;
            oBinaryDict.load("__personal__");
        }
        if (oResult.hasOwnProperty("created_dictionaries_list")) {
            this.lCreatedDictionaries = oResult.created_dictionaries_list;
            for (let sDicName of this.lCreatedDictionaries) {
                let xOption = createNode("option", {value: sDicName, textContent: sDicName});
                this.xDicSelector.appendChild(xOption);
            }
        }
    },

    addDictionary: function (sDicName) {
        if (!this.oDictionaries.hasOwnProperty(sDicName)) {
            let xOption = createNode("option", {value: sDicName, textContent: sDicName});
            this.xDicSelector.appendChild(xOption);
            this.xDicSelector.selectedIndex = this.xDicSelector.options.length-1;
            this.lCreatedDictionaries.push(sDicName);
            browser.storage.local.set({ "created_dictionaries_list": this.lCreatedDictionaries });
        }
    },

    deleteDictionary: function (sDicName) {
        this.saveDictionary(sDicName, null);
        if (sDicName != "__personal__") {
            let iDict = this.lCreatedDictionaries.indexOf(sDicName);
            if (iDict > -1) {
                this.lCreatedDictionaries.splice(iDict, 1);
            }
            browser.storage.local.set({ "created_dictionaries_list": this.lCreatedDictionaries });
            for (let xNode of this.xDicSelector.childNodes) {
                if (xNode.value == sDicName) {
                    this.xDicSelector.removeChild(xNode);
                }
            }
            this.xDicSelector.selectedIndex = 0;
            oBinaryDict.load("__personal__");
        }
    },

    getDictionary: function (sName) {
        if (sName == "__personal__") {
            return this.oPersonalDictionary;
        }
        else if (this.oDictionaries  &&  this.oDictionaries.hasOwnProperty(sName)) {
            //console.log(this.oDictionaries[sName]);
            return this.oDictionaries[sName];
        }
        return null;
    },

    saveDictionary: function (sName, oJSON) {
        if (sName == "__personal__") {
            browser.runtime.sendMessage({ sCommand: "setDictionary", dParam: {sDictionary: "personal", oDict: oJSON}, dInfo: {} });
            browser.storage.local.set({ "personal_dictionary": oJSON });
        }
        else {
            this.oDictionaries[sName] = oJSON;
            if (oJSON === null) {
                delete this.oDictionaries[sName];
            }
            browser.storage.local.set({ "shared_dictionaries": this.oDictionaries });
            this.sendDictionaryOnline(oJSON);
        }
    },

    sendDictionaryOnline: function (oJSON) {
        let sJSON = "";
        try {
            sJSON = JSON.stringify(oJSON);
        }
        catch (e) {
            showError(e);
            return e.message;
        }
        console.log("Send online dictionary: " + oJSON.sDicName);
        fetch("http://localhost/receive/", {
            method: "POST", // *GET, POST, PUT, DELETE, etc.
            //mode: "cors", // no-cors, cors, *same-origin
            //cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
            credentials: "include", // include, *same-origin, omit
            headers: {
                "Content-Type": "application/json",  // text/plain, application/json
                "Content-Length": sJSON.length
            },
            redirect: "follow", // manual, *follow, error
            referrer: "no-referrer", // no-referrer, *client
            body: sJSON, // body data type must match "Content-Type" header
        })
        .then((response) => {
            if (response.ok) {
                for (let param in response) {
                    console.log(param, response[param]);
                }
                console.log(response);
                return response.json();
            } else {
                console.log("Error: dictionary not sent: " + oJSON.sDicName);
                return null;
            }
        })
        .then((response) => {
            if (response) {
                console.log(response);
            } else {
                //
            }
        })
        .catch((e) => {
            showError(e);
        });
    }
}


const oBinaryDict = {

    oIBDAWG: null,
    sName: "",
    sDescription: "",

    load: function () {
        if (bChrome) {
    load: function (sName) {
        console.log("lexicon editor, load: " + sName);
            browser.storage.local.get("oPersonalDictionary", this._load.bind(this));
            return;
        }
        let xPromise = browser.storage.local.get("oPersonalDictionary");
        xPromise.then(this._load.bind(this), showError);
    },

        this.sName = sName;
    _load: function (oResult) {
        if (!oResult.hasOwnProperty("oPersonalDictionary")) {
            hideElement("export_button");
            return;
        }
        let oJSON = oResult.oPersonalDictionary;
        let oJSON = oDictHandler.getDictionary(sName);
        if (oJSON) {
            this.__load(oJSON);
            this.parseDict(oJSON);
        } else {
            oLexiconTable.clear();
            this.setDictData(0, "[néant]");
            hideElement("save_button");
        }
    },

    newDictionary: function () {
        this.oIBDAWG = null;
        this.sName = document.getElementById("new_dictionary_name").value;
        this.sDescription = document.getElementById("new_dictionary_description").value;
        oDictHandler.addDictionary(this.sName);
        oLexiconTable.clear();
        this.setDictData(0, "[néant]");
        hideElement("save_button");
        document.getElementById("new_dictionary_name").value = "";
        document.getElementById("new_dictionary_description").value = "";
        hideElement("new_dictionary_section")
    },

    __load: function (oJSON) {
    parseDict: function (oJSON) {
        try {
            this.oIBDAWG = new IBDAWG(oJSON);
        }
        catch (e) {
            console.error(e);
            this.setDictData(0, "#Erreur. Voir la console.");
            return;
        }
        let lEntry = [];
        for (let aRes of this.oIBDAWG.select()) {
            lEntry.push(aRes);
        }
        oLexiconTable.fill(lEntry);
        this.setDictData(this.oIBDAWG.nEntry, this.oIBDAWG.sDate);
    },

        hideElement("save_button");
    save: function (oJSON) {
        browser.storage.local.set({ "oPersonalDictionary": oJSON });
        browser.runtime.sendMessage({ sCommand: "setDictionary", dParam: {sDictionary: "personal", oDict: oJSON}, dInfo: {} });
    },

    import: function () {
        let xInput = document.getElementById("import_input");
        let xFile = xInput.files[0];
        let xURL = URL.createObjectURL(xFile);
        let sJSON = helpers.loadFile(xURL);
        if (sJSON) {
            try {
                let oJSON = JSON.parse(sJSON);
                this.__load(oJSON);
                this.save(oJSON);
                this.parseDict(oJSON);
                oDictHandler.saveDictionary(this.sName, oJSON);
            }
            catch (e) {
                console.error(e);
                this.setDictData(0, "#Erreur. Voir la console.");
                return;
            }
        } else {
            this.setDictData(0, "[néant]");
            this.save(null);
            oDictHandler.saveDictionary(this.sName, null);
        }
    },

    setDictData: function (nEntries, sDate) {
        document.getElementById("dic_num_entries").textContent = nEntries;
        document.getElementById("dic_save_date").textContent = sDate;
        if (nEntries == 0) {
            hideElement("export_button");
        } else {
            showElement("export_button");
        }
    },

    listen: function () {
        document.getElementById("dic_selector").addEventListener("change", () => {this.load(document.getElementById("dic_selector").value)}, false);
        document.getElementById("save_button").addEventListener("click", () => { this.build(); }, false);
        document.getElementById("export_button").addEventListener("click", () => { this.export(); }, false);
        document.getElementById("import_input").addEventListener("change", () => { this.import(); }, false);
        document.getElementById("new_dictionary_button").addEventListener("click", () => { switchDisplay("new_dictionary_section"); }, false);
        document.getElementById("delete_dictionary_button").addEventListener("click", () => { this.delete(); }, false);
        document.getElementById("create_dictionary_button").addEventListener("click", () => { this.newDictionary(); }, 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", xProgressNode);
            let oDAWG = new DAWG(lEntry, "S", "fr", "Français", this.sName, this.sDescription, xProgressNode);
            let oJSON = oDAWG.createBinaryJSON(1);
            this.save(oJSON);
            oDictHandler.saveDictionary(this.sName, oJSON);
            this.oIBDAWG = new IBDAWG(oJSON);
            this.setDictData(this.oIBDAWG.nEntry, this.oIBDAWG.sDate);
        } else {
            oDictHandler.saveDictionary(this.sName, null);
            this.setDictData(0, "[néant]");
            this.save(null);
        }
        hideElement("save_button");
    },

    delete: function () {
        if (confirm("Voulez-vous effacer le dictionnaire “"+this.sName+"” ?")) {
            oLexiconTable.clear();
            this.setDictData(0, "[néant]");
            oDictHandler.deleteDictionary(this.sName);
        }
    },

    export: function () {
        let xBlob = new Blob([ JSON.stringify(this.oIBDAWG.getJSON()) ], {type: 'application/json'});
        let sURL = URL.createObjectURL(xBlob);
        browser.downloads.download({ filename: "fr.personal.json", url: sURL, saveAs: true });
        browser.downloads.download({ filename: "fr."+this.sName+".json", url: sURL, saveAs: true });
    }
}


const oSearch = {

    oSpellChecker: null,
662
663
664
665
666
667
668
669

670
671
672
673
833
834
835
836
837
838
839

840
841
842
843
844







-
+




const oLexiconTable = new Table("lexicon_table", ["Flexions", "Lemmes", "Étiquettes"], "wait_progress", "num_entries");
const oSearchTable = new Table("search_table", ["Flexions", "Lemmes", "Étiquettes"], "wait_progress", "search_num_entries", false);
const oTagsTable = new Table("tags_table", ["Étiquette", "Signification"], "wait_progress", "", false);


oTagsInfo.load();
oSearch.load();
oBinaryDict.load();
oDictHandler.loadDictionaries();
oBinaryDict.listen();
oGenerator.listen();
oTabulations.listen();
oSearch.listen();

Modified gc_lang/fr/webext/panel/main.css from [85a3951146] to [2045c317c1].

82
83
84
85
86
87
88
89

90
91
92
93
94
95
96
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96







-
+







    height: 500px;
    font-family: "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif;
}
body {
    width: 450px;
    height: 500px;
}
/* 
/*
    Maximal height of a panel in WebExtention seems to be 500px.
    When going over this limit, a scrollbar appears which destructs the
    horizontal balance of elements.
    --> vertical scrolling is done with overflow in #page.
        #page must have the same height than body.
*/

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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




















+
+
+
+
-
+
-
-
-
-
-
+
+
+
-
-
-
+
-
-
-
-
-
+
-
-
-
-
-
-
+
+
+
+







    padding: 5px 10px;
    border-radius: 3px;
    font-size: 16px;
    text-align: center;
    cursor: pointer;
}


.option_section {
    padding: 10px;
    margin-top: 10px;
    border-radius: 5px;
    background-color: hsl(210, 20%, 96%);
}
.option_section label {
    font-size: 16px;
    line-height: 20px;
    color: hsl(210, 20%, 50%);
    font-weight: bold;
}
.option_description {
    padding: 0 0 0 20px;
    color: hsl(0, 0%, 0%);
    font-size: 12px;
}


/*
    Spell checking options
*/
#sc_options_page {
    display: none;
    padding: 20px;
}
.dictionaries_info {
    margin: 10px 0;
    padding: 10px;
    border-radius: 3px;
    background-color: hsl(0, 50%, 40%);
    color: hsl(0, 10%, 96%);
}
#sc_options_page h2 {
    margin-top: 20px;
    font: normal 22px 'Yanone Kaffeesatz', "Oswald", "Liberation Sans Narrow", sans-serif;
    color: hsl(210, 50%, 50%);
}
.button_row {
    display: flex;
    flex-direction: row-reverse;
    padding: 5px 0 0 0;

}
/*
    Options
*/
.option_section {
    padding: 10px;
.dic_button {
    margin-left: 10px;
    padding: 2px 10px;
    margin-top: 10px;
    border-radius: 5px;
    background-color: hsl(210, 20%, 96%);
    background-color: hsl(210, 50%, 50%);
}
.option_section label {
    font-size: 16px;
    line-height: 20px;
    color: hsl(210, 20%, 50%);
    color: hsl(210, 10%, 96%);
    font-weight: bold;
}
.option_description {
    padding: 0 0 0 20px;
    color: hsl(0, 0%, 0%);
    font-size: 12px;
    cursor: pointer;
    font-size: 14px;
    font-variant-caps: small-caps;
    border-radius: 3px;
}


/*
  Test page
*/
#test_page {
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







-
+

-
+



    animation: sk-bounce 2.0s infinite ease-in-out;
}
.double-bounce2 {
    animation-delay: -1.0s;
}

@keyframes sk-bounce {
    0%, 100% { 
    0%, 100% {
        transform: scale(0.0);
    } 50% { 
    } 50% {
        transform: scale(1.0);
    }
}

Modified gc_lang/fr/webext/panel/main.html from [c94c7f3f57] to [b83ce0fb48].

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
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







+
+
+
+
+
+






+
+














-

-
-
+
+

-
-
-
-
+
+
+
+
+
+
+
-
-
-



-
+
+
+
+







              <p class="option_description">Ces zones de texte sont les champs de formulaire usuels pour saisir du texte. Ils ne permettent que la saisie de texte brut, sans fioritures.</p>
          </div>
          <div class="option_section" id="ui_option_editablenode_box">
              <p><input type="checkbox" id="ui_option_editablenode" /> <label for="ui_option_editablenode">“Nodes” éditables</label></p>
              <p class="option_description">Ces zones de texte sont des sections de page web éditables. Il est fréquent que ces zones de texte apparaissent comme des “textareas” standard. Il est aussi fréquent que ces zones de texte soient couplées avec toutes sortes de logiciels (de simples scripts d’aide à la mise en page ou des applications complexes). Ces zones de texte permettent l’affichage de texte enrichi (italique, gras, hyperlien, images, etc.).</p>
          </div>
          <h2>Raccourcis clavier</h2>
          <p class="shortcut">CTRL+MAJ+L</p>
          <p class="shortcut_label">Lexicographe</p>
          <p class="shortcut">CTRL+MAJ+F</p>
          <p class="shortcut_label">Formateur de texte</p>
          <p class="shortcut">CTRL+MAJ+V</p>
          <p class="shortcut_label">Correcteur grammatical</p>
          <p class="shortcut">CTRL+MAJ+6</p>
          <p class="shortcut_label">Conjugueur (dans un onglet)</p>
          <p class="shortcut">CTRL+MAJ+7</p>
          <p class="shortcut_label">Conjugueur (dans une fenêtre)</p>
          <p class="shortcut">CTRL+MAJ+8</p>
          <p class="shortcut_label">Éditeur lexical</p>
          <p class="shortcut">CTRL+MAJ+9</p>
          <p class="shortcut_label">Dictionnaires communautaires</p>
        </section> <!-- #ui_options_page -->

        <section id="gc_options_page" class="page">
          <h1>OPTIONS GRAMMATICALES</h1>
          <div id="grammar_options">
            ${webextOptionsHTML}
          </div>
          <div id="default_options_button" class="button blue center">Options par défaut</div>
        </section> <!-- #gc_options_page -->

        <section id="sc_options_page" class="page">
          <h1>OPTIONS ORTHOGRAPHIQUES</h1>
          <div id="hunspell_options">
            <h2>DICTIONNAIRES DE GRAMMALECTE</h2>
            <p class="dictionaries_info">Ces dictionnaires ne sont utilisés que lors de l’analyse grammaticale.</p>
            <div class="option_section" id="main_dic_box">
                <p><input type="checkbox" id="main_dic" data-dictionary="personal" checked disabled="disabled" /> <label for="main_dic">Dictionnaire principal</label></p>
                <p class="option_description">Environ 83 000 entrées, 500 000 flexions.<br/>Ni éditable, ni désactivable.</p>
                <p><input type="checkbox" id="main_dic" data-dictionary="main" checked disabled="disabled" /> <label for="main_dic">Dictionnaire principal</label></p>
                <p class="option_description">Environ 83 000 entrées, 500 000 flexions.<br/>Ni éditable, ni désactivable.<br/>Ce dictionnaire est créé à partir du dictionnaire orthographique pour Firefox et LibreOffice, conçu sur le <a id="link_grammalecte" class="link" data-url="http://grammalecte.net/home.php?prj=fr">site de Grammalecte</a>.</p>
            </div>
            <div class="option_section" id="extended_dic_box" style="opacity: .33;">
                <p><input type="checkbox" id="extended_dic" data-dictionary="personal" disabled="disabled" /> <label for="extended_dic">Dictionnaire étendu</label></p>
                <p class="option_description">Fonctionnalité à venir.</p>
            </div>
            <div class="option_section" id="community_dic_box">
                <p><input type="checkbox" id="community_dic" data-dictionary="community" /> <label for="community_dic">Dictionnaire communautaire</label></p>
                <p class="option_description">Ce dictionnaire est créé et édité via l’éditeur lexical et est sauvegardé sur un serveur en ligne accessible à tous les membres.</p>
                <div class="button_row">
                  <div class="dic_button" id="dic_community_button">Éditer</div>
                  <div class="dic_button" id="dictionaries_button">Dictionnaires en ligne</div>
                </div>
            <div class="option_section" id="community_dic_box" style="opacity: .33;">
                <p><input type="checkbox" id="community_dic" data-dictionary="personal" disabled="disabled" /> <label for="community_dic">Dictionnaire communautaire</label></p>
                <p class="option_description">Fonctionnalité à venir.</p>
            </div>
            <div class="option_section" id="personal_dic_box">
                <p><input type="checkbox" id="personal_dic" data-dictionary="personal" /> <label for="personal_dic">Dictionnaire personnel</label></p>
                <p class="option_description">Ce dictionnaire est créé et édité via l’éditeur lexical.</p>
                <p class="option_description">Ce dictionnaire est créé et édité via l’éditeur lexical et n’est pas partagé.</p>
                <div class="button_row">
                  <div class="dic_button" id="dic_personal_button">Éditer</div>
                </div>
            </div>
          </div>

          <div id="hunspell_options" style="display: none; opacity: .25">
            <h2>DICTIONNAIRES HUNSPELL</h2>
            <p class="dictionaries_info">Les dictionnaires Hunspell ne sont utilisées que pour la correction orthographique proposée par Firefox (soulignement rouge).<br/><br/>Section inactive.<br/>Le système d’extension <i>WebExtension</i> ne propose pas encore d’interface de programmation pour le correcteur orthographique.</p>
            <div class="option_section" id="fr-FR-modern_box">

Modified gc_lang/fr/webext/panel/main.js from [d7a3e529e8] to [5094773a1c].

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
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







+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+















-
+







            else if (xElem.id.startsWith("ui_option_")) {
                storeUIOptions();
            }
            else if (xElem.id.startsWith("link_")) {
                browser.tabs.create({url: xElem.dataset.url});
            }
            else if (xElem.id == "conj_button") {
                browser.runtime.sendMessage({
                openConjugueurTab();
                    sCommand: "openConjugueurTab",
                    dParam: {},
                    dInfo: {}
                });
            }
            else if (xElem.id == "dictionaries_button") {
                browser.runtime.sendMessage({
                    sCommand: "openDictionaries",
                    dParam: { "dictionary": "__community__"},
                    dInfo: {}
                });
            }
            else if (xElem.id == "dic_community_button") {
                browser.runtime.sendMessage({
                    sCommand: "openLexiconEditor",
                    dParam: { "dictionary": "__community__"},
                    dInfo: {}
                });
            }
            else if (xElem.id == "dic_personal_button") {
                browser.runtime.sendMessage({
                    sCommand: "openLexiconEditor",
                    dParam: { "dictionary": "__personal__"},
                    dInfo: {}
                });
            }
        } else if (xElem.className.startsWith("select")) {
            showPage(xElem.dataset.page);
        }/* else if (xElem.tagName === "A") {
            openURL(xElem.getAttribute("href"));
        }*/
    },
    false
);


/* 
/*
    Message sender
    and response handling
*/
function handleResponse (oResponse) {
    console.log(`[Panel] received:`);
    console.log(oResponse);
}

function handleError (error) {
    console.log(`[Panel] Error:`);
    console.log(error);
}

function sendMessageAndWaitResponse (oData) {
    let xPromise = browser.runtime.sendMessage(oData);
    xPromise.then(handleResponse, handleError);  
    xPromise.then(handleResponse, handleError);
}


/*
    Messages received
*/
function handleMessage (oMessage, xSender, sendResponse) {
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
179
180
181
182
183
184
185


























186
187
188
189
190
191
192







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







}


function showTestResult (sText) {
    document.getElementById("tests_result").textContent = sText;
}

function openConjugueurTab () {
    if (bChrome) {
        browser.tabs.create({
            url: browser.extension.getURL("panel/conjugueur.html")
        });
        return;
    }
    let xConjTab = browser.tabs.create({
        url: browser.extension.getURL("panel/conjugueur.html")
    });
    xConjTab.then(onCreated, onError);
}

function openConjugueurTab () {
    if (bChrome) {
        browser.tabs.create({
            url: browser.extension.getURL("panel/conjugueur.html")
        });
        return;
    }
    let xConjTab = browser.tabs.create({
        url: browser.extension.getURL("panel/conjugueur.html")
    });
    xConjTab.then(onCreated, onError);
}


/*
    UI options
*/
function displayUIOptionsLoadedFromStorage () {
    if (bChrome) {
        browser.storage.local.get("ui_options", displayUIOptions);
232
233
234
235
236
237
238
239
240


241
242
243
244
245
246
247

248
249
250
251
252
253
254
231
232
233
234
235
236
237


238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
253







-
-
+
+






-
+








function displaySCOptions (dOptions) {
    if (!dOptions.hasOwnProperty("sc_options")) {
        console.log("no sc options found");
        return;
    }
    dOptions = dOptions.sc_options;
    //document.getElementById("extended_dic").checked = dOptions.extended_dic;
    //document.getElementById("community_dic").checked = dOptions.community_dic;
    //document.getElementById("extended_dic").checked = dOptions.extended;
    document.getElementById("community_dic").checked = dOptions.community;
    document.getElementById("personal_dic").checked = dOptions.personal;
}

function storeSCOptions () {
    browser.storage.local.set({"sc_options": {
        extended: false,
        community: false,
        community: document.getElementById("community_dic").checked,
        personal: document.getElementById("personal_dic").checked
    }});
}


/*
    GC options

Modified graphspell-js/dawg.js from [8cf9f1314b] to [f4d9319997].

416
417
418
419
420
421
422

423

424
425
426
427
428
429
430
416
417
418
419
420
421
422
423

424
425
426
427
428
429
430
431







+
-
+








    _getDate () {
        let oDate = new Date();
        let sMonth = (oDate.getMonth() + 1).toString().padStart(2, "0"); // Month+1: Because JS always sucks somehow.
        let sDay = (oDate.getDate()).toString().padStart(2, "0");
        let sHours = (oDate.getHours()).toString().padStart(2, "0");
        let sMinutes = (oDate.getMinutes()).toString().padStart(2, "0");
        let sSeconds = (oDate.getSeconds()).toString().padStart(2, "0");
        return `${oDate.getFullYear()}-${sMonth}-${sDay}, ${sHours}:${sMinutes}`;
        return `${oDate.getFullYear()}-${sMonth}-${sDay} ${sHours}:${sMinutes}:${sSeconds}`;
    }
}


const oNodeCounter = {
    nNextId: 0,

Added graphspell-js/dic_merger.js version [e44b159d9d].
























































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// Dictionaries merger

"use strict";

if (typeof(process) !== 'undefined') {
    var dawg = require("./dawg.js");
    var ibdawg = require("./ibdawg.js");
}
else if (typeof(require) !== 'undefined') {
    var dawg = require("resource://grammalecte/graphspell/dawg.js");
    var ibdawg = require("resource://grammalecte/graphspell/ibdawg.js");
}


const dic_merger = {

    merge: function (lDict, cStemming, sLangCode, sLangName, sDicName, sDescription, xProgressBar=null) {
        // merge a list of dictionaries (given as JSON objects)
        // return a merged dictionary as JSON object.
        if (xProgressBar) {
            xProgressBar.max = lDict.length;
            xProgressBar.value = 0;
        }
        let lEntry = [];
        for (let oDict of lDict) {
            // generate words list from selected dictionaries
            if (xProgressBar) {
                xProgressBar.value += 1;
            }
            try {
                let oIBDAWG = new IBDAWG(oDict);
                for (let aRes of oIBDAWG.select()) {
                    lEntry.push(aRes);
                }
            }
            catch (e) {
                console.error(e);
            }
        }
        if (xProgressBar) {
            xProgressBar.value = xProgressBar.max;
        }
        try {
            let oDAWG = new DAWG(lEntry, cStemming, sLangCode, sLangName, sDicName, sDescription, xProgressBar);
            let oDict = oDAWG.createBinaryJSON(1);
            return oDict;
        }
        catch (e) {
            console.log("Dictionaries merger: unable to generate merged dictionary");
            console.error(e);
            return null;
        }
    }

}

Modified graphspell/dawg.py from [70e7a3c81c] to [0043f035e5].

18
19
20
21
22
23
24


25
26
27
28
29

30
31
32




33

34



35
36
37
38
39
40
41
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







+
+





+



+
+
+
+
-
+

+
+
+







import re
import traceback

from . import str_transform as st
from .progressbar import ProgressBar



dLexiconData = {}

def readFile (spf):
    "generator: read file <spf> and return for each line a list of elements separated by a tabulation."
    print(" < Read lexicon: " + spf)
    if os.path.isfile(spf):
        dLexiconData.clear()
        with open(spf, "r", encoding="utf-8") as hSrc:
            for sLine in hSrc:
                sLine = sLine.strip()
                if sLine.startswith("##") :
                    m = re.match("## *(\\w+) *:(.*)$", sLine)
                    if m:
                        dLexiconData[m.group(1)] = m.group(2).strip()
                if sLine and not sLine.startswith("#"):
                elif sLine and not sLine.startswith("#"):
                    yield sLine.split("\t")
        if dLexiconData:
            print("Data from dictionary:")
            print(dLexiconData)
    else:
        raise OSError("# Error. File not found or not loadable: " + spf)



class DAWG:
    """DIRECT ACYCLIC WORD GRAPH"""
118
119
120
121
122
123
124





125
126
127
128
129
130
131
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146







+
+
+
+
+







                        + [ (dTag[tag]+nChar+nAff, dTagOccur[tag]) for tag in dTag ] )

        self.sFileName = src  if type(src) is str  else "[None]"
        self.sLangCode = sLangCode
        self.sLangName = sLangName
        self.sDicName = sDicName
        self.sDescription = sDescription
        if dLexiconData:
            self.sLangCode = dLexiconData.get("LangCode", self.sLangCode)
            self.sLangName = dLexiconData.get("LangName", self.sLangName)
            self.sDicName = dLexiconData.get("DicName", self.sDicName)
            self.sDescription = dLexiconData.get("Description", self.sDescription)
        self.nEntry = len(lWord)
        self.aPreviousEntry = []
        DawgNode.resetNextId()
        self.oRoot = DawgNode()
        self.lUncheckedNodes = []  # list of nodes that have not been checked for duplication.
        self.lMinimizedNodes = {}  # list of unique nodes that have been checked for duplication.
        self.lSortedNodes = []     # version 2 and 3
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







-
+







                hDst.write(self.oRoot.convToBytes3(self.nBytesArc, self.nBytesNodeAddress, self.nBytesOffset))
                for oNode in self.lSortedNodes:
                    hDst.write(oNode.convToBytes3(self.nBytesArc, self.nBytesNodeAddress, self.nBytesOffset))
        if bDebug:
            self._writeNodes(sPathFile, nCompressionMethod)

    def _getDate (self):
        return time.strftime("%Y.%m.%d, %H:%M")
        return time.strftime("%Y-%m-%d %H:%M:%S")

    def _writeNodes (self, sPathFile, nCompressionMethod):
        "for debugging only"
        print(" > Write nodes")
        with open(sPathFile+".nodes."+str(nCompressionMethod)+".txt", 'w', encoding='utf-8', newline="\n") as hDst:
            if nCompressionMethod == 1:
                hDst.write(self.oRoot.getTxtRepr1(self.nBytesArc, self.lArcVal)+"\n")

Modified lex_build.py from [346704203c] to [72263209a8].

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
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











-
+

-
+




















-
+



#!python3

# Lexicon builder

import argparse
from distutils import dir_util

import graphspell.dawg as fsa
from graphspell.ibdawg import IBDAWG


def build (spfSrc, sLangCode, sLangName, sfDict, bJSON=False, sDicName="", sFilter="", cStemmingMethod="S", nCompressMethod=1):
def build (spfSrc, sLangCode, sLangName, sfDict, bJSON=False, sDicName="", sDescription="", sFilter="", cStemmingMethod="S", nCompressMethod=1):
    "transform a text lexicon as a binary indexable dictionary"
    oDAWG = fsa.DAWG(spfSrc, cStemmingMethod, sLangCode, sLangName, sDicName, sFilter)
    oDAWG = fsa.DAWG(spfSrc, cStemmingMethod, sLangCode, sLangName, sDicName, sDescription, sFilter)
    dir_util.mkpath("graphspell/_dictionaries")
    oDAWG.writeInfo("graphspell/_dictionaries/" + sfDict + ".info.txt")
    oDAWG.writeBinary("graphspell/_dictionaries/" + sfDict + ".bdic", int(nCompressMethod))
    if bJSON:
        dir_util.mkpath("graphspell-js/_dictionaries")
        oDic = IBDAWG(sfDict + ".bdic")
        oDic.writeAsJSObject("graphspell-js/_dictionaries/" + sfDict + ".json", bBinaryDictAsHexString=True)


def main ():
    xParser = argparse.ArgumentParser()
    xParser.add_argument("src_lexicon", type=str, help="path and file name of the source lexicon")
    xParser.add_argument("lang_code", type=str, help="language code")
    xParser.add_argument("lang_name", type=str, help="language name")
    xParser.add_argument("dic_filename", type=str, help="dictionary file name (without extension)")
    xParser.add_argument("-js", "--json", help="Build dictionary in JSON", action="store_true")
    xParser.add_argument("-s", "--stemming", help="stemming method: S=suffixes, A=affixes, N=no stemming", type=str, choices=["S", "A", "N"], default="S")
    xParser.add_argument("-c", "--compress", help="compression method: 1, 2 (beta), 3, (beta)", type=int, choices=[1, 2, 3], default=1)
    xArgs = xParser.parse_args()
    build(xArgs.src_lexicon, xArgs.lang_code, xArgs.lang_name, xArgs.dic_filename, xArgs.json)
    


if __name__ == '__main__':
    main()

Modified lexicons/French.lex from [b69ae271b0] to [af55bd82c0].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7






8
9
10
11
12
13
14







-
-
-
-
-
-







# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Lexique simplifié pour Grammalecte v7.0
# Licence : MPL v2.0

## LangCode: fr
## LangName: Français
## DicName: fr.commun
## Description: Français commun (toutes variantes)
## Author: Olivier R.

# :POS ;LEX ~SEM =FQ /DIC
de	de	:G:D:e:i/*
de	de	:G:R:Rv/*
et	et	:G:Cc/*
à	à	:G:R:Rv/*
des	des	:G:D:e:p/*
du	du	:G:D:m:s/*

Modified make.py from [38cc0aad8b] to [23482f4c7e].

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
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







+

-
-
+
+





+




+




+
-
+








def buildDictionary (dVars, sType, bJavaScript=False):
    "build binary dictionary for Graphspell from lexicons"
    if sType == "main":
        spfLexSrc = dVars['lexicon_src']
        lSfDictDst = dVars['dic_filenames'].split(",")
        lDicName = dVars['dic_name'].split(",")
        lDescription = dVars['dic_description'].split(",")
        lFilter = dVars['dic_filter'].split(",")
        for sfDictDst, sDicName, sFilter in zip(lSfDictDst, lDicName, lFilter):
            lex_build.build(spfLexSrc, dVars['lang'], dVars['lang_name'], sfDictDst, bJavaScript, sDicName, sFilter, dVars['stemming_method'], int(dVars['fsa_method']))
        for sfDictDst, sDicName, sDescription, sFilter in zip(lSfDictDst, lDicName, lDescription, lFilter):
            lex_build.build(spfLexSrc, dVars['lang'], dVars['lang_name'], sfDictDst, bJavaScript, sDicName, sDescription, sFilter, dVars['stemming_method'], int(dVars['fsa_method']))
    else:
        if sType == "extended":
            spfLexSrc = dVars['lexicon_extended_src']
            sfDictDst = dVars['dic_extended_filename']
            sDicName = dVars['dic_extended_name']
            sDescription = dVars['dic_extended_description']
        elif sType == "community":
            spfLexSrc = dVars['lexicon_community_src']
            sfDictDst = dVars['dic_community_filename']
            sDicName = dVars['dic_community_name']
            sDescription = dVars['dic_community_description']
        elif sType == "personal":
            spfLexSrc = dVars['lexicon_personal_src']
            sfDictDst = dVars['dic_personal_filename']
            sDicName = dVars['dic_personal_name']
            sDescription = dVars['dic_personal_description']
        lex_build.build(spfLexSrc, dVars['lang'], dVars['lang_name'], sfDictDst, bJavaScript, sDicName, "", dVars['stemming_method'], int(dVars['fsa_method']))
        lex_build.build(spfLexSrc, dVars['lang'], dVars['lang_name'], sfDictDst, bJavaScript, sDicName, sDescription, "", dVars['stemming_method'], int(dVars['fsa_method']))



def main ():
    "build Grammalecte with requested options"
    print("Python: " + sys.version)
    xParser = argparse.ArgumentParser()