Index: doc/API_web.md ================================================================== --- doc/API_web.md +++ doc/API_web.md @@ -67,12 +67,12 @@ If you have disabled the spinning button, you can launch the Grammalecte panel with your custom button. oGrammalecteAPI.openPanelForNode("node_id") oGrammalecteAPI.openPanelForNode(node) -The node can be a textarea, an editable node or an iframe. -If the node is an iframe, the content won’t be modified by Grammalecte. +The node can be a textarea, an editable node or an iframe. **The node must have an identifier**. +If the node is an iframe, the content won’t be modified by Grammalecte, but events with results may be received (see below). ### Prevent Grammalecte to modify the node content If you don’t want Grammalecte to modify directly the node content, add the property: `data-grammalecte_result_via_event="true"`. @@ -93,19 +93,19 @@ oGrammalecteAPI.openPanelForText("your text") oGrammalecteAPI.openPanelForText("your text", "node_id") oGrammalecteAPI.openPanelForText("your text", node) -With the second parameter, Grammalecte will send an event to the node each times the text is modified within the panel. +With the second parameter, Grammalecte will send an event to the node each times the text is modified within the panel. **The node must have an identifier**. ### Parse a node and get errors oGrammalecteAPI.parseNode("node_id") oGrammalecteAPI.parseNode(node) -The node can be a textarea, an editable node or an iframe. The node must have an identifier. +The node can be a textarea, an editable node or an iframe. **The node must have an identifier**. Results (for each paragraph) will be sent in a succession of events at the node. node.addEventListener("GrammalecteResult", function (event) { const detail = (typeof(event.detail) === 'string') && JSON.parse(event.detail); if (detail.sType && detail.sType == "proofreading") { @@ -120,12 +120,12 @@ ### Parse text and get errors oGrammalecteAPI.parseText("your text", "node_id") oGrammalecteAPI.parseText("your text", node) -The node must have an identifier. Like with `oGrammalecteAPI.parseNode()`, results (for each paragraph) will be sent in a succession of events at the node. +**The node must have an identifier**. ### Get spelling suggestions Index: gc_lang/fr/build.py ================================================================== --- gc_lang/fr/build.py +++ gc_lang/fr/build.py @@ -1,35 +1,57 @@ # Builder for French language import os import platform import zipfile +import shutil +import json +import traceback from distutils import dir_util, file_util import helpers def build (sLang, dVars): "complementary build launched from make.py" createWebExtension(sLang, dVars) + convertWebExtensionForChrome(sLang, dVars) createMailExtension(sLang, dVars) createNodeJSPackage(sLang) def createWebExtension (sLang, dVars): "create Web-extension" - print("Building WebExtension") + print("> Building WebExtension for Firefox") helpers.createCleanFolder("_build/webext/"+sLang) dir_util.copy_tree("gc_lang/"+sLang+"/webext/", "_build/webext/"+sLang) dir_util.copy_tree("grammalecte-js", "_build/webext/"+sLang+"/grammalecte") dVars['webextOptionsHTML'] = _createOptionsForWebExtension(dVars) helpers.copyAndFileTemplate("_build/webext/"+sLang+"/manifest.json", "_build/webext/"+sLang+"/manifest.json", dVars) helpers.copyAndFileTemplate("_build/webext/"+sLang+"/panel/main.html", "_build/webext/"+sLang+"/panel/main.html", dVars) with helpers.CD("_build/webext/"+sLang): os.system("web-ext build") # Copy Firefox zip extension to _build - helpers.moveFolderContent("_build/webext/"+sLang+"/web-ext-artifacts", "_build", "fx-"+sLang+"-", True) + helpers.moveFolderContent("_build/webext/"+sLang+"/web-ext-artifacts", "_build", "firefox-", True) + + +def convertWebExtensionForChrome (sLang, dVars): + "Create the extension for Chrome" + print("> Converting WebExtension for Chrome") + try: + with open(f"_build/webext/{sLang}/manifest.json", "r", encoding="utf-8") as hSrc: + dManifest = json.load(hSrc) + if "applications" in dManifest: + del dManifest["applications"] + if "chrome_settings_overrides" in dManifest: + del dManifest["chrome_settings_overrides"] + with open(f"_build/webext/{sLang}/manifest.json", "w", encoding="utf-8") as hDst: + json.dump(dManifest, hDst, ensure_ascii=True, indent=2) + shutil.make_archive(f"_build/chrome-grammalecte-{sLang}-v{dVars['version']}", 'zip', "_build/webext/"+sLang) + except: + traceback.print_exc() + print(" Error. Converting the WebExtension for Chrome failed.") def _createOptionsForWebExtension (dVars): sHTML = "" sLang = dVars['sDefaultUILang'] @@ -42,11 +64,11 @@ return sHTML def createMailExtension (sLang, dVars): "create extension for Thunderbird (as MailExtension)" - print("Building extension for Thunderbird (MailExtension)") + print("> Building extension for Thunderbird (MailExtension)") 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) Index: gc_lang/fr/rules.grx ================================================================== --- gc_lang/fr/rules.grx +++ gc_lang/fr/rules.grx @@ -1490,18 +1490,20 @@ ## Caractères rares __/ocr(ocr_caractères_rares)__ \w*[{}<>&*#£^|]+\w* <<- \0 != "<" and \0 != ">" ->> _ # Erreur de numérisation ? Cette chaîne contient un caractère de fréquence rare. -__/ocr(ocr_doublons_caractères_rares)__ - [\]\[({}][\]\[({}]+ - <<- ->> _ # Erreur de numérisation ? Succession douteuse de caractères. - TEST: __ocr__ trouve {{l£}} temps TEST: __ocr__ elle s’{{avance*}} sur le seuil TEST: __ocr__ {{e||e}} vient TEST: __ocr__ par beaucoup d’argent ? {{{Il}} débouche le Jack Daniels + + +__/ocr(ocr_doublons_caractères_rares)__ + [\]\[({}][\]\[({}]+ + <<- ->> _ # Erreur de numérisation ? Succession douteuse de caractères. + TEST: __ocr__ {{[[}}voyons celà]. TEST: __ocr__ {{((}}voyons ceci). ## Mélange chiffres/lettres @@ -4164,11 +4166,11 @@ >arrière >petit >nièce <<- /tu/ ->> arrière-petite-nièce|arrière-petites-nièces # Mettez des traits d’union. [basket|volley] ball - <<- /tu/ ->> \1-ball # Il manque un trait d’union. + <<- /tu/ ->> \1-\2 # Il manque un trait d’union. bas [>côté|coté|cotés] <<- /tu/ morph(<1 , ":D") ->> bas-côté|bas-côtés # Il manque un trait d’union. [les|des|aux] beaux arts @@ -4181,10 +4183,22 @@ <<- /tu/ ->> centre-ville|centres-villes # Il manque un trait d’union. [chassé+ses] [croisé+ses] <<- /tu/ ->> chassé-croisé|chassés-croisés # Il manque un trait d’union. + château fort + <<- /tu/ not morph(>1, ":A.*:[me]:[si]") ->> \1-\2 # Il manque un trait d’union.|https://fr.wiktionary.org/wiki/ch%C3%A2teau-fort + + châteaux forts + <<- /tu/ ->> \1-\2 # Il manque un trait d’union.|https://fr.wiktionary.org/wiki/ch%C3%A2teau-fort + + coffre fort + <<- /tu/ not morph(>1, ":A.*:[me]:[si]") ->> \1-\2 # Il manque un trait d’union.|https://fr.wiktionary.org/wiki/coffre-fort + + coffres forts + <<- /tu/ ->> \1-\2 # Il manque un trait d’union.|https://fr.wiktionary.org/wiki/coffre-fort + >chef lieu <<- /tu/ ->> \1-\2 # Il manque un trait d’union. >chef d’ œuvre <<- /tu/ ->> \1-\2\3 # Il manque un trait d’union. @@ -4204,11 +4218,11 @@ <<- /tu/ morph(<1, ":D") ->> face-à-face # Si vous employez cette locution comme un nom, mettez des traits d’union.|https://fr.wiktionary.org/wiki/face-%C3%A0-face gagne >pain <<- /tu/ not morph(<1, ":O[sv]") ->> gagne-pain # Il manque un trait d’union. - [grand|grands] [>père|>mère] + [grand+s] [>père|>mère] <<- /tu/ ->> \1-\2 # Il manque un trait d’union. lèse majesté <<- /tu/ ->> \1-\2 # Il manque un trait d’union. @@ -4328,10 +4342,14 @@ TEST: {{le}} {{bouche à oreille}} TEST: {{chef lieu}} de ce canton TEST: un {{chassé croisé}} TEST: ne vois-tu pas que c’est un {{chef d’œuvre}} ? TEST: ils sont allés au {{centre ville}}. +TEST: un {{château fort}} n’est pas une demeure confortable +TEST: des {{châteaux forts}} +TEST: un {{coffre fort}} +TEST: des {{coffres forts}} TEST: Le {{moto club}} était sur le point de faire faillite. TEST: J’ai beaucoup de {{déjà vu}}. TEST: L’{{état major}} n’a pas encore tranché la question. TEST: Le {{face à face}} entre les forces de l’ordre et les manifestants se poursuit devant l’hôtel de ville à Bordeaux depuis environ 1h30. TEST: {{gagne pain}} de merde @@ -7466,11 +7484,11 @@ <<- /conf/ -1>> ailes # Confusion probable : “elle” est un pronom personnel féminin. Pour les oiseaux, les avions ou les parties d’un bâtiment ou d’une armée, écrivez “aile”.|https://fr.wiktionary.org/wiki/aile [elle+s] ?[droite|gauche]¿ du [château|palais|bâtiment|manoir] [elle+s] ?[droite|gauche]¿ [de|d’] la [maison|ferme] - <<- /conf/ -1>> aile|ailes + <<- /conf/ morph(<1, "|,|:D") -1>> aile|ailes # Confusion probable : “elle” est un pronom personnel féminin. Pour les oiseaux, les avions ou les parties d’un bâtiment ou d’une armée, écrivez “aile”.|https://fr.wiktionary.org/wiki/aile TEST: l’{{elle}} est en feu. TEST: sous l’{{elle}} de sa mère, il ne craint rien TEST: sur son {{elle}} droite @@ -8011,14 +8029,14 @@ TEST: Quelle dure {{a}} {{cuir}}, celle-là. # dans / d’en __conf_dans_dan_d_en__ - d’ en [le|la|l’|les|de|d’|des|du|un|une|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs|ce|cet|cette|ces] + d’ en [le|la|l’|les|de|d’|des|du|un|une|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs|ce|cet|cette|ces|chacun|plusieurs|quelques|certains|certaines] <<- /conf/ not (\3 == "ce" and value(>1, "|moment|")) -1:2>> dans # Confusion. Utilisez “dans” pour dire “à l’intérieur de quelque chose”. - [>dent|dan] [le|la|l’|les|un|une|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs|ce|cet|cette|ces] + [>dent|dan] [le|la|l’|les|un|une|mon|ton|son|ma|ta|sa|mes|tes|ses|notre|votre|nos|vos|leurs|ce|cet|cette|ces|chacun|plusieurs|quelques|certains|certaines] <<- /conf/ -2>> dans # Confusion. Utilisez “dans” pour dire “à l’intérieur de quelque chose”. TEST: {{dan}} la voiture TEST: ils sont partis {{d’en}} une direction différente TEST: {{dents}} les montagnes au loin. @@ -11566,32 +11584,32 @@ année [après|par] année siècle [après|par] siècle génération [après|par] génération <<- ~>> * - [à|de] ~^\d\d?$ h ?~^\d\d?$¿ - [à|de] ~^\d\d?$ h ?~^\d\d?$¿ [du|ce] [matin|soir] - [à|de] ~^\d\d?$ h ?~^\d\d?$¿ de l’ après-midi - [à|de] ~^\d\d?$ h ?~^\d\d?$¿ cet après-midi - [à|de] ~^\d\d?$ h ?~^\d\d?$¿ [demain|hier] ?[matin|soir|après-midi]¿ - jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ - jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ [du|ce] [matin|soir] - jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ de l’ après-midi - jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ cet après-midi - jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ [demain|hier] ?[matin|soir|après-midi]¿ + [à|de] ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ + [à|de] ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ [du|ce] [matin|soir] + [à|de] ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ de l’ après-midi + [à|de] ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ cet après-midi + [à|de] ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ [demain|hier] ?[matin|soir|après-midi]¿ + jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ + jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ [du|ce] [matin|soir] + jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ de l’ après-midi + jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ cet après-midi + jusqu’ à ~^\d\d?$ h ?~^\d\d?$¿ ?[tapantes|pétantes]¿ [demain|hier] ?[matin|soir|après-midi]¿ <<- ~>> * - [à|de] *HOUR - [à|de] *HOUR [du|ce] [matin|soir] - [à|de] *HOUR de l’ après-midi - [à|de] *HOUR cet après-midi - [à|de] *HOUR [demain|hier] ?[matin|soir|après-midi]¿ - jusqu’ à *HOUR - jusqu’ à *HOUR [du|ce] [matin|soir] - jusqu’ à *HOUR de l’ après-midi - jusqu’ à *HOUR cet après-midi - jusqu’ à *HOUR [demain|hier] ?[matin|soir|après-midi]¿ + [à|de] *HOUR ?[tapantes|pétantes]¿ + [à|de] *HOUR ?[tapantes|pétantes]¿ [du|ce] [matin|soir] + [à|de] *HOUR ?[tapantes|pétantes]¿ de l’ après-midi + [à|de] *HOUR ?[tapantes|pétantes]¿ cet après-midi + [à|de] *HOUR ?[tapantes|pétantes]¿ [demain|hier] ?[matin|soir|après-midi]¿ + jusqu’ à *HOUR ?[tapantes|pétantes]¿ + jusqu’ à *HOUR ?[tapantes|pétantes]¿ [du|ce] [matin|soir] + jusqu’ à *HOUR ?[tapantes|pétantes]¿ de l’ après-midi + jusqu’ à *HOUR ?[tapantes|pétantes]¿ cet après-midi + jusqu’ à *HOUR ?[tapantes|pétantes]¿ [demain|hier] ?[matin|soir|après-midi]¿ <<- ~>> * TEST: Le train de 2 h 47 {{arriveraient}} en retard. TEST: Le train de 2 h 47 du matin {{arriveraient}} en retard. TEST: Le train de 2h47 du matin {{arriveraient}} en retard. @@ -13457,10 +13475,11 @@ >chemise [de|d’] nuit >chemise sans >manche >chèque sans provision >chili con carne >chou à la crème ?[fouettée|diplomate|mousseline|patissière|pralinée]¿ + >cité u >clair comme [de|d’] l’ eau [de|d’] [boudin|roche|source] >clair comme du cristal >clair comme jus [de|d’] [boudin|>chaussette|chique] >classement sans suite [>clé|>clef] à molette @@ -13705,10 +13724,11 @@ [remis+es] à plat >requête en nullité >requête en non [conciliation|inscription|lieu] >requête en non révocation [de|d’] sursis >responsable qualité + [>resto|>restau] u >retour à la case départ >rivière à sec >robe [de|d’] [chambre|soirée] >robe du soir >robe sans >manche @@ -18889,17 +18909,20 @@ [>patte|pat] au [basilic|beurre|saumon|pesto|poulet|thon] [>patte|pat] [à|a] [>pain|>crêpe|>gaufre|>pizza|>tarte|>modeler|>tartiner] [>patte|pat] [d’|de] [>amande|>amende|>fruit] <<- /conf/ -1>> pâte|pâtes # Confusion. La patte est le membre d’un animal (ou d’une table…). La matière pâteuse s’écrit “pâte”. - >mettre ?[pas|jamais]¿ la main à la [>patte|pat] + >mettre la main à [la|cette] [>patte|pat] >coq en [>patte|pat] <<- /conf/ --1>> pâte # Confusion. La patte est le membre d’un animal (ou d’une table…). La matière pâteuse s’écrit “pâte”. >coup de [>pâte|pat] <<- /conf/ -3>> patte # Confusion. Une pâte est une matière pâteuse. Pour désigner le membre d’un animal, écrivez “patte”. + >montrer [>pâte|pat] [blanche+s] + <<- /conf/ -2:3>> patte blanche # Confusion. Une pâte est une matière pâteuse. Pour désigner le membre d’un animal, écrivez “patte”. + >retomber sur [mes|tes|ses|ces|nos|vos|leur|leurs] [>pâte|pat] <<- /conf/ --1>> pattes # Confusion. Une pâte est une matière pâteuse. Pour désigner le membre d’un animal, écrivez “patte”. >court sur [>pâte|pat] <<- /conf/ --1>> pattes # Confusion. Une pâte est une matière pâteuse. Pour désigner le membre d’un animal, écrivez “patte”. @@ -18910,13 +18933,15 @@ TEST: Elles ont acheté de la {{patte}} à tartiner. TEST: La {{patte}} à pizza est plus épaisse que la pâte à crêpes. TEST: Elle adore la {{patte}} d’amande. TEST: Il est comme un coq en {{pattes}} là. TEST: Elle n’a jamais mis la main à la {{patte}}. +TEST: ne mets jamais la main à la {{patte}} TEST: donne-moi un coup de {{pâte}} TEST: elle retombe toujours sur ses {{pâtes}}. TEST: il est court sur {{pâtes}} +TEST: elle va montrer {{pâte blanche}} et tout ira bien. # péché / pêcher __conf_péché_pêcher2__ >pêcher par excès [de|d’] @@ -19236,10 +19261,28 @@ <<- /conf/ not morph(<1, ">plaie/") -2>> =\2.replace("sep", "scep") # Confusion possible. Septique = corrompu, infecté. Sceptique = ayant des doutes. TEST: cette fosse {{sceptique}} est pleine. TEST: Je suis {{septique}} ! + +## se / ce +__conf_ce_se_nom__ + [de|par|pour|sans] se *WORD + <<- /conf/ analyse(\3, ":N.*:[me]:[si]", ":Y") -2>> ce # Confusion probable. Si “\-1” est bien un nom ou un adjectif, alors écrivez “ce”. + + se *WORD [ne|n’|me|m’|te|t’|se|s’] + se *WORD [le|la|l’|les|en|nous|vous|lui|leur|y] @:(?:[123][sp]|P)¬:G + se *WORD [nous|vous] [le|la|l’|les|en|y] @:(?:[123][sp]|P) + se *WORD [le|la|l’|les] [lui|leur|en|y] @:(?:[123][sp]|P) + se *WORD [lui|leur|y] en @:(?:[123][sp]|P) + se *WORD @:(?:[123][sp]|P)¬:G + <<- /conf/ analyse(\3, ":[NA]", ":Y") -2>> ce # Confusion probable. Si “\3” est bien un nom ou un adjectif, alors écrivez “ce”. + +TEST: que sais-tu de {{se}} {{type}} +TEST: {{se}} type nous emmerde. ->> ce +TEST: se doit d’être fort, ce con, sinon c’est la fin pour lui. + ## soit / soie / soi __conf_aller_de_soi__ >aller de [sois|>soie] <<- /conf/ -3>> soi # Confusion.|https://fr.wiktionary.org/wiki/aller_de_soi @@ -19309,18 +19352,35 @@ >tacher [de|d’] [le|la|l’|les] [lui|leur|en|y] $:Y >tacher [de|d’] [lui|leur] en $:Y <<- /conf/ -1>> =\1.replace("a", "â").replace("A", "Â") # Confusion. Tache signifie faire une salissure, une altération, une marque, une coloration… Pour parler de l’accomplissement d’un travail, écrivez “tâcher”. + [|,|(] *WORD >tache >être [de|d’] [ne|n’|me|m’|te|t’|se|s’] + [|,|(] *WORD >tache >être [de|d’] [le|la|l’|les|en|nous|vous|lui|leur|y] @:(?:Y|V1.*:Q) + [|,|(] *WORD >tache >être [de|d’] [nous|vous] [le|la|l’|les|en|y] @:(?:Y|V1.*:Q) + [|,|(] *WORD >tache >être [de|d’] [le|la|l’|les] [lui|leur|en|y] @:(?:Y|V1.*:Q) + [|,|(] *WORD >tache >être [de|d’] [lui|leur|y] en @:(?:Y|V1.*:Q) + [|,|(] *WORD >tache >être [de|d’] @:(?:Y|V1.*:Q) + [|,|(] *WORD >tache >consister [à|a] [ne|n’|me|m’|te|t’|se|s’] + [|,|(] *WORD >tache >consister [à|a] [le|la|l’|les|en|nous|vous|lui|leur|y] @:(?:Y|V1.*:Q) + [|,|(] *WORD >tache >consister [à|a] [nous|vous] [le|la|l’|les|en|y] @:(?:Y|V1.*:Q) + [|,|(] *WORD >tache >consister [à|a] [le|la|l’|les] [lui|leur|en|y] @:(?:Y|V1.*:Q) + [|,|(] *WORD >tache >consister [à|a] [lui|leur|y] en @:(?:Y|V1.*:Q) + [|,|(] *WORD >tache >consister [à|a] @:(?:Y|V1.*:Q) + <<- /conf/ morph(\2, ":D", ":R") -3>> =\3.replace("ach", "âch").replace("ACH", "ÂCH") + # Confusion. Une tache est une salissure, une altération, une marque, une coloration… Pour parler d’un travail à accomplir, écrivez “tâche”. + TEST: Quelle {{tache}} ingrate. TEST: Une {{tache}} valorisante. TEST: Elle se tue à la {{tache}}. TEST: Il a accompli la {{tache}} facilement. TEST: {{Tache}} de partir tôt. TEST: {{Tachez}} d’arriver à l’heure. TEST: {{Tache}} de ne pas faire trop de bruit. TEST: se tuer à la {{tache}} +TEST: leur {{tache}} consiste à obéir et à fermer leur gueule +TEST: ma {{tache}} est d’accomplir ce que personne d’autre ne peut faire. TEST: une tâche dévalorisante. TEST: peiner à la tâche. # taule / tôle @@ -23067,17 +23127,19 @@ [|,|(] [les|plusieurs|leurs] @:V¬:[NAY] [le|la|l’|les|leur] @:[123][sp]¬:[QNAG] [|,|(] [les|plusieurs|leurs] @:V¬:[NAY] ?[nous|vous]¿ @:[123][sp]¬:[QG] <<- /conf/ hasSimil(\3, ":[NA].*:[pi]") -3>> =suggSimil(\3, ":[NA].*:[pi]", True) # Confusion probable : “\3” est une forme verbale conjuguée. Si “\2” est un déterminant, il faut placer un nom après. - [|,|(] certains @:V¬:[NAY] [ne|n’|me|m’|te|s’|se|s’] + [|,|(] certains @:V¬:[NAY] [me|m’|te|s’|se|s’] + [|,|(] certains @:V¬:[NAY] [ne|n’] @:[123][sp] [|,|(] certains @:V¬:[NAY] [le|la|l’|les|leur] @:[123][sp]¬:[QNAG] [|,|(] certains @:V¬:[NAY] ?[nous|vous]¿ @:[123][sp]¬:[QG] <<- /conf/ hasSimil(\3, ":[NA].*:[me]:[pi]") -3>> =suggSimil(\3, ":[NA].*:[me]:[pi]", True) # Confusion probable : “\3” est une forme verbale conjuguée. Si “\2” est un déterminant, il faut placer un nom après. - [|,|(] certaines @:V¬:[NAY] [ne|n’|me|m’|te|s’|se|s’] + [|,|(] certaines @:V¬:[NAY] [me|m’|te|s’|se|s’] + [|,|(] certaines @:V¬:[NAY] [ne|n’] @:[123][sp] [|,|(] certaines @:V¬:[NAY] [le|la|l’|les|leur] @:[123][sp]¬:[QNAG] [|,|(] certaines @:V¬:[NAY] ?[nous|vous]¿ @:[123][sp]¬:[QG] <<- /conf/ hasSimil(\3, ":[NA].*:[fe]:[pi]") -3>> =suggSimil(\3, ":[NA].*:[fe]:[pi]", True) # Confusion probable : “\3” est une forme verbale conjuguée. Si “\2” est un déterminant, il faut placer un nom après. @@ -23097,10 +23159,11 @@ TEST: La consigner devenait une obligation. TEST: Les reconnaître semblait nécessaire. TEST: Son ministre du Budget, Gérald Darmanin, l’avait dit plus crûment sur RTL TEST: Certains jouent la carte de la dérision TEST: Certains font grève tous les vendredis pour soutenir cette cause. +TEST: Certains confient ne pas avoir mangé depuis deux ou trois jours __conf_suj_verbe_det_verbe_nom__ [|,|(] [je|j’|tu|il|elle|on|nous|vous|ils|elles|iel|iels|ce|c’] ?[ne|n’]¿ ?[le|la|l’|les|en|me|m’|te|t’|se|s’|nous|vous|lui|leur|y]¿ @:[123][sp] [le|un] @:[123][sp]¬:[NA].*:[me]:[si] [|,|(] [je|j’|tu|il|elle|on|nous|vous|ils|elles|iel|iels|ce|c’] ?[ne|n’]¿ [me|m’|te|t’|se|s’|nous|vous] [le|la|l’|les|en|y] @:[123][sp] [le|un] @:[123][sp]¬:[NA].*:[me]:[si] Index: gc_lang/fr/webext/content_scripts/api.js ================================================================== --- gc_lang/fr/webext/content_scripts/api.js +++ gc_lang/fr/webext/content_scripts/api.js @@ -6,15 +6,22 @@ const oGrammalecteAPI = { // functions callable from within pages // to be sent to the content-cript via an event “GrammalecteCall” sVersion: "1.0", + + generateNodeId: function (xNode) { + xNode.id = "grammalecte_generated_id_" + Date.now().toString(36) + "_" + (Math.floor(Math.random() * (1000000))).toString(36); + console.log("[Grammalecte API] generated id:", xNode.id); + return xNode.id; + }, openPanelForNode: function (vNode) { // Parameter: a HTML node or the identifier of a HTML node - if (vNode instanceof HTMLElement && vNode.id) { - let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "openPanelForNode", sNodeId: vNode.id}) }); + if (vNode instanceof HTMLElement) { + let sNodeId = vNode.id || this.generateNodeId(vNode); + let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "openPanelForNode", sNodeId: sNodeId}) }); document.dispatchEvent(xEvent); } else if (typeof(vNode) === "string" && document.getElementById(vNode)) { let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "openPanelForNode", sNodeId: vNode}) }); document.dispatchEvent(xEvent); @@ -26,16 +33,19 @@ openPanelForText: function (sText, vNode=null) { // Parameter: text to analyze, and optionaly a node to send results to. if (typeof(sText) === "string") { let sNodeId = ""; - if (vNode instanceof HTMLElement && vNode.id) { - sNodeId = vNode.id; + if (vNode instanceof HTMLElement) { + sNodeId = vNode.id || this.generateNodeId(vNode); } else if (typeof(vNode) === "string" && document.getElementById(vNode)) { sNodeId = vNode; } + else { + console.log("[Grammalecte API] No node identifier. No event, no result will be sent.") + } let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "openPanelForText", sText: sText, sNodeId: sNodeId}) }); document.dispatchEvent(xEvent); } else { console.log("[Grammalecte API] Error: parameter is not a text."); } @@ -43,12 +53,13 @@ parseNode: function (vNode) { /* Parameter: a HTML node (with a identifier) or the identifier of a HTML node. The result will be sent as an event “GrammalecteResult” to the node. */ - if (vNode instanceof HTMLElement && vNode.id) { - let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "parseNode", sNodeId: vNode.id}) }); + if (vNode instanceof HTMLElement) { + let sNodeId = vNode.id || this.generateNodeId(vNode); + let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "parseNode", sNodeId: sNodeId}) }); document.dispatchEvent(xEvent); } else if (typeof(vNode) === "string" && document.getElementById(vNode)) { let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "parseNode", sNodeId: vNode}) }); document.dispatchEvent(xEvent); @@ -59,12 +70,13 @@ }, parseText: function (sText, vNode) { // Parameter: text to analyze, and a node to send results to. if (typeof(sText) === "string") { - if (vNode instanceof HTMLElement && vNode.id) { - let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "parseText", sText: sText, sNodeId: vNode.id}) }); + if (vNode instanceof HTMLElement) { + let sNodeId = vNode.id || this.generateNodeId(vNode); + let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "parseText", sText: sText, sNodeId: sNodeId}) }); document.dispatchEvent(xEvent); } else if (typeof(vNode) === "string" && document.getElementById(vNode)) { let xEvent = new CustomEvent("GrammalecteCall", { detail: JSON.stringify({sCommand: "parseText", sText: sText, sNodeId: vNode}) }); document.dispatchEvent(xEvent); 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 @@ -470,12 +470,17 @@ if (oCommand.sNodeId && document.getElementById(oCommand.sNodeId)) { oGrammalecte.startGCPanel(document.getElementById(oCommand.sNodeId)); } break; case "openPanelForText": - if (oCommand.sText && oCommand.sNodeId && document.getElementById(oCommand.sNodeId)) { - oGrammalecte.startGCPanel(oCommand.sText, document.getElementById(oCommand.sNodeId)); + 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); Index: gc_lang/fr/webext/content_scripts/menu.js ================================================================== --- gc_lang/fr/webext/content_scripts/menu.js +++ gc_lang/fr/webext/content_scripts/menu.js @@ -3,10 +3,11 @@ /* jshint esversion:6, -W097 */ /* jslint esversion:6 */ /* global oGrammalecte, showError, window, document */ "use strict"; + class GrammalecteButton { constructor () { // the pearl button @@ -37,20 +38,25 @@ } examineNode (xNode) { if (xNode && xNode instanceof HTMLElement) { if (xNode === this.xTextNode) { + this.move(); return; } if ( ( (xNode.tagName == "TEXTAREA" && this._bTextArea && xNode.getAttribute("spellcheck") !== "false") - || (xNode.isContentEditable && this._bEditableNode) + || ( (xNode.tagName == "P" || xNode.tagName == "DIV") && xNode.isContentEditable && this._bEditableNode ) || (xNode.tagName == "IFRAME" && this._bIframe) ) && xNode.style.display !== "none" && xNode.style.visibility !== "hidden" && !(xNode.dataset.grammalecte_button && xNode.dataset.grammalecte_button == "false") ) { this.xTextNode = xNode; this.show() } + else { + this.xTextNode = null; + this.hide(); + } } else { this.xTextNode = null; this.hide(); } @@ -57,21 +63,26 @@ } show () { if (this.xTextNode) { this.xButton.style.display = "none"; // we hide it before showing it again to relaunch the animation - let oCoord = oGrammalecte.getElementCoord(this.xTextNode); - //console.log("top:", oCoord.left, "bottom:", oCoord.top, "left:", oCoord.bottom, "right:", oCoord.right); - this.xButton.style.top = `${oCoord.bottom}px`; - this.xButton.style.left = `${oCoord.left}px`; + this.move(); this.xButton.style.display = "block"; } } hide () { this.xButton.style.display = "none"; } + + move () { + if (this.xTextNode) { + let oCoord = oGrammalecte.getElementCoord(this.xTextNode); + this.xButton.style.top = `${oCoord.bottom}px`; + this.xButton.style.left = `${oCoord.left}px`; + } + } insertIntoPage () { this.bShadow = document.body.createShadowRoot || document.body.attachShadow; if (this.bShadow) { this.xShadowBtn = oGrammalecte.createNode("div", { style: "display:none; position:absolute; width:0; height:0;" }); Index: gc_lang/fr/webext/content_scripts/panel.js ================================================================== --- gc_lang/fr/webext/content_scripts/panel.js +++ gc_lang/fr/webext/content_scripts/panel.js @@ -17,10 +17,11 @@ this.bFlexible = bFlexible; this.bHorizStrech = false; this.bVertStrech = false; this.nPositionX = 2; this.nPositionY = 2; + this.bOpened = false; this.bWorking = false; this.bShadow = document.body.createShadowRoot || document.body.attachShadow; if (this.bShadow) { this.xShadowPanel = oGrammalecte.createNode("div", {id: this.sId+"_shadow", style: "width:0;height:0;"}); @@ -142,14 +143,16 @@ } } show () { this.xPanel.style.display = "flex"; + this.bOpened = true; } hide () { this.xPanel.style.display = "none"; + this.bOpened = false; } center () { this.nPosition = 5; this.setSizeAndPosition(); 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 @@ -273,21 +273,26 @@ endTimer () { window.clearTimeout(this.nTimer); } recheckParagraph (iParaNum) { + if (!this.bOpened) { + return; + } let sParagraphId = "grammalecte_paragraph" + iParaNum; let xParagraph = this.xParent.getElementById(sParagraphId); this._blockParagraph(xParagraph); - let sText = this.purgeText(xParagraph.textContent); + //let sText = this.purgeText(xParagraph.textContent); + let sText = this.oTextControl.getParagraph(iParaNum); oGrammalecteBackgroundPort.parseAndSpellcheck1(sText, "__GrammalectePanel__", sParagraphId); - this.oTextControl.setParagraph(iParaNum, sText); - this.oTextControl.write(); } refreshParagraph (sParagraphId, oResult) { // function called when results are sent by the Worker + if (!this.bOpened) { + return; + } try { let xParagraph = this.xParent.getElementById(sParagraphId); // save caret position let [nStart, nEnd] = oGrammalecte.getCaretPosition(xParagraph); xParagraph.dataset.caret_position_start = nStart; @@ -403,12 +408,15 @@ //let sParaNum = sErrorId.slice(0, sErrorId.indexOf("-")); let xNodeErr = this.xParent.getElementById("grammalecte_err" + sErrorId); xNodeErr.textContent = this.xParent.getElementById(sNodeSuggId).textContent; xNodeErr.className = "grammalecte_error_corrected"; xNodeErr.removeAttribute("style"); + let iParaNum = parseInt(sErrorId.slice(0, sErrorId.indexOf("-")), 10); + this.oTextControl.setParagraph(iParaNum, this.purgeText(this.xParent.getElementById("grammalecte_paragraph" + iParaNum).textContent)); + this.oTextControl.write(); this.oTooltip.hide(); - this.recheckParagraph(parseInt(sErrorId.slice(0, sErrorId.indexOf("-")), 10)); + this.recheckParagraph(iParaNum); } catch (e) { showError(e); } } @@ -1026,10 +1034,14 @@ } setParagraph (iParagraph, sText) { this.dParagraph.set(iParagraph, sText); } + + getParagraph (iParaNum) { + return this.dParagraph.get(iParaNum); + } eraseNodeContent () { while (this.xNode.firstChild) { this.xNode.removeChild(this.xNode.firstChild); } @@ -1038,28 +1050,32 @@ write () { if (this.xNode !== null) { if (this.bResultInEvent) { const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: this.getText() }) }); this.xNode.dispatchEvent(xEvent); - //console.log("Text to xNode:", xEvent.detail); + console.log("[Grammalecte debug] Text sent to xNode via event:", xEvent.detail); } else if (this.bTextArea) { this.xNode.value = this.getText(); + console.log("[Grammalecte debug] text written in textarea:", this.getText()); } else if (this.bIframe) { //console.log(this.getText()); } else { + let sText = ""; this.eraseNodeContent(); this.dParagraph.forEach((val, key) => { this.xNode.appendChild(document.createTextNode(val.normalize("NFC"))); this.xNode.appendChild(document.createElement("br")); + sText += val.normalize("NFC") + "\n"; }); + console.log("[Grammalecte debug] text written in editable node:", sText); } } else if (this.xResultNode !== null) { const xEvent = new CustomEvent("GrammalecteResult", { detail: JSON.stringify({ sType: "text", sText: this.getText() }) }); this.xResultNode.dispatchEvent(xEvent); - //console.log("Text to xResultNode:", xEvent.detail); + console.log("[Grammalecte debug] Text sent to xResultNode via event:", xEvent.detail); } } } Index: graphspell-js/char_player.js ================================================================== --- graphspell-js/char_player.js +++ graphspell-js/char_player.js @@ -66,20 +66,20 @@ // Similar chars d1to1: new Map([ - ["1", "liîLIÎ"], - ["2", "zZ"], - ["3", "eéèêEÉÈÊ"], - ["4", "aàâAÀÂ"], - ["5", "sgSG"], - ["6", "bdgBDG"], - ["7", "ltLT"], - ["8", "bB"], - ["9", "gbdGBD"], - ["0", "oôOÔ"], + ["1", "1liîLIÎ"], + ["2", "2zZ"], + ["3", "3eéèêEÉÈÊ"], + ["4", "4aàâAÀÂ"], + ["5", "5sgSG"], + ["6", "6bdgBDG"], + ["7", "7ltLT"], + ["8", "8bB"], + ["9", "9gbdGBD"], + ["0", "0oôOÔ"], ["a", "aAàÀâÂáÁäÄāĀæÆ"], ["A", "AaÀàÂâÁáÄäĀāÆæ"], ["à", "aAàÀâÂáÁäÄāĀæÆ"], ["À", "AaÀàÂâÁáÄäĀāÆæ"], @@ -287,10 +287,12 @@ ["AI", ["EI", "É", "È", "Ê", "Ë"]], ["ei", ["ai", "é", "è", "ê", "ë"]], ["EI", ["AI", "É", "È", "Ê", "Ë"]], ["ch", ["sh", "c", "ss"]], ["CH", ["SH", "C", "SS"]], + ["ck", ["qu", "q"]], + ["CK", ["QU", "Q"]], ["ct", ["x", "cc"]], ["CT", ["X", "CC"]], ["oa", ["oi",]], ["OA", ["OI",]], ["oe", ["œ",]], Index: graphspell-js/suggest.js ================================================================== --- graphspell-js/suggest.js +++ graphspell-js/suggest.js @@ -28,10 +28,12 @@ ["pcq", "parce que"], ["pd", "pendant|pédé"], ["pdq", "pendant que"], ["pdt", "pendant"], ["pdtq", "pendant que"], + ["pécunier", "pécuniaire"], + ["pécuniers", "pécuniaires"], ["pk", "pourquoi"], ["pkoi", "pourquoi"], ["pq", "pourquoi|PQ"], ["prq", "presque"], ["prsq", "presque"], Index: graphspell/char_player.py ================================================================== --- graphspell/char_player.py +++ graphspell/char_player.py @@ -53,20 +53,20 @@ # Similar chars d1to1 = { - "1": "liîLIÎ", - "2": "zZ", - "3": "eéèêEÉÈÊ", - "4": "aàâAÀÂ", - "5": "sgSG", - "6": "bdgBDG", - "7": "ltLT", - "8": "bB", - "9": "gbdGBD", - "0": "oôOÔ", + "1": "1liîLIÎ", + "2": "2zZ", + "3": "3eéèêEÉÈÊ", + "4": "4aàâAÀÂ", + "5": "5sgSG", + "6": "6bdgBDG", + "7": "7ltLT", + "8": "8bB", + "9": "9gbdGBD", + "0": "0oôOÔ", "a": "aAàÀâÂáÁäÄāĀæÆ", "A": "AaÀàÂâÁáÄäĀāÆæ", "à": "aAàÀâÂáÁäÄāĀæÆ", "À": "AaÀàÂâÁáÄäĀāÆæ", @@ -275,10 +275,12 @@ "AI": ("EI", "É", "È", "Ê", "Ë"), "ei": ("ai", "é", "è", "ê", "ë"), "EI": ("AI", "É", "È", "Ê", "Ë"), "ch": ("sh", "c", "ss"), "CH": ("SH", "C", "SS"), + "ck": ("qu", "q"), + "CK": ("QU", "Q"), "ct": ("x", "cc"), "CT": ("X", "CC"), "gg": ("gu",), "GG": ("GU",), "gu": ("gg",), Index: graphspell/fr.py ================================================================== --- graphspell/fr.py +++ graphspell/fr.py @@ -41,10 +41,12 @@ "pcq": "parce que", "pd": "pendant", "pdq": "pendant que", "pdt": "pendant", "pdtq": "pendant que", + "pécunier": "pécuniaire", + "pécuniers": "pécuniaires", "pk": "pourquoi", "pkoi": "pourquoi", "pq": "pourquoi|PQ", "prq": "presque", "prsq": "presque", Index: helpers.py ================================================================== --- helpers.py +++ helpers.py @@ -99,12 +99,12 @@ return Template(open(spf, "r", encoding="utf-8").read()).safe_substitute(dVars) def copyAndFileTemplate (spfSrc, spfDst, dVars): "write file as with variables filed with " - s = Template(open(spfSrc, "r", encoding="utf-8").read()).safe_substitute(dVars) - open(spfDst, "w", encoding="utf-8", newline="\n").write(s) + sText = Template(open(spfSrc, "r", encoding="utf-8").read()).safe_substitute(dVars) + open(spfDst, "w", encoding="utf-8", newline="\n").write(sText) def addFolderToZipAndFileFile (hZip, spSrc, spDst, dVars, bRecursive): "add folder content to zip archive and file files with " # recursive function