Index: gc_lang/fr/webext/background.js ================================================================== --- gc_lang/fr/webext/background.js +++ gc_lang/fr/webext/background.js @@ -37,12 +37,15 @@ console.log(e.data[1]); break; case "tokens": console.log("TOKENS"); console.log(e.data[1]); - browser.browserAction.setPopup({popup: "panel/main.html"}); - browser.runtime.sendMessage({sCommand: "show_tokens", oResult: e.data[1]}); + let xLxgTab = browser.tabs.create({ + url: browser.extension.getURL("panel/lexicographer.html"), + }); + xLxgTab.then(onCreated, onError); + break; break; case "error": console.log("ERROR"); console.log(e.data[1]); break; @@ -153,14 +156,99 @@ url: browser.extension.getURL("panel/conjugueur.html"), pinned: true }); xConjTab.then(onCreated, onError); break; - } - - + } }); +async function newwin () { + console.log("Async on"); + const getActive = browser.tabs.query({ currentWindow: true, active: true, }); + const xWindowInfo = await browser.windows.getLastFocused(); + // the pop-up will not resize itself as the panel would, so the dimensions can be passed as query params 'w' and 'h' + const width = 710, height = 980; // the maximum size for panels is somewhere around 700x800. Firefox needs some additional pixels: 14x42 for FF54 on Win 10 with dpi 1.25 + const left = Math.round(xWindowInfo.left + xWindowInfo.width - width - 25); + const top = Math.round(xWindowInfo.top + 74); // the actual frame height of the main window varies, but 74px should place the pop-up at the bottom if the button + const xWin = await browser.windows.create({ + type: 'panel', url: browser.extension.getURL("panel/conjugueur.html"), top: top, left: left, width: width, height: height, + }); + browser.windows.update(xWin.id, { top:top, left:left, }); // firefox currently ignores top and left in .create(), so move it here + console.log("Async done"); +} + +//newwin(); + + +/* + Worker (separate thread to avoid freezing Firefox) +*/ +/* +let xGCESharedWorker = new SharedWorker("gce_sharedworker.js"); + +xGCESharedWorker.port.onmessage = function (e) { + // https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent + try { + switch (e.data[0]) { + case "grammar_errors": + console.log("GRAMMAR ERRORS"); + console.log(e.data[1].aGrammErr); + //browser.runtime.sendMessage({sCommand: "grammar_errors", aGrammErr: e.data[1].aGrammErr}); + break; + case "spelling_and_grammar_errors": + console.log("SPELLING AND GRAMMAR ERRORS"); + console.log(e.data[1].aSpellErr); + console.log(e.data[1].aGrammErr); + break; + case "text_to_test_result": + console.log("TESTS RESULTS"); + console.log(e.data[1]); + break; + case "fulltests_result": + console.log("TESTS RESULTS"); + console.log(e.data[1]); + break; + case "options": + console.log("OPTIONS"); + console.log(e.data[1]); + break; + case "tokens": + console.log("TOKENS"); + console.log(e.data[1]); + let xLxgTab = browser.tabs.create({ + url: browser.extension.getURL("panel/lexicographer.html"), + }); + xLxgTab.then(onCreated, onError); + break; + case "error": + console.log("ERROR"); + console.log(e.data[1]); + break; + default: + console.log("Unknown command: " + e.data[0]); + } + } + catch (e) { + showError(e); + } +}; + +console.log("Content script [worker]"); +console.log(xGCESharedWorker); + + +//xGCESharedWorker.port.start(); +//console.log("Content script [port started]"); + +xGCESharedWorker.port.postMessage(["init", {sExtensionPath: browser.extension.getURL("."), sOptions: "", sContext: "Firefox"}]); + +console.log("Content script [worker initialzed]"); - +xGCESharedWorker.port.postMessage(["parse", {sText: "Vas... J’en aie mare...", sCountry: "FR", bDebug: false, bContext: false}]); +//xGCESharedWorker.port.postMessage(["parseAndSpellcheck", {sText: oRequest.sText, sCountry: "FR", bDebug: false, bContext: false}]); +//xGCESharedWorker.port.postMessage(["getListOfTokens", {sText: oRequest.sText}]); +//xGCESharedWorker.port.postMessage(["textToTest", {sText: oRequest.sText, sCountry: "FR", bDebug: false, bContext: false}]); +//xGCESharedWorker.port.postMessage(["fullTests"]); + +*/ Index: gc_lang/fr/webext/content_scripts/modify_page.js ================================================================== --- gc_lang/fr/webext/content_scripts/modify_page.js +++ gc_lang/fr/webext/content_scripts/modify_page.js @@ -1,28 +1,139 @@ -import { echo } from "../mymodule"; - -echo("CONTENT SCRIPRT!!!"); - -function handleMessage2 (oRequest, xSender, sendResponse) { - console.log(`[Content script] received: ${oRequest.content}`); - change(request.myparam); - //browser.runtime.onMessage.removeListener(handleMessage); - sendResponse({response: "response from content script"}); +// Modify page + +"use strict"; + +console.log("Content script [start]"); + +function showError (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); +} + +/* + Worker (separate thread to avoid freezing Firefox) +*/ +let xGCEWorker = new SharedWorker("../gce_sharedworker.js"); + +xGCEWorker.onerror = function(e) { + console.log('There is an error with your worker!'); + console.log(typeof(e)); + console.log(e); + for (let sParam in e) { + console.log(sParam); + console.log(e.sParam); + } +} + +xGCEWorker.port.onmessage = function (e) { + // https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent + try { + switch (e.data[0]) { + case "grammar_errors": + console.log("GRAMMAR ERRORS"); + console.log(e.data[1].aGrammErr); + //browser.runtime.sendMessage({sCommand: "grammar_errors", aGrammErr: e.data[1].aGrammErr}); + break; + case "spelling_and_grammar_errors": + console.log("SPELLING AND GRAMMAR ERRORS"); + console.log(e.data[1].aSpellErr); + console.log(e.data[1].aGrammErr); + break; + case "text_to_test_result": + console.log("TESTS RESULTS"); + console.log(e.data[1]); + break; + case "fulltests_result": + console.log("TESTS RESULTS"); + console.log(e.data[1]); + break; + case "options": + console.log("OPTIONS"); + console.log(e.data[1]); + break; + case "tokens": + console.log("TOKENS"); + console.log(e.data[1]); + let xLxgTab = browser.tabs.create({ + url: browser.extension.getURL("panel/lexicographer.html"), + }); + xLxgTab.then(onCreated, onError); + break; + case "error": + console.log("ERROR"); + console.log(e.data[1]); + break; + default: + console.log("Unknown command: " + e.data[0]); + } + } + catch (e) { + showError(e); + } +}; + +console.log("Content script [worker]"); +console.log(xGCEWorker); + + +//xGCEWorker.port.start(); +//console.log("Content script [port started]"); + +//xGCEWorker.port.postMessage(["init", {sExtensionPath: browser.extension.getURL("."), sOptions: "", sContext: "Firefox"}]); + +console.log("Content script [worker initialzed]"); + +xGCEWorker.port.postMessage(["parse", {sText: "Vas... J’en aie mare...", sCountry: "FR", bDebug: false, bContext: false}]); +//xGCEWorker.port.postMessage(["parseAndSpellcheck", {sText: oRequest.sText, sCountry: "FR", bDebug: false, bContext: false}]); +//xGCEWorker.port.postMessage(["getListOfTokens", {sText: oRequest.sText}]); +//xGCEWorker.port.postMessage(["textToTest", {sText: oRequest.sText, sCountry: "FR", bDebug: false, bContext: false}]); +//xGCEWorker.port.postMessage(["fullTests"]); + + + +function wrapTextareas() {; + let lNode = document.getElementsByTagName("textarea"); + for (let xNode of lNode) { + createGCButton(xNode); + } +} + +function createGCButton (xActiveTextZone) { + let xParentElement = xActiveTextZone.parentElement; + let xGCButton = document.createElement("div"); + xGCButton.textContent = "@"; + xGCButton.title = "Grammalecte" + xGCButton.style = "padding: 5px; color: #FFF; background-color: hsla(210, 50%, 50%, 80%); border-radius: 3px; cursor: pointer"; + xGCButton.onclick = function() { + console.log(xActiveTextZone.value); + }; + xParentElement.insertBefore(xGCButton, xActiveTextZone); } function removeEverything () { - while (document.body.firstChild) { - document.body.firstChild.remove(); - } + while (document.body.firstChild) { + document.body.firstChild.remove(); + } } function change (param) { - document.getElementById("title").setAttribute("background-color", "#809060"); - console.log("param: " + param); - document.getElementById("title").setAttribute("background-color", "#FF0000"); + document.getElementById("title").setAttribute("background-color", "#809060"); + console.log("param: " + param); + document.getElementById("title").setAttribute("background-color", "#FF0000"); } /* - Assign do_something() as a listener for messages from the extension. + Assign do_something() as a listener for messages from the extension. */ + + +function handleMessage2 (oRequest, xSender, sendResponse) { + console.log(`[Content script] received: ${oRequest.content}`); + change(request.myparam); + //browser.runtime.onMessage.removeListener(handleMessage); + sendResponse({response: "response from content script"}); +} + browser.runtime.onMessage.addListener(handleMessage2); + + +wrapTextareas(); ADDED 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 @@ -0,0 +1,240 @@ +/* + 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; + +onconnect = function(e) { + console.log("START CONNECTION"); + xPort = e.ports[0]; + + xPort.onmessage = function (e) { + console.log(e); + console.log(e.data[0]); + let oParam = e.data[1]; + switch (e.data[0]) { + case "init": + loadGrammarChecker(oParam.sExtensionPath, oParam.sOptions, oParam.sContext); + break; + case "parse": + parse(oParam.sText, oParam.sCountry, oParam.bDebug, oParam.bContext); + break; + case "parseAndSpellcheck": + parseAndSpellcheck(oParam.sText, oParam.sCountry, oParam.bDebug, oParam.bContext); + break; + case "getOptions": + getOptions(); + break; + case "getDefaultOptions": + getDefaultOptions(); + break; + case "setOptions": + setOptions(oParam.sOptions); + break; + case "setOption": + setOption(oParam.sOptName, oParam.bValue); + break; + case "resetOptions": + resetOptions(); + break; + case "textToTest": + textToTest(oParam.sText, oParam.sCountry, oParam.bDebug, oParam.bContext); + break; + case "fullTests": + fullTests(); + break; + case "getListOfTokens": + getListOfTokens(oParam.sText); + break; + default: + console.log("Unknown command: " + e.data[0]); + } + } + //xPort.start(); +} + + +let oDict = null; +let oTokenizer = null; +let oLxg = null; +let oTest = null; + + +function loadGrammarChecker (sExtensionPath, sGCOptions="", sContext="JavaScript") { + try { + console.log("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("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(); + // we always retrieve options from the gc_engine, for setOptions filters obsolete options + xPort.postMessage(["options", gc_engine.getOptions().gl_toString()]); + } + catch (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); + xPort.postMessage(["error", e.message]); + } +} + +function parse (sText, sCountry, bDebug, bContext) { + let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); + xPort.postMessage(["grammar_errors", {aGrammErr: aGrammErr}]); +} + +function parseAndSpellcheck (sText, sCountry, bDebug, bContext) { + let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); + let aSpellErr = oTokenizer.getSpellingErrors(sText, oDict); + xPort.postMessage(["spelling_and_grammar_errors", {aGrammErr: aGrammErr, aSpellErr: aSpellErr}]); +} + +function getOptions () { + xPort.postMessage(["options", gc_engine.getOptions().gl_toString()]); +} + +function getDefaultOptions () { + xPort.postMessage(["options", gc_engine.getDefaultOptions().gl_toString()]); +} + +function setOptions (sGCOptions) { + gc_engine.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); + xPort.postMessage(["options", gc_engine.getOptions().gl_toString()]); +} + +function setOption (sOptName, bValue) { + gc_engine.setOptions(new Map([ [sOptName, bValue] ])); + xPort.postMessage(["options", gc_engine.getOptions().gl_toString()]); +} + +function resetOptions () { + gc_engine.resetOptions(); + xPort.postMessage(["options", gc_engine.getOptions().gl_toString()]); +} + +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) { + if (!gc_engine || !oDict) { + xPort.postMessage(["error", "# Error: grammar checker or dictionary not loaded."]); + return; + } + let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); + let sMsg = ""; + for (let oErr of aGrammErr) { + sMsg += text.getReadableError(oErr) + "\n"; + } + xPort.postMessage(["text_to_test_result", sMsg]); +} + +function fullTests (sGCOptions='{"nbsp":true, "esp":true, "unit":true, "num":true}') { + if (!gc_engine || !oDict) { + xPort.postMessage(["error", "# Error: grammar checker or dictionary not loaded."]); + 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(["fulltests_result", sMsg]); +} + + +// Lexicographer + +function getListOfTokens (sText) { + try { + let aElem = []; + let aRes = null; + for (let oToken of oTokenizer.genTokens(sText)) { + aRes = oLxg.getInfoForToken(oToken); + if (aRes) { + aElem.push(aRes); + } + } + xPort.postMessage(["tokens", aElem]); + } + catch (e) { + helpers.logerror(e); + xPort.postMessage(["error", e.message]); + } +} Index: gc_lang/fr/webext/manifest.json ================================================================== --- gc_lang/fr/webext/manifest.json +++ gc_lang/fr/webext/manifest.json @@ -31,10 +31,16 @@ "background": { "scripts": [ "background.js" ] }, + "content_scripts": [ + { + "matches": ["*://*/*"], + "js": ["content_scripts/modify_page.js"] + } + ], "web_accessible_resources": [ "grammalecte/_dictionaries/French.json", "grammalecte/fr/conj_data.json", "grammalecte/fr/mfsp_data.json", "grammalecte/fr/phonet_data.json", ADDED gc_lang/fr/webext/panel/lexicographer.css Index: gc_lang/fr/webext/panel/lexicographer.css ================================================================== --- gc_lang/fr/webext/panel/lexicographer.css +++ gc_lang/fr/webext/panel/lexicographer.css @@ -0,0 +1,95 @@ +/* lxgpanel.css */ + +@import url("common.css"); + +header { + background-color: hsl(0, 0%, 96%); + padding: 10px 20px; + border-bottom: 1px solid hsl(0, 0%, 90%); + color: hsl(0, 0%, 0%); +} + + +body { + background-color: hsl(0, 0%, 98%); + font-family: "Trebuchet MS", sans-serif; + overflow-x: hidden; + color: hsl(0, 0%, 0%); +} + + + +#tokens_list { + padding: 50px 10px 10px 10px; +} + +#tokens_list .paragraph { + background-color: hsla(0, 0%, 90%, 1); + padding: 10px; + border-radius: 2px; + margin: 10px 5px; +} + +#tokens_list p { + margin: 8px; +} +#tokens_list p.separator { + margin: 20px 0; + padding: 5px 50px; + background-color: hsla(0, 0%, 75%, 1); + color: hsla(0, 0%, 96%, 1); + border-radius: 5px; + text-align: center; + font-size: 20px; +} +#tokens_list .token { + margin: 8px; +} +#tokens_list ul { + margin: 0 0 5px 40px; +} +#tokens_list b { + background-color: hsla(150, 10%, 50%, 1); + color: hsla(0, 0%, 96%, 1); + padding: 2px 5px; + border-radius: 2px; + text-decoration: none; +} +#tokens_list b.WORD { + background-color: hsla(150, 50%, 50%, 1); +} +#tokens_list b.ELPFX { + background-color: hsla(150, 30%, 50%, 1); +} +#tokens_list b.UNKNOWN { + background-color: hsla(0, 50%, 50%, 1); +} +#tokens_list b.NUM { + background-color: hsla(180, 50%, 50%, 1); +} +#tokens_list b.COMPLEX { + background-color: hsla(60, 50%, 50%, 1); +} +#tokens_list b.SEPARATOR { + background-color: hsla(210, 50%, 50%, 1); +} +#tokens_list b.LINK { + background-color: hsla(270, 50%, 50%, 1); +} +#tokens_list s { + color: hsla(0, 0%, 60%, 1); + text-decoration: none; +} +#tokens_list .textline { + text-decoration: bold; +} + +#tokens_list p.message { + margin-top: 20px; + padding: 10px 10px; + background-color: hsla(240, 10%, 50%, 1); + font-size: 18px; + color: hsla(240, 0%, 96%, 1); + border-radius: 3px; + text-align: center; +} ADDED gc_lang/fr/webext/panel/lexicographer.html Index: gc_lang/fr/webext/panel/lexicographer.html ================================================================== --- gc_lang/fr/webext/panel/lexicographer.html +++ gc_lang/fr/webext/panel/lexicographer.html @@ -0,0 +1,22 @@ + + + + + + + + +
+
+
+
+
+
+

Grammalecte · Lexicographe

+
+
+ +
+ + + ADDED gc_lang/fr/webext/panel/lexicographer.js Index: gc_lang/fr/webext/panel/lexicographer.js ================================================================== --- gc_lang/fr/webext/panel/lexicographer.js +++ gc_lang/fr/webext/panel/lexicographer.js @@ -0,0 +1,75 @@ +// JavaScript + + + +/* + Actions +*/ + +function startWaitIcon () { + document.getElementById("waiticon").hidden = false; +} + +function stopWaitIcon () { + document.getElementById("waiticon").hidden = true; +} + +function clearList () { + document.getElementById("tokens_list").textContent = ""; +} + +function addSeparator (sText) { + if (document.getElementById("tokens_list").textContent !== "") { + let xElem = document.createElement("p"); + xElem.className = "separator"; + xElem.textContent = sText; + document.getElementById("tokens_list").appendChild(xElem); + } +} + +function addMessage (sClass, sText) { + let xNode = document.createElement("p"); + xNode.className = sClass; + xNode.textContent = sText; + document.getElementById("tokens_list").appendChild(xNode); +} + +function addParagraphElems (sJSON) { + try { + let xNodeDiv = document.createElement("div"); + xNodeDiv.className = "paragraph"; + let lElem = JSON.parse(sJSON); + for (let oToken of lElem) { + xNodeDiv.appendChild(createTokenNode(oToken)); + } + document.getElementById("tokens_list").appendChild(xNodeDiv); + } + catch (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); + console.error(sJSON); + } +} + +function createTokenNode (oToken) { + let xTokenNode = document.createElement("div"); + xTokenNode.className = "token " + oToken.sType; + let xTokenValue = document.createElement("b"); + xTokenValue.className = oToken.sType; + xTokenValue.textContent = oToken.sValue; + xTokenNode.appendChild(xTokenValue); + let xSep = document.createElement("s"); + xSep.textContent = " : "; + xTokenNode.appendChild(xSep); + if (oToken.aLabel.length === 1) { + xTokenNode.appendChild(document.createTextNode(oToken.aLabel[0])); + } else { + let xTokenList = document.createElement("ul"); + for (let sLabel of oToken.aLabel) { + let xTokenLine = document.createElement("li"); + xTokenLine.textContent = sLabel; + xTokenList.appendChild(xTokenLine); + } + xTokenNode.appendChild(xTokenList); + } + return xTokenNode; +}