Index: gc_core/js/lang_core/gc_engine.js ================================================================== --- gc_core/js/lang_core/gc_engine.js +++ gc_core/js/lang_core/gc_engine.js @@ -42,10 +42,15 @@ let _dOptionsColors = null; let _oSpellChecker = null; let _oTokenizer = null; let _aIgnoredRules = new Set(); + +function echo (x) { + console.log(x); + return true; +} var gc_engine = { //// Informations Index: gc_lang/fr/config.ini ================================================================== --- gc_lang/fr/config.ini +++ gc_lang/fr/config.ini @@ -15,26 +15,30 @@ logo = logo.png # main dictionary lexicon_src = lexicons/French.lex dic_filenames = fr-allvars,fr-classic,fr-reform -dic_name = Français,Français (Classique/Moderne),Français (Réforme 1990) +dic_name = fr-allvars,fr-classic,fr-reform +dic_description = Français (Toutes variantes),Français (Classique),Français (Réforme 1990) dic_filter = ,[*CMPX]$,[*RPX]$ dic_default_filename_py = fr-allvars dic_default_filename_js = fr-allvars # extended dictionary lexicon_extended_src = lexicons/French.extended.lex dic_extended_filename = fr.extended -dic_extended_name = Français - dictionnaire étendu +dic_extended_name = fr.extended +dic_extended_description = Français - dictionnaire étendu # community dictionary lexicon_community_src = lexicons/French.community.lex dic_community_filename = fr.community -dic_community_name = Français - dictionnaire communautaire +dic_community_name = fr.community +dic_community_description = Français - dictionnaire communautaire # personal dictionary lexicon_personal_src = lexicons/French.personal.lex dic_personal_filename = fr.personal -dic_personal_name = Français - dictionnaire personnel +dic_personal_name = fr.personal +dic_personal_description = Français - dictionnaire personnel # Finite state automaton compression: 1, 2 (experimental) or 3 (experimental) fsa_method = 1 # stemming method: S for suffixes only, A for prefixes and suffixes stemming_method = S Index: gc_lang/fr/dictionnaire/genfrdic.py ================================================================== --- gc_lang/fr/dictionnaire/genfrdic.py +++ gc_lang/fr/dictionnaire/genfrdic.py @@ -524,15 +524,10 @@ def writeGrammarCheckerLexicon (self, spfDst, version): echo(' * Lexique simplifié >> [ {} ] '.format(spfDst)) with open(spfDst[:-4]+".lex", 'w', encoding='utf-8', newline="\n") as hDst: hDst.write(MPLHEADER) hDst.write("# Lexique simplifié pour Grammalecte v{}\n# Licence : MPL v2.0\n\n".format(version)) - hDst.write("## LangCode: fr\n") - hDst.write("## LangName: Français\n") - hDst.write("## DicName: fr.commun\n") - hDst.write("## Description: Français commun (toutes variantes)\n") - hDst.write("## Author: Olivier R.\n\n") hDst.write(Flexion.simpleHeader()) for oFlex in self.lFlexions: hDst.write(oFlex.getGrammarCheckerRepr()) def createFiles (self, spDst, lDictVars, nMode, bSimplified): Index: gc_lang/fr/modules-js/lexicographe.js ================================================================== --- gc_lang/fr/modules-js/lexicographe.js +++ gc_lang/fr/modules-js/lexicographe.js @@ -459,36 +459,40 @@ } } } getListOfTokensReduc (sText, bInfo=true) { - let aTokenList = this.getListOfTokens(sText.replace("'", "’").trim(), false); - let iKey = 0; + let lToken = this.getListOfTokens(sText.replace("'", "’").trim(), false); + let iToken = 0; let aElem = []; + if (lToken.length == 0) { + return aElem; + } do { - let oToken = aTokenList[iKey]; + let oToken = lToken[iToken]; let sMorphLoc = ''; let aTokenTempList = [oToken]; if (oToken.sType == "WORD" || oToken.sType == "WORD_ELIDED"){ - let iKeyTree = iKey + 1; + let iLocEnd = iToken + 1; let oLocNode = this.oLocGraph[oToken.sValue.toLowerCase()]; while (oLocNode) { - let oTokenNext = aTokenList[iKeyTree]; - iKeyTree++; + let oTokenNext = lToken[iLocEnd]; + iLocEnd++; if (oTokenNext) { oLocNode = oLocNode[oTokenNext.sValue.toLowerCase()]; } - if (oLocNode && iKeyTree <= aTokenList.length) { + if (oLocNode && iLocEnd <= lToken.length) { sMorphLoc = oLocNode["_:_"]; aTokenTempList.push(oTokenNext); } else { break; } } } if (sMorphLoc) { + // we have a locution let sValue = ''; for (let oTokenWord of aTokenTempList) { sValue += oTokenWord.sValue+' '; } let oTokenLocution = { @@ -522,26 +526,27 @@ aSubElem: aSubElem }); } else { aElem.push(oTokenLocution); } - iKey = iKey + aTokenTempList.length; + iToken = iToken + aTokenTempList.length; } else { + // No locution, we just add information if (bInfo) { let aRes = this.getInfoForToken(oToken); if (aRes) { aElem.push(aRes); } } else { aElem.push(oToken); } - iKey++; + iToken++; } - } while (iKey < aTokenList.length); + } while (iToken < lToken.length); return aElem; } } if (typeof(exports) !== 'undefined') { exports.Lexicographe = Lexicographe; } Index: gc_lang/fr/oxt/DictOptions/LexiconEditor.py ================================================================== --- gc_lang/fr/oxt/DictOptions/LexiconEditor.py +++ gc_lang/fr/oxt/DictOptions/LexiconEditor.py @@ -409,11 +409,11 @@ xGridDataModel = self.xGridModelLex.GridDataModel lEntry = [] for i in range(xGridDataModel.RowCount): lEntry.append(xGridDataModel.getRowData(i)) if lEntry: - oDAWG = dawg.DAWG(lEntry, "S", "fr", "Français", "fr.personal") + oDAWG = dawg.DAWG(lEntry, "S", "fr", "Français", "fr.personal", "Dictionnaire personnel") self.oPersonalDicJSON = oDAWG.getBinaryAsJSON() self.xOptionNode.setPropertyValue("personal_dic", json.dumps(self.oPersonalDicJSON, ensure_ascii=False)) self.xSettingNode.commitChanges() self.xNumDic.Label = str(self.oPersonalDicJSON["nEntry"]) self.xDateDic.Label = self.oPersonalDicJSON["sDate"] Index: gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-classique.aff ================================================================== --- gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-classique.aff +++ gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-classique.aff @@ -2,11 +2,11 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # AFFIXES DU DICTIONNAIRE ORTHOGRAPHIQUE FRANÇAIS “CLASSIQUE” v7.0 # par Olivier R. -- licence MPL 2.0 -# Généré le 09-01-2019 à 18:08 +# Généré le 11-01-2019 à 17:14 # Pour améliorer le dictionnaire, allez sur https://grammalecte.net/ SET UTF-8 Index: gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-moderne.aff ================================================================== --- gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-moderne.aff +++ gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-moderne.aff @@ -2,11 +2,11 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # AFFIXES DU DICTIONNAIRE ORTHOGRAPHIQUE FRANÇAIS “MODERNE” v7.0 # par Olivier R. -- licence MPL 2.0 -# Généré le 09-01-2019 à 18:08 +# Généré le 11-01-2019 à 17:14 # Pour améliorer le dictionnaire, allez sur https://grammalecte.net/ SET UTF-8 Index: gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-reforme1990.aff ================================================================== --- gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-reforme1990.aff +++ gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-reforme1990.aff @@ -2,11 +2,11 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # AFFIXES DU DICTIONNAIRE ORTHOGRAPHIQUE FRANÇAIS “RÉFORME 1990” v7.0 # par Olivier R. -- licence MPL 2.0 -# Généré le 09-01-2019 à 18:08 +# Généré le 11-01-2019 à 17:14 # Pour améliorer le dictionnaire, allez sur https://grammalecte.net/ SET UTF-8 Index: gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-toutesvariantes.aff ================================================================== --- gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-toutesvariantes.aff +++ gc_lang/fr/oxt/Dictionnaires/dictionaries/fr-toutesvariantes.aff @@ -2,11 +2,11 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # AFFIXES DU DICTIONNAIRE ORTHOGRAPHIQUE FRANÇAIS “TOUTES VARIANTES” v7.0 # par Olivier R. -- licence MPL 2.0 -# Généré le 09-01-2019 à 18:08 +# Généré le 11-01-2019 à 17:14 # Pour améliorer le dictionnaire, allez sur https://grammalecte.net/ SET UTF-8 Index: gc_lang/fr/rules.grx ================================================================== --- gc_lang/fr/rules.grx +++ gc_lang/fr/rules.grx @@ -505,11 +505,11 @@ __[s>(p_Mr_Mgr_Mme_point)__ M(?:r|gr|me) [A-ZÉ]([.])(?=\W+[a-zéèêâîïû]) @@$ <<- ~1>> * # Patronyme réduit à une seule lettre __[s](p_prénom_lettre_point_patronyme)__ - ([A-ZÉÈÂÎ][\w-]+)[  ][A-ZÉÈÂ]([.])[  ]([A-ZÉÈÂ][\w-]+) @@0,$,$ + ([A-ZÉÈÂÎ][\w-]+)[  ][A-ZÉÈÂ]([.])[  ]([A-ZÉÈÂ][\w-]+) @@0,*,$ <<- morph(\1, ":M[12]") and (morph(\3, ":(?:M[12]|V)") or not spell(\3)) ~2>> * __[s>(p_prénom_lettre_point)__ ([A-ZÉÈÂÎ][\w-]+)[  ][A-ZÉÈÂ]([.]) @@0,$ <<- morph(\1, ":M[12]") and after("^\\W+[a-zéèêîïâ]") ~2>> _ @@ -741,11 +741,11 @@ !! !! __[i>/poncfin(poncfin_règle1)__ ({w_1}) *$ @@0 - <<- before("\\w+(?:[.]|[   ][!?]) +(?:[A-ZÉÈÎ]\\w+|[ÀÔ])") -1>> \1.|\1 !|\1 ? # Il semble manquer une ponctuation finale (s’il s’agit d’un titre, le point final n’est pas requis). + <<- before("\\w+(?:\\.|[   ][!?]) +(?:[A-ZÉÈÎ]\\w+|[ÀÔ])") -1>> \1.|\1 !|\1 ? # Il semble manquer une ponctuation finale (s’il s’agit d’un titre, le point final n’est pas requis). TEST: __poncfin__ Vraiment. Quel {{ennui}} TEST: Internet : le nouvel eldorado TEST: OMC-FMI : Les nouveaux maîtres du monde Index: gc_lang/fr/tb/content/lex_editor.js ================================================================== --- gc_lang/fr/tb/content/lex_editor.js +++ gc_lang/fr/tb/content/lex_editor.js @@ -476,11 +476,11 @@ build: function () { let xProgressNode = document.getElementById("wait_progress"); let lEntry = oLexiconTable.getEntries(); if (lEntry.length > 0) { - let oDAWG = new DAWG(lEntry, "S", "fr", "Français", "fr.personal", xProgressNode); + let oDAWG = new DAWG(lEntry, "S", "fr", "Français", "fr.personal", "Dictionnaire personnel", xProgressNode); let oJSON = oDAWG.createBinaryJSON(1); oFileHandler.saveFile("fr.personal.json", JSON.stringify(oJSON)); this.oIBDAWG = new IBDAWG(oJSON); this.setDictData(this.oIBDAWG.nEntry, this.oIBDAWG.sDate); //browser.runtime.sendMessage({ sCommand: "setDictionary", dParam: {sType: "personal", oDict: oJSON}, dInfo: {} }); Index: gc_lang/fr/webext/background.js ================================================================== --- gc_lang/fr/webext/background.js +++ gc_lang/fr/webext/background.js @@ -117,25 +117,22 @@ dParam: { sDictionary: sDictionary, bActivate: bActivate }, dInfo: {} }); } -function initSCOptions (dSavedOptions) { - if (!dSavedOptions.hasOwnProperty("sc_options")) { +function initSCOptions (oData) { + if (!oData.hasOwnProperty("sc_options")) { browser.storage.local.set({"sc_options": { extended: true, community: true, personal: true }}); - setDictionaryOnOff("extended", true); setDictionaryOnOff("community", true); setDictionaryOnOff("personal", true); } else { - let dOptions = dSavedOptions.sc_options; - setDictionaryOnOff("extended", dOptions["extended"]); - setDictionaryOnOff("community", dOptions["community"]); - setDictionaryOnOff("personal", dOptions["personal"]); + setDictionaryOnOff("community", oData.sc_options["community"]); + setDictionaryOnOff("personal", oData.sc_options["personal"]); } } function setDictionary (sDictionary, oDictionary) { xGCEWorker.postMessage({ @@ -143,37 +140,41 @@ dParam: { sDictionary: sDictionary, oDict: oDictionary }, dInfo: {} }); } -function setSpellingDictionary (dSavedDictionary) { - if (dSavedDictionary.hasOwnProperty("oExtendedDictionary")) { - setDictionary("extended", dSavedDictionary["oExtendedDictionary"]); - } - if (dSavedDictionary.hasOwnProperty("oCommunityDictionary")) { - setDictionary("community", dSavedDictionary["oCommunityDictionary"]); - } - if (dSavedDictionary.hasOwnProperty("oPersonalDictionary")) { - setDictionary("personal", dSavedDictionary["oPersonalDictionary"]); +function setSpellingDictionaries (oData) { + if (oData.hasOwnProperty("oPersonalDictionary")) { + // deprecated (to be removed in 2020) + console.log("personal dictionary migration"); + browser.storage.local.set({ "personal_dictionary": oData["oPersonalDictionary"] }); + setDictionary("personal", oData["oPersonalDictionary"]); + browser.storage.local.remove("oPersonalDictionary"); + } + if (oData.hasOwnProperty("personal_dictionary")) { + setDictionary("personal", oData["personal_dictionary"]); + } + if (oData.hasOwnProperty("community_dictionary")) { + setDictionary("community", oData["community_dictionary"]); } } function init () { if (bChrome) { browser.storage.local.get("gc_options", initGrammarChecker); browser.storage.local.get("ui_options", initUIOptions); - browser.storage.local.get("oExtendedDictionary", setSpellingDictionary); - browser.storage.local.get("oCommunityDictionary", setSpellingDictionary); - browser.storage.local.get("oPersonalDictionary", setSpellingDictionary); + browser.storage.local.get("personal_dictionary", setSpellingDictionaries); + browser.storage.local.get("community_dictionary", setSpellingDictionaries); + browser.storage.local.get("oPersonalDictionary", setSpellingDictionaries); // deprecated browser.storage.local.get("sc_options", initSCOptions); return; } browser.storage.local.get("gc_options").then(initGrammarChecker, showError); browser.storage.local.get("ui_options").then(initUIOptions, showError); - browser.storage.local.get("oExtendedDictionary").then(setSpellingDictionary, showError); - browser.storage.local.get("oCommunityDictionary").then(setSpellingDictionary, showError); - browser.storage.local.get("oPersonalDictionary").then(setSpellingDictionary, showError); + browser.storage.local.get("personal_dictionary").then(setSpellingDictionaries, showError); + browser.storage.local.get("community_dictionary").then(setSpellingDictionaries, showError); + browser.storage.local.get("oPersonalDictionary").then(setSpellingDictionaries, showError); // deprecated browser.storage.local.get("sc_options").then(initSCOptions, showError); } init(); @@ -215,10 +216,19 @@ xGCEWorker.postMessage(oRequest); break; case "openURL": browser.tabs.create({url: dParam.sURL}); break; + case "openConjugueurTab": + openConjugueurTab(); + break; + case "openLexiconEditor": + openLexiconEditor(dParam["dictionary"]); + break; + case "openDictionaries": + openDictionaries(); + break; default: console.log("[background] Unknown command: " + sCommand); console.log(oRequest); } //sendResponse({response: "response from background script"}); @@ -226,10 +236,11 @@ 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, dParam, dInfo} = oRequest; switch (sCommand) { @@ -277,13 +288,15 @@ browser.contextMenus.create({ id: "separator_editable", type: "separator", contexts: ["editable"] }); // Page browser.contextMenus.create({ id: "rightClickLxgPage", title: "Lexicographe (page)", contexts: ["all"] }); // on all parts, due to unwanted selection browser.contextMenus.create({ id: "rightClickGCPage", title: "Correction grammaticale (page)", contexts: ["all"] }); browser.contextMenus.create({ id: "separator_page", type: "separator", contexts: ["all"] }); -// Conjugueur +// Tools 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: "dictionaries", title: "Dictionnaires", contexts: ["all"] }); +browser.contextMenus.create({ id: "lexicon_editor", title: "Éditeur lexical", 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"] }); @@ -323,10 +336,16 @@ openConjugueurWindow(); break; case "conjugueur_tab": openConjugueurTab(); break; + case "lexicon_editor": + openLexiconEditor(); + break; + case "dictionaries": + openDictionaries(); + break; // rescan page case "rescanPage": let xPort = dConnx.get(xTab.id); xPort.postMessage({sActionDone: "rescanPage"}); break; @@ -341,22 +360,50 @@ /* Keyboard shortcuts */ browser.commands.onCommand.addListener(function (sCommand) { switch (sCommand) { + case "lexicographer": + sendCommandToCurrentTab("shortcutLexicographer"); + break; + case "text_formatter": + sendCommandToCurrentTab("shortcutTextFormatter"); + break; + case "grammar_checker": + sendCommandToCurrentTab("shortcutGrammarChecker"); + break; case "conjugueur_tab": openConjugueurTab(); break; case "conjugueur_window": openConjugueurWindow(); break; - case "lex_editor": - openLexEditor(); + 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 */ @@ -370,34 +417,93 @@ function sendCommandToTab (sCommand, iTab) { let xTabPort = dConnx.get(iTab); xTabPort.postMessage({sActionDone: sCommand, result: null, dInfo: null, bEnd: false, bError: false}); } -function openLexEditor () { - if (bChrome) { - browser.tabs.create({ - url: browser.extension.getURL("panel/lex_editor.html") - }); - return; - } - let xLexEditor = browser.tabs.create({ - url: browser.extension.getURL("panel/lex_editor.html") - }); - xLexEditor.then(onCreated, onError); -} - -function openConjugueurTab () { - if (bChrome) { - browser.tabs.create({ - url: browser.extension.getURL("panel/conjugueur.html") - }); - return; - } - let xConjTab = browser.tabs.create({ - url: browser.extension.getURL("panel/conjugueur.html") - }); - xConjTab.then(onCreated, onError); +function sendCommandToCurrentTab (sCommand) { + console.log(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}); + } + }, onError); +} + +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, onError); + } + 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, onError); + } + else { + browser.tabs.update(nTabDictionaries, {active: true}); + } +} + +function onDictionariesOpened (xTab) { + nTabDictionaries = xTab.id; +} + +function openConjugueurTab () { + if (nTabDictionaries === 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, onError); + } + else { + browser.tabs.update(nTabConjugueur, {active: true}); + } +} + +function onConjugueurOpened (xTab) { + nTabConjugueur = xTab.id; } function openConjugueurWindow () { if (bChrome) { browser.windows.create({ @@ -412,16 +518,11 @@ url: browser.extension.getURL("panel/conjugueur.html"), type: "popup", width: 710, height: 980 }); - xConjWindow.then(onCreated, onError); } -function onCreated (xWindowInfo) { - //console.log(`Created window: ${xWindowInfo.id}`); -} - -function onError (error) { - console.log(`Error: ${error}`); +function onError (e) { + console.error(e); } 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 @@ -101,13 +101,11 @@ } } }, observePage: function () { - /* - When a textarea is added via jascript we add the menu :) - */ + // When a textarea is added via jascript we add the menu let that = this; this.xObserver = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { for (let i = 0; i < mutation.addedNodes.length; i++){ if (mutation.addedNodes[i].tagName == "TEXTAREA") { @@ -255,11 +253,10 @@ */ 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 "init": oGrammalecte.sExtensionUrl = oMessage.sUrl; // Start oGrammalecte.listenRightClick(); @@ -291,63 +288,41 @@ (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} - }); + parseAndSpellcheckEditableNode(oGrammalecte.xRightClickedNode); } 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: {} - }); + parseAndSpellcheckPage(); 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.innerText; - xGrammalectePort.postMessage({ - sCommand: "getListOfTokens", - dParam: {sText: sText}, - dInfo: {sTextAreaId: oGrammalecte.xRightClickedNode.id} - }); + lexicographerEditableNode(oGrammalecte.xRightClickedNode); } 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: {} - }); + lexicographerPage(); 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") { + if (oGrammalecte.xRightClickedNode.tagName == "TEXTAREA" || oGrammalecte.xRightClickedNode.tagName == "INPUT") { 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 { @@ -356,9 +331,86 @@ break; // rescan page command case "rescanPage": oGrammalecte.rescanPage(); break; + + } +}); + + +/* + Other messages from background +*/ +browser.runtime.onMessage.addListener(function (oMessage) { + let {sActionRequest} = oMessage; + let xActiveNode = document.activeElement; + switch (sActionRequest) { + /* + Commands received from the keyboard (shortcuts) + */ + case "shortcutLexicographer": + if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT")) { + lexicographerEditableNode(xActiveNode); + } else { + lexicographerPage(); + } + break; + case "shortcutTextFormatter": + if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT")) { + oGrammalecte.startFTPanel(xActiveNode); + } + break; + case "shortcutGrammarChecker": + if (xActiveNode && (xActiveNode.tagName == "TEXTAREA" || xActiveNode.tagName == "INPUT")) { + parseAndSpellcheckEditableNode(xActiveNode); + } else { + parseAndSpellcheckPage(); + } + break; default: console.log("[Content script] Unknown command: " + sActionDone); } }); + + +/* + Actions +*/ + +function parseAndSpellcheckPage () { + oGrammalecte.startGCPanel(); + xGrammalectePort.postMessage({ + sCommand: "parseAndSpellcheck", + dParam: {sText: oGrammalecte.getPageText(), sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {} + }); +} + +function parseAndSpellcheckEditableNode (xNode) { + oGrammalecte.startGCPanel(xNode); + let sText = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT") ? xNode.value : xNode.innerText; + xGrammalectePort.postMessage({ + sCommand: "parseAndSpellcheck", + dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {sTextAreaId: xNode.id} + }); +} + +function lexicographerPage () { + oGrammalecte.startLxgPanel(); + xGrammalectePort.postMessage({ + sCommand: "getListOfTokens", + dParam: {sText: oGrammalecte.getPageText()}, + dInfo: {} + }); +} + +function lexicographerEditableNode (xNode) { + oGrammalecte.startLxgPanel(); + let sText = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT") ? xNode.value : xNode.innerText; + xGrammalectePort.postMessage({ + sCommand: "getListOfTokens", + dParam: {sText: sText}, + dInfo: {sTextAreaId: xNode.id} + }); +} 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 @@ -62,11 +62,11 @@ start (xNode=null) { this.oTooltip.hide(); this.clear(); if (xNode) { this.oNodeControl.setNode(xNode); - if (xNode.tagName != "TEXTAREA") { + if (!(xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT")) { this.addMessage("Note : cette zone de texte n’est pas un champ de formulaire “textarea” mais un node HTML éditable. Une telle zone de texte est susceptible de contenir des éléments non textuels qui seront effacés lors de la correction."); } } } @@ -463,11 +463,11 @@ } setNode (xNode) { this.clear(); this.xNode = xNode; - this.bTextArea = (xNode.tagName == "TEXTAREA"); + this.bTextArea = (xNode.tagName == "TEXTAREA" || xNode.tagName == "INPUT"); this.xNode.disabled = true; this._loadText(); } clear () { Index: gc_lang/fr/webext/gce_worker.js ================================================================== --- gc_lang/fr/webext/gce_worker.js +++ gc_lang/fr/webext/gce_worker.js @@ -318,13 +318,10 @@ //console.log("setDictionary", sDictionary); switch (sDictionary) { case "main": oSpellChecker.setMainDictionary(oDict); break; - case "extended": - oSpellChecker.setExtendedDictionary(oDict); - break; case "community": oSpellChecker.setCommunityDictionary(oDict); break; case "personal": oSpellChecker.setPersonalDictionary(oDict); @@ -340,17 +337,10 @@ postMessage(createResponse("setDictionary", "# Error. SpellChecker not loaded.", dInfo, true)); return; } //console.log("setDictionaryOnOff", sDictionary, bActivate); switch (sDictionary) { - case "extended": - if (bActivate) { - oSpellChecker.activateExtendedDictionary(); - } else { - oSpellChecker.deactivateExtendedDictionary(); - } - break; case "community": if (bActivate) { oSpellChecker.activateCommunityDictionary(); } else { oSpellChecker.deactivateCommunityDictionary(); Index: gc_lang/fr/webext/manifest.json ================================================================== --- gc_lang/fr/webext/manifest.json +++ gc_lang/fr/webext/manifest.json @@ -78,27 +78,37 @@ "run_at": "document_idle" } ], "commands": { + "lexicographer": { + "suggested_key": { "default": "Ctrl+Shift+L" }, + "description": "Ouvre le lexicographe" + }, + "text_formatter": { + "suggested_key": { "default": "Ctrl+Shift+F" }, + "description": "Ouvre le formateur de texte" + }, + "grammar_checker": { + "suggested_key": { "default": "Ctrl+Shift+V" }, + "description": "Ouvre le correcteur grammatical" + }, "conjugueur_tab": { - "suggested_key": { - "default": "Ctrl+Shift+6" - }, + "suggested_key": { "default": "Ctrl+Shift+6" }, "description": "Ouvre le conjugueur dans un onglet" }, "conjugueur_window": { - "suggested_key": { - "default": "Ctrl+Shift+7" - }, + "suggested_key": { "default": "Ctrl+Shift+7" }, "description": "Ouvre le conjugueur dans une fenêtre" }, - "lex_editor": { - "suggested_key": { - "default": "Ctrl+Shift+8" - }, + "lexicon_editor": { + "suggested_key": { "default": "Ctrl+Shift+8" }, "description": "Ouvre l’éditeur lexical" + }, + "dictionaries": { + "suggested_key": { "default": "Ctrl+Shift+9" }, + "description": "Ouvre le gestionnaire de dictionnaires communautaires" } }, "web_accessible_resources": [ "content_scripts/panel.css", @@ -116,12 +126,15 @@ "grammalecte/fr/tests_data.json", "img/logo-16.png" ], "permissions": [ + "*://localhost/*", + "*://dic.grammalecte.net/*", "activeTab", "contextMenus", + "cookies", "downloads", "storage" ], "chrome_settings_overrides": { ADDED gc_lang/fr/webext/panel/dictionaries.css Index: gc_lang/fr/webext/panel/dictionaries.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/panel/dictionaries.css @@ -0,0 +1,193 @@ +/* + CSS Document + White + Design par Olivier R. +*/ + +* { margin: 0; padding: 0; } +img { border: none; } + + +/* Generic classes */ + +.fleft { float: left; } +.fright { float: right; } +.center { text-align: center; } +.right { text-align: right; } +.left { text-align: left; } +.justify { text-align: justify; } +.hidden { display: none; } +.clearer { clear: both; font-size: 0; height: 0; } + +body { + background: hsl(0, 0%, 100%) url(../img/lines.png); + font: normal 16px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif; + color: #505050; +} + +.inbox { + width: 800px; + margin: 20px auto 10px auto; + padding: 10px 30px 30px 30px; + background: hsl(0, 0%, 100%); + border: 2px solid hsl(210, 0%, 90%); + border-radius: 20px; +} + + +#message_box { + display: none; + position: fixed; + top: 33%; + left: calc(50% - 325px); +} +#message { + display: inline-block; + padding: 5px 20px; + width: 600px; + background-color: hsl(0, 50%, 50%); + color: hsl(0, 50%, 98%); + border-style: solid; + border-width: 3px 0 3px 3px; + border-color: hsla(0, 50%, 40%, .5); + border-radius: 5px 0 0 5px; +} +#message_close_button { + display: inline-block; + padding: 5px 10px; + background-color: hsl(0, 50%, 40%); + color: hsl(0, 90%, 90%); + border-style: solid; + border-width: 3px 3px 3px 0; + border-color: hsla(0, 50%, 30%, .5); + border-radius: 0 5px 5px 0; + cursor: pointer; +} + + +h1 { + margin: 5px 0 5px 0; + color: hsl(210, 50%, 50%); + font: bold 24px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif; +} +h2 { + margin: 10px 0 2px 0; + color: hsl(0, 50%, 50%); + font: bold 20px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif; +} + + +input[type=text].large { + display: inline-block; + width: 250px; + padding: 5px 10px; + border: 2px solid hsl(0, 0%, 80%); + border-radius: 3px; + height: 24px; + background: transparent; + font: normal 20px Tahoma, "Ubuntu Condensed"; + color: hsl(0, 0%, 20%); +} + +input[type=text].medium { + display: inline-block; + width: 175px; + padding: 2px 5px; + border: 2px solid hsl(0, 0%, 80%); + border-radius: 3px; + height: 20px; + background: transparent; + font: normal 18px Tahoma, "Ubuntu Condensed"; + color: hsl(0, 0%, 20%); +} + +input[placeholder] { + color: hsl(0, 0%, 50%); +} + + +#connect_panel { + background-color: hsl(210, 50%, 90%); + border-radius: 5px; + padding: 3px 10px; +} +#submit_button { + display: inline-block; + padding: 1px 5px; + background-color: hsl(210, 50%, 30%); + color: hsl(210, 0%, 100%); + border-radius: 3px; + cursor: pointer; +} + + +.dic_button { + margin: 2px; + display: inline-block; +} +.dic_button_close { + display: inline-block; + padding: 1px 5px; + background-color: hsl(0, 50%, 50%); + color: hsl(0, 90%, 90%); + border-style: solid; + border-width: 1px 0 1px 1px; + border-color: hsl(0, 50%, 45%); + border-radius: 3px 0 0 3px; + cursor: pointer; +} +.dic_button_label { + display: inline-block; + padding: 1px 10px; + background-color: hsl(210, 50%, 94%); + border-style: solid; + border-width: 1px 1px 1px 0; + border-color: hsl(210, 50%, 70%); + border-radius: 0 3px 3px 0; +} + +.apply { + display: none; + float: right; + padding: 2px 10px; + background: hsl(120, 50%, 30%); + color: hsl(120, 50%, 96%); + cursor: pointer; + border-radius: 3px; +} + + +/* + Table +*/ +#wait_progress { + width: 100%; + height: 4px; +} + +table { + border: 1px solid hsl(210, 10%, 50%); + width: 100%; + font-size: 14px; +} +th { + padding: 5px 10px; + border-left: 1px solid hsl(210, 10%, 90%); + text-align: left; +} +td { + padding: 0 10px; + vertical-align: top; +} +.delete_entry { + cursor: pointer; + font-weight: bold; + color: hsl(0, 100%, 50%); +} +.select_entry { + cursor: pointer; + background-color: hsl(210, 50%, 30%); + color: hsl(210, 50%, 100%); + border-radius: 3px; + text-align: center; +} ADDED gc_lang/fr/webext/panel/dictionaries.html Index: gc_lang/fr/webext/panel/dictionaries.html ================================================================== --- /dev/null +++ gc_lang/fr/webext/panel/dictionaries.html @@ -0,0 +1,57 @@ + + + + + Grammalecte · Dictionnaires communautaires + + + + + +
+
[néant]
×
+
+ +
+

Grammalecte · Dictionnaires communautaires

+ +
+
+

+ Non connecté. + + + Se connecter + | + Inscription +

+
+
+

+ Connecté. Identifiants : , +

+
+
+ +

Dictionnaires sélectionnés

+

[Aucun]

+ +

Appliquer les modifications
+

0 dictionnaires disponibles

+ + +
+
+ + + + + + + + + + + + + ADDED gc_lang/fr/webext/panel/dictionaries.js Index: gc_lang/fr/webext/panel/dictionaries.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/panel/dictionaries.js @@ -0,0 +1,441 @@ +// JavaScript + +"use strict"; + + +// Chrome don’t follow the W3C specification: +// https://browserext.github.io/browserext/ +let bChrome = false; +if (typeof(browser) !== "object") { + var browser = chrome; + bChrome = true; +} + + +/* + Common functions +*/ + +function showError (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); +} + +function createNode (sType, oAttr, oDataset=null) { + try { + let xNode = document.createElement(sType); + Object.assign(xNode, oAttr); + if (oDataset) { + Object.assign(xNode.dataset, oDataset); + } + return xNode; + } + catch (e) { + showError(e); + } +} + +function showElement (sElemId, sDisplay="block") { + if (document.getElementById(sElemId)) { + document.getElementById(sElemId).style.display = sDisplay; + } else { + console.log("HTML node named <" + sElemId + "> not found.") + } +} + +function hideElement (sElemId) { + if (document.getElementById(sElemId)) { + document.getElementById(sElemId).style.display = "none"; + } else { + console.log("HTML node named <" + sElemId + "> not found.") + } +} + +async function hashText (sText, sAlgorithm = 'SHA-256') { + let msgBuffer = new TextEncoder('utf-8').encode(sText); + let hashBuffer = await crypto.subtle.digest(sAlgorithm, msgBuffer); + let hashArray = Array.from(new Uint8Array(hashBuffer)); + return hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join(''); +} + + +const oMessage = { + show: function (sMessage, nDelay=10000) { + document.getElementById("message").textContent = sMessage; + showElement("message_box"); + window.setTimeout(this.close, nDelay); + }, + + listen: function () { + document.getElementById("message_close_button").addEventListener("click", (xEvent) => { this.close(); }); + }, + + close: function () { + hideElement("message_box"); + } +} + + +const oConnect = { + bConnected: false, + + init: function () { + if (bChrome) { + browser.cookies.getAll({ domain: "localhost" }, this._init.bind(this)); + return; + } + let xPromise = browser.cookies.getAll({ domain: "localhost" }); + xPromise.then(this._init.bind(this), showError); + }, + + _init: function (lData) { + for (let xCookie of lData) { + console.log(xCookie.name, xCookie.value); + this.bConnected = true; + } + if (this.bConnected) { + hideElement("connect_form"); + showElement("connect_info"); + } + else { + showElement("connect_form"); + hideElement("connect_info"); + } + }, + + listen: function () { + document.getElementById("submit_button").addEventListener("click", (xEvent) => { this.connect(); }); + }, + + connect: function () { + if (!this.checkValues()) { + oMessage.show("Les valeurs des champs du formulaire ne sont pas conformes."); + return; + } + let xForm = new FormData(document.getElementById('connect_form')); + for (let [k, v] of xForm.entries()) { + console.log("* ", k, v); + } + fetch("http://localhost/connect/", { + method: "POST", // *GET, POST, PUT, DELETE, etc. + //mode: "cors", // no-cors, cors, *same-origin + //cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached + headers: { + "Accept-Charset": "utf-8" + //"Content-Type": "multipart/form-data", // text/plain, application/json + }, + credentials: "omit", // include, *same-origin, omit + body: xForm + }) + .then((response) => { + if (response.ok) { + if (response.status == 204) { + oMessage.show("Échec d’identification. Vérifiez l’e-mail et le mot de passe envoyés…"); + return null; + } + for (let param in response) { + console.log(param, response[param]); + } + console.log(response.body); + return response.json(); + } else { + oMessage.show("Erreur. Le serveur ne semble pas en état de répondre. Veuillez réessayer ultérieurement."); + return null; + } + }) + .then((response) => { + if (response) { + console.log("response: ", response); + } + }) + .catch((e) => { + showError(e); + }); + }, + + checkValues () { + if (document.getElementById("email").value === "") { + return false; + } + let sEmail = document.getElementById("email").value; + if (sEmail.search(/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/) === -1) { + return false; + } + return true; + }, + + showId (sLogin, sEmail) { + document.getElementById("login_label").textContent = sLogin; + document.getElementById("email_label").textContent = sEmail; + hideElement("connect_form"); + showElement("connect_info"); + } +} + + +class Table { + + constructor (sNodeId, lColumn, sProgressBarId, sResultId="", bDeleteButtons=true, bActionButtons) { + this.sNodeId = sNodeId; + this.xTable = document.getElementById(sNodeId); + this.xApply = document.getElementById("apply"); + this.nColumn = lColumn.length; + this.lColumn = lColumn; + this.xProgressBar = document.getElementById(sProgressBarId); + this.xNumEntry = document.getElementById(sResultId); + this.lEntry = []; + this.nEntry = 0; + this.dSelectedDictionaries = new Map(); + this.lSelectedDictionaries = []; + this.dDict = new Map(); + this.bDeleteButtons = bDeleteButtons; + this.bActionButtons = bActionButtons; + this._createHeader(); + this.listen(); + } + + _createHeader () { + let xRowNode = createNode("tr"); + if (this.bDeleteButtons) { + xRowNode.appendChild(createNode("th", { textContent: "·", width: "12px" })); + } + for (let sColumn of this.lColumn) { + xRowNode.appendChild(createNode("th", { textContent: sColumn })); + } + this.xTable.appendChild(xRowNode); + } + + clear () { + while (this.xTable.firstChild) { + this.xTable.removeChild(this.xTable.firstChild); + } + this.lEntry = []; + this.nEntry = 0; + this._createHeader(); + this.showEntryNumber(); + } + + fill (lFlex) { + this.clear(); + if (lFlex.length > 0) { + this.xProgressBar.max = lFlex.length; + this.xProgressBar.value = 1; + for (let lData of lFlex) { + this._addRow(lData); + this.xProgressBar.value += 1; + } + this.xProgressBar.value = this.xProgressBar.max; + } + this.lEntry = lFlex; + this.nEntry = lFlex.length; + this.showEntryNumber(); + } + + addEntries (lFlex) { + this.lEntry.push(...lFlex); + for (let lData of lFlex) { + this._addRow(lData); + } + this.nEntry += lFlex.length; + this.showEntryNumber(); + } + + init () { + if (bChrome) { + browser.storage.local.get("selected_dictionaries_list", this._init.bind(this)); + return; + } + let xPromise = browser.storage.local.get("selected_dictionaries_list"); + xPromise.then(this._init.bind(this), showError); + } + + _init (oResult) { + if (oResult.hasOwnProperty("selected_dictionaries_list")) { + this.lSelectedDictionaries = oResult.selected_dictionaries_list; + console.log(this.lSelectedDictionaries); + } + this.getDictionarieslist(); + } + + getDictionarieslist () { + fetch("http://localhost/dictionaries/") + .then((response) => { + if (response.ok) { + return response.json(); + } else { + return null; + } + }) + .then((response) => { + if (response) { + this.fill(response); + this.showSelectedDictionaries(true); + } else { + // todo + } + }) + .catch((e) => { + showError(e); + }); + } + + getDictionary (sId, sName) { + console.log("get: "+sName); + fetch("http://localhost/download/"+sName) + .then((response) => { + if (response.ok) { + return response.json(); + } else { + console.log("dictionary not loaded: " + sName); + return null; + } + }) + .then((response) => { + if (response) { + this.selectEntry(sId, sName); + this.dDict.set(sName, response); + browser.storage.local.set({ "stored_dictionaries": this.dDict }); + } else { + // + } + }) + .catch((e) => { + showError(e); + }); + } + + showEntryNumber () { + if (this.xNumEntry) { + this.xNumEntry.textContent = this.nEntry; + } + } + + _addRow (lData) { + let [nDicId, sName, nEntry, sDescription, sLastUpdate, ...data] = lData; + let xRowNode = createNode("tr", { id: this.sNodeId + "_row_" + nDicId }); + if (this.bDeleteButtons) { + xRowNode.appendChild(createNode("td", { textContent: "×", className: "delete_entry", title: "Effacer cette entrée" }, { id_entry: nDicId })); + } + xRowNode.appendChild(createNode("td", { textContent: sName })); + xRowNode.appendChild(createNode("td", { textContent: nEntry })); + xRowNode.appendChild(createNode("td", { textContent: sDescription })); + xRowNode.appendChild(createNode("td", { textContent: sLastUpdate })); + if (this.bActionButtons) { + xRowNode.appendChild(createNode("td", { textContent: "+", className: "select_entry", title: "Sélectionner/Désélectionner cette entrée" }, { id_entry: nDicId, dict_name: sName })); + } + this.xTable.appendChild(xRowNode); + if (this.lSelectedDictionaries.includes(sName)) { + this.dSelectedDictionaries.set(sName, nDicId); + } + } + + listen () { + if (this.bDeleteButtons || this.bActionButtons) { + this.xTable.addEventListener("click", (xEvent) => { this.onTableClick(xEvent); }, false); + } + this.xApply.addEventListener("click", (xEvent) => { this.generateCommunityDictionary(xEvent); }, false); + } + + onTableClick (xEvent) { + try { + let xElem = xEvent.target; + if (xElem.className) { + switch (xElem.className) { + case "delete_entry": + this.deleteRow(xElem.dataset.id_entry, xElem.dataset.dict_name); + break; + case "select_entry": + this.getDictionary(xElem.dataset.id_entry, xElem.dataset.dict_name); + break; + } + } + } + catch (e) { + showError(e); + } + } + + deleteRow (iEntry) { + this.lEntry[parseInt(iEntry)] = null; + if (document.getElementById(this.sNodeId + "_row_" + iEntry)) { + document.getElementById(this.sNodeId + "_row_" + iEntry).style.display = "none"; + } + this.nEntry -= 1; + this.showEntryNumber(); + if (this.sNodeId == "lexicon_table") { + showElement("save_button", "inline-block"); + } + showElement("apply"); + } + + selectEntry (nEntryId, sDicName) { + let sRowId = this.sNodeId + "_row_" + nEntryId; + if (!this.dSelectedDictionaries.has(sDicName)) { + this.dSelectedDictionaries.set(sDicName, nEntryId); + document.getElementById(sRowId).style.backgroundColor = "hsl(210, 50%, 90%)"; + } + else { + this.dSelectedDictionaries.delete(sDicName); + document.getElementById(sRowId).style.backgroundColor = ""; + } + showElement("apply"); + this.showSelectedDictionaries(); + } + + clearSelectedDict () { + let xDicList = document.getElementById("dictionaries_list"); + while (xDicList.firstChild) { + xDicList.removeChild(xDicList.firstChild); + } + } + + showSelectedDictionaries (bUpdateTable=false) { + this.clearSelectedDict(); + let xDicList = document.getElementById("dictionaries_list"); + if (this.dSelectedDictionaries.size === 0) { + xDicList.textContent = "[Aucun]"; + return; + } + for (let [sName, nDicId] of this.dSelectedDictionaries) { + xDicList.appendChild(this._createDictLabel(nDicId, sName)); + if (bUpdateTable) { + document.getElementById(this.sNodeId + "_row_" + nDicId).style.backgroundColor = "hsl(210, 50%, 90%)"; + } + } + } + + _createDictLabel (nDicId, sLabel) { + let xLabel = createNode("div", {className: "dic_button"}); + let xCloseButton = createNode("div", {className: "dic_button_close", textContent: "×"}, {id_entry: nDicId}); + xCloseButton.addEventListener("click", () => { + this.dSelectedDictionaries.delete(sLabel); + document.getElementById(this.sNodeId+"_row_"+nDicId).style.backgroundColor = ""; + xLabel.style.display = "none"; + showElement("apply"); + }); + xLabel.appendChild(xCloseButton); + xLabel.appendChild(createNode("div", {className: "dic_button_label", textContent: sLabel})); + return xLabel; + } + + generateCommunityDictionary (xEvent) { + hideElement("apply"); + let lDict = []; + for (let sName of this.dSelectedDictionaries.keys()) { + lDict.push(this.dDict.get(sName)); + } + let oDict = dic_merger.merge(lDict, "S", "fr", "Français", "fr.community", "Dictionnaire communautaire (personnalisé)", this.xProgressBar); + console.log(oDict); + browser.storage.local.set({ "community_dictionary": oDict }); + browser.storage.local.set({ "selected_dictionaries_list": Array.from(this.dSelectedDictionaries.keys()) }); + browser.runtime.sendMessage({ sCommand: "setDictionary", dParam: {sDictionary: "community", oDict: oDict}, dInfo: {} }); + } +} + +const oDicTable = new Table("dictionaries_table", ["Nom", "Entrées", "Description", "Date"], "wait_progress", "num_dic", false, true); + +oDicTable.init(); + +oMessage.listen(); + +oConnect.init(); +oConnect.listen(); Index: gc_lang/fr/webext/panel/lex_editor.css ================================================================== --- gc_lang/fr/webext/panel/lex_editor.css +++ gc_lang/fr/webext/panel/lex_editor.css @@ -48,10 +48,15 @@ h3 { margin: 3px 0 2px 0; color: hsl(210, 50%, 50%); font: bold 16px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif; } + +#dic_selector { + color: hsl(210, 50%, 50%); + font: bold 16px "Trebuchet MS", "Fira Sans", "Liberation Sans", sans-serif; +} details { font-size: 11px; font-variant: small-caps; color: hsl(210, 50%, 50%); @@ -66,10 +71,11 @@ details.inline { padding: 3px; width: 260px; } + /* Main buttons */ #buttons { @@ -76,11 +82,11 @@ padding: 10px 0; justify-content: center; } .main_button { margin: 0 5px; - width: 100px; + min-width: 100px; padding: 10px 20px; background-color: hsl(210, 10%, 95%); border-radius: 5px; text-align: center; cursor: pointer; @@ -247,11 +253,11 @@ box-shadow: 0 0 2px hsl(150, 60%, 50%); } #lexicon_page { - + } /* Search page @@ -294,25 +300,57 @@ /* Dictionary */ -#save_button, #export_button, #import_button { +#new_dictionary_button, #delete_dictionary_button { + margin-left: 5px; + padding: 1px 5px; + background-color: hsl(150, 50%, 50%); + color: hsl(150, 0%, 100%); + border-radius: 3px; + cursor: pointer; +} +#delete_dictionary_button { + background-color: hsl(0, 50%, 50%); + color: hsl(0, 0%, 100%); +} + +#save_button { display: none; - float: right; - padding: 2px 10px; + margin-left: 5px; + padding: 1px 5px; background-color: hsl(150, 50%, 50%); color: hsl(150, 0%, 100%); border-radius: 3px; cursor: pointer; } #export_button, #import_button { display: block; margin-left: 5px; + float: right; + padding: 2px 10px; background-color: hsl(210, 50%, 50%); color: hsl(210, 0%, 100%); + border-radius: 3px; + cursor: pointer; +} + +#new_dictionary_section { + margin: 5px 0; + padding: 5px 10px; + background-color: hsl(210, 10%, 90%); + border-radius: 3px; +} +#create_dictionary_button { + padding: 1px 5px; + background-color: hsl(120, 50%, 30%); + color: hsl(120, 10%, 100%); + border-radius: 3px; + cursor: pointer; } + #wait_progress { width: 100%; height: 4px; } Index: gc_lang/fr/webext/panel/lex_editor.html ================================================================== --- gc_lang/fr/webext/panel/lex_editor.html +++ gc_lang/fr/webext/panel/lex_editor.html @@ -3,35 +3,50 @@ Grammalecte · Éditeur lexical - +

Éditeur lexical

-

Dictionnaire personnel

-
Enregistré le :
+

Dictionnaire

+
+ +
+
+
+
+ +
[]
0 entrées
- -
+
-
Exporter
- +
Exporter
+
+
-
Lexique
+
+ Lexique · 0 entrées Enregistrer +
Ajout
Recherche
Informations
@@ -154,11 +169,11 @@
Je suis venu.
Je suis parti.
J’ai venu.
J’ai parti.
- +
@@ -229,15 +244,15 @@

- +

Mots générés

- +
Ajouter au lexique
@@ -245,19 +260,12 @@

Votre lexique

-
-
- Enregistrer -
-

Nombre d’entrées : 0.

-
- - +
@@ -281,11 +289,11 @@

Résultats

- +
@@ -311,11 +319,11 @@

Il est déconseillé d’utiliser la catégorie ‹Autre› pour générer autre chose que des noms, des adjectifs, des noms propres, des verbes et des adverbes. Il n’y a aucune garantie que les étiquettes pour les autres catégories, notamment les mots grammaticaux, ne changeront pas.

- +
@@ -331,7 +339,7 @@ - + Index: gc_lang/fr/webext/panel/lex_editor.js ================================================================== --- gc_lang/fr/webext/panel/lex_editor.js +++ gc_lang/fr/webext/panel/lex_editor.js @@ -17,11 +17,11 @@ function showError (e) { console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); } -function createNode (sType, oAttr, oDataset=null) { +function createNode (sType, oAttr, oDataset=null) { try { let xNode = document.createElement(sType); Object.assign(xNode, oAttr); if (oDataset) { Object.assign(xNode.dataset, oDataset); @@ -31,13 +31,13 @@ catch (e) { showError(e); } } -function showElement (sElemId) { +function showElement (sElemId, sDisplay="block") { if (document.getElementById(sElemId)) { - document.getElementById(sElemId).style.display = "block"; + document.getElementById(sElemId).style.display = sDisplay; } else { console.log("HTML node named <" + sElemId + "> not found.") } } @@ -46,10 +46,24 @@ document.getElementById(sElemId).style.display = "none"; } else { console.log("HTML node named <" + sElemId + "> not found.") } } + +function switchDisplay (sElemId, sDisplay="block") { + if (document.getElementById(sElemId)) { + if (document.getElementById(sElemId).style.display != "none") { + document.getElementById(sElemId).style.display = "none"; + } else { + document.getElementById(sElemId).style.display = sDisplay; + } + } +} + +function showMessage (sMessage) { + console.log(sMessage); +} const oTabulations = { lPage: ["lexicon_page", "add_page", "search_page", "info_page"], @@ -125,12 +139,15 @@ clear () { while (this.xTable.firstChild) { this.xTable.removeChild(this.xTable.firstChild); } + this.lEntry = []; + this.nEntry = 0; this.iEntryIndex = 0; this._createHeader(); + this.showEntryNumber(); } fill (lFlex) { this.clear(); if (lFlex.length > 0) { @@ -200,11 +217,11 @@ document.getElementById(this.sNodeId + "_row_" + iEntry).style.display = "none"; } this.nEntry -= 1; this.showEntryNumber(); if (this.sNodeId == "lexicon_table") { - showElement("save_button"); + showElement("save_button", "inline-block"); } } getEntries () { return this.lEntry.filter((e) => e !== null); @@ -480,49 +497,195 @@ oGenWordsTable.clear(); document.getElementById("lemma").value = ""; document.getElementById("lemma").focus(); this.hideAllSections(); hideElement("editor"); - showElement("save_button"); + showElement("save_button", "inline-block"); this.clear(); this.cMainTag = ""; } catch (e) { showError(e); } } } + +const oDictHandler = { + oDictionaries: {}, + oPersonalDictionary: null, + lCreatedDictionaries: [], + xDicSelector: document.getElementById("dic_selector"), + + loadDictionaries: function () { + if (bChrome) { + browser.storage.local.get("shared_dictionaries", this._loadDictionaries.bind(this)); + browser.storage.local.get("personal_dictionary", this._loadDictionaries.bind(this)); + browser.storage.local.get("created_dictionaries_list", this._loadDictionaries.bind(this)); + return; + } + let xPromise = browser.storage.local.get("shared_dictionaries"); + xPromise.then(this._loadDictionaries.bind(this), showError); + let xPromise2 = browser.storage.local.get("personal_dictionary"); + xPromise2.then(this._loadDictionaries.bind(this), showError); + let xPromise3 = browser.storage.local.get("created_dictionaries_list"); + xPromise3.then(this._loadDictionaries.bind(this), showError); + }, + + _loadDictionaries: function (oResult) { + if (oResult.hasOwnProperty("shared_dictionaries")) { + this.oDictionaries = oResult.shared_dictionaries; + } + if (oResult.hasOwnProperty("personal_dictionary")) { + this.oPersonalDictionary = oResult.personal_dictionary; + oBinaryDict.load("__personal__"); + } + if (oResult.hasOwnProperty("created_dictionaries_list")) { + this.lCreatedDictionaries = oResult.created_dictionaries_list; + for (let sDicName of this.lCreatedDictionaries) { + let xOption = createNode("option", {value: sDicName, textContent: sDicName}); + this.xDicSelector.appendChild(xOption); + } + } + }, + + addDictionary: function (sDicName) { + if (!this.oDictionaries.hasOwnProperty(sDicName)) { + let xOption = createNode("option", {value: sDicName, textContent: sDicName}); + this.xDicSelector.appendChild(xOption); + this.xDicSelector.selectedIndex = this.xDicSelector.options.length-1; + this.lCreatedDictionaries.push(sDicName); + browser.storage.local.set({ "created_dictionaries_list": this.lCreatedDictionaries }); + } + }, + + deleteDictionary: function (sDicName) { + this.saveDictionary(sDicName, null); + if (sDicName != "__personal__") { + let iDict = this.lCreatedDictionaries.indexOf(sDicName); + if (iDict > -1) { + this.lCreatedDictionaries.splice(iDict, 1); + } + browser.storage.local.set({ "created_dictionaries_list": this.lCreatedDictionaries }); + for (let xNode of this.xDicSelector.childNodes) { + if (xNode.value == sDicName) { + this.xDicSelector.removeChild(xNode); + } + } + this.xDicSelector.selectedIndex = 0; + oBinaryDict.load("__personal__"); + } + }, + + getDictionary: function (sName) { + if (sName == "__personal__") { + return this.oPersonalDictionary; + } + else if (this.oDictionaries && this.oDictionaries.hasOwnProperty(sName)) { + //console.log(this.oDictionaries[sName]); + return this.oDictionaries[sName]; + } + return null; + }, + + saveDictionary: function (sName, oJSON) { + if (sName == "__personal__") { + browser.runtime.sendMessage({ sCommand: "setDictionary", dParam: {sDictionary: "personal", oDict: oJSON}, dInfo: {} }); + browser.storage.local.set({ "personal_dictionary": oJSON }); + } + else { + this.oDictionaries[sName] = oJSON; + if (oJSON === null) { + delete this.oDictionaries[sName]; + } + browser.storage.local.set({ "shared_dictionaries": this.oDictionaries }); + this.sendDictionaryOnline(oJSON); + } + }, + + sendDictionaryOnline: function (oJSON) { + let sJSON = ""; + try { + sJSON = JSON.stringify(oJSON); + } + catch (e) { + showError(e); + return e.message; + } + console.log("Send online dictionary: " + oJSON.sDicName); + fetch("http://localhost/receive/", { + method: "POST", // *GET, POST, PUT, DELETE, etc. + //mode: "cors", // no-cors, cors, *same-origin + //cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached + credentials: "include", // include, *same-origin, omit + headers: { + "Content-Type": "application/json", // text/plain, application/json + "Content-Length": sJSON.length + }, + redirect: "follow", // manual, *follow, error + referrer: "no-referrer", // no-referrer, *client + body: sJSON, // body data type must match "Content-Type" header + }) + .then((response) => { + if (response.ok) { + for (let param in response) { + console.log(param, response[param]); + } + console.log(response); + return response.json(); + } else { + console.log("Error: dictionary not sent: " + oJSON.sDicName); + return null; + } + }) + .then((response) => { + if (response) { + console.log(response); + } else { + // + } + }) + .catch((e) => { + showError(e); + }); + } +} + const oBinaryDict = { oIBDAWG: null, - - load: function () { - if (bChrome) { - browser.storage.local.get("oPersonalDictionary", this._load.bind(this)); - return; - } - let xPromise = browser.storage.local.get("oPersonalDictionary"); - xPromise.then(this._load.bind(this), showError); - }, - - _load: function (oResult) { - if (!oResult.hasOwnProperty("oPersonalDictionary")) { - hideElement("export_button"); - return; - } - let oJSON = oResult.oPersonalDictionary; + sName: "", + sDescription: "", + + load: function (sName) { + console.log("lexicon editor, load: " + sName); + this.sName = sName; + let oJSON = oDictHandler.getDictionary(sName); if (oJSON) { - this.__load(oJSON); + this.parseDict(oJSON); } else { oLexiconTable.clear(); this.setDictData(0, "[néant]"); + hideElement("save_button"); } }, - __load: function (oJSON) { + newDictionary: function () { + this.oIBDAWG = null; + this.sName = document.getElementById("new_dictionary_name").value; + this.sDescription = document.getElementById("new_dictionary_description").value; + oDictHandler.addDictionary(this.sName); + oLexiconTable.clear(); + this.setDictData(0, "[néant]"); + hideElement("save_button"); + document.getElementById("new_dictionary_name").value = ""; + document.getElementById("new_dictionary_description").value = ""; + hideElement("new_dictionary_section") + }, + + parseDict: function (oJSON) { try { this.oIBDAWG = new IBDAWG(oJSON); } catch (e) { console.error(e); @@ -533,15 +696,11 @@ for (let aRes of this.oIBDAWG.select()) { lEntry.push(aRes); } oLexiconTable.fill(lEntry); this.setDictData(this.oIBDAWG.nEntry, this.oIBDAWG.sDate); - }, - - save: function (oJSON) { - browser.storage.local.set({ "oPersonalDictionary": oJSON }); - browser.runtime.sendMessage({ sCommand: "setDictionary", dParam: {sDictionary: "personal", oDict: oJSON}, dInfo: {} }); + hideElement("save_button"); }, import: function () { let xInput = document.getElementById("import_input"); let xFile = xInput.files[0]; @@ -548,21 +707,21 @@ let xURL = URL.createObjectURL(xFile); let sJSON = helpers.loadFile(xURL); if (sJSON) { try { let oJSON = JSON.parse(sJSON); - this.__load(oJSON); - this.save(oJSON); + this.parseDict(oJSON); + oDictHandler.saveDictionary(this.sName, oJSON); } catch (e) { console.error(e); this.setDictData(0, "#Erreur. Voir la console."); return; } } else { this.setDictData(0, "[néant]"); - this.save(null); + oDictHandler.saveDictionary(this.sName, null); } }, setDictData: function (nEntries, sDate) { document.getElementById("dic_num_entries").textContent = nEntries; @@ -573,35 +732,47 @@ showElement("export_button"); } }, listen: function () { + document.getElementById("dic_selector").addEventListener("change", () => {this.load(document.getElementById("dic_selector").value)}, false); document.getElementById("save_button").addEventListener("click", () => { this.build(); }, false); document.getElementById("export_button").addEventListener("click", () => { this.export(); }, false); document.getElementById("import_input").addEventListener("change", () => { this.import(); }, false); + document.getElementById("new_dictionary_button").addEventListener("click", () => { switchDisplay("new_dictionary_section"); }, false); + document.getElementById("delete_dictionary_button").addEventListener("click", () => { this.delete(); }, false); + document.getElementById("create_dictionary_button").addEventListener("click", () => { this.newDictionary(); }, false); }, build: function () { let xProgressNode = document.getElementById("wait_progress"); let lEntry = oLexiconTable.getEntries(); if (lEntry.length > 0) { - let oDAWG = new DAWG(lEntry, "S", "fr", "Français", "fr.personal", xProgressNode); + let oDAWG = new DAWG(lEntry, "S", "fr", "Français", this.sName, this.sDescription, xProgressNode); let oJSON = oDAWG.createBinaryJSON(1); - this.save(oJSON); + oDictHandler.saveDictionary(this.sName, oJSON); this.oIBDAWG = new IBDAWG(oJSON); this.setDictData(this.oIBDAWG.nEntry, this.oIBDAWG.sDate); } else { + oDictHandler.saveDictionary(this.sName, null); this.setDictData(0, "[néant]"); - this.save(null); } hideElement("save_button"); }, + + delete: function () { + if (confirm("Voulez-vous effacer le dictionnaire “"+this.sName+"” ?")) { + oLexiconTable.clear(); + this.setDictData(0, "[néant]"); + oDictHandler.deleteDictionary(this.sName); + } + }, export: function () { let xBlob = new Blob([ JSON.stringify(this.oIBDAWG.getJSON()) ], {type: 'application/json'}); let sURL = URL.createObjectURL(xBlob); - browser.downloads.download({ filename: "fr.personal.json", url: sURL, saveAs: true }); + browser.downloads.download({ filename: "fr."+this.sName+".json", url: sURL, saveAs: true }); } } const oSearch = { @@ -664,10 +835,10 @@ const oTagsTable = new Table("tags_table", ["Étiquette", "Signification"], "wait_progress", "", false); oTagsInfo.load(); oSearch.load(); -oBinaryDict.load(); +oDictHandler.loadDictionaries(); oBinaryDict.listen(); oGenerator.listen(); oTabulations.listen(); oSearch.listen(); Index: gc_lang/fr/webext/panel/main.css ================================================================== --- gc_lang/fr/webext/panel/main.css +++ gc_lang/fr/webext/panel/main.css @@ -84,11 +84,11 @@ } body { width: 450px; height: 500px; } -/* +/* Maximal height of a panel in WebExtention seems to be 500px. When going over this limit, a scrollbar appears which destructs the horizontal balance of elements. --> vertical scrolling is done with overflow in #page. #page must have the same height than body. @@ -245,10 +245,29 @@ font-size: 16px; text-align: center; cursor: pointer; } + +.option_section { + padding: 10px; + margin-top: 10px; + border-radius: 5px; + background-color: hsl(210, 20%, 96%); +} +.option_section label { + font-size: 16px; + line-height: 20px; + color: hsl(210, 20%, 50%); + font-weight: bold; +} +.option_description { + padding: 0 0 0 20px; + color: hsl(0, 0%, 0%); + font-size: 12px; +} + /* Spell checking options */ #sc_options_page { @@ -265,30 +284,24 @@ #sc_options_page h2 { margin-top: 20px; font: normal 22px 'Yanone Kaffeesatz', "Oswald", "Liberation Sans Narrow", sans-serif; color: hsl(210, 50%, 50%); } - -/* - Options -*/ -.option_section { - padding: 10px; - margin-top: 10px; - border-radius: 5px; - background-color: hsl(210, 20%, 96%); -} -.option_section label { - font-size: 16px; - line-height: 20px; - color: hsl(210, 20%, 50%); - font-weight: bold; -} -.option_description { - padding: 0 0 0 20px; - color: hsl(0, 0%, 0%); - font-size: 12px; +.button_row { + display: flex; + flex-direction: row-reverse; + padding: 5px 0 0 0; +} +.dic_button { + margin-left: 10px; + padding: 2px 10px; + background-color: hsl(210, 50%, 50%); + color: hsl(210, 10%, 96%); + cursor: pointer; + font-size: 14px; + font-variant-caps: small-caps; + border-radius: 3px; } /* Test page @@ -372,11 +385,11 @@ .double-bounce2 { animation-delay: -1.0s; } @keyframes sk-bounce { - 0%, 100% { + 0%, 100% { transform: scale(0.0); - } 50% { + } 50% { transform: scale(1.0); } } Index: gc_lang/fr/webext/panel/main.html ================================================================== --- gc_lang/fr/webext/panel/main.html +++ gc_lang/fr/webext/panel/main.html @@ -86,16 +86,24 @@

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

Raccourcis clavier

+

CTRL+MAJ+L

+

Lexicographe

+

CTRL+MAJ+F

+

Formateur de texte

+

CTRL+MAJ+V

+

Correcteur grammatical

CTRL+MAJ+6

Conjugueur (dans un onglet)

CTRL+MAJ+7

Conjugueur (dans une fenêtre)

CTRL+MAJ+8

Éditeur lexical

+

CTRL+MAJ+9

+

Dictionnaires communautaires

OPTIONS GRAMMATICALES

@@ -106,26 +114,28 @@

OPTIONS ORTHOGRAPHIQUES

DICTIONNAIRES DE GRAMMALECTE

-

Ces dictionnaires ne sont utilisés que lors de l’analyse grammaticale.

-

-

Environ 83 000 entrées, 500 000 flexions.
Ni éditable, ni désactivable.

-
-
-

-

Fonctionnalité à venir.

-
-
-

-

Fonctionnalité à venir.

+

+

Environ 83 000 entrées, 500 000 flexions.
Ni éditable, ni désactivable.
Ce dictionnaire est créé à partir du dictionnaire orthographique pour Firefox et LibreOffice, conçu sur le site de Grammalecte.

+
+
+

+

Ce dictionnaire est créé et édité via l’éditeur lexical et est sauvegardé sur un serveur en ligne accessible à tous les membres.

+
+
Éditer
+
Dictionnaires en ligne
+

-

Ce dictionnaire est créé et édité via l’éditeur lexical.

+

Ce dictionnaire est créé et édité via l’éditeur lexical et n’est pas partagé.

+
+
Éditer
+