Index: gc_core/js/ibdawg.js ================================================================== --- gc_core/js/ibdawg.js +++ gc_core/js/ibdawg.js @@ -214,15 +214,13 @@ } _suggest (sRemain, nMaxDel=0, nDeep=0, iAddr=0, sNewWord="", bAvoidLoop=false) { // returns a set of suggestions // recursive function - //show(nDeep, sNewWord + ":" + sRemain) let aSugg = new Set(); if (sRemain == "") { if (this._convBytesToInteger(this.byDic.slice(iAddr, iAddr+this.nBytesArc)) & this._finalNodeMask) { - //show(nDeep, "___" + sNewWord + "___"); aSugg.add(sNewWord); } for (let sTail of this._getTails(iAddr)) { aSugg.add(sNewWord+sTail); } @@ -298,18 +296,16 @@ _suggestWithCrushedUselessChars (sWord, nDeep=0, iAddr=0, sNewWord="", bAvoidLoop=false) { let aSugg = new Set(); if (sWord.length == 0) { if (this._convBytesToInteger(this.byDic.slice(iAddr, iAddr+this.nBytesArc)) & this._finalNodeMask) { - show(nDeep, "!!! " + sNewWord + " !!!"); aSugg.add(sNewWord); } return aSugg; } let cCurrent = sWord.slice(0, 1); for (let [cChar, jAddr] of this._getSimilarArcsAndCrushedChars(cCurrent, iAddr)) { - show(nDeep, cChar); aSugg.gl_update(this._suggestWithCrushedUselessChars(sWord.slice(1), nDeep+1, jAddr, sNewWord+cChar)); } return aSugg; } Index: gc_lang/fr/webext/background.js ================================================================== --- gc_lang/fr/webext/background.js +++ gc_lang/fr/webext/background.js @@ -69,10 +69,19 @@ } catch (e) { showError(e); } }; + +function initUIOptions (dSavedOptions) { + if (!dSavedOptions.hasOwnProperty("ui_options")) { + browser.storage.local.set({"ui_options": { + textarea: true, + editablenode: false + }}); + } +} function initGrammarChecker (dSavedOptions) { let dOptions = (dSavedOptions.hasOwnProperty("gc_options")) ? dSavedOptions.gc_options : null; xGCEWorker.postMessage({ sCommand: "init", @@ -82,24 +91,15 @@ } function init () { if (bChrome) { browser.storage.local.get("gc_options", initGrammarChecker); + browser.storage.local.get("ui_options", initUIOptions); return; } - let xPromise = browser.storage.local.get("gc_options"); - xPromise.then( - initGrammarChecker, - function (e) { - showError(e); - xGCEWorker.postMessage({ - sCommand: "init", - dParam: {sExtensionPath: browser.extension.getURL("."), dOptions: null, sContext: "Firefox"}, - dInfo: {} - }); - } - ); + browser.storage.local.get("gc_options").then(initGrammarChecker, showError); + browser.storage.local.get("ui_options").then(initUIOptions, showError); } init(); @@ -174,69 +174,71 @@ /* Context Menu */ -browser.contextMenus.create({ - id: "getListOfTokens", - title: "Analyser", - contexts: ["selection"] -}); - -browser.contextMenus.create({ - id: "parseAndSpellcheck", - title: "Corriger", - contexts: ["selection"] -}); - -browser.contextMenus.create({ - id: "separator1", - type: "separator", - contexts: ["selection"] -}); - -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: "separator2", - type: "separator", - contexts: ["editable"] -}); - -browser.contextMenus.create({ - id: "rescanPage", - title: "Rechercher à nouveau les zones de texte", - contexts: ["editable"] -}); + +// Selected text +browser.contextMenus.create({ id: "rightClickLxgSelectedText", title: "Lexicographe (sélection)", contexts: ["selection"] }); +browser.contextMenus.create({ id: "rightClickGCSelectedText", title: "Correction grammaticale (sélection)", contexts: ["selection"] }); +browser.contextMenus.create({ id: "separator_selection", type: "separator", contexts: ["selection"] }); +// Editable content +browser.contextMenus.create({ id: "rightClickTFEditableNode", title: "Formateur de texte (zone de texte)", contexts: ["editable"] }); +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: ["page"] }); +browser.contextMenus.create({ id: "rightClickGCPage", title: "Correction grammaticale (page)", contexts: ["page"] }); +browser.contextMenus.create({ id: "separator_page", type: "separator", contexts: ["page"] }); +// 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 // xTab = https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/Tab // confusing: no way to get the node where we click?! switch (xInfo.menuItemId) { - case "parseAndSpellcheck": - parseAndSpellcheckSelectedText(xTab.id, xInfo.selectionText); + // editable node + // page + case "rightClickTFEditableNode": + case "rightClickLxgEditableNode": + case "rightClickGCEditableNode": + case "rightClickLxgPage": + case "rightClickGCPage": + sendCommandToTab(xInfo.menuItemId, xTab.id); + break; + // selected text + case "rightClickGCSelectedText": + sendCommandToTab("rightClickGCSelectedText", xTab.id); + xGCEWorker.postMessage({ + sCommand: "parseAndSpellcheck", + dParam: {sText: xInfo.selectionText, sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {iReturnPort: xTab.id} + }); break; - case "getListOfTokens": - getListOfTokensFromSelectedText(xTab.id, xInfo.selectionText); + case "rightClickLxgSelectedText": + sendCommandToTab("rightClickLxgSelectedText", xTab.id); + xGCEWorker.postMessage({ + sCommand: "getListOfTokens", + dParam: {sText: xInfo.selectionText}, + dInfo: {iReturnPort: xTab.id} + }); break; + // 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: @@ -272,32 +274,13 @@ dOptions = helpers.mapToObject(dOptions); } browser.storage.local.set({"gc_options": dOptions}); } -function parseAndSpellcheckSelectedText (iTab, sText) { - // send message to the tab - let xTabPort = dConnx.get(iTab); - xTabPort.postMessage({sActionDone: "openGCPanel", result: null, dInfo: null, bEnd: false, bError: false}); - // send command to the worker - xGCEWorker.postMessage({ - sCommand: "parseAndSpellcheck", - dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false}, - dInfo: {iReturnPort: iTab} - }); -} - -function getListOfTokensFromSelectedText (iTab, sText) { - // send message to the tab - let xTabPort = dConnx.get(iTab); - xTabPort.postMessage({sActionDone: "openLxgPanel", result: null, dInfo: null, bEnd: false, bError: false}); - // send command to the worker - xGCEWorker.postMessage({ - sCommand: "getListOfTokens", - dParam: {sText: sText}, - dInfo: {iReturnPort: iTab} - }); +function sendCommandToTab (sCommand, iTab) { + let xTabPort = dConnx.get(iTab); + xTabPort.postMessage({sActionDone: sCommand, result: null, dInfo: null, bEnd: false, bError: false}); } function openConjugueurTab () { if (bChrome) { browser.tabs.create({ Index: gc_lang/fr/webext/content_scripts/init.js ================================================================== --- gc_lang/fr/webext/content_scripts/init.js +++ gc_lang/fr/webext/content_scripts/init.js @@ -35,26 +35,61 @@ Array.filter(document.getElementsByClassName(sContainerClass), function (oElem) { oElem.appendChild(img); }); } */ + const oGrammalecte = { nMenu: 0, lMenu: [], oTFPanel: null, oLxgPanel: null, oGCPanel: null, + + oMessageBox: null, + + xRightClickedNode: null, + + listenRightClick: function () { + // Node where a right click is done + // Bug report: https://bugzilla.mozilla.org/show_bug.cgi?id=1325814 + document.addEventListener('contextmenu', function (xEvent) { + this.xRightClickedNode = xEvent.target; + }.bind(this), true); + }, + + clearRightClickedNode: function () { + this.xRightClickedNode = null; + }, createMenus: function () { - let lNode = document.getElementsByTagName("textarea"); - for (let xNode of lNode) { - if (xNode.style.display !== "none" && xNode.style.visibility !== "hidden") { - this.lMenu.push(new GrammalecteMenu(this.nMenu, xNode)); - this.nMenu += 1; + if (bChrome) { + browser.storage.local.get("ui_options", this._createMenus.bind(this)); + return; + } + browser.storage.local.get("ui_options").then(this._createMenus.bind(this), showError); + }, + + _createMenus: function (dOptions) { + if (dOptions.hasOwnProperty("ui_options")) { + dOptions = dOptions.ui_options; + if (dOptions.textarea) { + for (let xNode of document.getElementsByTagName("textarea")) { + if (xNode.style.display !== "none" && xNode.style.visibility !== "hidden") { + this.lMenu.push(new GrammalecteMenu(this.nMenu, xNode)); + this.nMenu += 1; + } + } + } + if (dOptions.editablenode) { + for (let xNode of document.querySelectorAll("[contenteditable]")) { + this.lMenu.push(new GrammalecteMenu(this.nMenu, xNode)); + this.nMenu += 1; + } } } }, rescanPage: function () { @@ -63,10 +98,11 @@ if (this.oGCPanel !== null) { this.oGCPanel.hide(); } for (let oMenu of this.lMenu) { oMenu.deleteNodes(); } this.lMenu.length = 0; // to clear an array + this.listenRightClick(); this.createMenus(); }, createTFPanel: function () { if (this.oTFPanel === null) { @@ -88,10 +124,53 @@ if (this.oGCPanel === null) { this.oGCPanel = new GrammalecteGrammarChecker("grammalecte_gc_panel", "Grammalecte", 500, 700); this.oGCPanel.insertIntoPage(); } }, + + createMessageBox: function () { + if (this.oMessageBox === null) { + this.oMessageBox = new GrammalecteMessageBox("grammalecte_message_box", "Grammalecte", 400, 250); + this.oMessageBox.insertIntoPage(); + } + }, + + startGCPanel: function (xNode=null) { + this.createGCPanel(); + this.oGCPanel.clear(); + this.oGCPanel.show(); + this.oGCPanel.start(xNode); + this.oGCPanel.startWaitIcon(); + }, + + startLxgPanel: function () { + this.createLxgPanel(); + this.oLxgPanel.clear(); + this.oLxgPanel.show(); + this.oLxgPanel.startWaitIcon(); + }, + + startFTPanel: function (xNode=null) { + this.createTFPanel(); + this.oTFPanel.start(xNode); + this.oTFPanel.show(); + }, + + showMessage: function (sMessage) { + this.createMessageBox(); + this.oMessageBox.show(); + this.oMessageBox.addMessage(sMessage); + }, + + getPageText: function () { + let sPageText = document.body.innerText; + let nPos = sPageText.indexOf("__grammalecte_panel__"); + if (nPos >= 0) { + sPageText = sPageText.slice(0, nPos); + } + return sPageText; + }, createNode: function (sType, oAttr, oDataset=null) { try { let xNode = document.createElement(sType); Object.assign(xNode, oAttr); @@ -112,10 +191,11 @@ */ 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 "parseAndSpellcheck": if (!bEnd) { oGrammalecte.oGCPanel.addParagraphResult(result); } else { @@ -133,25 +213,79 @@ } break; case "getSpellSuggestions": oGrammalecte.oGCPanel.oTooltip.setSpellSuggestionsFor(result.sWord, result.aSugg, dInfo.sErrorId); break; - // Design WTF: context menus are made in background, not in content-script. - // Commands from context menu received here to initialize panels - case "openGCPanel": - oGrammalecte.createGCPanel(); - oGrammalecte.oGCPanel.clear(); - oGrammalecte.oGCPanel.show(); - oGrammalecte.oGCPanel.start(); - oGrammalecte.oGCPanel.startWaitIcon(); - break; - case "openLxgPanel": - oGrammalecte.createLxgPanel(); - oGrammalecte.oLxgPanel.clear(); - oGrammalecte.oLxgPanel.show(); - oGrammalecte.oLxgPanel.startWaitIcon(); - break; + /* + 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.textContent; + 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); @@ -160,6 +294,7 @@ /* Start */ +oGrammalecte.listenRightClick(); oGrammalecte.createMenus(); Index: gc_lang/fr/webext/content_scripts/menu.css ================================================================== --- gc_lang/fr/webext/content_scripts/menu.css +++ gc_lang/fr/webext/content_scripts/menu.css @@ -18,11 +18,11 @@ border-radius: 50%; text-align: center; cursor: pointer; box-shadow: 0 0 0 0 hsla(210, 50%, 50%, .5); z-index: 2147483640; /* maximum is 2147483647: https://stackoverflow.com/questions/491052/minimum-and-maximum-value-of-z-index */ - animation: grammalecte-spin 2s ease 3; + animation: grammalecte-spin 2s ease 1; } .grammalecte_menu_main_button:hover { border: 4px solid hsla(210, 80%, 35%, .5); background-color: hsla(210, 80%, 55%, .5); animation: grammalecte-spin .5s linear infinite; Index: gc_lang/fr/webext/content_scripts/menu.js ================================================================== --- gc_lang/fr/webext/content_scripts/menu.js +++ gc_lang/fr/webext/content_scripts/menu.js @@ -3,81 +3,77 @@ "use strict"; class GrammalecteMenu { - constructor (nMenu, xTextArea) { + constructor (nMenu, xNode) { this.sMenuId = "grammalecte_menu" + nMenu; this.xButton = oGrammalecte.createNode("div", {className: "grammalecte_menu_main_button", textContent: " "}); this.xButton.onclick = () => { this.switchMenu(); }; - this.xMenu = this._createMenu(xTextArea); - this._insertAfter(this.xButton, xTextArea); - this._insertAfter(this.xMenu, xTextArea); + this.xMenu = this._createMenu(xNode); + this._insertAfter(this.xButton, xNode); + this._insertAfter(this.xMenu, xNode); } _insertAfter (xNewNode, xReferenceNode) { xReferenceNode.parentNode.insertBefore(xNewNode, xReferenceNode.nextSibling); } - _createMenu (xTextArea) { + _createMenu (xNode) { try { + let sText = (xNode.tagName == "TEXTAREA") ? xNode.value : xNode.textContent; let xMenu = oGrammalecte.createNode("div", {id: this.sMenuId, className: "grammalecte_menu"}); + xMenu.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_menu_header", textContent: "GRAMMALECTE"})); // Text formatter - let xTFButton = oGrammalecte.createNode("div", {className: "grammalecte_menu_item", textContent: "Formateur de texte"}); - xTFButton.onclick = () => { - this.switchMenu(); - oGrammalecte.createTFPanel(); - oGrammalecte.oTFPanel.start(xTextArea); - oGrammalecte.oTFPanel.show(); - }; + if (xNode.tagName == "TEXTAREA") { + let xTFButton = oGrammalecte.createNode("div", {className: "grammalecte_menu_item", textContent: "Formateur de texte"}); + xTFButton.onclick = () => { + this.switchMenu(); + oGrammalecte.createTFPanel(); + oGrammalecte.oTFPanel.start(xNode); + oGrammalecte.oTFPanel.show(); + }; + xMenu.appendChild(xTFButton); + } // lexicographe let xLxgButton = oGrammalecte.createNode("div", {className: "grammalecte_menu_item", textContent: "Lexicographe"}); xLxgButton.onclick = () => { - this.switchMenu(); - oGrammalecte.createLxgPanel(); - oGrammalecte.oLxgPanel.clear(); - oGrammalecte.oLxgPanel.show(); - oGrammalecte.oLxgPanel.startWaitIcon(); + this.switchMenu(); + oGrammalecte.startLxgPanel(); xGrammalectePort.postMessage({ sCommand: "getListOfTokens", - dParam: {sText: xTextArea.value}, - dInfo: {sTextAreaId: xTextArea.id} + dParam: {sText: sText}, + dInfo: {sTextAreaId: xNode.id} }); }; + xMenu.appendChild(xLxgButton); // Grammar checker let xGCButton = oGrammalecte.createNode("div", {className: "grammalecte_menu_item", textContent: "Correction grammaticale"}); xGCButton.onclick = () => { - this.switchMenu(); - oGrammalecte.createGCPanel(); - oGrammalecte.oGCPanel.start(xTextArea); - oGrammalecte.oGCPanel.show(); - oGrammalecte.oGCPanel.startWaitIcon(); + this.switchMenu(); + oGrammalecte.startGCPanel(xNode); xGrammalectePort.postMessage({ sCommand: "parseAndSpellcheck", - dParam: {sText: xTextArea.value, sCountry: "FR", bDebug: false, bContext: false}, - dInfo: {sTextAreaId: xTextArea.id} + dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {sTextAreaId: xNode.id} }); }; + xMenu.appendChild(xGCButton); // Conjugation tool let xConjButton = oGrammalecte.createNode("div", {className: "grammalecte_menu_item_block", textContent: "Conjugueur"}); let xConjButtonTab = oGrammalecte.createNode("div", {className: "grammalecte_menu_button", textContent: "Onglet"}); xConjButtonTab.onclick = () => { - this.switchMenu(); - xGrammalectePort.postMessage({sCommand: "openConjugueurTab", dParam: null, dInfo: null}); + this.switchMenu(); + xGrammalectePort.postMessage({sCommand: "openConjugueurTab", dParam: null, dInfo: null}); }; let xConjButtonWin = oGrammalecte.createNode("div", {className: "grammalecte_menu_button", textContent: "Fenêtre"}); xConjButtonWin.onclick = () => { - this.switchMenu(); - xGrammalectePort.postMessage({sCommand: "openConjugueurWindow", dParam: null, dInfo: null}); + this.switchMenu(); + xGrammalectePort.postMessage({sCommand: "openConjugueurWindow", dParam: null, dInfo: null}); }; xConjButton.appendChild(xConjButtonTab); xConjButton.appendChild(xConjButtonWin); - // Create - xMenu.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_menu_header", textContent: "GRAMMALECTE"})); - xMenu.appendChild(xTFButton); - xMenu.appendChild(xLxgButton); - xMenu.appendChild(xGCButton); xMenu.appendChild(xConjButton); //xMenu.appendChild(oGrammalecte.createNode("img", {scr: browser.extension.getURL("img/logo-16.png")})); // can’t work, due to content-script policy: https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 xMenu.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_menu_footer"})); return xMenu; @@ -91,9 +87,9 @@ this.xMenu.parentNode.removeChild(this.xMenu); this.xButton.parentNode.removeChild(this.xButton); } switchMenu () { - let xMenu = document.getElementById(this.sMenuId); + let xMenu = document.getElementById(this.sMenuId); xMenu.style.display = (xMenu.style.display == "block") ? "none" : "block"; } } ADDED gc_lang/fr/webext/content_scripts/message_box.css Index: gc_lang/fr/webext/content_scripts/message_box.css ================================================================== --- gc_lang/fr/webext/content_scripts/message_box.css +++ gc_lang/fr/webext/content_scripts/message_box.css @@ -0,0 +1,65 @@ +/* + CSS + Message box for Grammalecte +*/ + +.grammalecte_message_box { + padding: 0; + margin: 0; + position: fixed; + box-sizing: content-box; + z-index: 2147483641; /* maximum is 2147483647: https://stackoverflow.com/questions/491052/minimum-and-maximum-value-of-z-index */ + border: 2px solid hsl(210, 10%, 50%); + border-radius: 10px 10px 10px 10px; + background-color: hsl(210, 0%, 100%); + color: hsl(0, 0%, 0%); + font-family: "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif; + box-shadow: 0 0 2px 1px hsla(210, 50%, 50%, .5); + line-height: normal; + text-shadow: none; + text-decoration: none; +} +.grammalecte_message_box img { + display: inline-block; + margin: 0; + padding: 0; +} + +.grammalecte_message_box_bar { + position: sticky; + width: 100%; + background-color: hsl(210, 0%, 90%); + border-radius: 10px 10px 0 0; + border-bottom: 1px solid hsl(210, 10%, 80%); + color: hsl(210, 10%, 4%); + font-size: 20px; +} +.grammalecte_message_box_title { + padding: 10px 20px; +} +.grammalecte_message_box_label { + display: inline-block; + padding: 0 10px; +} +.grammalecte_message_box_invisible_marker { + position: absolute; + /*visibility: hidden;*/ + font-size: 6px; + color: hsl(210, 0%, 90%); /* same color than panel_bar background */ +} + +.grammalecte_message_box_content { + position: absolute; + min-width: 100%; + height: calc(100% - 55px); /* panel height - title_bar */ + overflow: auto; +} + +.grammalecte_message_box_message { + margin: 10px; + padding: 10px; + border-radius: 5px; + background-color: hsl(0, 50%, 40%); + color: hsl(0, 50%, 96%); + font-size: 20px; +} ADDED gc_lang/fr/webext/content_scripts/message_box.js Index: gc_lang/fr/webext/content_scripts/message_box.js ================================================================== --- gc_lang/fr/webext/content_scripts/message_box.js +++ gc_lang/fr/webext/content_scripts/message_box.js @@ -0,0 +1,82 @@ +// JavaScript +// Panel creator + +"use strict"; + + +class GrammalecteMessageBox { + + constructor (sId, sTitle, nWidth, nHeight) { + this.sId = sId; + this.nWidth = nWidth; + this.nHeight = nHeight; + this.xMessageBoxBar = oGrammalecte.createNode("div", {className: "grammalecte_message_box_bar"}); + this.xMessageBoxContent = oGrammalecte.createNode("div", {className: "grammalecte_message_box_content"}); + this.xMessageBox = this._createPanel(sTitle); + this.center(); + } + + _createPanel (sTitle) { + try { + let xMessageBox = oGrammalecte.createNode("div", {id: this.sId, className: "grammalecte_message_box"}); + this.xMessageBoxBar.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_message_box_invisible_marker", textContent: "__grammalecte_panel__"})); + this.xMessageBoxBar.appendChild(this._createButtons()); + let xTitle = oGrammalecte.createNode("div", {className: "grammalecte_panel_title"}); + xTitle.appendChild(this._createLogo()); + xTitle.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_message_box_label", textContent: sTitle})); + this.xMessageBoxBar.appendChild(xTitle); + xMessageBox.appendChild(this.xMessageBoxBar); + + xMessageBox.appendChild(this.xMessageBoxContent); + return xMessageBox; + } + catch (e) { + showError(e); + } + } + + _createLogo () { + let xImg = document.createElement("img"); + xImg.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAC8UlEQVQ4jX3TbUgTcRwH8P89ddu5u9tt082aZmpFEU4tFz0QGTUwCi0heniR9MSUIKRaD0RvIlKigsooo+iNFa0XJYuwIjEK19OcDtPElsG0ktyp591t7u7+vUh7MPX3+vf5/n8/+P0BmKJIPUUVlh2rdVVeesWlzEybqg+bFOsoylnqPmNavGFfknV2Omu2Lvja3vxAURKJib3opHizu8riLK6gjRyuKgmoSoMRFENRUqfXTzvBGK62LC2uoFkOl4RhjQ8+qWt7dPNE3sbdp+2LXbsGe9qb4rIo/BfwFy6nWQ4ThWGNDzbcfu29dMDh2nHU7CypYNLmzTda0/L5cNuzmDQi/A4Y27k6eQxLI79wS/11D0AAMNvs6XT6ojVJjJEgTbMy2BT77xBMp09KcpaWV1uc41jQoi0NdUHfjeOO9WWn7AVF7s7n986SithPJGeupBh2PCSP/xxqxAp3eq6wuUV7Wc6MSZIEhA8vHjbfOe/OcW3zmAuKy+nUzAyD2bow8ODaEROFq8AyZ5WBYdEZXGqGxZ61HJV+9HYCJRbTNA0QBA40HWunaKN5dKg/DBKxeCIe09Th/m4MJwiMSZmLEzMQAABQRuNqgu8NYX3doTcMpvCkLbtQZ2AJkrPOZG1zlnY13T+Hy9EehY90h57eqcorcZ/lctZuMzAsOjLEqwNv66/6vZcPYRBC+C3cGaBxhSet2av1BpYgTTY7k5y2JPT41slIR6Axv8R9nnOs+4Pf+2r992uOxGVJwgAAAEINfgt3BGgsESWtWas1iGDyl+CT/u7WpvxNFRc4x7qtBoZFhSFejb7z1fq9NYfjsiT+cwcQavBruCOgU4SIGo18amuoq3Js3FNlynVtH385+s53ze+t8cRkURx3yMTTRBAEQVAUXbFlf3XystJKA2NExeFBdWASDAAA+MQACCEEmqbJ0b6PMC7JwhDU8YFHV5u9NZ64LErT/oW/63tPV6uJwmKoOND78u7Fg5NhAAD4CVbzY9cwrWQrAAAAAElFTkSuQmCC"; + return xImg; + } + + _createButtons () { + let xButtonLine = oGrammalecte.createNode("div", {className: "grammalecte_panel_commands"}); + xButtonLine.appendChild(this._createCloseButton()); + return xButtonLine; + } + + _createCloseButton () { + let xButton = oGrammalecte.createNode("div", {className: "grammalecte_close_button", textContent: "×", title: "Fermer la fenêtre"}); + xButton.onclick = function () { this.hide(); }.bind(this); // better than writing “let that = this;” before the function? + return xButton; + } + + insertIntoPage () { + document.body.appendChild(this.xMessageBox); + } + + show () { + this.xMessageBox.style.display = "block"; + } + + hide () { + this.xMessageBox.style.display = "none"; + this.clear(); + } + + addMessage (sMessage) { + this.xMessageBoxContent.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_message_box_message", textContent: sMessage})); + } + + clear () { + while (this.xMessageBoxContent.firstChild) { + this.xMessageBoxContent.removeChild(this.xMessageBoxContent.firstChild); + } + } + + center () { + this.xMessageBox.style = `top: 50%; left: 50%; width: ${this.nWidth}px; height: ${this.nHeight}px; margin-top: -${this.nHeight/2}px; margin-left: -${this.nWidth/2}px;`; + } +} Index: gc_lang/fr/webext/content_scripts/panel.css ================================================================== --- gc_lang/fr/webext/content_scripts/panel.css +++ gc_lang/fr/webext/content_scripts/panel.css @@ -39,10 +39,16 @@ } .grammalecte_panel_label { display: inline-block; padding: 0 10px; } +.grammalecte_panel_invisible_marker { + position: absolute; + /*visibility: hidden;*/ + font-size: 6px; + color: hsl(210, 0%, 90%); /* same color than panel_bar background */ +} .grammalecte_panel_commands { float: right; } .grammalecte_copy_button { @@ -91,10 +97,19 @@ position: absolute; min-width: 100%; height: calc(100% - 55px); /* panel height - title_bar */ overflow: auto; } + +.grammalecte_panel_message { + margin: 10px; + padding: 10px; + border-radius: 5px; + background-color: hsl(0, 50%, 40%); + color: hsl(0, 50%, 96%); + font-size: 16px; +} /* Spinner */ Index: gc_lang/fr/webext/content_scripts/panel.js ================================================================== --- gc_lang/fr/webext/content_scripts/panel.js +++ gc_lang/fr/webext/content_scripts/panel.js @@ -19,10 +19,11 @@ } _createPanel (sTitle) { try { let xPanel = oGrammalecte.createNode("div", {id: this.sId, className: "grammalecte_panel"}); + this.xPanelBar.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_panel_invisible_marker", textContent: "__grammalecte_panel__"})); this.xPanelBar.appendChild(this._createButtons()); let xTitle = oGrammalecte.createNode("div", {className: "grammalecte_panel_title"}); xTitle.appendChild(this._createLogo()); xTitle.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_panel_label", textContent: sTitle})); this.xPanelBar.appendChild(xTitle); @@ -89,10 +90,11 @@ this.xPanel.style.display = "block"; } hide () { this.xPanel.style.display = "none"; + oGrammalecte.clearRightClickedNode(); } center () { let nHeight = (this.bFlexible) ? window.innerHeight-100 : this.nHeight; this.xPanel.style = `top: 50%; left: 50%; width: ${this.nWidth}px; height: ${nHeight}px; margin-top: -${nHeight/2}px; margin-left: -${this.nWidth/2}px;`; 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 @@ -50,18 +50,22 @@ this.xParagraphList = oGrammalecte.createNode("div", {id: "grammalecte_paragraph_list"}); this.xContentNode.appendChild(this.xParagraphList); this.xPanelContent.addEventListener("click", onGrammalecteGCPanelClick, false); this.oTooltip = new GrammalecteTooltip(this.xContentNode); this.xPanelContent.appendChild(this.xContentNode); - this.oTAC = new GrammalecteTextAreaControl(); + this.oNodeControl = new GrammalecteNodeControl(); } - start (xTextArea=null) { + start (xNode=null) { this.oTooltip.hide(); this.clear(); - if (xTextArea) { - this.oTAC.setTextArea(xTextArea); + if (xNode) { + if (xNode.tagName == "TEXTAREA") { + this.oNodeControl.setNode(xNode); + } else { + this.addMessage("Cette zone de texte n’est pas un champ de formulaire “textarea” mais un node HTML éditable. Les modifications ne seront pas répercutées automatiquement. Une fois votre texte corrigé, vous pouvez utiliser le bouton ‹∑› pour copier le texte dans le presse-papiers."); + } } } clear () { while (this.xParagraphList.firstChild) { @@ -70,11 +74,11 @@ this.aIgnoredErrors.clear(); } hide () { this.xPanel.style.display = "none"; - this.oTAC.clear(); + this.oNodeControl.clear(); } addParagraphResult (oResult) { try { if (oResult && (oResult.sParagraph.trim() !== "" || oResult.aGrammErr.length > 0 || oResult.aSpellErr.length > 0)) { @@ -85,12 +89,12 @@ xActionsBar.appendChild(oGrammalecte.createNode("div", {id: "grammalecte_hide" + oResult.iParaNum, className: "grammalecte_paragraph_button grammalecte_red", textContent: "×", style: "font-weight: bold;"})); // paragraph let xParagraph = oGrammalecte.createNode("p", {id: "grammalecte_paragraph"+oResult.iParaNum, className: "grammalecte_paragraph", lang: "fr", contentEditable: "true"}, {para_num: oResult.iParaNum}); xParagraph.setAttribute("spellcheck", "false"); // doesn’t seem possible to use “spellcheck” as a common attribute. xParagraph.addEventListener("keyup", function (xEvent) { - this.oTAC.setParagraph(parseInt(xEvent.target.dataset.para_num), this.purgeText(xEvent.target.textContent)); - this.oTAC.write(); + this.oNodeControl.setParagraph(parseInt(xEvent.target.dataset.para_num), this.purgeText(xEvent.target.textContent)); + this.oNodeControl.write(); }.bind(this) , true); this._tagParagraph(xParagraph, oResult.sParagraph, oResult.iParaNum, oResult.aGrammErr, oResult.aSpellErr); // creation xNodeDiv.appendChild(xActionsBar); @@ -111,12 +115,12 @@ xGrammalectePort.postMessage({ sCommand: "parseAndSpellcheck1", dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false}, dInfo: {sParagraphId: sParagraphId} }); - this.oTAC.setParagraph(iParaNum, sText); - this.oTAC.write(); + this.oNodeControl.setParagraph(iParaNum, sText); + this.oNodeControl.write(); } refreshParagraph (sParagraphId, oResult) { try { let xParagraph = document.getElementById(sParagraphId); @@ -235,11 +239,11 @@ addSummary () { // todo } addMessage (sMessage) { - let xNode = oGrammalecte.createNode("div", {className: "grammalecte_gc_panel_message", textContent: sMessage}); + let xNode = oGrammalecte.createNode("div", {className: "grammalecte_panel_message", textContent: sMessage}); this.xParagraphList.appendChild(xNode); } _copyToClipboard (sText) { // recipe from https://github.com/mdn/webextensions-examples/blob/master/context-menu-copy-link-with-types/clipboard-helper.js @@ -408,58 +412,65 @@ } } } -class GrammalecteTextAreaControl { +class GrammalecteNodeControl { constructor () { - this._xTextArea = null; - this._dParagraph = new Map(); + this.xNode = null; + this.dParagraph = new Map(); + this.bTextArea = null; + this.bWriteEN = false; // write editable node } - setTextArea (xNode) { + setNode (xNode) { this.clear(); - this._xTextArea = xNode; - this._xTextArea.disabled = true; + this.xNode = xNode; + this.bTextArea = (xNode.tagName == "TEXTAREA"); + this.xNode.disabled = true; this._loadText(); } clear () { - if (this._xTextArea !== null) { - this._xTextArea.disabled = false; - this._xTextArea = null; + if (this.xNode !== null) { + this.xNode.disabled = false; + this.xNode = null; } - this._dParagraph.clear(); + this.dParagraph.clear(); } setParagraph (iParagraph, sText) { - if (this._xTextArea !== null) { - this._dParagraph.set(iParagraph, sText); + if (this.xNode !== null) { + this.dParagraph.set(iParagraph, sText); } } _loadText () { - let sText = this._xTextArea.value; + let sText = (this.bTextArea) ? this.xNode.value : this.xNode.innerText; let i = 0; let iStart = 0; let iEnd = 0; sText = sText.replace("\r\n", "\n").replace("\r", "\n"); while ((iEnd = sText.indexOf("\n", iStart)) !== -1) { - this._dParagraph.set(i, sText.slice(iStart, iEnd)); + 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.dParagraph.set(i, sText.slice(iStart)); + //console.log("Paragraphs number: " + (i+1)); } write () { - if (this._xTextArea !== null) { + if (this.xNode !== null && (this.bTextArea || this.bWriteEN)) { let sText = ""; - this._dParagraph.forEach(function (val, key) { + this.dParagraph.forEach(function (val, key) { sText += val + "\n"; }); - this._xTextArea.value = sText.slice(0,-1); + if (this.bTextArea) { + this.xNode.value = sText.slice(0,-1); + } else { + this.xNode.textContent = sText.slice(0,-1); + } } } } Index: gc_lang/fr/webext/content_scripts/panel_lxg.js ================================================================== --- gc_lang/fr/webext/content_scripts/panel_lxg.js +++ gc_lang/fr/webext/content_scripts/panel_lxg.js @@ -22,12 +22,13 @@ if (this._xContentNode.textContent !== "") { this._xContentNode.appendChild(oGrammalecte.createNode("div", {className: "grammalecte_lxg_separator", textContent: sText})); } } - addMessage (sClass, sText) { - this._xContentNode.appendChild(oGrammalecte.createNode("div", {className: sClass, textContent: sText})); + addMessage (sMessage) { + let xNode = oGrammalecte.createNode("div", {className: "grammalecte_panel_message", textContent: sMessage}); + this._xContentNode.appendChild(xNode); } addListOfTokens (lTokens) { try { if (lTokens) { 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 @@ -160,17 +160,19 @@ } /* Actions */ - start (xTextArea) { - this.xTextArea = xTextArea; - if (bChrome) { - browser.storage.local.get("tf_options", this.setOptions.bind(this)); - } else { - let xPromise = browser.storage.local.get("tf_options"); - xPromise.then(this.setOptions.bind(this), this.reset.bind(this)); + start (xNode) { + if (xNode !== null && xNode.tagName == "TEXTAREA") { + this.xTextArea = xNode; + if (bChrome) { + browser.storage.local.get("tf_options", this.setOptions.bind(this)); + } else { + let xPromise = browser.storage.local.get("tf_options"); + xPromise.then(this.setOptions.bind(this), this.reset.bind(this)); + } } } switchGroup (sOptName) { if (document.getElementById(sOptName).dataset.selected == "true") { DELETED gc_lang/fr/webext/gce_sharedworker.js Index: gc_lang/fr/webext/gce_sharedworker.js ================================================================== --- gc_lang/fr/webext/gce_sharedworker.js +++ gc_lang/fr/webext/gce_sharedworker.js @@ -1,271 +0,0 @@ -/* - WORKER: - https://developer.mozilla.org/en-US/docs/Web/API/Worker - https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope - - - JavaScript sucks. - No module available in WebExtension at the moment! :( - No require, no import/export. - - In Worker, we have importScripts() which imports everything in this scope. - - In order to use the same base of code with XUL-addon for Thunderbird and SDK-addon for Firefox, - all modules have been “objectified”. And while they are still imported via “require” - in the previous extensions, they are loaded as background scripts in WebExtension sharing - the same memory space… - - When JavaScript become a modern language, “deobjectify” the modules… - - ATM, import/export are not available by default: - — Chrome 60 – behind the Experimental Web Platform flag in chrome:flags. - — Firefox 54 – behind the dom.moduleScripts.enabled setting in about:config. - — Edge 15 – behind the Experimental JavaScript Features setting in about:flags. - - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export -*/ - -"use strict"; - - -console.log("GC Engine SharedWorker [start]"); -//console.log(self); - -importScripts("grammalecte/helpers.js"); -importScripts("grammalecte/str_transform.js"); -importScripts("grammalecte/ibdawg.js"); -importScripts("grammalecte/text.js"); -importScripts("grammalecte/tokenizer.js"); -importScripts("grammalecte/fr/conj.js"); -importScripts("grammalecte/fr/mfsp.js"); -importScripts("grammalecte/fr/phonet.js"); -importScripts("grammalecte/fr/cregex.js"); -importScripts("grammalecte/fr/gc_options.js"); -importScripts("grammalecte/fr/gc_rules.js"); -importScripts("grammalecte/fr/gc_engine.js"); -importScripts("grammalecte/fr/lexicographe.js"); -importScripts("grammalecte/tests.js"); -/* - Warning. - Initialization can’t be completed at startup of the worker, - for we need the path of the extension to load data stored in JSON files. - This path is retrieved in background.js and passed with the event “init”. -*/ - - -/* - Message Event Object - https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent -*/ - -let xPort = null; - -function createResponse (sActionDone, result, dInfo, bError=false) { - return { - "sActionDone": sActionDone, - "result": result, // can be of any type - "dInfo": dInfo, - "bError": bError - }; -} - -function createErrorResult (e, sDescr="no description") { - return { - "sType": "error", - "sDescription": sDescr, - "sMessage": e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message - }; -} - -function showData (e) { - for (let sParam in e) { - console.log(sParam); - console.log(e[sParam]); - } -} - -onconnect = function (e) { - console.log("[Sharedworker] START CONNECTION"); - xPort = e.ports[0]; - - xPort.onmessage = function (e) { - console.log("[Sharedworker] ONMESSAGE"); - let {sCommand, dParam, dInfo} = e.data; - console.log(e.data); - switch (sCommand) { - case "init": - init(dParam.sExtensionPath, dParam.sOptions, dParam.sContext, dInfo); - break; - case "parse": - parse(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); - break; - case "parseAndSpellcheck": - parseAndSpellcheck(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); - break; - case "getOptions": - getOptions(dInfo); - break; - case "getDefaultOptions": - getDefaultOptions(dInfo); - break; - case "setOptions": - setOptions(dParam.sOptions, dInfo); - break; - case "setOption": - setOption(dParam.sOptName, dParam.bValue, dInfo); - break; - case "resetOptions": - resetOptions(dInfo); - break; - case "textToTest": - textToTest(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); - break; - case "fullTests": - fullTests('{"nbsp":true, "esp":true, "unit":true, "num":true}', dInfo); - break; - case "getListOfTokens": - getListOfTokens(dParam.sText, dInfo); - break; - default: - console.log("Unknown command: " + sCommand); - showData(e.data); - } - } - //xPort.start(); -} - -let bInitDone = false; - -let oDict = null; -let oTokenizer = null; -let oLxg = null; -let oTest = null; - - -function init (sExtensionPath, sGCOptions="", sContext="JavaScript", dInfo={}) { - try { - if (!bInitDone) { - console.log("[Sharedworker] Loading… Extension path: " + sExtensionPath); - conj.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/conj_data.json")); - phonet.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/phonet_data.json")); - mfsp.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/mfsp_data.json")); - console.log("[Sharedworker] Modules have been initialized…"); - gc_engine.load(sContext, sExtensionPath+"grammalecte/_dictionaries"); - oDict = gc_engine.getDictionary(); - oTest = new TestGrammarChecking(gc_engine, sExtensionPath+"/grammalecte/fr/tests_data.json"); - oLxg = new Lexicographe(oDict); - if (sGCOptions !== "") { - gc_engine.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); - } - oTokenizer = new Tokenizer("fr"); - //tests(); - bInitDone = true; - } else { - console.log("[Sharedworker] Already initialized…") - } - // we always retrieve options from the gc_engine, for setOptions filters obsolete options - xPort.postMessage(createResponse("init", gc_engine.getOptions().gl_toString(), dInfo)); - } - catch (e) { - helpers.logerror(e); - xPort.postMessage(createResponse("init", createErrorResult(e, "init failed"), dInfo, true)); - } -} - -function parse (sText, sCountry, bDebug, bContext, dInfo={}) { - let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); - xPort.postMessage({sActionDone: "parse", result: aGrammErr, dInfo: dInfo}); -} - -function parseAndSpellcheck (sText, sCountry, bDebug, bContext, dInfo={}) { - let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); - let aSpellErr = oTokenizer.getSpellingErrors(sText, oDict); - xPort.postMessage(createResponse("parseAndSpellcheck", {aGrammErr: aGrammErr, aSpellErr: aSpellErr}, dInfo)); -} - -function getOptions (dInfo={}) { - xPort.postMessage(createResponse("getOptions", gc_engine.getOptions().gl_toString(), dInfo)); -} - -function getDefaultOptions (dInfo={}) { - xPort.postMessage(createResponse("getDefaultOptions", gc_engine.getDefaultOptions().gl_toString(), dInfo)); -} - -function setOptions (sGCOptions, dInfo={}) { - gc_engine.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); - xPort.postMessage(createResponse("setOptions", gc_engine.getOptions().gl_toString(), dInfo)); -} - -function setOption (sOptName, bValue, dInfo={}) { - gc_engine.setOptions(new Map([ [sOptName, bValue] ])); - xPort.postMessage(createResponse("setOption", gc_engine.getOptions().gl_toString(), dInfo)); -} - -function resetOptions (dInfo={}) { - gc_engine.resetOptions(); - xPort.postMessage(createResponse("resetOptions", gc_engine.getOptions().gl_toString(), dInfo)); -} - -function tests () { - console.log(conj.getConj("devenir", ":E", ":2s")); - console.log(mfsp.getMasForm("emmerdeuse", true)); - console.log(mfsp.getMasForm("pointilleuse", false)); - console.log(phonet.getSimil("est")); - let aRes = gc_engine.parse("Je suit..."); - for (let oErr of aRes) { - console.log(text.getReadableError(oErr)); - } -} - -function textToTest (sText, sCountry, bDebug, bContext, dInfo={}) { - if (!gc_engine || !oDict) { - xPort.postMessage(createResponse("textToTest", "# Grammar checker or dictionary not loaded.", dInfo)); - return; - } - let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); - let sMsg = ""; - for (let oErr of aGrammErr) { - sMsg += text.getReadableError(oErr) + "\n"; - } - xPort.postMessage(createResponse("textToTest", sMsg, dInfo)); -} - -function fullTests (sGCOptions="", dInfo={}) { - if (!gc_engine || !oDict) { - xPort.postMessage(createResponse("fullTests", "# Grammar checker or dictionary not loaded.", dInfo)); - return; - } - let dMemoOptions = gc_engine.getOptions(); - if (sGCOptions) { - gc_engine.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); - } - let sMsg = ""; - for (let sRes of oTest.testParse()) { - sMsg += sRes + "\n"; - console.log(sRes); - } - gc_engine.setOptions(dMemoOptions); - xPort.postMessage(createResponse("fullTests", sMsg, dInfo)); -} - - -// Lexicographer - -function getListOfTokens (sText, dInfo={}) { - try { - let aElem = []; - let aRes = null; - for (let oToken of oTokenizer.genTokens(sText)) { - aRes = oLxg.getInfoForToken(oToken); - if (aRes) { - aElem.push(aRes); - } - } - xPort.postMessage(createResponse("getListOfTokens", aElem, dInfo)); - } - catch (e) { - helpers.logerror(e); - xPort.postMessage(createResponse("getListOfTokens", createErrorResult(e, "no tokens"), dInfo, true)); - } -} Index: gc_lang/fr/webext/img/contextmenu.png ================================================================== --- gc_lang/fr/webext/img/contextmenu.png +++ gc_lang/fr/webext/img/contextmenu.png cannot compute difference between binary files Index: gc_lang/fr/webext/manifest.json ================================================================== --- gc_lang/fr/webext/manifest.json +++ gc_lang/fr/webext/manifest.json @@ -51,18 +51,20 @@ "css": [ "content_scripts/panel.css", "content_scripts/panel_tf.css", "content_scripts/panel_gc.css", "content_scripts/panel_lxg.css", + "content_scripts/message_box.css", "content_scripts/menu.css" ], "js": [ "content_scripts/panel.js", "grammalecte/fr/textformatter.js", "content_scripts/panel_tf.js", "content_scripts/panel_gc.js", "content_scripts/panel_lxg.js", + "content_scripts/message_box.js", "content_scripts/menu.js", "content_scripts/init.js" ], "run_at": "document_end" }, Index: gc_lang/fr/webext/panel/main.css ================================================================== --- gc_lang/fr/webext/panel/main.css +++ gc_lang/fr/webext/panel/main.css @@ -153,24 +153,25 @@ } /* Help */ -#help_page { +#ui_options_page { display: none; padding: 20px; } -#help_page h2 { +#ui_options_page h2 { + margin-top: 10px; font: bold 20px 'Yanone Kaffeesatz', "Oswald", "Liberation Sans Narrow", sans-serif; color: hsl(210, 50%, 50%); } -#help_page .shortcut { +#ui_options_page .shortcut { margin-top: 10px; font-weight: bold; font-size: 13px; } -#help_page .shortcut_label { +#ui_options_page .shortcut_label { margin-left: 30px; } /* Grammar checking options @@ -230,23 +231,28 @@ padding: 10px; border-radius: 3px; background-color: hsl(0, 50%, 40%); color: hsl(0, 10%, 96%); } -.dict_section { + + +/* + Options +*/ +.option_section { padding: 10px; margin-top: 10px; border-radius: 5px; background-color: hsl(210, 20%, 96%); } -.dict_section label { +.option_section label { font-size: 16px; line-height: 20px; color: hsl(210, 20%, 50%); font-weight: bold; } -.dict_description { +.option_description { padding: 0 0 0 20px; color: hsl(0, 0%, 0%); font-size: 12px; } Index: gc_lang/fr/webext/panel/main.html ================================================================== --- gc_lang/fr/webext/panel/main.html +++ gc_lang/fr/webext/panel/main.html @@ -14,15 +14,15 @@
@@ -48,25 +48,34 @@ et tous ceux qui l’ont soutenu

-
-

AIDE

-
-

Grammalecte affiche un bouton d’accès au menu en bas à gauche des zones de texte usuelles pour accéder aux fonctionnalités existantes.

-

-

-

Pour les autres zones de texte (HTML éditable), il faut sélectionner le texte et utiliser le menu contextuel.

-

-

Raccourcis clavier

-

CTRL+MAJ+6

-

Conjugueur (dans un onglet)

-

CTRL+MAJ+7

-

Conjugueur (dans une fenêtre)

+
+

OPTIONS D’INTERFACE

+

Menu contextuel

+

Les commandes permettant de lancer Grammalecte sont trouvables via le menu contextuel.

+

+

Boutons d’accès

+

Grammalecte peut aussi afficher un bouton d’accès au menu en bas à gauche des zones de texte pour accéder aux fonctionnalités existantes.

+

+

+

Vous pouvez activer ou désactiver l’affichage de ces boutons sur les zones de texte. Il existe deux types de zones de texte.

+
+

+

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.

+
+
+

+

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.). Il est assez fréquent que des “textareas” invisibles se cachent derrière ces “nodes” éditables.

-
+

Raccourcis clavier

+

CTRL+MAJ+6

+

Conjugueur (dans un onglet)

+

CTRL+MAJ+7

+

Conjugueur (dans une fenêtre)

+

OPTIONS GRAMMATICALES

${webextOptionsHTML} @@ -76,25 +85,25 @@

OPTIONS ORTHOGRAPHIQUES

Section inactive. Le nouveau système d’extension de Firefox (WebExtension) ne propose pas encore d’interface de programmation pour le correcteur orthographique.

-
+

-

Ce dictionnaire propose l’orthographe telle qu’elle est écrite aujourd’hui le plus couramment. C’est le dictionnaire recommandé si le français n’est pas votre langue maternelle ou si vous ne désirez qu’une seule graphie correcte par mot.

+

Ce dictionnaire propose l’orthographe telle qu’elle est écrite aujourd’hui le plus couramment. C’est le dictionnaire recommandé si le français n’est pas votre langue maternelle ou si vous ne désirez qu’une seule graphie correcte par mot.

-
+

-

Il s’agit du dictionnaire “Moderne”, avec des graphies classiques en sus, certaines encore communément utilisées, d’autres désuètes. C’est le dictionnaire recommandé si le français est votre langue maternelle.

+

Il s’agit du dictionnaire “Moderne”, avec des graphies classiques en sus, certaines encore communément utilisées, d’autres désuètes. C’est le dictionnaire recommandé si le français est votre langue maternelle.

-
+

-

Avec ce dictionnaire, 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 “Moderne”.

+

Avec ce dictionnaire, 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 “Moderne”.

-
+

-

Ce dictionnaire contient les variantes graphiques, classiques, 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.

+

Ce dictionnaire contient les variantes graphiques, classiques, 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.

Index: gc_lang/fr/webext/panel/main.js ================================================================== --- gc_lang/fr/webext/panel/main.js +++ gc_lang/fr/webext/panel/main.js @@ -50,10 +50,13 @@ browser.runtime.sendMessage({ sCommand: "setOption", dParam: {sOptName: xElem.dataset.option, bValue: xElem.checked}, dInfo: {} }); + } + else if (xElem.id.startsWith("ui_option_")) { + storeUIOptions(); } else if (xElem.id.startsWith("link_")) { browser.tabs.create({url: xElem.dataset.url}); } } else if (xElem.className.startsWith("select")) { @@ -121,10 +124,13 @@ // show the selected one document.getElementById(sPageName).style.display = "block"; if (sPageName == "gc_options_page") { setGCOptionsFromStorage(); } + else if (sPageName == "ui_options_page") { + setUIOptionsFromStorage(); + } } catch (e) { showError(e); } } @@ -133,10 +139,47 @@ function showTestResult (sText) { document.getElementById("tests_result").textContent = sText; } +/* + UI options +*/ + +function setUIOptionsFromStorage () { + if (bChrome) { + browser.storage.local.get("ui_options", setUIOptions); + return; + } + let xPromise = browser.storage.local.get("ui_options"); + xPromise.then(setUIOptions, showError); +} + +function setUIOptions (dOptions) { + if (!dOptions.hasOwnProperty("ui_options")) { + console.log("no ui options found"); + return; + } + dOptions = dOptions.ui_options; + for (let sOpt in dOptions) { + if (document.getElementById("ui_option_"+sOpt)) { + document.getElementById("ui_option_"+sOpt).checked = dOptions[sOpt]; + } + } +} + +function storeUIOptions () { + browser.storage.local.set({"ui_options": { + textarea: ui_option_textarea.checked, + editablenode: ui_option_editablenode.checked + }}); +} + + +/* + GC options +*/ function setGCOptionsFromStorage () { if (bChrome) { browser.storage.local.get("gc_options", _setGCOptions); return; }