Index: gc_lang/fr/mailext/background.js ================================================================== --- gc_lang/fr/mailext/background.js +++ gc_lang/fr/mailext/background.js @@ -165,10 +165,11 @@ { file: "compose.css" }, ],*/ js: [ // Any number of code or file objects could be listed here. //{ code: `document.body.textContent = "Hey look, the script ran!";` }, + { file: "content_scripts/editor.js" }, { file: "content_scripts/html_src.js" }, { file: "content_scripts/panel.js" }, { file: "grammalecte/fr/textformatter.js" }, { file: "content_scripts/panel_tf.js" }, { file: "content_scripts/panel_gc.js" }, Index: gc_lang/fr/webext/content_scripts/editor.js ================================================================== --- gc_lang/fr/webext/content_scripts/editor.js +++ gc_lang/fr/webext/content_scripts/editor.js @@ -4,22 +4,25 @@ "use strict"; +/* + Editor for HTML page (Thunderbird or Iframe) +*/ class HTMLPageEditor { - constructor (xRootNode=document.rootElement, bCheckSignature=false) { - this.xRootNode = xRootNode; + constructor (xDocument, bCheckSignature=false) { + this.xDocument = xDocument; + this.xRootNode = xDocument.rootElement; this.lNode = []; this.bCheckSignature = bCheckSignature; - this._lParsableNodes = ["P", "LI"]; + this._lParsableNodes = ["P", "LI", "H1", "H2", "H3", "H4", "H5"]; this._lRootNodes = ["DIV", "UL", "OL"]; - } - * _getParsableNodes (xRootNode) { + * _getParsableNodes () { // recursive function try { for (let xNode of this.xRootNode.childNodes) { if (xNode.className !== "moz-cite-prefix" && xNode.tagName !== "BLOCKQUOTE" && (xNode.nodeType == Node.TEXT_NODE || (xNode.nodeType == Node.ELEMENT_NODE && !xNode.textContent.startsWith(">"))) @@ -56,11 +59,11 @@ catch (e) { showError(e); } } - getPageText () { + getText () { try { let sPageText = ""; for (let [i, sLine] of this.getParagraphs()) { sPageText += sLine + "\n"; } @@ -78,11 +81,11 @@ catch (e) { showError(e); } } - writeParagraph (iPara, sText) { + setParagraph (iPara, sText) { try { return this.lNode[iPara].textContent = sText; } catch (e) { showError(e); @@ -91,6 +94,142 @@ changeParagraph (iPara, sModif, iStart, iEnd) { let sText = this.getParagraph(iPara); this.writeParagraph(iPara, sText.slice(0, iStart) + sModif + sText.slice(iEnd)); } + + 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") { + // 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(sText); + } + 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, sText); + 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); + } + } } Index: gc_lang/fr/webext/content_scripts/panel_gc.js ================================================================== --- gc_lang/fr/webext/content_scripts/panel_gc.js +++ gc_lang/fr/webext/content_scripts/panel_gc.js @@ -57,12 +57,11 @@ this.xParagraphList = oGrammalecte.createNode("div", {id: "grammalecte_paragraph_list"}); this.xGCPanelContent.appendChild(this.xParagraphList); this.xPanelContent.addEventListener("click", onGrammalecteGCPanelClick, false); this.oTooltip = new GrammalecteTooltip(this.xParent, this.xGCPanelContent); this.xPanelContent.appendChild(this.xGCPanelContent); - this.xNode = null; - this.oTextControl = new GrammalecteTextControl(); + this.oTextControl = null; this.nLastResult = 0; this.iLastEditedParagraph = -1; // Lexicographer this.nLxgCount = 0; this.xLxgPanelContent = oGrammalecte.createNode("div", {id: "grammalecte_lxg_panel_content"}); @@ -136,20 +135,17 @@ this.oTooltip.hide(); this.bWorking = false; this.clear(); this.hideMessage(); this.resetTimer(); - if (typeof(what) === "string") { - // text - this.xNode = null; - this.oTextControl.setResultNode(xResultNode); - this.oTextControl.setText(what); - } - else if (what.nodeType && what.nodeType === 1) { // 1 = Node.ELEMENT_NODE - // node - this.xNode = what; - this.oTextControl.setNode(this.xNode); + 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.oMessageBox.showMessage("[BUG] Analyse d’un élément inconnu…"); console.log("[Grammalecte] Unknown element:", what); @@ -243,11 +239,10 @@ xParagraph.dataset.timer_id = window.setTimeout(this.recheckParagraph.bind(this), 3000, oResult.iParaNum); this.iLastEditedParagraph = oResult.iParaNum; } // write text this.oTextControl.setParagraph(parseInt(xEvent.target.dataset.para_num, 10), this.purgeText(xEvent.target.textContent)); - this.oTextControl.write(); }.bind(this) , true); this._tagParagraph(xParagraph, oResult.sParagraph, oResult.iParaNum, oResult.aGrammErr, oResult.aSpellErr); // creation xNodeDiv.appendChild(xActionsBar); @@ -413,11 +408,10 @@ xNodeErr.textContent = this.xParent.getElementById(sNodeSuggId).textContent; xNodeErr.className = "grammalecte_error_corrected"; xNodeErr.removeAttribute("style"); let iParaNum = parseInt(sErrorId.slice(0, sErrorId.indexOf("-")), 10); this.oTextControl.setParagraph(iParaNum, this.purgeText(this.xParent.getElementById("grammalecte_paragraph" + iParaNum).textContent)); - this.oTextControl.write(); this.oTooltip.hide(); this.recheckParagraph(iParaNum); this.iLastEditedParagraph = iParaNum; } catch (e) { @@ -938,135 +932,5 @@ xSuggBlock.appendChild(document.createTextNode("# Oups. Le mécanisme de suggestion orthographique a rencontré un bug… (Ce module est encore en phase β.)")); showError(e); } } } - - -class GrammalecteTextControl { - - constructor () { - 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 if for text analysed without node - } - - setNode (xNode) { - this.clear(); - this.xNode = xNode; - this.bResultInEvent = Boolean(xNode.dataset.grammalecte_result_via_event && xNode.dataset.grammalecte_result_via_event == "true"); - this.bTextArea = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT"); - this.bIframe = (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); - } - } - - setResultNode (xNode) { - if (xNode instanceof HTMLElement) { - this.xResultNode = xNode; - this.bResultInEvent = true; - } - } - - setText (sText) { - this.clear(); - 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(sText); - } - - 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)); - } - } - - 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, sText); - } - - 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); - } - } -} Index: gc_lang/fr/webext/content_scripts/panel_tf.js ================================================================== --- gc_lang/fr/webext/content_scripts/panel_tf.js +++ gc_lang/fr/webext/content_scripts/panel_tf.js @@ -523,11 +523,10 @@ //window.setCursor("auto"); // restore pointer const t1 = Date.now(); this.xParent.getElementById('grammalecte_tf_time_res').textContent = this.getTimeRes((t1-t0)/1000); oGrammalecte.oGCPanel.oTextControl.loadText(sText); - oGrammalecte.oGCPanel.oTextControl.write(); this.bTextChanged = true; } catch (e) { showError(e); } Index: gc_lang/fr/webext/manifest.json ================================================================== --- gc_lang/fr/webext/manifest.json +++ gc_lang/fr/webext/manifest.json @@ -47,10 +47,11 @@ "*://*.wikisource.org/*", "*://*.wikipedia.org/*", "*://*.wiktionary.org/*" ], "js": [ + "content_scripts/editor.js", "content_scripts/html_src.js", "content_scripts/panel.js", "grammalecte/fr/textformatter.js", "content_scripts/panel_tf.js", "content_scripts/panel_gc.js", @@ -65,15 +66,17 @@ "*://*.wikisource.org/*", "*://*.wikipedia.org/*", "*://*.wiktionary.org/*" ], "js": [ + "content_scripts/editor.js", "content_scripts/html_src.js", "content_scripts/panel.js", "grammalecte/fr/textformatter.js", "content_scripts/panel_tf.js", "content_scripts/panel_gc.js", + "content_scripts/message_box.js", "content_scripts/menu.js", "content_scripts/init.js" ], "run_at": "document_idle" }