Grammalecte  Check-in [7217592c77]

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

Overview
Comment:merge comdic
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:7217592c77f23a9e439f41473408c523dadc5c7b57117a66d605a52efff780ea
User & Date: olr 2019-04-03 14:18:54
Context
2019-04-03
14:34
[tb] remove extended dictionary labels check-in: 77ef752105 user: olr tags: tb, trunk
14:18
merge comdic check-in: 7217592c77 user: olr tags: trunk
13:31
[fr] mise à jour du dictionnaire check-in: 0b6aa78216 user: olr tags: fr, trunk
2019-03-15
14:08
[fx] fix merge Closed-Leaf check-in: e8375db0be user: olr tags: comdic, fx
Changes

Changes to gc_core/js/lang_core/gc_engine.js.

40
41
42
43
44
45
46





47
48
49
50
51
52
53
let _sAppContext = "";                                  // what software is running
let _dOptions = null;
let _dOptionsColors = null;
let _oSpellChecker = null;
let _oTokenizer = null;
let _aIgnoredRules = new Set();








var gc_engine = {

    //// Informations

    lang: "${lang}",







>
>
>
>
>







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}",

Changes to gc_lang/fr/config.ini.

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
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 = Français,Français (Classique/Moderne),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
# community dictionary
lexicon_community_src = lexicons/French.community.lex
dic_community_filename = fr.community

dic_community_name = 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
# 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







|
>






>
|



>
|



>
|







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

Changes to gc_lang/fr/dictionnaire/genfrdic.py.

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
                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







<
<
<
<
<







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

Changes to gc_lang/fr/modules-js/lexicographe.js.

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
...
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
            if (aRes) {
                yield aRes;
            }
        }
    }

    getListOfTokensReduc (sText, bInfo=true) {
        let aTokenList = this.getListOfTokens(sText.replace("'", "’").trim(), false);
        let iKey = 0;
        let aElem = [];



        do {
            let oToken = aTokenList[iKey];
            let sMorphLoc = '';
            let aTokenTempList = [oToken];
            if (oToken.sType == "WORD" || oToken.sType == "WORD_ELIDED"){
                let iKeyTree = iKey + 1;
                let oLocNode = this.oLocGraph[oToken.sValue.toLowerCase()];
                while (oLocNode) {
                    let oTokenNext = aTokenList[iKeyTree];
                    iKeyTree++;
                    if (oTokenNext) {
                        oLocNode = oLocNode[oTokenNext.sValue.toLowerCase()];
                    }
                    if (oLocNode && iKeyTree <= aTokenList.length) {
                        sMorphLoc = oLocNode["_:_"];
                        aTokenTempList.push(oTokenNext);
                    } else {
                        break;
                    }
                }
            }

            if (sMorphLoc) {

                let sValue = '';
                for (let oTokenWord of aTokenTempList) {
                    sValue += oTokenWord.sValue+' ';
                }
                let oTokenLocution = {
                    'nStart': aTokenTempList[0].nStart,
                    'nEnd': aTokenTempList[aTokenTempList.length-1].nEnd,
................................................................................
                        sValue: oTokenLocution.sValue,
                        aLabel: aFormatedTag,
                        aSubElem: aSubElem
                    });
                } else {
                    aElem.push(oTokenLocution);
                }
                iKey = iKey + aTokenTempList.length;
            } else {

                if (bInfo) {
                    let aRes = this.getInfoForToken(oToken);
                    if (aRes) {
                        aElem.push(aRes);
                    }
                } else {
                    aElem.push(oToken);
                }
                iKey++;
            }
        } while (iKey < aTokenList.length);
        return aElem;
    }
}


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







|
|

>
>
>

|



|


|
|



|









>







 







|

>








|

|








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
...
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
            if (aRes) {
                yield aRes;
            }
        }
    }

    getListOfTokensReduc (sText, bInfo=true) {
        let lToken = this.getListOfTokens(sText.replace("'", "’").trim(), false);
        let iToken = 0;
        let aElem = [];
        if (lToken.length == 0) {
            return aElem;
        }
        do {
            let oToken = lToken[iToken];
            let sMorphLoc = '';
            let aTokenTempList = [oToken];
            if (oToken.sType == "WORD" || oToken.sType == "WORD_ELIDED"){
                let iLocEnd = iToken + 1;
                let oLocNode = this.oLocGraph[oToken.sValue.toLowerCase()];
                while (oLocNode) {
                    let oTokenNext = lToken[iLocEnd];
                    iLocEnd++;
                    if (oTokenNext) {
                        oLocNode = oLocNode[oTokenNext.sValue.toLowerCase()];
                    }
                    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,
................................................................................
                        sValue: oTokenLocution.sValue,
                        aLabel: aFormatedTag,
                        aSubElem: aSubElem
                    });
                } else {
                    aElem.push(oTokenLocution);
                }
                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);
                }
                iToken++;
            }
        } while (iToken < lToken.length);
        return aElem;
    }
}


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

Changes to gc_lang/fr/oxt/DictOptions/DictOptions.py.

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
..
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
...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
...
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
# BUTTONS_OK, BUTTONS_OK_CANCEL, BUTTONS_YES_NO, BUTTONS_YES_NO_CANCEL, BUTTONS_RETRY_CANCEL, BUTTONS_ABORT_IGNORE_RETRY
# DEFAULT_BUTTON_OK, DEFAULT_BUTTON_CANCEL, DEFAULT_BUTTON_RETRY, DEFAULT_BUTTON_YES, DEFAULT_BUTTON_NO, DEFAULT_BUTTON_IGNORE
from com.sun.star.awt.MessageBoxType import INFOBOX, ERRORBOX # MESSAGEBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX

def MessageBox (xDocument, sMsg, sTitle, nBoxType=INFOBOX, nBoxButtons=BUTTONS_OK):
    xParentWin = xDocument.CurrentController.Frame.ContainerWindow
    ctx = uno.getComponentContext()
    xToolkit = ctx.ServiceManager.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx) 
    xMsgBox = xToolkit.createMessageBox(xParentWin, nBoxType, nBoxButtons, sTitle, sMsg)
    return xMsgBox.execute()


class DictOptions (unohelper.Base, XActionListener, XJobExecutor):

    def __init__ (self, ctx):
        self.ctx = ctx
        self.xSvMgr = self.ctx.ServiceManager
        self.xContainer = None
        self.xDialog = None
        
    def _addWidget (self, name, wtype, x, y, w, h, **kwargs):
        xWidget = self.xDialog.createInstance('com.sun.star.awt.UnoControl%sModel' % wtype)
        xWidget.Name = name
        xWidget.PositionX = x
        xWidget.PositionY = y
        xWidget.Width = w
        xWidget.Height = h
................................................................................
        self.xDesktop = self.xSvMgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.ctx)
        self.xDocument = self.xDesktop.getCurrentComponent()
        self.xOptionNode = helpers.getConfigSetting("/org.openoffice.Lightproof_grammalecte/Other/", True)

        # dialog
        self.xDialog = self.xSvMgr.createInstanceWithContext('com.sun.star.awt.UnoControlDialogModel', self.ctx)
        self.xDialog.Width = 200
        self.xDialog.Height = 310
        self.xDialog.Title = self.dUI.get('title', "#title#")
        xWindowSize = helpers.getWindowSize()
        self.xDialog.PositionX = int((xWindowSize.Width / 2) - (self.xDialog.Width / 2))
        self.xDialog.PositionY = int((xWindowSize.Height / 2) - (self.xDialog.Height / 2))

        # fonts
        xFDTitle = uno.createUnoStruct("com.sun.star.awt.FontDescriptor")
        xFDTitle.Height = 9
        xFDTitle.Weight = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD")
        xFDTitle.Name = "Verdana"
        
        xFDSubTitle = uno.createUnoStruct("com.sun.star.awt.FontDescriptor")
        xFDSubTitle.Height = 10
        xFDSubTitle.Weight = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD")
        xFDSubTitle.Name = "Verdana"

        # widget
        nX = 10
        nY1 = 10
        nY2 = nY1 + 60
        nY3 = nY2 + 25
        nY4 = nY3 + 25
        nY5 = nY4 + 45
        nY6 = nY5 + 95

        nWidth = self.xDialog.Width - 20
        nHeight = 10

        # Graphspell dictionary section
        self._addWidget("graphspell_section", 'FixedLine', nX, nY1, nWidth, nHeight, Label = self.dUI.get("graphspell_section", "#err"), FontDescriptor = xFDTitle)
        self.xMainDic = self._addWidget('activate_main', 'CheckBox', nX, nY1+15, nWidth, nHeight, Label = self.dUI.get('activate_main', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088, State = True)
        self._addWidget('activate_main_descr', 'FixedText', nX+10, nY1+25, nWidth-10, nHeight*2, Label = self.dUI.get('activate_main_descr', "#err"), MultiLine = True)
        self._addWidget('spelling', 'FixedText', nX+10, nY1+45, nWidth-80, nHeight, Label = self.dUI.get('spelling', "#err"), FontDescriptor = xFDSubTitle)
        self.xInfoDicButton = self._addWidget('info_dic_button', 'Button', nX+160, nY1+45, 12, 9, Label = "‹i›")
        self.xSelClassic = self._addWidget('classic', 'RadioButton', nX+10, nY1+55, 50, nHeight, Label = self.dUI.get('classic', "#err"))
        self.xSelReform = self._addWidget('reform', 'RadioButton', nX+65, nY1+55, 55, nHeight, Label = self.dUI.get('reform', "#err"))
        self.xSelAllvars = self._addWidget('allvars', 'RadioButton', nX+120, nY1+55, 60, nHeight, Label = self.dUI.get('allvars', "#err"))
        self.xExtendedDic = self._addWidget('activate_extended', 'CheckBox', nX, nY2+15, nWidth, nHeight, Label = self.dUI.get('activate_extended', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088, Enabled = False)
        self._addWidget('activate_extended_descr', 'FixedText', nX+10, nY2+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_extended_descr', "#err"), MultiLine = True)
        self.xCommunityDic = self._addWidget('activate_community', 'CheckBox', nX, nY3+15, nWidth, nHeight, Label = self.dUI.get('activate_community', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088, Enabled = False)
        self._addWidget('activate_community_descr', 'FixedText', nX+10, nY3+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_community_descr', "#err"), MultiLine = True)
        self.xPersonalDic = self._addWidget('activate_personal', 'CheckBox', nX, nY4+15, nWidth, nHeight, Label = self.dUI.get('activate_personal', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088)
        self._addWidget('activate_personal_descr', 'FixedText', nX+10, nY4+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_personal_descr', "#err"), MultiLine = True)
        
        # Spell suggestion engine section
        self._addWidget("suggestion_section", 'FixedLine', nX, nY5, nWidth, nHeight, Label = self.dUI.get("suggestion_section", "#err"), FontDescriptor = xFDTitle)
        self.xGraphspellSugg = self._addWidget('activate_spell_sugg', 'CheckBox', nX, nY5+15, nWidth, nHeight, Label = self.dUI.get('activate_spell_sugg', "#err"))
        self._addWidget('activate_spell_sugg_descr', 'FixedText', nX, nY5+25, nWidth, nHeight*6, Label = self.dUI.get('activate_spell_sugg_descr', "#err"), MultiLine = True)

        # Restart message
        self._addWidget('restart', 'FixedText', nX, nY6, nWidth, nHeight*2, Label = self.dUI.get('restart', "#err"), FontDescriptor = xFDTitle, MultiLine = True, TextColor = 0x880000)

        # Button
        self._addWidget('apply_button', 'Button', self.xDialog.Width-115, self.xDialog.Height-20, 50, 14, Label = self.dUI.get('apply_button', "#err"), FontDescriptor = xFDTitle, TextColor = 0x005500)
        self._addWidget('cancel_button', 'Button', self.xDialog.Width-60, self.xDialog.Height-20, 50, 14, Label = self.dUI.get('cancel_button', "#err"), FontDescriptor = xFDTitle, TextColor = 0x550000)

        self._loadOptions()

................................................................................
        self.xContainer.execute()

    # XActionListener
    def actionPerformed (self, xActionEvent):
        try:
            if xActionEvent.ActionCommand == 'Apply':
                xChild = self.xOptionNode.getByName("o_fr")
                #xChild.setPropertyValue("use_extended_dic", self.xExtendedDic.State)
                #xChild.setPropertyValue("use_community_dic", self.xCommunityDic.State)
                xChild.setPropertyValue("use_personal_dic", self.xPersonalDic.State)
                xChild.setPropertyValue("use_graphspell_sugg", self.xGraphspellSugg.State)
                sMainDicName = "classic"
                if self.xSelClassic.State:
                    sMainDicName = "classic"
                elif self.xSelReform.State:
................................................................................
                self.xContainer.endExecute()
            elif xActionEvent.ActionCommand == 'InfoDic':
                MessageBox(self.xDocument, self.dUI.get('spelling_descr', "#err"), "Orthographe du français", nBoxType=INFOBOX, nBoxButtons=BUTTONS_OK)
            else:
                self.xContainer.endExecute()
        except:
            traceback.print_exc()
    
    # XJobExecutor
    def trigger (self, args):
        try:
            dialog = DictOptions(self.ctx)
            dialog.run()
        except:
            traceback.print_exc()

    def _loadOptions (self):
        try:
            xChild = self.xOptionNode.getByName("o_fr")
            #self.xGraphspell.State = xChild.getPropertyValue("use_graphspell")
            self.xGraphspellSugg.State = xChild.getPropertyValue("use_graphspell_sugg")
            #self.xExtendedDic.State = xChild.getPropertyValue("use_extended_dic")
            #self.xCommunityDic.State = xChild.getPropertyValue("use_community_dic")
            self.xPersonalDic.State = xChild.getPropertyValue("use_personal_dic")
            sMainDicName = xChild.getPropertyValue("main_dic_name")
            if sMainDicName == "classic":
                self.xSelClassic.State = 1
            elif sMainDicName == "reform":
                self.xSelReform.State = 1







|











|







 







|










|










|
|
<













<
<
|
|
|
|
|

|
|
|


|







 







<







 







|













<







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
..
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
...
128
129
130
131
132
133
134

135
136
137
138
139
140
141
...
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
# BUTTONS_OK, BUTTONS_OK_CANCEL, BUTTONS_YES_NO, BUTTONS_YES_NO_CANCEL, BUTTONS_RETRY_CANCEL, BUTTONS_ABORT_IGNORE_RETRY
# DEFAULT_BUTTON_OK, DEFAULT_BUTTON_CANCEL, DEFAULT_BUTTON_RETRY, DEFAULT_BUTTON_YES, DEFAULT_BUTTON_NO, DEFAULT_BUTTON_IGNORE
from com.sun.star.awt.MessageBoxType import INFOBOX, ERRORBOX # MESSAGEBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX

def MessageBox (xDocument, sMsg, sTitle, nBoxType=INFOBOX, nBoxButtons=BUTTONS_OK):
    xParentWin = xDocument.CurrentController.Frame.ContainerWindow
    ctx = uno.getComponentContext()
    xToolkit = ctx.ServiceManager.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)
    xMsgBox = xToolkit.createMessageBox(xParentWin, nBoxType, nBoxButtons, sTitle, sMsg)
    return xMsgBox.execute()


class DictOptions (unohelper.Base, XActionListener, XJobExecutor):

    def __init__ (self, ctx):
        self.ctx = ctx
        self.xSvMgr = self.ctx.ServiceManager
        self.xContainer = None
        self.xDialog = None

    def _addWidget (self, name, wtype, x, y, w, h, **kwargs):
        xWidget = self.xDialog.createInstance('com.sun.star.awt.UnoControl%sModel' % wtype)
        xWidget.Name = name
        xWidget.PositionX = x
        xWidget.PositionY = y
        xWidget.Width = w
        xWidget.Height = h
................................................................................
        self.xDesktop = self.xSvMgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.ctx)
        self.xDocument = self.xDesktop.getCurrentComponent()
        self.xOptionNode = helpers.getConfigSetting("/org.openoffice.Lightproof_grammalecte/Other/", True)

        # dialog
        self.xDialog = self.xSvMgr.createInstanceWithContext('com.sun.star.awt.UnoControlDialogModel', self.ctx)
        self.xDialog.Width = 200
        self.xDialog.Height = 285
        self.xDialog.Title = self.dUI.get('title', "#title#")
        xWindowSize = helpers.getWindowSize()
        self.xDialog.PositionX = int((xWindowSize.Width / 2) - (self.xDialog.Width / 2))
        self.xDialog.PositionY = int((xWindowSize.Height / 2) - (self.xDialog.Height / 2))

        # fonts
        xFDTitle = uno.createUnoStruct("com.sun.star.awt.FontDescriptor")
        xFDTitle.Height = 9
        xFDTitle.Weight = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD")
        xFDTitle.Name = "Verdana"

        xFDSubTitle = uno.createUnoStruct("com.sun.star.awt.FontDescriptor")
        xFDSubTitle.Height = 10
        xFDSubTitle.Weight = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD")
        xFDSubTitle.Name = "Verdana"

        # widget
        nX = 10
        nY1 = 10
        nY2 = nY1 + 60
        nY3 = nY2 + 25
        nY4 = nY3 + 45
        nY5 = nY4 + 95


        nWidth = self.xDialog.Width - 20
        nHeight = 10

        # Graphspell dictionary section
        self._addWidget("graphspell_section", 'FixedLine', nX, nY1, nWidth, nHeight, Label = self.dUI.get("graphspell_section", "#err"), FontDescriptor = xFDTitle)
        self.xMainDic = self._addWidget('activate_main', 'CheckBox', nX, nY1+15, nWidth, nHeight, Label = self.dUI.get('activate_main', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088, State = True)
        self._addWidget('activate_main_descr', 'FixedText', nX+10, nY1+25, nWidth-10, nHeight*2, Label = self.dUI.get('activate_main_descr', "#err"), MultiLine = True)
        self._addWidget('spelling', 'FixedText', nX+10, nY1+45, nWidth-80, nHeight, Label = self.dUI.get('spelling', "#err"), FontDescriptor = xFDSubTitle)
        self.xInfoDicButton = self._addWidget('info_dic_button', 'Button', nX+160, nY1+45, 12, 9, Label = "‹i›")
        self.xSelClassic = self._addWidget('classic', 'RadioButton', nX+10, nY1+55, 50, nHeight, Label = self.dUI.get('classic', "#err"))
        self.xSelReform = self._addWidget('reform', 'RadioButton', nX+65, nY1+55, 55, nHeight, Label = self.dUI.get('reform', "#err"))
        self.xSelAllvars = self._addWidget('allvars', 'RadioButton', nX+120, nY1+55, 60, nHeight, Label = self.dUI.get('allvars', "#err"))


        self.xCommunityDic = self._addWidget('activate_community', 'CheckBox', nX, nY2+15, nWidth, nHeight, Label = self.dUI.get('activate_community', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088, Enabled = False)
        self._addWidget('activate_community_descr', 'FixedText', nX+10, nY2+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_community_descr', "#err"), MultiLine = True)
        self.xPersonalDic = self._addWidget('activate_personal', 'CheckBox', nX, nY3+15, nWidth, nHeight, Label = self.dUI.get('activate_personal', "#err"), FontDescriptor = xFDSubTitle, TextColor = 0x000088)
        self._addWidget('activate_personal_descr', 'FixedText', nX+10, nY3+25, nWidth-10, nHeight*1, Label = self.dUI.get('activate_personal_descr', "#err"), MultiLine = True)

        # Spell suggestion engine section
        self._addWidget("suggestion_section", 'FixedLine', nX, nY4, nWidth, nHeight, Label = self.dUI.get("suggestion_section", "#err"), FontDescriptor = xFDTitle)
        self.xGraphspellSugg = self._addWidget('activate_spell_sugg', 'CheckBox', nX, nY4+15, nWidth, nHeight, Label = self.dUI.get('activate_spell_sugg', "#err"))
        self._addWidget('activate_spell_sugg_descr', 'FixedText', nX, nY4+25, nWidth, nHeight*6, Label = self.dUI.get('activate_spell_sugg_descr', "#err"), MultiLine = True)

        # Restart message
        self._addWidget('restart', 'FixedText', nX, nY5, nWidth, nHeight*2, Label = self.dUI.get('restart', "#err"), FontDescriptor = xFDTitle, MultiLine = True, TextColor = 0x880000)

        # Button
        self._addWidget('apply_button', 'Button', self.xDialog.Width-115, self.xDialog.Height-20, 50, 14, Label = self.dUI.get('apply_button', "#err"), FontDescriptor = xFDTitle, TextColor = 0x005500)
        self._addWidget('cancel_button', 'Button', self.xDialog.Width-60, self.xDialog.Height-20, 50, 14, Label = self.dUI.get('cancel_button', "#err"), FontDescriptor = xFDTitle, TextColor = 0x550000)

        self._loadOptions()

................................................................................
        self.xContainer.execute()

    # XActionListener
    def actionPerformed (self, xActionEvent):
        try:
            if xActionEvent.ActionCommand == 'Apply':
                xChild = self.xOptionNode.getByName("o_fr")

                #xChild.setPropertyValue("use_community_dic", self.xCommunityDic.State)
                xChild.setPropertyValue("use_personal_dic", self.xPersonalDic.State)
                xChild.setPropertyValue("use_graphspell_sugg", self.xGraphspellSugg.State)
                sMainDicName = "classic"
                if self.xSelClassic.State:
                    sMainDicName = "classic"
                elif self.xSelReform.State:
................................................................................
                self.xContainer.endExecute()
            elif xActionEvent.ActionCommand == 'InfoDic':
                MessageBox(self.xDocument, self.dUI.get('spelling_descr', "#err"), "Orthographe du français", nBoxType=INFOBOX, nBoxButtons=BUTTONS_OK)
            else:
                self.xContainer.endExecute()
        except:
            traceback.print_exc()

    # XJobExecutor
    def trigger (self, args):
        try:
            dialog = DictOptions(self.ctx)
            dialog.run()
        except:
            traceback.print_exc()

    def _loadOptions (self):
        try:
            xChild = self.xOptionNode.getByName("o_fr")
            #self.xGraphspell.State = xChild.getPropertyValue("use_graphspell")
            self.xGraphspellSugg.State = xChild.getPropertyValue("use_graphspell_sugg")

            #self.xCommunityDic.State = xChild.getPropertyValue("use_community_dic")
            self.xPersonalDic.State = xChild.getPropertyValue("use_personal_dic")
            sMainDicName = xChild.getPropertyValue("main_dic_name")
            if sMainDicName == "classic":
                self.xSelClassic.State = 1
            elif sMainDicName == "reform":
                self.xSelReform.State = 1

Changes to gc_lang/fr/oxt/DictOptions/LexiconEditor.py.

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")
            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", "")







|







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", "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", "")

Changes to gc_lang/fr/oxt/DictOptions/do_strings.py.

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
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
..
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    if sLang in dStrings:
        return dStrings[sLang]
    return dStrings["fr"]

dStrings = {
    "fr": {
        "title": "Grammalecte · Options orthographiques",
        
        "spelling_section": "Correcteur orthographique",
        "activate_main": "Activer le correcteur orthographique de Grammalecte",
        "activate_main_descr": "Supplante le correcteur orthographique inclus dans LibreOffice (Hunspell).",

        "suggestion_section": "Moteur de suggestion orthographique",
        "activate_spell_sugg": "Moteur de suggestion de Grammalecte",
        "activate_spell_sugg_descr": "Les suggestions orthographiques des mots non reconnus par le correcteur sont fournies par Grammalecte. Si ces suggestions ne vous satisfont pas (ou si c’est trop lent), vous pouvez désactiver cette option : les suggestions orthographiques seront alors fournies par le correcteur de LibreOffice. Mais, dans ce cas, les mots que vous avez ajoutés au dictionnaire personnel de Grammalecte ne pourront pas être inclus aux suggestions.",
................................................................................
        "activate_main": "Dictionnaire principal",
        "activate_main_descr": "Environ 83 000 entrées, 500 000 flexions.\nNi éditable, ni désactivable.",
        "spelling": "Orthographe",
        "spelling_descr": "Le dictionnaire “Classique” propose l’orthographe telle qu’elle est écrite aujourd’hui le plus couramment. C’est le dictionnaire recommandé. Il contient les graphies usuelles et classiques, certaines encore communément utilisées, d’autres désuètes.\n\nAvec le dictionnaire “Réforme 1990”, seule l’orthographe réformée est reconnue. Attendu que bon nombre de graphies réformées sont considérées comme erronées par beaucoup, ce dictionnaire est déconseillé. Les graphies passées dans l’usage sont déjà incluses dans le dictionnaire “Classique”.\n\nLe dictionnaire “Toutes variantes” contient toutes les graphies, classiques ou réformées, ainsi que d’autres plus rares encore. Ce dictionnaire est déconseillé à ceux qui ne connaissent pas très bien la langue française.",
        "allvars": "Toutes variantes",
        "classic": "Classique",
        "reform": "Réforme 1990",
        "activate_extended": "Dictionnaire étendu",
        "activate_extended_descr": "Fonctionnalité à venir",
        "activate_community": "Dictionnaire communautaire",
        "activate_community_descr": "Fonctionnalité à venir",
        "activate_personal": "Dictionnaire personnel",
        "activate_personal_descr": "Créable et éditable via l’éditeur lexical.",

        "restart": "Le changement ne prendra effet qu’après le redémarrage du logiciel.",

        "apply_button": "Appliquer",
        "cancel_button": "Annuler",
    },
    "en": {
        "title": "Grammalecte · Spelling options",
        
        "spelling_section": "Spell checker",
        "activate_main": "Activate the spell checker from Grammalecte",
        "activate_main_descr": "Overrides the spell checker included in LibreOffice (Hunspell)",

        "suggestion_section": "Spell suggestion engine",
        "activate_spell_sugg": "Suggestion engine of Grammalecte",
        "activate_spell_sugg_descr": "Spelling suggestions for words unrecognized by the spellchecker are provided by Grammalecte. If you aren’t satisfied by these suggestions (or if it’s too slow), you can disable this option: spelling suggestions will then be provided by the LibreOffice proofreader. In this case, words you have added in your Grammalecte’s custom dictionary won’t be included in suggestions.",
................................................................................
        "activate_main": "Main dictionary",
        "activate_main_descr": "About 83 000 entries, 500 000 flexions.\nNot editable, not deactivable.",
        "spelling": "Spelling",
        "spelling_descr": "The dictionary “Classic” offers the French spelling as it is written nowadays most often. This is the recommended dictionary. It contains usual and classical spellings, some of them still widely used, others obsolete.\n\nWith the dictionary “Reform 1990”, only the reformed spelling is recognized. As many of reformed spellings are considered erroneous by many people, this dictionary is unadvised. Reformed spellings commonly used are already included in the “Classic” dictionary.\n\nThe dictionary “All variants” contains all spelling variants, classical and reformed, and some others even rarer. This dictionary is unadvised for those who don’t know very well the French language.",
        "allvars": "All variants",
        "classic": "Classic",
        "reform": "Reform 1990",
        "activate_extended": "Extended dictionary",
        "activate_extended_descr": "Feature to come.",
        "activate_community": "Community dictionary",
        "activate_community_descr": "Feature to come.",
        "activate_personal": "Personal dictionary",
        "activate_personal_descr": "Creatible and editable via the lexicon editor.",

        "restart": "The modification will be effective only after restarting the software.",

        "apply_button": "Apply",
        "cancel_button": "Cancel",
    },
}







|







 







<
<












|







 







<
<











2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
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
..
48
49
50
51
52
53
54


55
56
57
58
59
60
61
62
63
64
65
    if sLang in dStrings:
        return dStrings[sLang]
    return dStrings["fr"]

dStrings = {
    "fr": {
        "title": "Grammalecte · Options orthographiques",

        "spelling_section": "Correcteur orthographique",
        "activate_main": "Activer le correcteur orthographique de Grammalecte",
        "activate_main_descr": "Supplante le correcteur orthographique inclus dans LibreOffice (Hunspell).",

        "suggestion_section": "Moteur de suggestion orthographique",
        "activate_spell_sugg": "Moteur de suggestion de Grammalecte",
        "activate_spell_sugg_descr": "Les suggestions orthographiques des mots non reconnus par le correcteur sont fournies par Grammalecte. Si ces suggestions ne vous satisfont pas (ou si c’est trop lent), vous pouvez désactiver cette option : les suggestions orthographiques seront alors fournies par le correcteur de LibreOffice. Mais, dans ce cas, les mots que vous avez ajoutés au dictionnaire personnel de Grammalecte ne pourront pas être inclus aux suggestions.",
................................................................................
        "activate_main": "Dictionnaire principal",
        "activate_main_descr": "Environ 83 000 entrées, 500 000 flexions.\nNi éditable, ni désactivable.",
        "spelling": "Orthographe",
        "spelling_descr": "Le dictionnaire “Classique” propose l’orthographe telle qu’elle est écrite aujourd’hui le plus couramment. C’est le dictionnaire recommandé. Il contient les graphies usuelles et classiques, certaines encore communément utilisées, d’autres désuètes.\n\nAvec le dictionnaire “Réforme 1990”, seule l’orthographe réformée est reconnue. Attendu que bon nombre de graphies réformées sont considérées comme erronées par beaucoup, ce dictionnaire est déconseillé. Les graphies passées dans l’usage sont déjà incluses dans le dictionnaire “Classique”.\n\nLe dictionnaire “Toutes variantes” contient toutes les graphies, classiques ou réformées, ainsi que d’autres plus rares encore. Ce dictionnaire est déconseillé à ceux qui ne connaissent pas très bien la langue française.",
        "allvars": "Toutes variantes",
        "classic": "Classique",
        "reform": "Réforme 1990",


        "activate_community": "Dictionnaire communautaire",
        "activate_community_descr": "Fonctionnalité à venir",
        "activate_personal": "Dictionnaire personnel",
        "activate_personal_descr": "Créable et éditable via l’éditeur lexical.",

        "restart": "Le changement ne prendra effet qu’après le redémarrage du logiciel.",

        "apply_button": "Appliquer",
        "cancel_button": "Annuler",
    },
    "en": {
        "title": "Grammalecte · Spelling options",

        "spelling_section": "Spell checker",
        "activate_main": "Activate the spell checker from Grammalecte",
        "activate_main_descr": "Overrides the spell checker included in LibreOffice (Hunspell)",

        "suggestion_section": "Spell suggestion engine",
        "activate_spell_sugg": "Suggestion engine of Grammalecte",
        "activate_spell_sugg_descr": "Spelling suggestions for words unrecognized by the spellchecker are provided by Grammalecte. If you aren’t satisfied by these suggestions (or if it’s too slow), you can disable this option: spelling suggestions will then be provided by the LibreOffice proofreader. In this case, words you have added in your Grammalecte’s custom dictionary won’t be included in suggestions.",
................................................................................
        "activate_main": "Main dictionary",
        "activate_main_descr": "About 83 000 entries, 500 000 flexions.\nNot editable, not deactivable.",
        "spelling": "Spelling",
        "spelling_descr": "The dictionary “Classic” offers the French spelling as it is written nowadays most often. This is the recommended dictionary. It contains usual and classical spellings, some of them still widely used, others obsolete.\n\nWith the dictionary “Reform 1990”, only the reformed spelling is recognized. As many of reformed spellings are considered erroneous by many people, this dictionary is unadvised. Reformed spellings commonly used are already included in the “Classic” dictionary.\n\nThe dictionary “All variants” contains all spelling variants, classical and reformed, and some others even rarer. This dictionary is unadvised for those who don’t know very well the French language.",
        "allvars": "All variants",
        "classic": "Classic",
        "reform": "Reform 1990",


        "activate_community": "Community dictionary",
        "activate_community_descr": "Feature to come.",
        "activate_personal": "Personal dictionary",
        "activate_personal_descr": "Creatible and editable via the lexicon editor.",

        "restart": "The modification will be effective only after restarting the software.",

        "apply_button": "Apply",
        "cancel_button": "Cancel",
    },
}

Changes to gc_lang/fr/rules.grx.

503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
...
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
__[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,$,$
    <<- 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)__
................................................................................
!!
!!!! Ponctuation de fin de paragraphe                                                             !!
!!
!!

__[i>/poncfin(poncfin_règle1)__
    ({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).

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


!!







|







 







|







503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
...
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
__[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,*,$
    <<- 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)__
................................................................................
!!
!!!! Ponctuation de fin de paragraphe                                                             !!
!!
!!

__[i>/poncfin(poncfin_règle1)__
    ({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).

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


!!

Changes to gc_lang/fr/tb/content/lex_editor.js.

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







|







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", "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 {

Changes to gc_lang/fr/webext/background.js.

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
...
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
...
275
276
277
278
279
280
281
282
283
284


285
286
287
288
289
290
291
...
321
322
323
324
325
326
327






328
329
330
331
332
333
334
...
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
...
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
...
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426

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

function initSCOptions (dSavedOptions) {
    if (!dSavedOptions.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"]);
    }
}

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"]);




    }
    if (dSavedDictionary.hasOwnProperty("oCommunityDictionary")) {
        setDictionary("community", dSavedDictionary["oCommunityDictionary"]);


    }
    if (dSavedDictionary.hasOwnProperty("oPersonalDictionary")) {
        setDictionary("personal", dSavedDictionary["oPersonalDictionary"]);


    }
}

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("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("sc_options").then(initSCOptions, showError);
}

init();


browser.runtime.onInstalled.addListener(function (oDetails) {
................................................................................
        case "setDictionary":
        case "setDictionaryOnOff":
            xGCEWorker.postMessage(oRequest);
            break;
        case "openURL":
            browser.tabs.create({url: dParam.sURL});
            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) {

    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":
................................................................................
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
browser.contextMenus.create({ id: "conjugueur_window",          title: "Conjugueur [fenêtre]",                      contexts: ["all"] });
browser.contextMenus.create({ id: "conjugueur_tab",             title: "Conjugueur [onglet]",                       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
................................................................................
        // conjugueur
        case "conjugueur_window":
            openConjugueurWindow();
            break;
        case "conjugueur_tab":
            openConjugueurTab();
            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);
................................................................................


/*
    Keyboard shortcuts
*/
browser.commands.onCommand.addListener(function (sCommand) {
    switch (sCommand) {









        case "conjugueur_tab":
            openConjugueurTab();
            break;
        case "conjugueur_window":
            openConjugueurWindow();
            break;
        case "lex_editor":
            openLexEditor();



            break;
    }
});


















/*
    Actions
*/

function storeGCOptions (dOptions) {
    if (dOptions instanceof Map) {
................................................................................
}

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




















function openLexEditor () {

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

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








}

function openConjugueurWindow () {
    if (bChrome) {
        browser.windows.create({
            url: browser.extension.getURL("panel/conjugueur.html"),
            type: "popup",
................................................................................
    }
    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}`);

}







|
|





<



<
<
|
|











|
|
|
>
>
>
>

<
<
>
>

<
<
>
>







|
|
|





|
|
|







 







>
>
>
>
>
>
>
>
>











>







 







|


>
>







 







>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>






|
|
>
>
>




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







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

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

>
|
|
|
<
>
|
|
|
|
|
|
>
>
>
>
>
>
>
>







 







<



<
<
<
<
|
<
>

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
...
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
...
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
...
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
...
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
...
516
517
518
519
520
521
522

523
524
525




526

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

function initSCOptions (oData) {
    if (!oData.hasOwnProperty("sc_options")) {
        browser.storage.local.set({"sc_options": {
            extended: true,
            community: true,
            personal: true
        }});

        setDictionaryOnOff("community", true);
        setDictionaryOnOff("personal", true);
    } else {


        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 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 (oData.hasOwnProperty("personal_dictionary")) {
        setDictionary("personal", oData["personal_dictionary"]);
    }


    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("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("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) {
................................................................................
        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":
................................................................................
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"] });
// 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
................................................................................
        // 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);
................................................................................


/*
    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 "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) {
................................................................................
}

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 openLexiconEditor (sName="__personal__") {
    if (nTabLexiconEditor === null) {
        if (bChrome) {
            browser.tabs.create({
                url: browser.extension.getURL("panel/lex_editor.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(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",
................................................................................
    }
    let xConjWindow = browser.windows.create({
        url: browser.extension.getURL("panel/conjugueur.html"),
        type: "popup",
        width: 710,
        height: 980
    });

}






function onError (e) {

    console.error(e);
}

Changes to gc_lang/fr/webext/content_scripts/init.js.

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
...
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











































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

    observePage: function () {
        /*
            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 (that.oOptions === null || that.oOptions.textarea) {
                            oGrammalecte.lMenu.push(new GrammalecteMenu(oGrammalecte.nMenu, mutation.addedNodes[i]));
................................................................................
/*
    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();
................................................................................
        /*
            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);
                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",
                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}
                });
            } 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: {}
            });
            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") {
                    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;


































        default:
            console.log("[Content script] Unknown command: " + sActionDone);
    }
});


















































<
|
<







 







<







 







|
<
<
<
<
<
<





<
<
|
<
<
<








<
<
<
<
<
|
<





|
<
<
<
<
<








|












>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
99
100
101
102
103
104
105

106

107
108
109
110
111
112
113
...
251
252
253
254
255
256
257

258
259
260
261
262
263
264
...
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
                    this.nMenu += 1;
                }
            }
        }
    },

    observePage: function () {

        //    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 (that.oOptions === null || that.oOptions.textarea) {
                            oGrammalecte.lMenu.push(new GrammalecteMenu(oGrammalecte.nMenu, mutation.addedNodes[i]));
................................................................................
/*
    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;

    switch (sActionDone) {
        case "init":
            oGrammalecte.sExtensionUrl = oMessage.sUrl;
            // Start
            oGrammalecte.listenRightClick();
            oGrammalecte.createMenus();
            oGrammalecte.observePage();
................................................................................
        /*
            Commands received from the context menu
            (Context menu are initialized in background)
        */
        // Grammar checker commands
        case "rightClickGCEditableNode":
            if (oGrammalecte.xRightClickedNode !== null) {
                parseAndSpellcheckEditableNode(oGrammalecte.xRightClickedNode);






            } 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":


            parseAndSpellcheckPage();



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





                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":
            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" || 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}
    });
}

Changes to gc_lang/fr/webext/content_scripts/panel_gc.js.

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
...
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
    }

    start (xNode=null) {
        this.oTooltip.hide();
        this.clear();
        if (xNode) {
            this.oNodeControl.setNode(xNode);
            if (xNode.tagName != "TEXTAREA") {
                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) {
................................................................................
        this.dParagraph = new Map();
        this.bTextArea = null;
    }

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

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







|







 







|







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
...
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
    }

    start (xNode=null) {
        this.oTooltip.hide();
        this.clear();
        if (xNode) {
            this.oNodeControl.setNode(xNode);
            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) {
................................................................................
        this.dParagraph = new Map();
        this.bTextArea = null;
    }

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

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

Changes to gc_lang/fr/webext/gce_worker.js.

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
...
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
        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:
................................................................................
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;







<
<
<







 







<
<
<
<
<
<
<







316
317
318
319
320
321
322



323
324
325
326
327
328
329
...
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 "community":
            oSpellChecker.setCommunityDictionary(oDict);
            break;
        case "personal":
            oSpellChecker.setPersonalDictionary(oDict);
            break;
        default:
................................................................................
function setDictionaryOnOff (sDictionary, bActivate, dInfo) {
    if (!oSpellChecker) {
        postMessage(createResponse("setDictionary", "# Error. SpellChecker not loaded.", dInfo, true));
        return;
    }
    //console.log("setDictionaryOnOff", sDictionary, bActivate);
    switch (sDictionary) {







        case "community":
            if (bActivate) {
                oSpellChecker.activateCommunityDictionary();
            } else {
                oSpellChecker.deactivateCommunityDictionary();
            }
            break;

Changes to gc_lang/fr/webext/manifest.json.

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
...
114
115
116
117
118
119
120


121
122

123
124
125
126
127
128
129
        "content_scripts/init.js"
      ],
      "run_at": "document_idle"
    }
  ],

  "commands": {
    "conjugueur_tab": {
      "suggested_key": {



        "default": "Ctrl+Shift+6"

      },






      "description": "Ouvre le conjugueur dans un onglet"
    },
    "conjugueur_window": {
      "suggested_key": {
        "default": "Ctrl+Shift+7"
      },
      "description": "Ouvre le conjugueur dans une fenêtre"
    },
    "lex_editor": {
      "suggested_key": {
        "default": "Ctrl+Shift+8"
      },
      "description": "Ouvre l’éditeur lexical"
    }
  },

  "web_accessible_resources": [
    "content_scripts/panel.css",
    "content_scripts/panel_tf.css",
    "content_scripts/panel_gc.css",
................................................................................
    "grammalecte/fr/mfsp_data.json",
    "grammalecte/fr/phonet_data.json",
    "grammalecte/fr/tests_data.json",
    "img/logo-16.png"
  ],

  "permissions": [


    "activeTab",
    "contextMenus",

    "downloads",
    "storage"
  ],

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







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



|
|
|
|
|
|
|
|
|
|







 







>
>


>







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
...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
        "content_scripts/init.js"
      ],
      "run_at": "document_idle"
    }
  ],

  "commands": {
    "lexicographer": {
      "suggested_key": { "default": "Ctrl+Shift+L" },
      "description": "Ouvre le lexicographe"
    },
    "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": { "default": "Ctrl+Shift+7" },
      "description": "Ouvre le conjugueur dans une fenêtre"
    },
    "lexicon_editor": {
      "suggested_key": { "default": "Ctrl+Shift+8" },
      "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",
................................................................................
    "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.



































































































































































































































































































































































































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



















































































































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



















































































































































































































































































































































































































































































































































































































































































































































































































































































































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

Changes to gc_lang/fr/webext/panel/lex_editor.css.

46
47
48
49
50
51
52





53
54
55
56
57
58
59
..
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
...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
...
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
    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;
}






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



/*
    Main buttons
*/
#buttons {
    padding: 10px 0;
    justify-content: center;
}
.main_button {
    margin: 0 5px;
    width: 100px;
    padding: 10px 20px;
    background-color: hsl(210, 10%, 95%);
    border-radius: 5px;
    text-align: center;
    cursor: pointer;
}
#lexicon_button {
................................................................................
    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 {
................................................................................
    width: 500px;
}


/*
    Dictionary
*/













#save_button, #export_button, #import_button {
    display: none;
    float: right;

    padding: 2px 10px;
    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;


    background-color: hsl(210, 50%, 50%);
    color: hsl(210, 0%, 100%);


}















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


/*







>
>
>
>
>







 







>










|







 







|







 







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

<
>
|








>
>


>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
..
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
...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
...
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
    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;
}
................................................................................
    cursor: auto;
}
details.inline {
    padding: 3px;
    width: 260px;
}



/*
    Main buttons
*/
#buttons {
    padding: 10px 0;
    justify-content: center;
}
.main_button {
    margin: 0 5px;
    min-width: 100px;
    padding: 10px 20px;
    background-color: hsl(210, 10%, 95%);
    border-radius: 5px;
    text-align: center;
    cursor: pointer;
}
#lexicon_button {
................................................................................
    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 {
................................................................................
    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 {
    display: none;

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


/*

Changes to gc_lang/fr/webext/panel/lex_editor.html.

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
...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
...
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
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
...
329
330
331
332
333
334
335
336
337
<!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><span id="dic_num_entries">0</span> entrées</div>
          <!--<div id="import_button" class="fright">Importer</div>-->
          <div class="fright">

            <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>
        <progress id="wait_progress" value="0"></progress>
      </div>

      <div id="buttons" class="container">
        <div id="lexicon_button" class="main_button">Lexique</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">
................................................................................
                      <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>
................................................................................
                  <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;">
................................................................................

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

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







|








|
>
>
>
>
>
>
>
>
|

<
<
>



|
<
>
>
>
>
>
>
>





|
>
>







 







|







 







|



|











<
<
<
<
<
<
<

|







 







|







 







|







 







|

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
...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
...
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
...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
...
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
...
337
338
339
340
341
342
343
344
345
<!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</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>
            <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">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 · <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">
................................................................................
                      <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>
................................................................................
                  <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>







        <table id="lexicon_table">

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


      <div id="search_page">
        <div class="columns">
          <div style="margin-right: 20px;">
................................................................................

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

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

Changes to gc_lang/fr/webext/panel/lex_editor.js.

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
...
123
124
125
126
127
128
129


130
131

132
133
134
135
136
137
138
...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
...
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
...
662
663
664
665
666
667
668
669
670
671
672
673
    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) {
    if (document.getElementById(sElemId)) {
        document.getElementById(sElemId).style.display = "block";
    } 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.")
    }
}
















const oTabulations = {

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

    showPage: function (sRequestedPage) {
................................................................................
        this.xTable.appendChild(xRowNode);
    }

    clear () {
        while (this.xTable.firstChild) {
            this.xTable.removeChild(this.xTable.firstChild);
        }


        this.iEntryIndex = 0;
        this._createHeader();

    }

    fill (lFlex) {
        this.clear();
        if (lFlex.length > 0) {
            this.xProgressBar.max = lFlex.length;
            this.xProgressBar.value = 1;
................................................................................
        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");
        }
    }

    getEntries () {
        return this.lEntry.filter((e) => e !== null);
    }
}
................................................................................
        try {
            oLexiconTable.addEntries(this.createFlexLemmaTagArray());
            oGenWordsTable.clear();
            document.getElementById("lemma").value = "";
            document.getElementById("lemma").focus();
            this.hideAllSections();
            hideElement("editor");
            showElement("save_button");
            this.clear();
            this.cMainTag = "";
        }
        catch (e) {
            showError(e);
        }
    }
}


const oBinaryDict = {


    oIBDAWG: null,



    load: function () {
        if (bChrome) {
            browser.storage.local.get("oPersonalDictionary", this._load.bind(this));



            return;
        }
        let xPromise = browser.storage.local.get("oPersonalDictionary");
        xPromise.then(this._load.bind(this), showError);




    },

    _load: function (oResult) {
        if (!oResult.hasOwnProperty("oPersonalDictionary")) {
            hideElement("export_button");



















































            return;
        }
        let oJSON = oResult.oPersonalDictionary;











































































        if (oJSON) {
            this.__load(oJSON);
        } else {
            oLexiconTable.clear();
            this.setDictData(0, "[néant]");

        }
    },














    __load: 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);
    },

    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);
            }
            catch (e) {
                console.error(e);
                this.setDictData(0, "#Erreur. Voir la console.");
                return;
            }
        } else {
            this.setDictData(0, "[néant]");
            this.save(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("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);



    },

    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 oJSON = oDAWG.createBinaryJSON(1);
            this.save(oJSON);
            this.oIBDAWG = new IBDAWG(oJSON);
            this.setDictData(this.oIBDAWG.nEntry, this.oIBDAWG.sDate);
        } else {

            this.setDictData(0, "[néant]");
            this.save(null);
        }
        hideElement("save_button");
    },









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


const oSearch = {

    oSpellChecker: null,
................................................................................
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();
oBinaryDict.listen();
oGenerator.listen();
oTabulations.listen();
oSearch.listen();







|













|

|












>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>


>







 







|







 







|










|
<
>
|
>
>

|

<
>
>
>


|
|
>
>
>
>


|
|
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|



>



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







 







<
<
<
<
<
>










|
|








|







 







>



>
>
>






|

|



>

<



>
>
>
>
>
>
>
>




|







 







|




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
...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
...
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
...
833
834
835
836
837
838
839
840
841
842
843
844
    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.")
    }
}

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) {
................................................................................
        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;
................................................................................
        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");
        }
    }

    getEntries () {
        return this.lEntry.filter((e) => e !== null);
    }
}
................................................................................
        try {
            oLexiconTable.addEntries(this.createFlexLemmaTagArray());
            oGenWordsTable.clear();
            document.getElementById("lemma").value = "";
            document.getElementById("lemma").focus();
            this.hideAllSections();
            hideElement("editor");
            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 (sName) {
        console.log("lexicon editor, load: " + sName);
        this.sName = sName;
        let oJSON = oDictHandler.getDictionary(sName);
        if (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")
    },

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

    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.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]");
            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", this.sName, this.sDescription, xProgressNode);
            let oJSON = oDAWG.createBinaryJSON(1);
            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]");

        }
        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."+this.sName+".json", url: sURL, saveAs: true });
    }
}


const oSearch = {

    oSpellChecker: null,
................................................................................
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();
oDictHandler.loadDictionaries();
oBinaryDict.listen();
oGenerator.listen();
oTabulations.listen();
oSearch.listen();

Changes to gc_lang/fr/webext/panel/main.css.

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
243
244
245
246
247
248
249



















250
251
252
253
254
255
256
...
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
...
370
371
372
373
374
375
376
377
378
379
380
381
382
    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.
*/

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





















/*
    Spell checking options
*/
#sc_options_page {
    display: none;
    padding: 20px;
................................................................................
    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%);
}

/*
    Options
*/
.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;
}


/*
  Test page
*/
#test_page {
................................................................................
    animation: sk-bounce 2.0s infinite ease-in-out;
}
.double-bounce2 {
    animation-delay: -1.0s;
}

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







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<
<







 







|

|



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
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
...
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
...
383
384
385
386
387
388
389
390
391
392
393
394
395
    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.
*/

................................................................................
    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;
................................................................................
    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;
}
.dic_button {
    margin-left: 10px;
    padding: 2px 10px;
    background-color: hsl(210, 50%, 50%);
    color: hsl(210, 10%, 96%);
    cursor: pointer;
    font-size: 14px;
    font-variant-caps: small-caps;
    border-radius: 3px;






}


/*
  Test page
*/
#test_page {
................................................................................
    animation: sk-bounce 2.0s infinite ease-in-out;
}
.double-bounce2 {
    animation-delay: -1.0s;
}

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

Changes to gc_lang/fr/webext/panel/main.html.

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


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



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







>
>
>
>
>
>






>
>







 







<

|
|

|
>
|
|
>
>
|
<
<
<



|
>
>
>







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>

            <div class="option_section" id="main_dic_box">
                <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="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>
            <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 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">

Changes to gc_lang/fr/webext/panel/main.js.

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
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
...
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
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
            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") {

                openConjugueurTab();
























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


/*
    Messages received
*/
function handleMessage (oMessage, xSender, sendResponse) {
................................................................................
}


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);
................................................................................

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("personal_dic").checked = dOptions.personal;
}

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


/*
    GC options







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











|







 







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
|






|







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
...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
...
179
180
181
182
183
184
185


























186
187
188
189
190
191
192
...
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
            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({
                    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);
}


/*
    Messages received
*/
function handleMessage (oMessage, xSender, sendResponse) {
................................................................................
}


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




























/*
    UI options
*/
function displayUIOptionsLoadedFromStorage () {
    if (bChrome) {
        browser.storage.local.get("ui_options", displayUIOptions);
................................................................................

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;
    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: document.getElementById("community_dic").checked,
        personal: document.getElementById("personal_dic").checked
    }});
}


/*
    GC options

Changes to graphspell-js/dawg.js.

416
417
418
419
420
421
422

423
424
425
426
427
428
429
430

    _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");

        return `${oDate.getFullYear()}-${sMonth}-${sDay}, ${sHours}:${sMinutes}`;
    }
}


const oNodeCounter = {
    nNextId: 0,








>
|







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}:${sSeconds}`;
    }
}


const oNodeCounter = {
    nNextId: 0,

Added graphspell-js/dic_merger.js.















































































































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

}

Changes to graphspell/dawg.py.

18
19
20
21
22
23
24


25
26
27
28
29

30
31
32




33
34



35
36
37
38
39
40
41
...
118
119
120
121
122
123
124





125
126
127
128
129
130
131
...
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
import re
import traceback

from . import str_transform as st
from .progressbar import ProgressBar





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

        with open(spf, "r", encoding="utf-8") as hSrc:
            for sLine in hSrc:
                sLine = sLine.strip()




                if sLine and not sLine.startswith("#"):
                    yield sLine.split("\t")



    else:
        raise OSError("# Error. File not found or not loadable: " + spf)



class DAWG:
    """DIRECT ACYCLIC WORD GRAPH"""
................................................................................
                        + [ (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





        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
................................................................................
                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")

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







>
>





>



>
>
>
>
|

>
>
>







 







>
>
>
>
>







 







|







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
...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
...
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
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()
                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"""
................................................................................
                        + [ (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
................................................................................
                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:%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")

Changes to lex_build.py.

27
28
29
30
31
32
33
34
35
36
37
38
    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()







|




27
28
29
30
31
32
33
34
35
36
37
38
    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()

Changes to lexicons/French.lex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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 v6.4
# 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/*







<
<
<
<
<
<







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 v6.4
# Licence : MPL v2.0







# :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/*

Changes to make.py.

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

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(",")

        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']))
    else:
        if sType == "extended":
            spfLexSrc = dVars['lexicon_extended_src']
            sfDictDst = dVars['dic_extended_filename']
            sDicName = dVars['dic_extended_name']

        elif sType == "community":
            spfLexSrc = dVars['lexicon_community_src']
            sfDictDst = dVars['dic_community_filename']
            sDicName = dVars['dic_community_name']

        elif sType == "personal":
            spfLexSrc = dVars['lexicon_personal_src']
            sfDictDst = dVars['dic_personal_filename']
            sDicName = dVars['dic_personal_name']
        lex_build.build(spfLexSrc, dVars['lang'], dVars['lang_name'], sfDictDst, bJavaScript, sDicName, "", "", dVars['stemming_method'], int(dVars['fsa_method']))



def main ():
    "build Grammalecte with requested options"
    print("Python: " + sys.version)
    xParser = argparse.ArgumentParser()
    xParser.add_argument("lang", type=str, nargs='+', help="lang project to generate (name of folder in /lang)")







>

|
|





>




>




|
|







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, 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, sDescription, "", dVars['stemming_method'], int(dVars['fsa_method']))


def main ():
    "build Grammalecte with requested options"
    print("Python: " + sys.version)
    xParser = argparse.ArgumentParser()
    xParser.add_argument("lang", type=str, nargs='+', help="lang project to generate (name of folder in /lang)")