Grammalecte  Check-in [45e4affdff]

Overview
Comment:[fx] iframe edition now available
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | major_change | fx
Files: files | file ages | folders
SHA3-256: 45e4affdff50a33d7719aedb9b1364bdf6b1759102ef068dcd77bb3e12e27cab
User & Date: olr on 2020-07-31 10:03:18
Other Links: manifest | tags
Context
2020-07-31
11:47
[core][fr] split core tests and modules test check-in: 4078670807 user: olr tags: trunk, fr, core
10:03
[fx] iframe edition now available check-in: 45e4affdff user: olr tags: trunk, major_change, fx
08:10
[build][fr] add option for launching standard Firefox, because dev edition is buggy check-in: 5255a0e102 user: olr tags: trunk, fr, build
Changes

Modified gc_lang/fr/webext/content_scripts/editor.js from [5a17b10c3e] to [dc19e7b8ee].

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









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




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




-
-
+
-
+
+







// JavaScript

// Editor for HTML page


"use strict";


/*
    Text Editor
*/
var oGrammalecteTextEditor = {

    dParagraphs: new Map(),

    loadText: function (sText) {
        // function also used by the text formatter
        this.dParagraphs.clear();
        if (typeof(sText) === "string") {
            let i = 0;
            let iStart = 0;
            let iEnd = 0;
            sText = this.purgeText(sText).normalize("NFC");
            while ((iEnd = sText.indexOf("\n", iStart)) !== -1) {
                this.dParagraphs.set(i, sText.slice(iStart, iEnd));
                i++;
                iStart = iEnd+1;
            }
            this.dParagraphs.set(i, sText.slice(iStart));
            //console.log("Paragraphs number: " + (i+1));
        }
        else {
            console.log("[Grammalecte] Error. This is not a text:", sText);
        }
    },

    getText: function () {
        return [...this.dParagraphs.values()].join("\n").normalize("NFC");
    },

    getParagraphs: function () {
        return this.dParagraphs.entries();
    },

    getParagraph: function (iParagraph) {
        return this.dParagraphs.get(iParagraph);
    },

    setParagraph: function (iParagraph, sParagraph) {
        this.dParagraphs.set(iParagraph, this.purgeText(sParagraph).normalize("NFC"));
    },

    purgeText: function (sText) {
        return sText.replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
    },

    clear: function () {
        this.dParagraphs.clear();
    }
}


/*
    Editor for HTML page (Thunderbird or Iframe)
*/
class HTMLPageEditor {

	constructor (xDocument, bCheckSignature=false) {
        this.xDocument = xDocument;
        this.xRootNode = xDocument.body;
        //console.log(xDocument.body);
        //console.log(xDocument.body.innerHTML);
	constructor (xWhat, xResultNode=null, bCheckSignature=true) {
        this.xWhat = xWhat;
        this.bIframe = false;
        //console.log(xWhat);
        if (xWhat.tagName  &&  xWhat.tagName == "IFRAME") {
            // iframe
            this.xDocument = xWhat.contentWindow.document;
            this.bIframe = true;
        }
        else if (xWhat instanceof HTMLDocument) {
            // page
            this.xDocument = xWhat;
        }
        else {
            console.log("[Grammalecte] HTMLPageEditor: unknown element");
        }
        this.xRootNode = this.xDocument.body;
        //console.log(this.xDocument.body);
        //console.log(this.xDocument.body.innerHTML);
        this.bResultInEvent = Boolean(this.xWhat.dataset && this.xWhat.dataset.grammalecte_result_via_event && this.xWhat.dataset.grammalecte_result_via_event == "true");
        this.xResultNode = false; // only useful for text analysed without node
        if (xResultNode instanceof HTMLElement) {
            this.xResultNode = xResultNode;
            this.bResultInEvent = true;
        }
        this.lNode = [];
        this.bCheckSignature = bCheckSignature;
        this._lParsableNodes = ["P", "LI", "H1", "H2", "H3", "H4", "H5", "H6"];
        this._lRootNodes = ["DIV", "UL", "OL"];
        if (bThunderbird) {
            oGrammalecte.oGCPanel.addMessageToGCPanel("❗ Les éléments de formatage direct (non textuels) sont susceptibles d’être effacés lors de la correction.");
        oGrammalecte.oGCPanel.addMessageToGCPanel("❗ Les éléments de formatage direct (non textuels) sont susceptibles d’être effacés lors de la correction.");
        }
        let sText = this.getTextFromPage();
        oGrammalecteTextEditor.loadText(sText);
    }

    * _getParsableNodes (xRootNode) {
        // recursive function
        try {
            for (let xNode of xRootNode.childNodes) {
                if (xNode.className !== "moz-cite-prefix" && xNode.tagName !== "BLOCKQUOTE"
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
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







-
+

+
-
+

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




+



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


-
+

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







-
-
-
+
+
+


-
-
-
-
+
+
+
+
+







+











-

-


















-

+



-
-
-
-
-
-
-










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







-




-
+



-
+



-
+



-
-
+
+











-
+




+
-
-
+
+

-
-
-

+

-
-
+
+

-
-
+
+



-
+





            }
        }
        catch (e) {
            showError(e);
        }
    }

    * getParagraphs () {
    getTextFromPage () {
        try {
            // return this.xRootNode.innerText;
            let i = 0;
            let sPageText = "";
            for (let xNode of this._getParsableNodes(this.xRootNode)) {
                if (xNode.textContent.trim() !== "") {
                this.lNode.push(xNode);
                yield [i, xNode.textContent];
                    this.lNode.push(xNode);
                    sPageText += xNode.textContent + "\n";
                i += 1;
            }
        }
                }
            }
        catch (e) {
            showError(e);
        }
    }

    getText () {
        try {
            let sPageText = "";
            //console.log(sPageText);
            for (let [i, sLine] of this.getParagraphs()) {
                sPageText += sLine + "\n";
            }
            return sPageText.slice(0,-1).normalize("NFC");
        }
        catch (e) {
            showError(e);
            return "[Grammalecte] Erreur. Aucun texte récupéré.";
        }
    }

    getParagraph (iPara) {
    getText () {
        try {
            return this.lNode[iPara].textContent;
        }
        return oGrammalecteTextEditor.getText();
    }
        catch (e) {
            showError(e);
        }

    getParagraph (iParagraph) {
        return oGrammalecteTextEditor.getParagraph(iParagraph);
    }

    setParagraph (iPara, sText) {
    setParagraph (iParagraph, sText) {
        try {
            oGrammalecteTextEditor.setParagraph(iParagraph, sText);
            if (this.bResultInEvent) {
                const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: oGrammalecteTextEditor.getText() }) });
                if (this.xResultNode) {
                    this.xResultNode.dispatchEvent(xEvent);
                } else {
                    this.xWhat.dispatchEvent(xEvent);
                }
                //console.log("[Grammalecte debug] Text sent to xWhat via event:", xEvent.detail);
            }
            if (iPara < this.lNode.length) {
                this.lNode[iPara].textContent = oGrammalecte.purgeText(sText).normalize("NFC");
            else if (iParagraph < this.lNode.length) {
                this.lNode[iParagraph].textContent = oGrammalecteTextEditor.getParagraph(iParagraph);
            }
        }
        catch (e) {
            showError(e);
        }
    }

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

    loadText (sText) {
        let lParagraphs = sText.split("\n");
        for (let iPara = 0;  iPara < lParagraphs.length;  iPara++) {
            this.setParagraph(iPara, lParagraphs[iPara]);
    write () {
        for (let [i, sParagraph] of oGrammalecteTextEditor.getParagraphs()) {
            if (i < this.lNode.length) {
                this.lNode[iParagraph].textContent = sParagraph;
            }
        }
    }

    clear () {
        this.xDocument = null;
        this.xRootNode = null;
        this.lNode.length = 0;
        oGrammalecteTextEditor.clear();
    }
}


/*
    Editor for TextNode (Textarea or editable node)
*/
class TextNodeEditor {

    constructor (what, xResultNode=null) {
        this.xNode = null;
        this.dParagraph = new Map();
        this.bTextArea = false;
        this.bIframe = false;
        this.bResultInEvent = false; // if true, the node content is not modified, but an event is dispatched on the node with the modified text
        this.xResultNode = null; // only useful for text analysed without node
        if (xResultNode instanceof HTMLElement) {
            this.xResultNode = xResultNode;
            this.bResultInEvent = true;
        }
        if (typeof(what) == "string") {
            // SIMPLE TEXT
            if (!this.xResultNode) {
                oGrammalecte.oGCPanel.addMessageToGCPanel("⛔ Aucun champ textuel défini. Les changements ne seront pas répercutés sur la zone d’où le texte a été extrait.");
            }
            this.loadText(what);
        }
        else if (what.nodeType && what.nodeType === 1) {
            // NODE
            this.xNode = what;
            this.bResultInEvent = Boolean(this.xNode.dataset.grammalecte_result_via_event && this.xNode.dataset.grammalecte_result_via_event == "true");
            this.bTextArea = (this.xNode.tagName == "TEXTAREA" || this.xNode.tagName == "INPUT");
            this.bIframe = (this.xNode.tagName == "IFRAME");
            if (this.bTextArea) {
                // text area
                this.xNode.disabled = true;
                this.loadText(this.xNode.value);
            }
            else if (this.bIframe) {
                // iframe
                if (!this.bResultInEvent) {
                    oGrammalecte.oGCPanel.addMessageToGCPanel("⛔ La zone analysée est un cadre contenant une autre page web (“iframe”). Les changements faits ne peuvent être pas répercutés dans cette zone.");
                }
                this.loadText(this.xNode.contentWindow.document.body.innerText);
            }
            else {
                // editable node
                oGrammalecte.oGCPanel.addMessageToGCPanel("❗ La zone de texte analysée est un champ textuel enrichi susceptible de contenir des éléments non textuels qui seront effacés lors de la correction.");
                this.loadText(this.xNode.innerText);
            }
        }
    }

    loadText (sText) {
        // function also used by the text formatter
        if (typeof(sText) === "string") {
        oGrammalecteTextEditor.loadText(sText);
            this.dParagraph.clear();
            let i = 0;
            let iStart = 0;
            let iEnd = 0;
            sText = sText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").normalize("NFC");
            while ((iEnd = sText.indexOf("\n", iStart)) !== -1) {
                this.dParagraph.set(i, sText.slice(iStart, iEnd));
                i++;
                iStart = iEnd+1;
            }
            this.dParagraph.set(i, sText.slice(iStart));
            //console.log("Paragraphs number: " + (i+1));
        }
        this.write();
    }

    clear () {
        if (this.xNode !== null) {
            this.xNode.disabled = false;
            this.bTextArea = false;
            this.bIframe = false;
            this.bResultInEvent = false;
            this.xNode = null;
            this.xResultNode = null;
        }
        this.dParagraph.clear();
        oGrammalecteTextEditor.clear();
    }

    getText () {
        return [...this.dParagraph.values()].join("\n").normalize("NFC");
        return oGrammalecteTextEditor.getText();
    }

    setParagraph (iParagraph, sText) {
        this.dParagraph.set(iParagraph, oGrammalecte.purgeText(sText).normalize("NFC"));
        oGrammalecteTextEditor.setParagraph(iParagraph, sText);
        this.write();
    }

    getParagraph (iParaNum) {
        return this.dParagraph.get(iParaNum);
    getParagraph (iParagraph) {
        return oGrammalecteTextEditor.getParagraph(iParagraph);
    }

    _eraseNodeContent () {
        while (this.xNode.firstChild) {
            this.xNode.removeChild(this.xNode.firstChild);
        }
    }

    write () {
        if (this.xNode !== null) {
            if (this.bResultInEvent) {
                const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: this.getText() }) });
                const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: oGrammalecteTextEditor.getText() }) });
                this.xNode.dispatchEvent(xEvent);
                //console.log("[Grammalecte debug] Text sent to xNode via event:", xEvent.detail);
            }
            else if (this.bTextArea) {
                // Text area
                this.xNode.value = this.getText();
                //console.log("[Grammalecte debug] text written in textarea:", this.getText());
                this.xNode.value = oGrammalecteTextEditor.getText();
                //console.log("[Grammalecte debug] text written in textarea:", oGrammalecteTextEditor.getText());
            }
            else if (this.bIframe) {
                //console.log(this.getText());
            }
            else {
                // Editable node
                this._eraseNodeContent();
                this.dParagraph.forEach((val, key) => {
                    this.xNode.appendChild(document.createTextNode(val.normalize("NFC")));
                for (let [i, sParagraph] of oGrammalecteTextEditor.getParagraphs()) {
                    this.xNode.appendChild(document.createTextNode(sParagraph));
                    this.xNode.appendChild(document.createElement("br"));
                });
                //console.log("[Grammalecte debug] text written in editable node:", this.getText());
                };
                //console.log("[Grammalecte debug] text written in editable node:", oGrammalecteTextEditor.getText());
            }
        }
        else if (this.xResultNode !== null) {
            const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: this.getText() }) });
            const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: oGrammalecteTextEditor.getText() }) });
            this.xResultNode.dispatchEvent(xEvent);
            //console.log("[Grammalecte debug] Text sent to xResultNode via event:", xEvent.detail);
        }
    }
}

Modified gc_lang/fr/webext/content_scripts/init.js from [7bc3a8461c] to [d6a977e5a7].

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
146
147
148
149
150
151
152




153
154
155
156
157
158
159







-
-
-
-







        let nPos = sPageText.indexOf("__grammalecte_panel__");
        if (nPos >= 0) {
            sPageText = sPageText.slice(0, nPos).normalize("NFC");
        }
        return sPageText;
    },

    purgeText: function (sText) {
        return sText.replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
    },

    createNode: function (sType, oAttr, oDataset=null) {
        try {
            let xNode = document.createElement(sType);
            Object.assign(xNode, oAttr);
            if (oDataset) {
                Object.assign(xNode.dataset, oDataset);
            }
506
507
508
509
510
511
512
513

514
515
516
517
518
519
520
502
503
504
505
506
507
508

509
510
511
512
513
514
515
516







-
+







            let {sActionRequest} = oMessage;
            let xActiveNode = oGrammalecte.findOriginEditableNode(document.activeElement);
            switch (sActionRequest) {
                /*
                    Commands received from the keyboard (shortcuts)
                */
                case "shortcutGrammarChecker":
                    if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT" || xActiveNode.isContentEditable)) {
                    if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT" || xActiveNode.tagName == "IFRAME" || xActiveNode.isContentEditable)) {
                        oGrammalecte.startGCPanel(xActiveNode);
                    } else {
                        oGrammalecte.startGCPanel(oGrammalecte.getPageText());
                    }
                    break;
                default:
                    console.log("[Grammalecte] Content-script. Unknown command: ", sActionRequest);

Modified gc_lang/fr/webext/content_scripts/panel_gc.js from [4f6823afb1] to [5158e88aeb].

138
139
140
141
142
143
144
145
146
147
148
149
150
151















152
153
154
155
156
157
158
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







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








    start (what, xResultNode=null) {
        this.oTooltip.hide();
        this.bWorking = false;
        this.clear();
        this.hideMessage();
        this.resetTimer();
        if (typeof(what) === "string" && what === "__ThunderbirdComposeWindow__") {
            // Thunderbird compose window
            this.oTextControl = new HTMLPageEditor(document);
        }
        else if (typeof(what) === "string" || (what.nodeType && what.nodeType === 1)) {
            // Text or node
            this.oTextControl = new TextNodeEditor(what, xResultNode);
        if (typeof(what) === "string") {
            if (what === "__ThunderbirdComposeWindow__") {
                // Thunderbird compose window
                this.oTextControl = new HTMLPageEditor(document);
            } else {
                this.oTextControl = new TextNodeEditor(what, xResultNode);
            }
        }
        else if (what.nodeType && what.nodeType === 1) {
            if (what.tagName == "IFRAME") {
                this.oTextControl = new HTMLPageEditor(what, xResultNode);
            }
            else {
                this.oTextControl = new TextNodeEditor(what, xResultNode);
            }
        }
        else {
            // error
            oGrammalecte.showMessage("[BUG] Analyse d’un élément inconnu…");
            console.log("[Grammalecte] Unknown element:", what);
        }
    }