Index: gc_lang/fr/build.py ================================================================== --- gc_lang/fr/build.py +++ gc_lang/fr/build.py @@ -70,44 +70,30 @@ spfZip = "_build/" + dVars['tb_identifier'] + "-v" + dVars['version'] + '.mailext.xpi' hZip = zipfile.ZipFile(spfZip, mode='w', compression=zipfile.ZIP_DEFLATED) _copyGrammalecteJSPackageInZipFile(hZip, sLang) for spf in ["LICENSE.txt", "LICENSE.fr.txt"]: hZip.write(spf) - dVars = _createOptionsForThunderbird(dVars) + helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/webext/gce_worker.js") helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/mailext", "", dVars, True) helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/webext/3rd", "3rd", dVars, True) helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/webext/_locales", "_locales", dVars, True) helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/webext/content_scripts", "content_scripts", dVars, True) helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/webext/fonts", "fonts", dVars, True) helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/webext/img", "img", dVars, True) helpers.addFolderToZipAndFileFile(hZip, "gc_lang/"+sLang+"/webext/panel", "panel", dVars, True) hZip.close() - #spExtension = dVars['win_tb_debug_extension_path'] if platform.system() == "Windows" else dVars['linux_tb_debug_extension_path'] - #if os.path.isdir(spExtension): - # file_util.copy_file(spfZip, spExtension + "/" + dVars['tb_identifier']+ ".xpi") # Filename for TB is just - # print(f"TB extension copied in <{spExtension}>") - #spExtension = dVars['win_tb_beta_extension_path'] if platform.system() == "Windows" else dVars['linux_tb_beta_extension_path'] - #if os.path.isdir(spExtension): - # print(f"TB extension copied in <{spExtension}>") - # file_util.copy_file(spfZip, spExtension + "/" + dVars['tb_identifier']+ ".xpi") # Filename for TB is just - - -def _createOptionsForThunderbird (dVars): - dVars['sXULTabs'] = "" - dVars['sXULTabPanels'] = "" - # dialog options - for sSection, lOpt in dVars['lStructOpt']: - dVars['sXULTabs'] += ' \n' - dVars['sXULTabPanels'] += ' \n \n' - # translation data - for sLang in dVars['dOptLabel'].keys(): - dVars['gc_options_labels_'+sLang] = "\n".join( [ "' for sOpt in dVars['dOptLabel'][sLang] ] ) - return dVars + # Note about copying Thunderbird extension directly into the profile: + # In Options > Configuration editor (about:config), deactivate option + # If is changed, you must reinstall the extension manually + spExtension = dVars['win_tb_debug_extension_path'] if platform.system() == "Windows" else dVars['linux_tb_debug_extension_path'] + if os.path.isdir(spExtension): + file_util.copy_file(spfZip, f"{spExtension}/{dVars['tb_identifier']}.xpi") # Filename for TB is just + print(f"Thunderbird extension copied in <{spExtension}>") + spExtension = dVars['win_tb_beta_extension_path'] if platform.system() == "Windows" else dVars['linux_tb_beta_extension_path'] + if os.path.isdir(spExtension): + file_util.copy_file(spfZip, f"{spExtension}/{dVars['tb_identifier']}.xpi") # Filename for TB is just + print(f"Thunderbird extension copied in <{spExtension}>") def _copyGrammalecteJSPackageInZipFile (hZip, sLang, sAddPath=""): for sf in os.listdir("grammalecte-js"): if not os.path.isdir("grammalecte-js/"+sf): Index: gc_lang/fr/config.ini ================================================================== --- gc_lang/fr/config.ini +++ gc_lang/fr/config.ini @@ -57,11 +57,10 @@ tb_name = Grammalecte [fr] win_tb_path = C:\Program Files\Mozilla Thunderbird\thunderbird.exe win_tb_beta_path = C:\Program Files\Thunderbird Daily\thunderbird.exe linux_tb_path = /usr/bin/thunderbird linux_tb_beta_path = /usr/bin/thunderbird -# useless now win_tb_debug_extension_path = D:\_temp\tb-debug.profile\extensions linux_tb_debug_extension_path = ~/tb-debug.profile/extensions win_tb_beta_extension_path = D:\_temp\tb-beta.profile\extensions linux_tb_beta_extension_path = ~/tb-beta.profile/extensions # Set Thunderbird folder in your PATH variable DELETED gc_lang/fr/mailext/README.txt Index: gc_lang/fr/mailext/README.txt ================================================================== --- gc_lang/fr/mailext/README.txt +++ gc_lang/fr/mailext/README.txt @@ -1,14 +0,0 @@ - -= GRAMMALECTE = - -French grammar checker -By Olivier R. (olivier /at/ grammalecte /dot/ net) - -Website: https://grammalecte.net/ - -License: GPL 3 -- http://www.gnu.org/copyleft/gpl.html - -Grammalecte for Firefox is a derivative tool born from the version -for LibreOffice written in Python. - -Written in JavaScript ES6/ES7. Index: gc_lang/fr/mailext/background.js ================================================================== --- gc_lang/fr/mailext/background.js +++ gc_lang/fr/mailext/background.js @@ -1,10 +1,29 @@ // Background +/* jshint esversion:6, -W097 */ +/* jslint esversion:6 */ +/* global helpers, showError, Worker, chrome, console */ + "use strict"; -// Draft for later + +console.log("[Grammalecte] background"); + + +// Chrome don’t follow the W3C specification: +// https://browserext.github.io/browserext/ +let bChrome = false; +let bThunderbird = false; +if (typeof(browser) !== "object") { + var browser = chrome; + bChrome = true; +} +if (typeof(messenger) === "object") { + bThunderbird = true; +} + const oWorkerHandler = { xGCEWorker: null, nLastTimeWorkerResponse: 0, // milliseconds since 1970-01-01 @@ -107,20 +126,63 @@ const oInitHandler = { initUIOptions: function () { + if (bChrome) { + browser.storage.local.get("ui_options", this._initUIOptions); + browser.storage.local.get("autorefresh_option", this._initUIOptions); + return; + } browser.storage.local.get("ui_options").then(this._initUIOptions, showError); browser.storage.local.get("autorefresh_option").then(this._initUIOptions, showError); }, initGrammarChecker: function () { + if (bChrome) { + browser.storage.local.get("gc_options", this._initGrammarChecker); + browser.storage.local.get("main_dic_name", this._setSpellingDictionaries); + browser.storage.local.get("personal_dictionary", this._setSpellingDictionaries); + browser.storage.local.get("community_dictionary", this._setSpellingDictionaries); + browser.storage.local.get("oPersonalDictionary", this._setSpellingDictionaries); // deprecated + browser.storage.local.get("sc_options", this._initSCOptions); + return; + } browser.storage.local.get("gc_options").then(this._initGrammarChecker, showError); + browser.storage.local.get("main_dic_name", this._setSpellingDictionaries); browser.storage.local.get("personal_dictionary").then(this._setSpellingDictionaries, showError); browser.storage.local.get("community_dictionary").then(this._setSpellingDictionaries, showError); + browser.storage.local.get("oPersonalDictionary").then(this._setSpellingDictionaries, showError); // deprecated browser.storage.local.get("sc_options").then(this._initSCOptions, showError); }, + + registerComposeScripts: async function () { + // For Thunderbird only + if (bThunderbird) { + let xRegisteredScripts = await browser.composeScripts.register({ + /*css: [ + // Any number of code or file objects could be listed here. + { code: "body { background-color: red; }" }, + { 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/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" }, + { file: "content_scripts/message_box.js" }, + { file: "content_scripts/menu.js" }, + { file: "content_scripts/init.js" } + ] + }); + // To unregister scripts: + // await xRegisteredScripts.unregister(); + } + }, _initUIOptions: function (oSavedOptions) { if (!oSavedOptions.hasOwnProperty("ui_options")) { browser.storage.local.set({"ui_options": { textarea: true, @@ -150,10 +212,20 @@ showError(e); } }, _setSpellingDictionaries: function (oData) { + if (oData.hasOwnProperty("oPersonalDictionary")) { + // deprecated (to be removed in 2020) + console.log("personal dictionary migration"); + browser.storage.local.set({ "personal_dictionary": oData["oPersonalDictionary"] }); + oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionary", oParam: { sDictionary: "personal", oDict: oData["oPersonalDictionary"] }, oInfo: {} }); + browser.storage.local.remove("oPersonalDictionary"); + } + if (oData.hasOwnProperty("main_dic_name")) { + oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionary", oParam: { sDictionary: "main", oDict: oData["main_dic_name"] }, oInfo: {sExtPath: browser.extension.getURL("")} }); + } if (oData.hasOwnProperty("community_dictionary")) { oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionary", oParam: { sDictionary: "community", oDict: oData["community_dictionary"] }, oInfo: {} }); } if (oData.hasOwnProperty("personal_dictionary")) { oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionary", oParam: { sDictionary: "personal", oDict: oData["personal_dictionary"] }, oInfo: {} }); @@ -170,20 +242,224 @@ oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionaryOnOff", oParam: { sDictionary: "personal", bActivate: true }, oInfo: {} }); } else { oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionaryOnOff", oParam: { sDictionary: "community", bActivate: oData.sc_options["community"] }, oInfo: {} }); oWorkerHandler.xGCEWorker.postMessage({ sCommand: "setDictionaryOnOff", oParam: { sDictionary: "personal", bActivate: oData.sc_options["personal"] }, oInfo: {} }); } - } + }, } // start the Worker for the GC oWorkerHandler.start(); // init the options stuff and start the GC oInitHandler.initUIOptions(); oInitHandler.initGrammarChecker(); +oInitHandler.registerComposeScripts(); // Thunderbird only + + +// When the extension is installed or updated +browser.runtime.onInstalled.addListener(function (oDetails) { + // launched at installation or update + // https://developer.mozilla.org/fr/Add-ons/WebExtensions/API/runtime/onInstalled + if (oDetails.reason == "update" || oDetails.reason == "installed") { + // todo + //browser.tabs.create({url: "http://grammalecte.net"}); + } +}); + + + +/* + Ports from content-scripts +*/ + +let dConnx = new Map(); + + +/* + Messages from the extension (not the Worker) +*/ +function handleMessage (oRequest, xSender, sendResponse) { + // message from panels + //console.log(xSender); + let {sCommand, oParam, oInfo} = oRequest; + switch (sCommand) { + case "getOptions": + case "getDefaultOptions": + case "setOptions": + case "setOption": + case "resetOptions": + case "textToTest": + case "fullTests": + case "setDictionary": + case "setDictionaryOnOff": + oWorkerHandler.xGCEWorker.postMessage(oRequest); + break; + case "restartWorker": + oWorkerHandler.restart(oParam["nDelayLimit"]); + break; + case "openURL": + browser.tabs.create({url: oParam.sURL}); + break; + case "openConjugueurTab": + openConjugueurTab(); + break; + case "openLexiconEditor": + openLexiconEditor(oParam["dictionary"]); + break; + case "openDictionaries": + openDictionaries(); + break; + default: + console.log("[background] Unknown command: " + sCommand); + console.log(oRequest); + } + //sendResponse({response: "response from background script"}); +} + +browser.runtime.onMessage.addListener(handleMessage); + + +function handleConnexion (xPort) { + // Messages from tabs + let iPortId = xPort.sender.tab.id; // identifier for the port: each port can be found at dConnx[iPortId] + dConnx.set(iPortId, xPort); + xPort.onMessage.addListener(function (oRequest) { + let {sCommand, oParam, oInfo} = oRequest; + switch (sCommand) { + case "ping": + //console.log("[background] ping"); + xPort.postMessage({sActionDone: "ping", result: null, bInfo: null, bEnd: true, bError: false}); + break; + case "parse": + case "parseAndSpellcheck": + case "parseAndSpellcheck1": + case "parseFull": + case "getListOfTokens": + case "getSpellSuggestions": + case "getVerb": + oRequest.oInfo.iReturnPort = iPortId; // we pass the id of the return port to receive answer + oWorkerHandler.xGCEWorker.postMessage(oRequest); + break; + case "restartWorker": + oWorkerHandler.restart(oParam["nDelayLimit"]); + break; + case "openURL": + browser.tabs.create({url: oParam.sURL}); + break; + case "openConjugueurTab": + openConjugueurTab(); + break; + case "openConjugueurWindow": + openConjugueurWindow(); + break; + case "openLexiconEditor": + openLexiconEditor(); + break; + default: + console.log("[background] Unknown command: " + sCommand); + console.log(oRequest); + } + }); + //xPort.postMessage({sActionDone: "newId", result: iPortId}); + xPort.postMessage({sActionDone: "init", sUrl: browser.extension.getURL("")}); +} + +browser.runtime.onConnect.addListener(handleConnexion); + + +/* + Context Menu + (not for MailExtension) +*/ +if (!bThunderbird) { + // Analyze + browser.contextMenus.create({ id: "grammar_checker_editable", title: "Analyser cette zone de texte", contexts: ["editable"] }); + browser.contextMenus.create({ id: "grammar_checker_selection", title: "Analyser la sélection", contexts: ["selection"] }); + browser.contextMenus.create({ id: "grammar_checker_iframe", title: "Analyser le contenu de ce cadre", contexts: ["frame"] }); + browser.contextMenus.create({ id: "grammar_checker_page", title: "Analyser la page", contexts: ["all"] }); + browser.contextMenus.create({ id: "separator_tools", type: "separator", contexts: ["all"] }); + // Tools + browser.contextMenus.create({ id: "conjugueur_tab", title: "Conjugueur [onglet]", contexts: ["all"] }); + browser.contextMenus.create({ id: "conjugueur_window", title: "Conjugueur [fenêtre]", contexts: ["all"] }); + //browser.contextMenus.create({ id: "dictionaries", title: "Dictionnaires", contexts: ["all"] }); + browser.contextMenus.create({ id: "lexicon_editor", title: "Éditeur lexical", contexts: ["all"] }); + + 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) { + // analyze + case "grammar_checker_editable": + case "grammar_checker_page": + sendCommandToTab(xTab.id, xInfo.menuItemId); + break; + case "grammar_checker_iframe": + sendCommandToTab(xTab.id, xInfo.menuItemId, xInfo.frameId); + break; + case "grammar_checker_selection": + sendCommandToTab(xTab.id, xInfo.menuItemId, xInfo.selectionText); + break; + // tools + case "conjugueur_window": + openConjugueurWindow(); + break; + case "conjugueur_tab": + openConjugueurTab(); + break; + case "lexicon_editor": + openLexiconEditor(); + break; + case "dictionaries": + openDictionaries(); + break; + default: + console.log("[Background] Unknown menu id: " + xInfo.menuItemId); + console.log(xInfo); + console.log(xTab); + } + }); +} + + + +/* + Keyboard shortcuts +*/ +browser.commands.onCommand.addListener(function (sCommand) { + switch (sCommand) { + case "grammar_checker": + sendCommandToCurrentTab("shortcutGrammarChecker"); + break; + case "conjugueur_tab": + openConjugueurTab(); + break; + case "lexicon_editor": + openLexiconEditor(); + break; + case "dictionaries": + openDictionaries(); + break; + } +}); + + +/* + Tabs +*/ +let nTabLexiconEditor = null; +let nTabDictionaries = null; +let nTabConjugueur = null; +browser.tabs.onRemoved.addListener(function (nTabId, xRemoveInfo) { + switch (nTabId) { + case nTabLexiconEditor: nTabLexiconEditor = null; break; + case nTabDictionaries: nTabDictionaries = null; break; + case nTabConjugueur: nTabConjugueur = null; break; + } +}); /* Actions */ @@ -192,11 +468,124 @@ if (dOptions instanceof Map) { dOptions = helpers.mapToObject(dOptions); } browser.storage.local.set({"gc_options": dOptions}); } + +function sendCommandToTab (iTab, sCommand, result=null) { + let xTabPort = dConnx.get(iTab); + xTabPort.postMessage({sActionDone: sCommand, result: result, oInfo: null, bEnd: false, bError: false}); +} + +function sendCommandToCurrentTab (sCommand) { + if (bChrome) { + browser.tabs.query({ currentWindow: true, active: true }, (lTabs) => { + for (let xTab of lTabs) { + //console.log(xTab); + browser.tabs.sendMessage(xTab.id, {sActionRequest: sCommand}); + } + }); + return; + } + browser.tabs.query({ currentWindow: true, active: true }).then((lTabs) => { + for (let xTab of lTabs) { + //console.log(xTab); + browser.tabs.sendMessage(xTab.id, {sActionRequest: sCommand}); + } + }, showError); +} + +function sendCommandToAllTabs (sCommand) { + for (let [iTab, xTabPort] of dConnx.entries()) { + xTabPort.postMessage({sActionDone: sCommand, result: null, oInfo: null, bEnd: false, bError: false}); + } +} + +function openLexiconEditor (sName="__personal__") { + if (nTabLexiconEditor === null) { + if (bChrome) { + browser.tabs.create({ + url: browser.extension.getURL("panel/lex_editor.html") + }, onLexiconEditorOpened); + return; + } + let xLexEditor = browser.tabs.create({ + url: browser.extension.getURL("panel/lex_editor.html") + }); + xLexEditor.then(onLexiconEditorOpened, showError); + } + else { + browser.tabs.update(nTabLexiconEditor, {active: true}); + } +} + +function onLexiconEditorOpened (xTab) { + nTabLexiconEditor = xTab.id; +} + +function openDictionaries () { + if (nTabDictionaries === null) { + if (bChrome) { + browser.tabs.create({ + url: browser.extension.getURL("panel/dictionaries.html") + }, onDictionariesOpened); + return; + } + let xLexEditor = browser.tabs.create({ + url: browser.extension.getURL("panel/dictionaries.html") + }); + xLexEditor.then(onDictionariesOpened, showError); + } + else { + browser.tabs.update(nTabDictionaries, {active: true}); + } +} + +function onDictionariesOpened (xTab) { + nTabDictionaries = xTab.id; +} + +function openConjugueurTab () { + if (nTabConjugueur === null) { + if (bChrome) { + browser.tabs.create({ + url: browser.extension.getURL("panel/conjugueur.html") + }, onConjugueurOpened); + return; + } + let xConjTab = browser.tabs.create({ + url: browser.extension.getURL("panel/conjugueur.html") + }); + xConjTab.then(onConjugueurOpened, showError); + } + else { + browser.tabs.update(nTabConjugueur, {active: true}); + } +} + +function onConjugueurOpened (xTab) { + nTabConjugueur = xTab.id; +} + +function openConjugueurWindow () { + if (bChrome) { + browser.windows.create({ + url: browser.extension.getURL("panel/conjugueur.html"), + type: "popup", + width: 710, + height: 980 + }); + return; + } + let xConjWindow = browser.windows.create({ + url: browser.extension.getURL("panel/conjugueur.html"), + type: "popup", + width: 710, + height: 980 + }); +} function showError (e) { console.error(e); //console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); } DELETED gc_lang/fr/mailext/gce_worker.js Index: gc_lang/fr/mailext/gce_worker.js ================================================================== --- gc_lang/fr/mailext/gce_worker.js +++ gc_lang/fr/mailext/gce_worker.js @@ -1,428 +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("[Worker] GC Engine Worker [start]"); -//console.log(self); - -importScripts("grammalecte/graphspell/helpers.js"); -importScripts("grammalecte/graphspell/str_transform.js"); -importScripts("grammalecte/graphspell/char_player.js"); -importScripts("grammalecte/graphspell/suggest.js"); -importScripts("grammalecte/graphspell/ibdawg.js"); -importScripts("grammalecte/graphspell/spellchecker.js"); -importScripts("grammalecte/text.js"); -importScripts("grammalecte/graphspell/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_rules_graph.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”. -*/ - - -function createResponse (sActionDone, result, oInfo, bEnd, bError=false) { - return { - "sActionDone": sActionDone, - "result": result, // can be of any type - "oInfo": oInfo, - "bEnd": bEnd, - "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]); - } -} - - -/* - Message Event Object - https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent -*/ -onmessage = function (e) { - let {sCommand, oParam, oInfo} = e.data; - switch (sCommand) { - case "init": - init(oParam.sExtensionPath, oParam.dOptions, oParam.sContext, oInfo); - break; - case "parse": - parse(oParam.sText, oParam.sCountry, oParam.bDebug, oParam.bContext, oInfo); - break; - case "parseAndSpellcheck": - parseAndSpellcheck(oParam.sText, oParam.sCountry, oParam.bDebug, oParam.bContext, oInfo); - break; - case "parseAndSpellcheck1": - parseAndSpellcheck1(oParam.sText, oParam.sCountry, oParam.bDebug, oParam.bContext, oInfo); - break; - case "parseFull": - parseFull(oParam.sText, oParam.sCountry, oParam.bDebug, oParam.bContext, oInfo); - break; - case "getListOfTokens": - getListOfTokens(oParam.sText, oInfo); - break; - case "getOptions": - getOptions(oInfo); - break; - case "getDefaultOptions": - getDefaultOptions(oInfo); - break; - case "setOptions": - setOptions(oParam.sOptions, oInfo); - break; - case "setOption": - setOption(oParam.sOptName, oParam.bValue, oInfo); - break; - case "resetOptions": - resetOptions(oInfo); - break; - case "textToTest": - textToTest(oParam.sText, oParam.sCountry, oParam.bDebug, oParam.bContext, oInfo); - break; - case "fullTests": - fullTests(oInfo); - break; - case "setDictionary": - setDictionary(oParam.sDictionary, oParam.oDict, oInfo); - break; - case "setDictionaryOnOff": - setDictionaryOnOff(oParam.sDictionary, oParam.bActivate, oInfo); - break; - case "getSpellSuggestions": - getSpellSuggestions(oParam.sWord, oInfo); - break; - case "getVerb": - getVerb(oParam.sVerb, oParam.bPro, oParam.bNeg, oParam.bTpsCo, oParam.bInt, oParam.bFem, oInfo); - break; - default: - console.log("[Worker] Unknown command: " + sCommand); - showData(e.data); - } -} - - - -let bInitDone = false; - -let oSpellChecker = null; -let oTokenizer = null; -let oLxg = null; -let oTest = null; -let oLocution = null; - - -/* - Technical note: - This worker don’t work as a PromiseWorker (which returns a promise), so when we send request - to this worker, we can’t wait the return of the answer just after the request made. - The answer is received by the background in another function (onmessage). - That’s why the full text to analyze is send in one block, but analyse is returned paragraph - by paragraph. -*/ - -function init (sExtensionPath, dOptions=null, sContext="JavaScript", oInfo={}) { - try { - if (!bInitDone) { - console.log("[Worker] 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("[Worker] Modules have been initialized…"); - gc_engine.load(sContext, "sCSS", sExtensionPath+"grammalecte/graphspell/_dictionaries"); - oSpellChecker = gc_engine.getSpellChecker(); - oTest = new TestGrammarChecking(gc_engine, sExtensionPath+"/grammalecte/fr/tests_data.json"); - oTokenizer = new Tokenizer("fr"); - oLocution = helpers.loadFile(sExtensionPath + "/grammalecte/fr/locutions_data.json"); - oLxg = new Lexicographe(oSpellChecker, oTokenizer, oLocution); - if (dOptions !== null) { - if (!(dOptions instanceof Map)) { - dOptions = helpers.objectToMap(dOptions); - } - gc_engine.setOptions(dOptions); - } - //tests(); - bInitDone = true; - } else { - console.log("[Worker] Already initialized…") - } - // we always retrieve options from the gc_engine, for setOptions filters obsolete options - dOptions = helpers.mapToObject(gc_engine.getOptions()); - postMessage(createResponse("init", dOptions, oInfo, true)); - } - catch (e) { - console.error(e); - postMessage(createResponse("init", createErrorResult(e, "init failed"), oInfo, true, true)); - } -} - - -function parse (sText, sCountry, bDebug, bContext, oInfo={}) { - sText = sText.replace(/­/g, "").normalize("NFC"); - for (let sParagraph of text.getParagraph(sText)) { - let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, bContext); - postMessage(createResponse("parse", aGrammErr, oInfo, false)); - } - postMessage(createResponse("parse", null, oInfo, true)); -} - -function parseAndSpellcheck (sText, sCountry, bDebug, bContext, oInfo={}) { - let i = 0; - sText = sText.replace(/­/g, "").normalize("NFC"); - for (let sParagraph of text.getParagraph(sText)) { - let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, null, bContext); - let aSpellErr = oSpellChecker.parseParagraph(sParagraph); - postMessage(createResponse("parseAndSpellcheck", {sParagraph: sParagraph, iParaNum: i, aGrammErr: aGrammErr, aSpellErr: aSpellErr}, oInfo, false)); - i += 1; - } - postMessage(createResponse("parseAndSpellcheck", null, oInfo, true)); -} - -function parseAndSpellcheck1 (sParagraph, sCountry, bDebug, bContext, oInfo={}) { - sParagraph = sParagraph.replace(/­/g, "").normalize("NFC"); - let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, null, bContext); - let aSpellErr = oSpellChecker.parseParagraph(sParagraph); - postMessage(createResponse("parseAndSpellcheck1", {sParagraph: sParagraph, aGrammErr: aGrammErr, aSpellErr: aSpellErr}, oInfo, true)); -} - -function parseFull (sText, sCountry, bDebug, bContext, oInfo={}) { - let i = 0; - sText = sText.replace(/­/g, "").normalize("NFC"); - for (let sParagraph of text.getParagraph(sText)) { - let lSentence = gc_engine.parse(sParagraph, sCountry, bDebug, null, bContext, true); - console.log("*", lSentence); - postMessage(createResponse("parseFull", {sParagraph: sParagraph, iParaNum: i, lSentence: lSentence}, oInfo, false)); - i += 1; - } - postMessage(createResponse("parseFull", null, oInfo, true)); -} - -function getListOfTokens (sText, oInfo={}) { - // lexicographer - try { - sText = sText.replace(/­/g, "").normalize("NFC"); - for (let sParagraph of text.getParagraph(sText)) { - if (sParagraph.trim() !== "") { - postMessage(createResponse("getListOfTokens", oLxg.getListOfTokensReduc(sParagraph, true), oInfo, false)); - } - } - postMessage(createResponse("getListOfTokens", null, oInfo, true)); - } - catch (e) { - console.error(e); - postMessage(createResponse("getListOfTokens", createErrorResult(e, "no tokens"), oInfo, true, true)); - } -} - -function getOptions (oInfo={}) { - let dOptions = helpers.mapToObject(gc_engine.getOptions()); - postMessage(createResponse("getOptions", dOptions, oInfo, true)); -} - -function getDefaultOptions (oInfo={}) { - let dOptions = helpers.mapToObject(gc_engine.getDefaultOptions()); - postMessage(createResponse("getDefaultOptions", dOptions, oInfo, true)); -} - -function setOptions (dOptions, oInfo={}) { - if (!(dOptions instanceof Map)) { - dOptions = helpers.objectToMap(dOptions); - } - gc_engine.setOptions(dOptions); - dOptions = helpers.mapToObject(gc_engine.getOptions()); - postMessage(createResponse("setOptions", dOptions, oInfo, true)); -} - -function setOption (sOptName, bValue, oInfo={}) { - console.log(sOptName+": "+bValue); - if (sOptName) { - gc_engine.setOption(sOptName, bValue); - let dOptions = helpers.mapToObject(gc_engine.getOptions()); - postMessage(createResponse("setOption", dOptions, oInfo, true)); - } -} - -function resetOptions (oInfo={}) { - gc_engine.resetOptions(); - let dOptions = helpers.mapToObject(gc_engine.getOptions()); - postMessage(createResponse("resetOptions", dOptions, oInfo, true)); -} - -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, oInfo={}) { - if (!gc_engine) { - postMessage(createResponse("textToTest", "# Grammar checker not loaded.", oInfo, true)); - return; - } - sText = sText.replace(/­/g, "").normalize("NFC"); - let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); - let sMsg = ""; - for (let oErr of aGrammErr) { - sMsg += text.getReadableError(oErr) + "\n"; - } - if (sMsg == "") { - sMsg = "Aucune erreur détectée."; - } - postMessage(createResponse("textToTest", sMsg, oInfo, true)); -} - -function fullTests (oInfo={}) { - if (!gc_engine) { - postMessage(createResponse("fullTests", "# Grammar checker not loaded.", oInfo, true)); - return; - } - let dMemoOptions = gc_engine.getOptions(); - let dTestOptions = gc_engine.getDefaultOptions(); - dTestOptions.set("nbsp", true); - dTestOptions.set("esp", true); - dTestOptions.set("unit", true); - dTestOptions.set("num", true); - gc_engine.setOptions(dTestOptions); - let sMsg = ""; - for (let sRes of oTest.testParse()) { - sMsg += sRes + "\n"; - console.log(sRes); - } - gc_engine.setOptions(dMemoOptions); - postMessage(createResponse("fullTests", sMsg, oInfo, true)); -} - - -// SpellChecker - -function setDictionary (sDictionary, oDict, oInfo) { - if (!oSpellChecker) { - postMessage(createResponse("setDictionary", "# Error. SpellChecker not loaded.", oInfo, true)); - return; - } - //console.log("setDictionary", sDictionary); - switch (sDictionary) { - case "main": - oSpellChecker.setMainDictionary(oDict, oInfo["sExtPath"]+"/grammalecte/graphspell/_dictionaries"); - break; - case "community": - oSpellChecker.setCommunityDictionary(oDict); - break; - case "personal": - oSpellChecker.setPersonalDictionary(oDict); - break; - default: - console.log("[worker] setDictionary: Unknown dictionary <"+sDictionary+">"); - } - postMessage(createResponse("setDictionary", true, oInfo, true)); -} - -function setDictionaryOnOff (sDictionary, bActivate, oInfo) { - if (!oSpellChecker) { - postMessage(createResponse("setDictionary", "# Error. SpellChecker not loaded.", oInfo, true)); - return; - } - //console.log("setDictionaryOnOff", sDictionary, bActivate); - switch (sDictionary) { - case "community": - if (bActivate) { - oSpellChecker.activateCommunityDictionary(); - } else { - oSpellChecker.deactivateCommunityDictionary(); - } - break; - case "personal": - if (bActivate) { - oSpellChecker.activatePersonalDictionary(); - } else { - oSpellChecker.deactivatePersonalDictionary(); - } - break; - default: - console.log("[worker] setDictionaryOnOff: Unknown dictionary <"+sDictionary+">"); - } - postMessage(createResponse("setDictionaryOnOff", true, oInfo, true)); -} - -function getSpellSuggestions (sWord, oInfo) { - if (!oSpellChecker) { - postMessage(createResponse("getSpellSuggestions", "# Error. SpellChecker not loaded.", oInfo, true)); - return; - } - let i = 0; - for (let aSugg of oSpellChecker.suggest(sWord)) { - postMessage(createResponse("getSpellSuggestions", {sWord: sWord, aSugg: aSugg, iSuggBlock: i}, oInfo, true)); - i += 1; - } -} - - -// Conjugueur - -function getVerb (sWord, bPro, bNeg, bTpsCo, bInt, bFem, oInfo) { - try { - let oVerb = null; - let oConjTable = null; - if (conj.isVerb(sWord)) { - oVerb = new Verb(sWord); - oConjTable = oVerb.createConjTable(bPro, bNeg, bTpsCo, bInt, bFem); - } - postMessage(createResponse("getVerb", { oVerb: oVerb, oConjTable: oConjTable }, oInfo, true)); - } - catch (e) { - console.error(e); - postMessage(createResponse("getVerb", createErrorResult(e, "no verb"), oInfo, true, true)); - } -} Index: gc_lang/fr/mailext/manifest.json ================================================================== --- gc_lang/fr/mailext/manifest.json +++ gc_lang/fr/mailext/manifest.json @@ -23,26 +23,45 @@ "80": "img/logo-80.png", "96": "img/logo-96.png" }, "browser_action": { "default_icon": "img/logo-32.png", - "default_popup": "panel/main.html", "default_title": "Grammalecte [fr]", + "default_popup": "panel/main.html", "browser_style": false }, "compose_action": { - "default_icon": "img/logo-32.png", "default_area": "maintoolbar", - "default_popup": "panel/main.html", + "default_icon": "img/logo-32.png", "default_title": "Analyser", "browser_style": false }, "background": { "scripts": [ "grammalecte/graphspell/helpers.js", "background.js" ] - } + }, + + "commands": { + "grammar_checker": { + "suggested_key": { "default": "Ctrl+Shift+F" }, + "description": "Ouvre le correcteur grammatical" + }, + "conjugueur_tab": { + "suggested_key": { "default": "Ctrl+Shift+6" }, + "description": "Ouvre le conjugueur" + }, + "lexicon_editor": { + "suggested_key": { "default": "Ctrl+Shift+7" }, + "description": "Ouvre l’éditeur lexical" + } + }, + "permissions": [ + "compose", + "downloads", + "storage" + ] } 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 @@ -20,15 +20,18 @@ } // Chrome don’t follow the W3C specification: // https://browserext.github.io/browserext/ let bChrome = false; +let bThunderbird = false; if (typeof(browser) !== "object") { var browser = chrome; bChrome = true; } - +if (typeof(messenger) === "object") { + bThunderbird = true; +} /* function loadImage (sContainerClass, sImagePath) { let xRequest = new XMLHttpRequest(); xRequest.open('GET', browser.extension.getURL("")+sImagePath, false); @@ -284,11 +287,11 @@ bConnected: false, xConnect: browser.runtime.connect({name: "content-script port"}), start: function () { - //console.log("[Grammalecte] background port: start."); + console.log("[Grammalecte] background port: start."); this.listen(); this.listen2(); //this.ping(); }, @@ -499,75 +502,95 @@ oGrammalecteBackgroundPort.start(); +/* + ComposeAction + (Thunderbird only) +*/ +if (bThunderbird) { + console.log("Listen..."); + try { + browser.composeAction.onClicked.addListener(function (oTab, oData) { + console.log("START"); + oGrammalecte.startGCPanel("J'en aie mare..."); + }); + } + catch (e) { + showError(e) + } + console.log("Done."); +} + /* Callable API for the webpage. - -*/ -document.addEventListener("GrammalecteCall", function (xEvent) { - // GrammalecteCall events are dispatched by functions in the API script - // The script is loaded below. - try { - let oCommand = JSON.parse(xEvent.detail); - switch (oCommand.sCommand) { - case "openPanelForNode": - if (oCommand.sNodeId && document.getElementById(oCommand.sNodeId)) { - oGrammalecte.startGCPanel(document.getElementById(oCommand.sNodeId)); - } - break; - case "openPanelForText": - if (oCommand.sText) { - if (oCommand.sText && oCommand.sNodeId && document.getElementById(oCommand.sNodeId)) { - oGrammalecte.startGCPanel(oCommand.sText, document.getElementById(oCommand.sNodeId)); - } - else { - oGrammalecte.startGCPanel(oCommand.sText); - } - } - break; - case "parseNode": - if (oCommand.sNodeId && document.getElementById(oCommand.sNodeId)) { - let xNode = document.getElementById(oCommand.sNodeId); - if (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT") { - oGrammalecteBackgroundPort.parseAndSpellcheck(xNode.value, oCommand.sNodeId); - } - else if (xNode.tagName == "IFRAME") { - oGrammalecteBackgroundPort.parseAndSpellcheck(xNode.contentWindow.document.body.innerText, oCommand.sNodeId); - } - else { - oGrammalecteBackgroundPort.parseAndSpellcheck(xNode.innerText, oCommand.sNodeId); - } - } - break; - case "parseText": - if (oCommand.sText && oCommand.sNodeId) { - oGrammalecteBackgroundPort.parseAndSpellcheck(oCommand.sText, oCommand.sNodeId); - } - break; - case "getSpellSuggestions": - if (oCommand.sWord && oCommand.sDestination) { - oGrammalecteBackgroundPort.getSpellSuggestions(oCommand.sWord, oCommand.sDestination, oCommand.sErrorId); - } - break; - default: - console.log("[Grammalecte] Event: Unknown command", oCommand.sCommand); - } - } - catch (e) { - showError(e); - } -}); - -// The API script must be injected this way to be callable by the page -let xScriptGrammalecteAPI = document.createElement("script"); -xScriptGrammalecteAPI.src = browser.extension.getURL("content_scripts/api.js"); -document.documentElement.appendChild(xScriptGrammalecteAPI); + (Not for Thunderbird) +*/ +if (!bThunderbird) { + document.addEventListener("GrammalecteCall", function (xEvent) { + // GrammalecteCall events are dispatched by functions in the API script + // The script is loaded below. + try { + let oCommand = JSON.parse(xEvent.detail); + switch (oCommand.sCommand) { + case "openPanelForNode": + if (oCommand.sNodeId && document.getElementById(oCommand.sNodeId)) { + oGrammalecte.startGCPanel(document.getElementById(oCommand.sNodeId)); + } + break; + case "openPanelForText": + if (oCommand.sText) { + if (oCommand.sText && oCommand.sNodeId && document.getElementById(oCommand.sNodeId)) { + oGrammalecte.startGCPanel(oCommand.sText, document.getElementById(oCommand.sNodeId)); + } + else { + oGrammalecte.startGCPanel(oCommand.sText); + } + } + break; + case "parseNode": + if (oCommand.sNodeId && document.getElementById(oCommand.sNodeId)) { + let xNode = document.getElementById(oCommand.sNodeId); + if (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT") { + oGrammalecteBackgroundPort.parseAndSpellcheck(xNode.value, oCommand.sNodeId); + } + else if (xNode.tagName == "IFRAME") { + oGrammalecteBackgroundPort.parseAndSpellcheck(xNode.contentWindow.document.body.innerText, oCommand.sNodeId); + } + else { + oGrammalecteBackgroundPort.parseAndSpellcheck(xNode.innerText, oCommand.sNodeId); + } + } + break; + case "parseText": + if (oCommand.sText && oCommand.sNodeId) { + oGrammalecteBackgroundPort.parseAndSpellcheck(oCommand.sText, oCommand.sNodeId); + } + break; + case "getSpellSuggestions": + if (oCommand.sWord && oCommand.sDestination) { + oGrammalecteBackgroundPort.getSpellSuggestions(oCommand.sWord, oCommand.sDestination, oCommand.sErrorId); + } + break; + default: + console.log("[Grammalecte] Event: Unknown command", oCommand.sCommand); + } + } + catch (e) { + showError(e); + } + }); + + // The API script must be injected this way to be callable by the page + let xScriptGrammalecteAPI = document.createElement("script"); + xScriptGrammalecteAPI.src = browser.extension.getURL("content_scripts/api.js"); + document.documentElement.appendChild(xScriptGrammalecteAPI); +} /* Note: Initialization starts when the background is connected. See: oGrammalecteBackgroundPort.listen() -> case "init" */ Index: helpers.py ================================================================== --- helpers.py +++ helpers.py @@ -117,10 +117,17 @@ def copyAndFileTemplate (spfSrc, spfDst, dVars): "write file as with variables filed with " sText = Template(open(spfSrc, "r", encoding="utf-8").read()).safe_substitute(dVars) open(spfDst, "w", encoding="utf-8", newline="\n").write(sText) + +def addFileToZipAndFileFile (hZip, spfSrc, spfDst, dVars): + if spfSrc.endswith((".py", ".js", ".json", ".html", ".htm", ".css", ".xcu", ".xul", ".rdf", ".dtd", ".properties")): + hZip.writestr(spfDst, fileFile(spfSrc, dVars)) + else: + hZip.write(spfSrc, spfDst) + def addFolderToZipAndFileFile (hZip, spSrc, spDst, dVars, bRecursive): "add folder content to zip archive and file files with " # recursive function spSrc = spSrc.strip("/ ") @@ -130,9 +137,6 @@ spfDst = (spDst + "/" + sf).strip("/ ") if os.path.isdir(spfSrc): if bRecursive: addFolderToZipAndFileFile(hZip, spfSrc, spfDst, dVars, bRecursive) else: - if spfSrc.endswith((".py", ".js", ".json", ".html", ".htm", ".css", ".xcu", ".xul", ".rdf", ".dtd", ".properties")): - hZip.writestr(spfDst, fileFile(spfSrc, dVars)) - else: - hZip.write(spfSrc, spfDst) + addFileToZipAndFileFile(hZip, spfSrc, spfDst, dVars)