Grammalecte  Check-in [45e4affdff]

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

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 2020-07-31 10:03:18
Context
2020-07-31
11:47
[core][fr] split core tests and modules test check-in: 4078670807 user: olr tags: core, fr, trunk
10:03
[fx] iframe edition now available check-in: 45e4affdff user: olr tags: fx, major_change, trunk
08:10
[build][fr] add option for launching standard Firefox, because dev edition is buggy check-in: 5255a0e102 user: olr tags: build, fr, trunk
Changes

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

1
2
3
4
5
6
7
8
9






















































10
11
12
13
14











15




16
17
18






19
20
21
22
23
24
25


26
27
28
29
30
31
32
..
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
...
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
// JavaScript

// Editor for HTML page


"use strict";


/*






















































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






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


    }

    * _getParsableNodes (xRootNode) {
        // recursive function
        try {
            for (let xNode of xRootNode.childNodes) {
                if (xNode.className !== "moz-cite-prefix" && xNode.tagName !== "BLOCKQUOTE"
................................................................................
            }
        }
        catch (e) {
            showError(e);
        }
    }

    * getParagraphs () {
        try {
            let i = 0;

            for (let xNode of this._getParsableNodes(this.xRootNode)) {

                this.lNode.push(xNode);
                yield [i, xNode.textContent];
                i += 1;
            }
        }
        catch (e) {
            showError(e);
        }
    }

    getText () {
        try {
            let sPageText = "";
            for (let [i, sLine] of this.getParagraphs()) {
                sPageText += sLine + "\n";
            }
            return sPageText.slice(0,-1).normalize("NFC");
        }
        catch (e) {
            showError(e);

        }
    }

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


    }

    setParagraph (iPara, sText) {
        try {










            if (iPara < this.lNode.length) {
                this.lNode[iPara].textContent = oGrammalecte.purgeText(sText).normalize("NFC");

            }
        }
        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) {
        let lParagraphs = sText.split("\n");
        for (let iPara = 0;  iPara < lParagraphs.length;  iPara++) {
            this.setParagraph(iPara, lParagraphs[iPara]);

        }
    }

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

    }
}


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

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

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

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

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

    _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() }) });
                this.xNode.dispatchEvent(xEvent);
                //console.log("[Grammalecte debug] Text sent to xNode via event:", xEvent.detail);
            }
            else if (this.bTextArea) {

                this.xNode.value = this.getText();
                //console.log("[Grammalecte debug] text written in textarea:", this.getText());
            }
            else if (this.bIframe) {
                //console.log(this.getText());
            }
            else {

                this._eraseNodeContent();
                this.dParagraph.forEach((val, key) => {

                    this.xNode.appendChild(document.createTextNode(val.normalize("NFC")));
                    this.xNode.appendChild(document.createElement("br"));
                });
                //console.log("[Grammalecte debug] text written in editable node:", this.getText());
            }
        }
        else if (this.xResultNode !== null) {
            const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: this.getText() }) });
            this.xResultNode.dispatchEvent(xEvent);
            //console.log("[Grammalecte debug] Text sent to xResultNode via event:", xEvent.detail);
        }
    }
}









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




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




<
|
<
>
>







 







|

|
>

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




>



|
|
<
|
<
<
|
>
>


|

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







|
|
|


|
|
|
|
>







>











<

<







 







<

>



<
<
<
<
<
<
<










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







<




|



|



|



|
|











|




>
|
|

<
<
<

>

<
>
|

|
|



|





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
...
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
...
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
// 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 (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"];

        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"
................................................................................
            }
        }
        catch (e) {
            showError(e);
        }
    }

    getTextFromPage () {
        try {
            // return this.xRootNode.innerText;
            let sPageText = "";
            for (let xNode of this._getParsableNodes(this.xRootNode)) {
                if (xNode.textContent.trim() !== "") {
                    this.lNode.push(xNode);
                    sPageText += xNode.textContent + "\n";

                }
            }







            //console.log(sPageText);



            return sPageText.slice(0,-1).normalize("NFC");
        }
        catch (e) {
            showError(e);
            return "[Grammalecte] Erreur. Aucun texte récupéré.";
        }
    }

    getText () {
        return oGrammalecteTextEditor.getText();

    }



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

    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);
            }
            else if (iParagraph < this.lNode.length) {

                this.lNode[iParagraph].textContent = oGrammalecteTextEditor.getParagraph(iParagraph);
            }
        }
        catch (e) {
            showError(e);
        }
    }

    loadText (sText) {
        oGrammalecteTextEditor.loadText(sText);
        this.write();
    }

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

            if (this.bTextArea) {
                // text area
                this.xNode.disabled = true;
                this.loadText(this.xNode.value);
            }







            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
        oGrammalecteTextEditor.loadText(sText);













        this.write();
    }

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

            this.bResultInEvent = false;
            this.xNode = null;
            this.xResultNode = null;
        }
        oGrammalecteTextEditor.clear();
    }

    getText () {
        return oGrammalecteTextEditor.getText();
    }

    setParagraph (iParagraph, sText) {
        oGrammalecteTextEditor.setParagraph(iParagraph, sText);
        this.write();
    }

    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: 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 = oGrammalecteTextEditor.getText();
                //console.log("[Grammalecte debug] text written in textarea:", oGrammalecteTextEditor.getText());
            }



            else {
                // Editable node
                this._eraseNodeContent();

                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:", oGrammalecteTextEditor.getText());
            }
        }
        else if (this.xResultNode !== null) {
            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);
        }
    }
}

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

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
...
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
        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);
            }
................................................................................
            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)) {
                        oGrammalecte.startGCPanel(xActiveNode);
                    } else {
                        oGrammalecte.startGCPanel(oGrammalecte.getPageText());
                    }
                    break;
                default:
                    console.log("[Grammalecte] Content-script. Unknown command: ", sActionRequest);







<
<
<
<







 







|







146
147
148
149
150
151
152




153
154
155
156
157
158
159
...
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
        let nPos = sPageText.indexOf("__grammalecte_panel__");
        if (nPos >= 0) {
            sPageText = sPageText.slice(0, nPos).normalize("NFC");
        }
        return sPageText;
    },





    createNode: function (sType, oAttr, oDataset=null) {
        try {
            let xNode = document.createElement(sType);
            Object.assign(xNode, oAttr);
            if (oDataset) {
                Object.assign(xNode.dataset, oDataset);
            }
................................................................................
            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.tagName == "IFRAME" || xActiveNode.isContentEditable)) {
                        oGrammalecte.startGCPanel(xActiveNode);
                    } else {
                        oGrammalecte.startGCPanel(oGrammalecte.getPageText());
                    }
                    break;
                default:
                    console.log("[Grammalecte] Content-script. Unknown command: ", sActionRequest);

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

138
139
140
141
142
143
144

145
146
147


148

149
150




151

152
153
154
155
156
157
158

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

        }
        else {
            // error
            oGrammalecte.showMessage("[BUG] Analyse d’un élément inconnu…");
            console.log("[Grammalecte] Unknown element:", what);
        }
    }







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







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