Index: CHANGELOG.txt ================================================================== --- CHANGELOG.txt +++ CHANGELOG.txt @@ -22,16 +22,16 @@ Lexicographer French Conjugueur ## 0.4 Suggestion mechanisms - Simplier user options writing + Simpler user options writing Application Launcher Author field edition ## 0.5 Grammalecte is an autonomous package, free from Hunspell and LibreOffice Indexable binary dictionary (DAWG-FSA) generator Disambiguator Multi-actions rules (bi-passes engine again) - Simplier options for word boundaries + Simpler options for word boundaries Unit tests Index: LICENSE.fr.txt ================================================================== --- LICENSE.fr.txt +++ LICENSE.fr.txt @@ -113,11 +113,11 @@ vous assurer qu’eux aussi reçoivent ou peuvent recevoir son code source. Et vous devez leur montrer les termes de cette licence afin qu’ils connaissent leurs droits. Les développeurs qui utilisent la GPL GNU protègent vos droits en deux -étapes : (1) ils affirment leur droits d’auteur (“copyright”) sur le +étapes : (1) ils affirment leurs droits d’auteur (“copyright”) sur le logiciel, et (2) vous accordent cette Licence qui vous donne la permission légale de le copier, le distribuer et/ou le modifier. Pour la protection des développeurs et auteurs, la GPL stipule clairement qu’il n’y a pas de garantie pour ce logiciel libre. Aux fins @@ -130,11 +130,11 @@ à l’installation ou l’exécution de versions modifiées du logiciel à l’intérieur de ces dispositifs, alors que les fabricants le peuvent. Ceci est fondamentalement incompatible avec le but de protéger la liberté des utilisateurs de modifier le logiciel. L’aspect systématique de tels abus se produit dans le secteur des produits destinés aux -utilisateurs individuels, ce qui est précidément ce qui est le plus +utilisateurs individuels, ce qui est précisément ce qui est le plus inacceptable. Aussi, nous avons conçu cette version de la GPL pour prohiber cette pratique pour ces produits. Si de tels problèmes surviennent dans d’autres domaines, nous nous tenons prêt à étendre cette restriction à ces domaines dans de futures versions de la GPL, autant qu’il sera nécessaire pour protéger la liberté des utilisateurs. @@ -357,16 +357,16 @@ Une compilation d’un Travail Couvert avec d’autres travaux séparés et indépendants, qui ne sont pas par leur nature des extensions du Travail Couvert, et qui ne sont pas combinés avec lui de façon à former un programme plus large, dans ou sur un volume de stockage ou un support -de distribution, est appelé un « aggrégat » si la compilation et son +de distribution, est appelé un « agrégat » si la compilation et son Droit d’Auteur résultant ne sont pas utilisés pour limiter l’accès ou -les droits légaux des utilisateurs de la compilation en deça de ce que +les droits légaux des utilisateurs de la compilation en deçà de ce que permettent les travaux individuels. L’inclusion d’un Travail Couvert -dans un aggrégat ne cause pas l’application de cette Licence aux -autres parties de l’aggrégat. +dans un agrégat ne cause pas l’application de cette Licence aux +autres parties de l’agrégat. Article 6. Acheminement des formes non sources. Vous pouvez acheminer sous forme de code objet un Travail Couvert @@ -392,11 +392,11 @@ de la source, ou soit (2) un accès permettant de copier le Source Correspondant depuis un serveur réseau sans frais. c) Acheminer des copies individuelles du code objet avec une copie de l’offre écrite de fournir le Source Correspondant. Cette - alternative est permise seulement occasionellement et non + alternative est permise seulement occasionnellement et non commercialement, et seulement si vous avez reçu le code objet avec une telle offre, en accord avec l’article 6 alinéa b. d) Acheminer le code objet en offrant un accès depuis un emplacement désigné (gratuit ou contre facturation) et offrir un accès @@ -418,11 +418,11 @@ d’égal-à-égal, pourvu que vous informiez les autres participants sur où le code objet et le Source Correspondant du travail sont offerts sans frais au public général suivant l’article 6 alinéa d. Une portion séparable du code objet, dont le code source est exclu du Source Correspondant en tant que Bibliothèque Système, n’a pas - besoin d’être inclu dans l’acheminement du travail sous forme de + besoin d’être inclus dans l’acheminement du travail sous forme de code objet. Un « Produit Utilisateur » est soit (1) un « Produit de Consommation, » ce qui signifie toute propriété personnelle tangible normalement utilisée à des fins personnelles, familiales ou relatives au foyer, @@ -457,11 +457,11 @@ Utilisateur est transféré au Destinataire définitivement ou pour un terme fixé (indépendamment de la façon dont la transaction est caractérisée), le Source Correspondant acheminé selon cet article-ci doit être accompagné des Informations d’Installation. Mais cette obligation ne s’applique pas si ni vous ni aucune tierce partie ne -détient la possibilité d’intaller un code objet modifié sur le Produit +détient la possibilité d’installer un code objet modifié sur le Produit Utilisateur (par exemple, le travail a été installé en mémoire morte). L’obligation de fournir les Informations d’Installation n’inclue pas celle de continuer à fournir un service de support, une garantie ou des mises à jour pour un travail qui a été modifié ou installé par le @@ -479,23 +479,23 @@ la copie. Article 7. Termes additionnels. -Les « permissions additionelles » désignent les termes qui +Les « permissions additionnelles » désignent les termes qui supplémentent ceux de cette Licence en émettant des exceptions à l’une ou plusieurs de ses conditions. Les permissions additionnelles qui sont applicables au Programme entier doivent être traitées comme si -elles étaient incluent dans cette Licence, dans les limites de leur +elles étaient incluses dans cette Licence, dans les limites de leur validité suivant la loi applicable. Si des permissions additionnelles s’appliquent seulement à une partie du Programme, cette partie peut être utilisée séparément suivant ces permissions, mais le Programme tout entier reste gouverné par cette Licence sans regard aux -permissions additionelles. +permissions additionnelles. Quand vous acheminez une copie d’un Travail Couvert, vous pouvez à -votre convenance ôter toute permission additionelle de cette copie, ou +votre convenance ôter toute permission additionnelle de cette copie, ou de n’importe quelle partie de celui-ci. (Des permissions additionnelles peuvent être rédigées de façon à requérir leur propre suppression dans certains cas où vous modifiez le travail.) Vous pouvez placer les permissions additionnelles sur le matériel acheminé, ajoutées par vous à un Travail Couvert pour lequel vous avez ou pouvez @@ -664,11 +664,11 @@ à cette partie. Si vous acheminez un Travail Couvert, dépendant en connaissance d’une licence de brevet, et si le Source Correspondant du travail n’est pas disponible à quiconque copie, sans frais et suivant les termes de cette -Licence, à travers un serveur réseau publiquement acessible ou tout +Licence, à travers un serveur réseau publiquement accessible ou tout autre moyen immédiatement accessible, alors vous devez soit (1) rendre la Source Correspondante ainsi disponible, soit (2) vous engager à vous priver pour vous-même du bénéfice de la licence de brevet pour ce travail particulier, soit (3) vous engager, d’une façon consistante avec les obligations de cette Licence, à étendre la licence de brevet @@ -714,11 +714,11 @@ Si des conditions vous sont imposées (que ce soit par décision judiciaire, par un accord ou autrement) qui contredisent les conditions de cette Licence, elles ne vous excusent pas des conditions de cette Licence. Si vous ne pouvez pas acheminer un Travail Couvert de façon à -satisfaire simulténément vos obligations suivant cette Licence et +satisfaire simultanément vos obligations suivant cette Licence et toutes autres obligations pertinentes, alors en conséquence vous ne pouvez pas du tout l’acheminer. Par exemple, si vous avez un accord sur des termes qui vous obligent à collecter pour le réacheminement des royalties depuis ceux à qui vous acheminez le Programme, la seule façon qui puisse vous permettre de satisfaire à la fois à ces termes et ceux @@ -762,12 +762,12 @@ versions futures de la Licence Générale Publique GNU peut être utilisée, la déclaration publique d’acceptation d’une version par cet intermédiaire vous autorise à choisir cette version pour le Programme. Des versions ultérieures de la licence peuvent vous donner des -permissions additionelles ou différentes. Cependant aucune obligation -additionelle n’est imposée à l’un des auteurs ou titulaires de Droit +permissions additionnelles ou différentes. Cependant aucune obligation +additionnelle n’est imposée à l’un des auteurs ou titulaires de Droit d’Auteur du fait de votre choix de suivre une version ultérieure. Article 15. Déclaration d’absence de garantie. Index: THANKS.txt ================================================================== --- THANKS.txt +++ THANKS.txt @@ -1,11 +1,11 @@ # THANKS ## Thanks to all who contributed to the project László Németh (creator of Lightproof, from which Grammalecte forked), -Dominique Pelé (dominiko), +Dominique Pellé (dominiko), Jean-Luc T. (Tbj), Pierre Choffardet (pitpit), ## Thanks to all those who supported us @@ -179,6 +179,7 @@ Yann Asset, Yann Brelière, Yannick Geynet, Yelin -et aux centaines de contributeurs qui ont préféré garder l’anonymat, ainsi qu’à la GPL3 de Richard Matthew Stallman. +and to hundred of supporters who prefered to keep their anonymity, +and to the GPL3 by Richard Matthew Stallman. Index: compile_rules.py ================================================================== --- compile_rules.py +++ compile_rules.py @@ -449,11 +449,11 @@ del lRule[-1] # tGroups positioning codes are useless for Python # error messages for aAction in lRuleJS[6]: if aAction[1] == "-": aAction[2] = aAction[2].replace(" ", " ") # nbsp --> nnbsp - aAction[4] = aAction[4].replace("« ", "« ").replace(" »", " »") + aAction[4] = aAction[4].replace("« ", "« ").replace(" »", " »").replace(" :", " :").replace(" :", " :") # js regexes lRuleJS[1], lNegLookBehindRegex = regex2js( dJSREGEXES.get(lRuleJS[3], lRuleJS[1]) ) lRuleJS.append(lNegLookBehindRegex) return lRuleJS Index: gc_core/js/helpers.js ================================================================== --- gc_core/js/helpers.js +++ gc_core/js/helpers.js @@ -30,10 +30,19 @@ } else { console.error(sMsg); } } +function inspect (o) { + let sMsg = "__inspect__: " + typeof o; + for (let sParam in o) { + sMsg += "\n" + sParam + ": " + o.sParam; + } + sMsg += "\n" + JSON.stringify(o) + "\n__end__"; + echo(sMsg); +} + // load ressources in workers (suggested by Mozilla extensions reviewers) // for more options have a look here: https://gist.github.com/Noitidart/ec1e6b9a593ec7e3efed // if not in workers, use sdk/data.load() instead function loadFile (spf) { @@ -78,11 +87,12 @@ obj[k] = v; } return obj; } +exports.setLogOutput = setLogOutput; exports.echo = echo; exports.logerror = logerror; +exports.inspect = inspect; exports.objectToMap = objectToMap; exports.mapToObject = mapToObject; -exports.setLogOutput = setLogOutput; exports.loadFile = loadFile; 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 @@ -218,11 +218,11 @@ sMessage = oEvalFunc[sMsg.slice(1)](s, m) } else { sMessage = sMsg._expand(m); } if (bIdRule) { - sMessage += " #" + sLineId + " #" + sRuleId; + sMessage += " ##" + sLineId + " #" + sRuleId; } oErr["sMessage"] = sMessage; // URL oErr["URL"] = sURL || ""; // Context Index: gc_core/js/text.js ================================================================== --- gc_core/js/text.js +++ gc_core/js/text.js @@ -37,19 +37,20 @@ } function getReadableError (oErr) { // Returns an error oErr as a readable error try { - let s = "\n* " + oErr['nStart'] + ":" + oErr['nEnd'] + " # " + oErr['sRuleId']+":\n"; - s += " " + oErr["sMessage"]; + let sResult = "\n* " + oErr['nStart'] + ":" + oErr['nEnd'] + + " # " + oErr['sLineId'] + " # " + oErr['sRuleId'] + ":\n"; + sResult += " " + oErr["sMessage"]; if (oErr["aSuggestions"].length > 0) { - s += "\n > Suggestions : " + oErr["aSuggestions"].join(" | "); + sResult += "\n > Suggestions : " + oErr["aSuggestions"].join(" | "); } if (oErr["URL"] !== "") { - s += "\n > URL: " + oErr["URL"]; + sResult += "\n > URL: " + oErr["URL"]; } - return s; + return sResult; } catch (e) { helpers.logerror(e); return "\n# Error. Data: " + oErr.toString(); } Index: gc_core/js/tokenizer.js ================================================================== --- gc_core/js/tokenizer.js +++ gc_core/js/tokenizer.js @@ -3,17 +3,16 @@ "use strict"; const helpers = require("resource://grammalecte/helpers.js"); - const aPatterns = { // All regexps must start with ^. "default": [ [/^[   \t]+/, 'SPACE'], - [/^[,.;:!?…«»“”"()/·]+/, 'SEPARATOR'], + [/^[,.;:!?…«»“”‘’"(){}\[\]/·–—]+/, 'SEPARATOR'], [/^(?:https?:\/\/|www[.]|[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_]+[@.][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_]+[@.])[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.\/?&!%=+*"'@$#-]+/, 'LINK'], [/^[#@][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]+/, 'TAG'], [/^<[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+.*?>|<\/[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+ *>/, 'HTML'], [/^\[\/?[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+\]/, 'PSEUDOHTML'], [/^&\w+;(?:\w+;|)/, 'HTMLENTITY'], @@ -22,19 +21,19 @@ [/^[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+(?:[’'`-][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+)*/, 'WORD'] ], "fr": [ [/^[   \t]+/, 'SPACE'], - [/^[,.;:!?…«»“”"()/·]+/, 'SEPARATOR'], + [/^[,.;:!?…«»“”‘’"(){}\[\]/·–—]+/, 'SEPARATOR'], [/^(?:https?:\/\/|www[.]|[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_]+[@.][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_]+[@.])[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_.\/?&!%=+*"'@$#-]+/, 'LINK'], [/^[#@][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st_-]+/, 'TAG'], [/^<[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+.*?>|<\/[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+ *>/, 'HTML'], [/^\[\/?[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+\]/, 'PSEUDOHTML'], [/^&\w+;(?:\w+;|)/, 'HTMLENTITY'], [/^(?:l|d|n|m|t|s|j|c|ç|lorsqu|puisqu|jusqu|quoiqu|qu)['’`]/i, 'ELPFX'], [/^\d\d?[hm]\d\d\b/, 'HOUR'], - [/^\d+(?:er|nd|e|de|ième|ème|eme)\b/, 'ORDINAL'], + [/^\d+(?:er|nd|e|de|ième|ème|eme)s?\b/, 'ORDINAL'], [/^-?\d+(?:[.,]\d+|)/, 'NUM'], [/^[a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+(?:[’'`-][a-zA-Zà-öÀ-Ö0-9ø-ÿØ-ßĀ-ʯfi-st]+)*/, 'WORD'] ] } @@ -44,11 +43,11 @@ constructor (sLang) { this.sLang = sLang; if (!aPatterns.hasOwnProperty(sLang)) { this.sLang = "default"; } - this.aRules = aPatterns[sLang]; + this.aRules = aPatterns[this.sLang]; }; * genTokens (sText) { let m; let i = 0; @@ -55,11 +54,17 @@ while (sText) { let nCut = 1; for (let [zRegex, sType] of this.aRules) { try { if ((m = zRegex.exec(sText)) !== null) { - yield { "sType": sType, "sValue": m[0], "nStart": i, "nEnd": i + m[0].length } + if (sType == 'SEPARATOR') { + for (let c of m[0]) { + yield { "sType": sType, "sValue": c, "nStart": i, "nEnd": i + m[0].length } + } + } else { + yield { "sType": sType, "sValue": m[0], "nStart": i, "nEnd": i + m[0].length } + } nCut = m[0].length; break; } } catch (e) { @@ -67,9 +72,19 @@ } } i += nCut; sText = sText.slice(nCut); } + }; + + getSpellingErrors (sText, oDict) { + let aSpellErr = []; + for (let oToken of this.genTokens(sText)) { + if (oToken.sType === 'WORD' && !oDict.isValidToken(oToken.sValue)) { + aSpellErr.push(oToken); + } + } + return aSpellErr; } } exports.Tokenizer = Tokenizer; Index: gc_lang/fr/config.ini ================================================================== --- gc_lang/fr/config.ini +++ gc_lang/fr/config.ini @@ -3,11 +3,11 @@ lang_name = French locales = fr_FR fr_BE fr_CA fr_CH fr_LU fr_MC fr_BF fr_CI fr_SN fr_ML fr_NE fr_TG fr_BJ country_default = FR name = Grammalecte implname = grammalecte -version = 0.5.17.2 +version = 0.5.18 author = Olivier R. provider = Dicollecte link = http://grammalecte.net description = Correcteur grammatical pour le français. extras = README_fr.txt Index: gc_lang/fr/modules-js/lexicographe.js ================================================================== --- gc_lang/fr/modules-js/lexicographe.js +++ gc_lang/fr/modules-js/lexicographe.js @@ -2,13 +2,15 @@ // License: MPL 2 "use strict"; ${string} +${map} const helpers = require("resource://grammalecte/helpers.js"); +const tkz = require("resource://grammalecte/tokenizer.js"); const _dTAGS = new Map ([ [':G', "[mot grammatical]"], [':N', " nom,"], @@ -155,10 +157,42 @@ ["m'en", " (me) pronom personnel objet + (en) pronom adverbial"], ["t'en", " (te) pronom personnel objet + (en) pronom adverbial"], ["s'en", " (se) pronom personnel objet + (en) pronom adverbial"] ]); +const _dSeparator = new Map ([ + ['.', "point"], + ['·', "point médian"], + ['…', "points de suspension"], + [':', "deux-points"], + [';', "point-virgule"], + [',', "virgule"], + ['?', "point d’interrogation"], + ['!', "point d’exclamation"], + ['(', "parenthèse ouvrante"], + [')', "parenthèse fermante"], + ['[', "crochet ouvrante"], + [']', "crochet fermante"], + ['{', "accolade ouvrante"], + ['}', "accolade fermante"], + ['-', "tiret"], + ['—', "tiret cadratin"], + ['–', "tiret demi-cadratin"], + ['«', "guillemet ouvrant (chevrons)"], + ['»', "guillemet fermant (chevrons)"], + ['“', "guillemet ouvrant double"], + ['”', "guillemet fermant double"], + ['‘', "guillemet ouvrant"], + ['’', "guillemet fermant"], + ['/', "signe de la division"], + ['+', "signe de l’addition"], + ['*', "signe de la multiplication"], + ['=', "signe de l’égalité"], + ['<', "inférieur à"], + ['>', "supérieur à"], +]); + class Lexicographe { constructor (oDict) { this.oDict = oDict; @@ -165,71 +199,58 @@ this._zElidedPrefix = new RegExp ("^([dljmtsncç]|quoiqu|lorsqu|jusqu|puisqu|qu)['’](.+)", "i"); this._zCompoundWord = new RegExp ("([a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ]+)-((?:les?|la)-(?:moi|toi|lui|[nv]ous|leur)|t-(?:il|elle|on)|y|en|[mts][’'](?:y|en)|les?|l[aà]|[mt]oi|leur|lui|je|tu|ils?|elles?|on|[nv]ous)$", "i"); this._zTag = new RegExp ("[:;/][a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ*][^:;/]*", "g"); }; - analyzeText (sText) { - sText = sText.replace(/[.,.?!:;…\/()\[\]“”«»"„{}–—#+*<>%=\n]/g, " ").replace(/\s+/g, " "); - let iStart = 0; - let iEnd = 0; - let sHtml = '
\n'; - while ((iEnd = sText.indexOf(" ", iStart)) !== -1) { - sHtml += this.analyzeWord(sText.slice(iStart, iEnd)); - iStart = iEnd + 1; - } - sHtml += this.analyzeWord(sText.slice(iStart)); - return sHtml + '
\n'; - } - - analyzeWord (sWord) { + getInfoForToken (oToken) { + // Token: .sType, .sValue, .nStart, .nEnd + // return a list [type, token_string, values] + let m = null; try { - if (!sWord) { - return ""; - } - if (sWord._count("-") > 4) { - return '

' + sWord + " : élément complexe indéterminé

\n"; - } - if (sWord._isDigit()) { - return '

' + sWord + " : nombre

\n"; - } - - let sHtml = ""; - // préfixes élidés - let m = this._zElidedPrefix.exec(sWord); - if (m !== null) { - sWord = m[2]; - sHtml += "

" + m[1] + "’ : " + _dPFX.get(m[1].toLowerCase()) + "

\n"; - } - // mots composés - let m2 = this._zCompoundWord.exec(sWord); - if (m2 !== null) { - sWord = m2[1]; - } - // Morphologies - let lMorph = this.oDict.getMorph(sWord); - if (lMorph.length === 1) { - sHtml += "

" + sWord + " : " + this.formatTags(lMorph[0]) + "

\n"; - } else if (lMorph.length > 1) { - sHtml += "

" + sWord + "

\n"; - } else { - sHtml += '

' + sWord + " : absent du dictionnaire

\n"; - } - // suffixe d’un mot composé - if (m2) { - sHtml += "

-" + m2[2] + " : " + this._formatSuffix(m2[2].toLowerCase()) + "

\n"; - } - // Verbes - //let aVerb = new Set([ for (s of lMorph) if (s.includes(":V")) s.slice(1, s.indexOf(" ")) ]); - return sHtml; + switch (oToken.sType) { + case 'SEPARATOR': + return { sType: oToken.sType, sValue: oToken.sValue, aLabel: [_dSeparator._get(oToken.sValue, "caractère indéterminé")] }; + break; + case 'NUM': + return { sType: oToken.sType, sValue: oToken.sValue, aLabel: ["nombre"] }; + break; + case 'LINK': + return { sType: oToken.sType, sValue: oToken.sValue.slice(0,40)+"…", aLabel: ["hyperlien"] }; + break; + case 'ELPFX': + let sTemp = oToken.sValue.replace("’", "").replace("'", "").replace("`", "").toLowerCase(); + return { sType: oToken.sType, sValue: oToken.sValue, aLabel: [_dPFX._get(sTemp, "préfixe élidé inconnu")] }; + break; + case 'WORD': + if (oToken.sValue._count("-") > 4) { + return { sType: "COMPLEX", sValue: oToken.sValue, aLabel: ["élément complexe indéterminé"] }; + } + else if (this.oDict.isValidToken(oToken.sValue)) { + let lMorph = this.oDict.getMorph(oToken.sValue); + let aElem = [ for (s of lMorph) if (s.includes(":")) this._formatTags(s) ]; + return { sType: oToken.sType, sValue: oToken.sValue, aLabel: aElem}; + } + else if (m = this._zCompoundWord.exec(oToken.sValue)) { + // mots composés + let lMorph = this.oDict.getMorph(m[1]); + let aElem = [ for (s of lMorph) if (s.includes(":")) this._formatTags(s) ]; + aElem.push("-" + m[2] + ": " + this._formatSuffix(m[2].toLowerCase())); + return { sType: oToken.sType, sValue: oToken.sValue, aLabel: aElem }; + } + else { + return { sType: "UNKNOWN", sValue: oToken.sValue, aLabel: ["inconnu du dictionnaire"] }; + } + break; + } } catch (e) { helpers.logerror(e); - return "#erreur"; } + return null; }; - formatTags (sTags) { + _formatTags (sTags) { let sRes = ""; sTags = sTags.replace(/V([0-3][ea]?)[itpqnmr_eaxz]+/, "V$1"); let m; while ((m = this._zTag.exec(sTags)) !== null) { sRes += _dTAGS.get(m[0]); Index: gc_lang/fr/xpi/data/gc_panel.css ================================================================== --- gc_lang/fr/xpi/data/gc_panel.css +++ gc_lang/fr/xpi/data/gc_panel.css @@ -5,19 +5,16 @@ header { background-color: hsl(0, 0%, 96%); padding: 10px 20px; border-bottom: 1px solid hsl(0, 0%, 90%); color: hsl(0, 0%, 0%); - z-index: 99; } - - body { - background-color: hsl(0, 0%, 98%); - font-family: Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; - overflow-x: hidden; + background-color: hsl(0, 0%, 98%); + font-family: Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; + overflow-x: hidden; color: hsl(0, 0%, 0%); } #special_message { display: none; @@ -50,16 +47,20 @@ border-radius: 5px; text-align: center; color: hsla(0, 0%, 100%, .9); font-size: 16px; font-weight: bold; - z-index: 99; } #message { padding: 10px 20px; } + +/* + Error list +*/ + #errorlist { padding: 60px 20px 30px 20px; } #errorlist p.message { @@ -69,14 +70,19 @@ font-size: 18px; color: hsla(240, 0%, 96%, 1); border-radius: 3px; text-align: center; } + #errorlist p.green { background-color: hsla(120, 10%, 50%, 1); color: hsla(120, 0%, 96%, 1); } + +.paragraph_block { + margin: 0 0 10px 0; +} .paragraph { background-color: hsla(0, 0%, 90%, 1); padding: 10px; border-radius: 2px; @@ -86,11 +92,11 @@ } .softred { background-color: hsla(0, 20%, 90%, 1); } .softgreen { - background-color: hsla(120, 20%, 90%, 1); + background-color: hsla(120, 15%, 90%, 1); } .paragraph a { background-color: hsla(210, 50%, 50%, 1); padding: 1px 5px; @@ -103,36 +109,12 @@ background-color: hsla(210, 60%, 40%, 1); color: hsla(0, 0%, 100%, 1); text-shadow: 0 0 3px hsl(210, 30%, 60%); } -.paragraph a.sugg { - padding: 1px 6px; - background-color: hsla(150, 50%, 40%, 1); - color: hsla(150, 0%, 96%, 1); - border-radius: 2px; - cursor: pointer; - text-decoration: none; -} -.paragraph a.sugg:hover { - background-color: hsla(150, 70%, 30%, 1); - color: hsla(0, 0%, 100%, 1); - text-shadow: 0 0 3px hsl(150, 30%, 60%); -} - -.paragraph a.ignore { - padding: 0 2px; - background-color: hsla(30, 20%, 60%, 1); - color: hsla(30, 0%, 96%, 1); - border-radius: 2px; - cursor: pointer; - text-decoration: none; -} -.paragraph a.ignore:hover { - background-color: hsla(30, 20%, 50%, 1); - color: hsla(0, 0%, 100%, 1); - text-shadow: 0 0 3px hsl(30, 30%, 60%); +.paragraph u { + text-decoration: none; } .paragraph u.corrected, .paragraph u.ignored { background-color: hsla(120, 50%, 70%, 1); @@ -152,62 +134,147 @@ } .paragraph u.error:hover { cursor: pointer; } -.paragraph u.error .tooltip, .paragraph u.error .tooltip_on { - position: absolute; - background-color: hsla(210, 10%, 90%, 1); - font-family: Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; - top: 90%; - left: 0; - width: 250px; - font-size: 12px; - line-height: 18px; - color: hsla(0, 10%, 20%, 1); - cursor: default; - /*visibility: hidden;*/ - display: none; - padding: 10px; - box-shadow: 0 0 6px hsla(0, 0%, 0%, 0.3); - z-index: 10; - border: 2px solid hsl(0, 0%, 0%); - border-radius: 3px; - text-decoration: none; -} -.paragraph u.error .tooltip_on { - display: block; -} - -.tooltip_on s { - color: hsla(0, 0%, 66%, 1); - font-weight: bold; - font-size: 8px; - line-height: 16px; - text-transform: uppercase; - text-decoration: none; -} - -.debug { - float: right; - background-color: hsla(0, 5%, 35%, 1); - padding: 2px 5px; - margin-left: 5px; - border-radius: 2px; - color: hsla(0, 0%, 96%, 1); + +/* + TOOLTIPS +*/ +.tooltip { + position: absolute; + display: none; + width: 300px; + border-radius: 5px; + box-shadow: 0 0 6px hsla(0, 0%, 0%, 0.3); + font-family: Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; + font-size: 12px; + line-height: 18px; + cursor: default; + text-decoration: none; +} +#gc_tooltip { + border: 3px solid hsl(210, 50%, 30%); + color: hsla(210, 10%, 20%, 1); +} +#sc_tooltip { + border: 3px solid hsl(0, 50%, 30%); + color: hsla(0, 10%, 20%, 1); +} +#gc_tooltip_arrow, #sc_tooltip_arrow { + position: absolute; + display: none; +} +#gc_tooltip #gc_rule_id { + display: none; + margin: 0 0 5px 0; + border: 1px solid hsl(210, 50%, 60%); + background-color: hsl(210, 50%, 40%); + padding: 2px 5px; + border-radius: 2px; + color: hsla(210, 0%, 96%, 1); font-size: 11px; font-style: normal; + text-align: center; +} +#gc_message_block { + padding: 5px 10px 10px 10px; + background-color: hsl(210, 50%, 30%); + color: hsl(210, 50%, 96%); +} +#sc_message_block { + padding: 5px 10px 10px 10px; + background-color: hsl(0, 50%, 30%); + color: hsl(0, 50%, 96%); +} +#gc_message, #sc_message { + font-size: 15px; + margin-bottom: 5px; +} +a#gc_ignore, a#sc_ignore { + padding: 0 2px; + background-color: hsla(30, 30%, 40%, 1); + color: hsla(30, 0%, 96%, 1); + border-radius: 2px; + cursor: pointer; + text-decoration: none; +} +a#gc_ignore:hover, a#sc_ignore:hover { + background-color: hsla(30, 30%, 50%, 1); + color: hsla(0, 0%, 100%, 1); + text-shadow: 0 0 3px hsl(30, 30%, 60%); +} +a#gc_url { + padding: 0 2px; + background-color: hsla(210, 50%, 50%, 1); + color: hsla(210, 0%, 96%, 1); + border-radius: 2px; + cursor: pointer; + text-decoration: none; +} +a#gc_url:hover { + background-color: hsla(210, 50%, 60%, 1); + color: hsla(0, 0%, 100%, 1); + text-shadow: 0 0 3px hsl(210, 30%, 60%); +} +#gc_sugg_title { + padding: 0 10px; + background-color: hsl(210, 10%, 90%); + color: hsl(210, 50%, 30%); + font-size: 10px; + font-weight: bold; +} +#sc_sugg_title { + padding: 0 10px; + background-color: hsl(0, 10%, 90%); + color: hsl(0, 50%, 30%); + font-size: 9px; + font-weight: bold; +} +#gc_sugg_block { + padding: 10px; + background-color: hsl(210, 10%, 96%); + border-radius: 0 0 2px 2px; + line-height: 20px; +} +#sc_sugg_block { + padding: 10px; + background-color: hsl(0, 10%, 96%); + border-radius: 0 0 2px 2px; + line-height: 20px; +} +#gc_sugg_block a.sugg { + padding: 1px 6px; + background-color: hsla(180, 60%, 40%, 1); + color: hsla(180, 0%, 96%, 1); + border-radius: 2px; + cursor: pointer; + text-decoration: none; +} +#gc_sugg_block a.sugg:hover { + background-color: hsla(180, 70%, 45%, 1); + color: hsla(0, 0%, 100%, 1); +} +#sc_sugg_block a.sugg { + padding: 1px 6px; + background-color: hsla(30, 90%, 45%, 1); + color: hsla(30, 0%, 96%, 1); + border-radius: 2px; + cursor: pointer; + text-decoration: none; } - -.data { - font-style: normal; +#sc_sugg_block a.sugg:hover { + background-color: hsla(30, 100%, 50%, 1); + color: hsla(0, 0%, 100%, 1); } +/* + Action buttons +*/ .actions { - margin-top: -10px; - margin-bottom: 10px; + margin: 0 0 5px 10px; } .actions .button { background-color: hsl(0, 0%, 50%); text-align: center; @@ -216,11 +283,10 @@ padding: 1px 4px 3px 4px; /*width: 18px; height: 18px;*/ cursor: pointer; font-size: 14px; - font-weight: bold; color: hsl(0, 0%, 96%); border-radius: 2px; } .actions .button:hover { background-color: hsl(0, 0%, 40%); @@ -249,10 +315,13 @@ } .actions .orange:hover { background-color: hsl(30, 70%, 40%); color: hsl(30, 0%, 100%); } +.actions .bold { + font-weight: bold; +} /* ERRORS */ @@ -266,16 +335,16 @@ background-color: hsl(240, 10%, 40%); color: hsl(240, 0%, 100%); } /* elems */ -.spell { +.WORD { background-color: hsl(0, 50%, 50%); color: hsl(0, 0%, 96%); /*text-decoration: underline wavy hsl(0, 50%, 50%);*/ } -.spell:hover { +.WORD:hover { background-color: hsl(0, 60%, 40%); color: hsl(0, 0%, 100%); } /* elems */ Index: gc_lang/fr/xpi/data/gc_panel.html ================================================================== --- gc_lang/fr/xpi/data/gc_panel.html +++ gc_lang/fr/xpi/data/gc_panel.html @@ -49,10 +49,34 @@
+ ^ + ^ + +
+ +
+

+

Erreur grammaticale.

+ Ignorer   + Voulez-vous en savoir plus ?… +
+
SUGGESTIONS :
+
+
+ +
+ +
+

Mot inconnu du dictionnaire.

+ Ignorer +
+
SUGGESTIONS :
+
+
Index: gc_lang/fr/xpi/data/gc_panel.js ================================================================== --- gc_lang/fr/xpi/data/gc_panel.js +++ gc_lang/fr/xpi/data/gc_panel.js @@ -2,272 +2,460 @@ let nPanelWidth = 0; // must be set at launch let bExpanded = true; /* - Events + Ignored errors + idendified by + sIgnoredKey: +*/ +let aIgnoredErrors = new Set(); + + +/* + Events */ -if (Date.now() < Date.UTC(2017, 6, 12)) { - try { - document.getElementById('special_message').style.display = "block"; - document.getElementById('errorlist').style.padding = "20px 20px 30px 20px"; - } catch (e) { - console.log(e.message + e.lineNumber); - } -} +showSpecialMessage(); document.getElementById('close').addEventListener("click", function (event) { - bExpanded = true; // size is reset in ui.js - self.port.emit('closePanel'); + bExpanded = true; // size is reset in ui.js + self.port.emit('closePanel'); }); document.getElementById('expand_reduce').addEventListener("click", function (event) { - if (bExpanded) { - self.port.emit("resize", "reduce", 10); // the number has no meaning here - bExpanded = false; - } else { - self.port.emit("resize", "expand", 10); // the number has no meaning here - bExpanded = true; - } + if (bExpanded) { + self.port.emit("resize", "reduce", 10); // the number has no meaning here + bExpanded = false; + } else { + self.port.emit("resize", "expand", 10); // the number has no meaning here + bExpanded = true; + } }); document.getElementById('copy_to_clipboard').addEventListener("click", function (event) { - copyToClipboard(); + copyToClipboard(); }); document.getElementById('closemsg').addEventListener("click", function (event) { - closeMessageBox(); + closeMessageBox(); }); self.port.on("setPanelWidth", function (n) { - nPanelWidth = n; + nPanelWidth = n; +}); + +self.port.on("addMessage", function (sClass, sText) { + addMessage(sClass, sText); }); -self.port.on("addElem", function (sHtml) { - let xElem = document.createElement("div"); - xElem.innerHTML = sHtml; - document.getElementById("errorlist").appendChild(xElem); +self.port.on("addParagraph", function (sText, iParagraph, sJSON) { + addParagraph(sText, iParagraph, sJSON); }); -self.port.on("refreshParagraph", function (sIdParagr, sHtml) { - document.getElementById("paragr"+sIdParagr).innerHTML = sHtml; - let sClassName = (sHtml.includes('' + sSugg + ' '; - iSugg += 1; - } - } +self.port.on("suggestionsFor", function (sWord, sSuggestions, sErrId) { + setSpellSuggestionsFor(sWord, sSuggestions, sErrId); }); window.addEventListener( - "click", - function (xEvent) { - let xElem = xEvent.target; - if (xElem.id) { - if (xElem.id.startsWith("sugg")) { - applySuggestion(xElem.id); - } else if (xElem.id.startsWith("ignore")) { - ignoreError(xElem.id); - } else if (xElem.id.startsWith("check")) { - sendBackAndCheck(xElem.id); - } else if (xElem.id.startsWith("edit")) { - switchEdition(xElem.id); - } else if (xElem.id.startsWith("end")) { - document.getElementById(xElem.id).parentNode.parentNode.style.display = "none"; - } else if (xElem.tagName === "U" && xElem.id.startsWith("err")) { - showTooltip(xElem.id); - } else if (xElem.id.startsWith("resize")) { - self.port.emit("resize", xElem.id, 10); - } else { - hideAllTooltips(); - } - } else if (xElem.tagName === "A") { - self.port.emit("openURL", xElem.getAttribute("href")); - } else { - hideAllTooltips(); - } - }, - false + "click", + function (xEvent) { + try { + let xElem = xEvent.target; + if (xElem.id) { + if (xElem.id.startsWith("sugg")) { + applySuggestion(xElem.id); + } else if (xElem.id.endsWith("_ignore")) { + ignoreError(xElem.id); + } else if (xElem.id.startsWith("check")) { + sendBackAndCheck(xElem.id); + /*} else if (xElem.id.startsWith("edit")) { + switchEdition(xElem.id);*/ + } else if (xElem.id.startsWith("end")) { + document.getElementById(xElem.id).parentNode.parentNode.style.display = "none"; + } else if (xElem.tagName === "U" && xElem.id.startsWith("err") + && xElem.className !== "corrected" && xElem.className !== "ignored") { + showTooltip(xElem.id); + } else if (xElem.id.startsWith("resize")) { + self.port.emit("resize", xElem.id, 10); + } else if (xElem.id === "gc_url") { + self.port.emit("openURL", xElem.getAttribute("href")); + } else { + hideAllTooltips(); + } + } else if (xElem.tagName === "A") { + self.port.emit("openURL", xElem.getAttribute("href")); + } else { + hideAllTooltips(); + } + } + catch (e) { + showError(e); + } + }, + false ); /* - Actions + Actions */ + +function showError (e) { + console.error("\n" + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); +} function closeMessageBox () { - document.getElementById("messagebox").style.display = "none"; - document.getElementById("message").textContent = ""; -} - -function applySuggestion (sElemId) { // sugg - try { - let sIdParagr = sElemId.slice(4, sElemId.indexOf("_")); - startWaitIcon("paragr"+sIdParagr); - let sIdErr = "err" + sElemId.slice(4, sElemId.indexOf("-")); - document.getElementById(sIdErr).textContent = document.getElementById(sElemId).textContent; - document.getElementById(sIdErr).className = "corrected"; - document.getElementById(sIdErr).removeAttribute("style"); - self.port.emit("correction", sIdParagr, getPurgedTextOfElem("paragr"+sIdParagr)); - stopWaitIcon("paragr"+sIdParagr); - } catch (e) { - console.log(e.message + e.lineNumber); - } -} - -function ignoreError (sElemId) { // ignore - let sIdErr = "err" + sElemId.slice(6); - let xTooltipElem = document.getElementById("tooltip"+sElemId.slice(6)); - document.getElementById(sIdErr).removeChild(xTooltipElem); - document.getElementById(sIdErr).className = "ignored"; - document.getElementById(sIdErr).removeAttribute("style"); -} - -function showTooltip (sElemId) { // err - hideAllTooltips(); - let sTooltipId = "tooltip" + sElemId.slice(3); - let xTooltipElem = document.getElementById(sTooltipId); - let nLimit = nPanelWidth - 300; // paragraph width - tooltip width - if (document.getElementById(sElemId).offsetLeft > nLimit) { - xTooltipElem.style.left = "-" + (document.getElementById(sElemId).offsetLeft - nLimit) + "px"; - } - xTooltipElem.setAttribute("contenteditable", false); - xTooltipElem.className = 'tooltip_on'; - if (document.getElementById(sElemId).className === "error spell" && xTooltipElem.textContent.endsWith(":")) { - // spelling mistake - self.port.emit("getSuggestionsForTo", document.getElementById(sElemId).innerHTML.replace(/, we also remove style attribute - let xParagraphElem = document.getElementById(sId); - for (xNode of xParagraphElem.getElementsByTagName("u")) { - if (xNode.id.startsWith('err')) { - xNode.innerHTML = xNode.innerHTML.replace(/<\/?u>/g, ""); - xNode.removeAttribute("style"); - } - } - // we remove style attribute on tooltip - for (xNode of xParagraphElem.getElementsByTagName("span")) { - if (xNode.id.startsWith('tooltip')) { - xNode.removeAttribute("style"); - } - } - // now, we remove tooltips, then errors, and we change some html entities - let sText = xParagraphElem.innerHTML; - sText = sText.replace(/ *$/ig, ""); - sText = sText.replace(/(.+?)<\/u>/g, "$1"); - sText = sText.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"); - return sText; -} - -function getPurgedTextOfElem2 (sId) { - // It is better to remove tooltips via DOM and retrieve textContent, - // but for some reason getElementsByClassName “hazardously” forgets elements. - // Unused. Needs investigation. - let xParagraphElem = document.getElementById(sId).cloneNode(true); - for (let xNode of xParagraphElem.getElementsByClassName("tooltip")) { - xNode.parentNode.removeChild(xNode); - } - return xParagraphElem.textContent; + document.getElementById("messagebox").style.display = "none"; + document.getElementById("message").textContent = ""; +} + +function addMessage (sClass, sText) { + let xNode = document.createElement("p"); + xNode.className = sClass; + xNode.textContent = sText; + document.getElementById("errorlist").appendChild(xNode); +} + +function addParagraph (sText, iParagraph, sJSON) { + try { + let xNodeDiv = document.createElement("div"); + xNodeDiv.className = "paragraph_block"; + // actions + let xDivActions = document.createElement("div"); + xDivActions.className = "actions"; + let xDivClose = document.createElement("div"); + xDivClose.id = "end" + iParagraph.toString(); + xDivClose.className = "button red bold"; + xDivClose.textContent = "×"; + xDivActions.appendChild(xDivClose); + /*let xDivEdit = document.createElement("div"); + xDivEdit.id = "edit" + iParagraph.toString(); + xDivEdit.className = "button"; + xDivEdit.textContent = "Éditer"; + xDivActions.appendChild(xDivEdit);*/ + let xDivCheck = document.createElement("div"); + xDivCheck.id = "check" + iParagraph.toString(); + xDivCheck.className = "button green"; + xDivCheck.textContent = "Réanalyser"; + xDivCheck.setAttribute("title", "Réanalyser le texte"); + xDivActions.appendChild(xDivCheck); + xNodeDiv.appendChild(xDivActions); + // paragraph + let xParagraph = document.createElement("p"); + xParagraph.id = "paragr" + iParagraph.toString(); + xParagraph.lang = "fr"; + xParagraph.setAttribute("spellcheck", false); + xParagraph.setAttribute("contenteditable", true); + let oErrors = JSON.parse(sJSON); + xParagraph.className = (oErrors.aGrammErr.length || oErrors.aSpellErr.length) ? "paragraph softred" : "paragraph"; + _tagParagraph(sText, xParagraph, iParagraph, oErrors.aGrammErr, oErrors.aSpellErr); + xNodeDiv.appendChild(xParagraph); + document.getElementById("errorlist").appendChild(xNodeDiv); + } + catch (e) { + showError(e); + } +} + +function refreshParagraph (sText, sIdParagr, sJSON) { + try { + let xParagraph = document.getElementById("paragr"+sIdParagr); + let oErrors = JSON.parse(sJSON); + xParagraph.className = (oErrors.aGrammErr.length || oErrors.aSpellErr.length) ? "paragraph softred" : "paragraph softgreen"; + xParagraph.textContent = ""; + _tagParagraph(sText, xParagraph, sIdParagr, oErrors.aGrammErr, oErrors.aSpellErr); + } + catch (e) { + showError(e); + } +} + +function _tagParagraph (sParagraph, xParagraph, iParagraph, aSpellErr, aGrammErr) { + try { + if (aGrammErr.length === 0 && aSpellErr.length === 0) { + xParagraph.textContent = sParagraph; + return + } + aGrammErr.push(...aSpellErr); + aGrammErr.sort(function (a, b) { + if (a["nStart"] < b["nStart"]) + return -1; + if (a["nStart"] > b["nStart"]) + return 1; + return 0; + }); + + let nErr = 0; // we count errors to give them an identifier + let nEndLastErr = 0; + for (let oErr of aGrammErr) { + let nStart = oErr["nStart"]; + let nEnd = oErr["nEnd"]; + if (nStart >= nEndLastErr) { + oErr['sErrorId'] = iParagraph.toString() + "_" + nErr.toString(); // error identifier + oErr['sIgnoredKey'] = iParagraph.toString() + ":" + nStart.toString() + ":" + nEnd.toString() + ":" + sParagraph.slice(nStart, nEnd); + if (nEndLastErr < nStart) { + xParagraph.appendChild(document.createTextNode(sParagraph.slice(nEndLastErr, nStart))); + } + xParagraph.appendChild(_createError(sParagraph.slice(nStart, nEnd), oErr)); + xParagraph.insertAdjacentHTML("beforeend", ""); + nEndLastErr = nEnd; + } + nErr += 1; + } + if (nEndLastErr <= sParagraph.length) { + xParagraph.appendChild(document.createTextNode(sParagraph.slice(nEndLastErr))); + } + } + catch (e) { + showError(e); + } +} + +function _createError (sUnderlined, oErr) { + let xNodeErr = document.createElement("u"); + xNodeErr.id = "err" + oErr['sErrorId']; + xNodeErr.textContent = sUnderlined; + xNodeErr.dataset.error_id = oErr['sErrorId']; + xNodeErr.dataset.ignored_key = oErr['sIgnoredKey']; + xNodeErr.dataset.error_type = (oErr['sType'] === "WORD") ? "spelling" : "grammar"; + xNodeErr.setAttribute("href", "#"); + xNodeErr.setAttribute("onclick", "return false;"); + if (xNodeErr.dataset.error_type === "grammar") { + xNodeErr.dataset.gc_message = oErr['sMessage']; + xNodeErr.dataset.gc_url = oErr['URL']; + if (xNodeErr.dataset.gc_message.includes(" #")) { + xNodeErr.dataset.line_id = oErr['sLineId']; + xNodeErr.dataset.rule_id = oErr['sRuleId']; + } + xNodeErr.dataset.suggestions = oErr["aSuggestions"].join("|"); + } + xNodeErr.className = (aIgnoredErrors.has(xNodeErr.dataset.ignored_key)) ? "ignored" : "error " + oErr['sType']; + return xNodeErr; +} + +function applySuggestion (sSuggId) { // sugg + try { + let sErrorId = document.getElementById(sSuggId).dataset.error_id; + let sIdParagr = sErrorId.slice(0, sErrorId.indexOf("_")); + startWaitIcon("paragr"+sIdParagr); + let xNodeErr = document.getElementById("err" + sErrorId); + xNodeErr.textContent = document.getElementById(sSuggId).textContent; + xNodeErr.className = "corrected"; + xNodeErr.removeAttribute("style"); + self.port.emit("correction", sIdParagr, getPurgedTextOfParagraph("paragr"+sIdParagr)); + hideAllTooltips(); + stopWaitIcon("paragr"+sIdParagr); + } + catch (e) { + showError(e); + } +} + +function ignoreError (sIgnoreButtonId) { // ignore + try { + //console.log("ignore button: " + sIgnoreButtonId + " // error id: " + document.getElementById(sIgnoreButtonId).dataset.error_id); + let xNodeErr = document.getElementById("err"+document.getElementById(sIgnoreButtonId).dataset.error_id); + aIgnoredErrors.add(xNodeErr.dataset.ignored_key); + xNodeErr.className = "ignored"; + xNodeErr.removeAttribute("style"); + hideAllTooltips(); + } + catch (e) { + showError(e); + } +} + +function showTooltip (sNodeErrorId) { // err + try { + hideAllTooltips(); + let xNodeErr = document.getElementById(sNodeErrorId); + let sTooltipId = (xNodeErr.dataset.error_type === "grammar") ? "gc_tooltip" : "sc_tooltip"; + let xNodeTooltip = document.getElementById(sTooltipId); + let xNodeTooltipArrow = document.getElementById(sTooltipId+"_arrow"); + let nLimit = nPanelWidth - 330; // paragraph width - tooltip width + xNodeTooltipArrow.style.top = (xNodeErr.offsetTop + 16) + "px" + xNodeTooltipArrow.style.left = (xNodeErr.offsetLeft + Math.floor((xNodeErr.offsetWidth / 2))-4) + "px" // 4 is half the width of the arrow. + xNodeTooltip.style.top = (xNodeErr.offsetTop + 20) + "px"; + xNodeTooltip.style.left = (xNodeErr.offsetLeft > nLimit) ? nLimit + "px" : xNodeErr.offsetLeft + "px"; + if (xNodeErr.dataset.error_type === "grammar") { + // grammar error + if (xNodeErr.dataset.gc_message.includes(" ##")) { + let n = xNodeErr.dataset.gc_message.indexOf(" ##"); + document.getElementById("gc_message").textContent = xNodeErr.dataset.gc_message.slice(0, n); + document.getElementById("gc_rule_id").textContent = "Règle : " + xNodeErr.dataset.gc_message.slice(n+2); + document.getElementById("gc_rule_id").style.display = "block"; + } else { + document.getElementById("gc_message").textContent = xNodeErr.dataset.gc_message; + } + if (xNodeErr.dataset.gc_url != "") { + document.getElementById("gc_url").style.display = "inline"; + document.getElementById("gc_url").setAttribute("href", xNodeErr.dataset.gc_url); + } else { + document.getElementById("gc_url").style.display = "none"; + } + document.getElementById("gc_ignore").dataset.error_id = xNodeErr.dataset.error_id; + let iSugg = 0; + let xGCSugg = document.getElementById("gc_sugg_block"); + xGCSugg.textContent = ""; + for (let sSugg of xNodeErr.dataset.suggestions.split("|")) { + xGCSugg.appendChild(_createSuggestion(xNodeErr.dataset.error_id, iSugg, sSugg)); + xGCSugg.appendChild(document.createTextNode(" ")); + iSugg += 1; + } + } + xNodeTooltipArrow.style.display = "block"; + xNodeTooltip.style.display = "block"; + if (xNodeErr.dataset.error_type === "spelling") { + // spelling mistake + document.getElementById("sc_ignore").dataset.error_id = xNodeErr.dataset.error_id; + //console.log("getSuggFor: " + xNodeErr.textContent.trim() + " // error_id: " + xNodeErr.dataset.error_id); + self.port.emit("getSuggestionsForTo", xNodeErr.textContent.trim(), xNodeErr.dataset.error_id); + } + } + catch (e) { + showError(e); + } +} + +function _createSuggestion (sErrId, iSugg, sSugg) { + let xNodeSugg = document.createElement("a"); + xNodeSugg.id = "sugg" + sErrId + "-" + iSugg.toString(); + xNodeSugg.className = "sugg"; + xNodeSugg.setAttribute("href", "#"); + xNodeSugg.setAttribute("onclick", "return false;"); + xNodeSugg.dataset.error_id = sErrId; + xNodeSugg.textContent = sSugg; + return xNodeSugg; +} + +/*function switchEdition (sEditButtonId) { // edit + let xParagraph = document.getElementById("paragr" + sEditButtonId.slice(4)); + if (xParagraph.hasAttribute("contenteditable") === false + || xParagraph.getAttribute("contenteditable") === "false") { + xParagraph.setAttribute("contenteditable", true); + document.getElementById(sEditButtonId).className = "button orange"; + xParagraph.focus(); + } else { + xParagraph.setAttribute("contenteditable", false); + document.getElementById(sEditButtonId).className = "button"; + } +}*/ + +function sendBackAndCheck (sCheckButtonId) { // check + startWaitIcon(); + let sIdParagr = sCheckButtonId.slice(5); + self.port.emit("modifyAndCheck", sIdParagr, getPurgedTextOfParagraph("paragr"+sIdParagr)); + stopWaitIcon(); } function hideAllTooltips () { - for (let xElem of document.getElementsByClassName("tooltip_on")) { - xElem.className = "tooltip"; - xElem.removeAttribute("style"); - } + document.getElementById("gc_tooltip").style.display = "none"; + document.getElementById("gc_rule_id").style.display = "none"; + document.getElementById("sc_tooltip").style.display = "none"; + document.getElementById("gc_tooltip_arrow").style.display = "none"; + document.getElementById("sc_tooltip_arrow").style.display = "none"; +} + +function setSpellSuggestionsFor (sWord, sSuggestions, sErrId) { + // spell checking suggestions + try { + // console.log("setSuggestionsFor: " + sWord + " > " + sSuggestions + " // " + sErrId); + let xSuggBlock = document.getElementById("sc_sugg_block"); + xSuggBlock.textContent = ""; + if (sSuggestions === "") { + xSuggBlock.appendChild(document.createTextNode("Aucune.")); + } else if (sSuggestions.startsWith("#")) { + xSuggBlock.appendChild(document.createTextNode(sSuggestions)); + } else { + let lSugg = sSuggestions.split("|"); + let iSugg = 0; + for (let sSugg of lSugg) { + xSuggBlock.appendChild(_createSuggestion(sErrId, iSugg, sSugg)); + xSuggBlock.appendChild(document.createTextNode(" ")); + iSugg += 1; + } + } + } + catch (e) { + showError(e); + } +} + +function getPurgedTextOfParagraph (sNodeParagrId) { + let sText = document.getElementById(sNodeParagrId).textContent; + sText = sText.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"); + return sText; } function copyToClipboard () { - startWaitIcon(); - try { - document.getElementById("clipboard_msg").textContent = "copie en cours…"; - let sText = ""; - for (let xNode of document.getElementById("errorlist").getElementsByClassName("paragraph")) { - sText += getPurgedTextOfElem(xNode.id); - sText += "\n"; - } - self.port.emit('copyToClipboard', sText); - document.getElementById("clipboard_msg").textContent = "-> presse-papiers"; - window.setTimeout(function() { document.getElementById("clipboard_msg").textContent = "∑"; } , 3000); - } - catch (e) { - console.log(e.lineNumber + ": " +e.message); - } - stopWaitIcon(); + startWaitIcon(); + try { + let xClipboardButton = document.getElementById("clipboard_msg"); + xClipboardButton.textContent = "copie en cours…"; + let sText = ""; + for (let xNode of document.getElementById("errorlist").getElementsByClassName("paragraph")) { + sText += xNode.textContent + "\n"; + } + self.port.emit('copyToClipboard', sText); + xClipboardButton.textContent = "-> presse-papiers"; + window.setTimeout(function() { xClipboardButton.textContent = "∑"; } , 3000); + } + catch (e) { + console.log(e.lineNumber + ": " +e.message); + } + stopWaitIcon(); } function startWaitIcon (sIdParagr=null) { - if (sIdParagr) { - document.getElementById(sIdParagr).disabled = true; - document.getElementById(sIdParagr).style.opacity = .3; - } - document.getElementById("waiticon").hidden = false; + if (sIdParagr) { + document.getElementById(sIdParagr).disabled = true; + document.getElementById(sIdParagr).style.opacity = .3; + } + document.getElementById("waiticon").hidden = false; } function stopWaitIcon (sIdParagr=null) { - if (sIdParagr) { - document.getElementById(sIdParagr).disabled = false; - document.getElementById(sIdParagr).style.opacity = 1; - } - document.getElementById("waiticon").hidden = true; + if (sIdParagr) { + document.getElementById(sIdParagr).disabled = false; + document.getElementById(sIdParagr).style.opacity = 1; + } + document.getElementById("waiticon").hidden = true; +} + +function showSpecialMessage () { + if (Date.now() < Date.UTC(2017, 6, 12)) { + try { + document.getElementById('special_message').style.display = "block"; + document.getElementById('errorlist').style.padding = "20px 20px 30px 20px"; + } catch (e) { + showError(e); + } + } } ADDED gc_lang/fr/xpi/data/img/gc_tooltip_arrow.png Index: gc_lang/fr/xpi/data/img/gc_tooltip_arrow.png ================================================================== --- /dev/null +++ gc_lang/fr/xpi/data/img/gc_tooltip_arrow.png cannot compute difference between binary files ADDED gc_lang/fr/xpi/data/img/sc_tooltip_arrow.png Index: gc_lang/fr/xpi/data/img/sc_tooltip_arrow.png ================================================================== --- /dev/null +++ gc_lang/fr/xpi/data/img/sc_tooltip_arrow.png cannot compute difference between binary files Index: gc_lang/fr/xpi/data/lxg_panel.css ================================================================== --- gc_lang/fr/xpi/data/lxg_panel.css +++ gc_lang/fr/xpi/data/lxg_panel.css @@ -40,28 +40,43 @@ color: hsla(0, 0%, 96%, 1); border-radius: 5px; text-align: center; font-size: 20px; } +#wordlist .token { + margin: 8px; +} #wordlist ul { margin: 0 0 5px 40px; } #wordlist b { - background-color: hsla(150, 50%, 50%, 1); + background-color: hsla(150, 10%, 50%, 1); color: hsla(0, 0%, 96%, 1); padding: 2px 5px; border-radius: 2px; text-decoration: none; } -#wordlist b.unknown { +#wordlist b.WORD { + background-color: hsla(150, 50%, 50%, 1); +} +#wordlist b.ELPFX { + background-color: hsla(150, 30%, 50%, 1); +} +#wordlist b.UNKNOWN { background-color: hsla(0, 50%, 50%, 1); } -#wordlist b.nb { +#wordlist b.NUM { + background-color: hsla(180, 50%, 50%, 1); +} +#wordlist b.COMPLEX { + background-color: hsla(60, 50%, 50%, 1); +} +#wordlist b.SEPARATOR { background-color: hsla(210, 50%, 50%, 1); } -#wordlist b.mbok { - background-color: hsla(60, 50%, 50%, 1); +#wordlist b.LINK { + background-color: hsla(270, 50%, 50%, 1); } #wordlist s { color: hsla(0, 0%, 60%, 1); text-decoration: none; } Index: gc_lang/fr/xpi/data/lxg_panel.js ================================================================== --- gc_lang/fr/xpi/data/lxg_panel.js +++ gc_lang/fr/xpi/data/lxg_panel.js @@ -25,22 +25,19 @@ self.port.emit('openConjugueur'); }); */ self.port.on("addSeparator", function (sText) { - if (document.getElementById("wordlist").innerHTML !== "") { - let xElem = document.createElement("p"); - xElem.className = "separator"; - xElem.innerHTML = sText; - document.getElementById("wordlist").appendChild(xElem); - } + addSeparator(sText); +}); + +self.port.on("addParagraphElems", function (sJSON) { + addParagraphElems(sJSON); }); -self.port.on("addElem", function (sHtml) { - let xElem = document.createElement("div"); - xElem.innerHTML = sHtml; - document.getElementById("wordlist").appendChild(xElem); +self.port.on("addMessage", function (sClass, sText) { + addMessage(sClass, sText); }); self.port.on("clear", function (sHtml) { document.getElementById("wordlist").textContent = ""; }); @@ -65,10 +62,71 @@ } }, false ); + +/* + Actions +*/ + +function addSeparator (sText) { + if (document.getElementById("wordlist").textContent !== "") { + let xElem = document.createElement("p"); + xElem.className = "separator"; + xElem.textContent = sText; + document.getElementById("wordlist").appendChild(xElem); + } +} + +function addMessage (sClass, sText) { + let xNode = document.createElement("p"); + xNode.className = sClass; + xNode.textContent = sText; + document.getElementById("wordlist").appendChild(xNode); +} + +function addParagraphElems (sJSON) { + try { + let xNodeDiv = document.createElement("div"); + xNodeDiv.className = "paragraph"; + let lElem = JSON.parse(sJSON); + for (let oToken of lElem) { + xNodeDiv.appendChild(createTokenNode(oToken)); + } + document.getElementById("wordlist").appendChild(xNodeDiv); + } + catch (e) { + console.error("\n" + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); + console.error(sJSON); + } +} + +function createTokenNode (oToken) { + let xTokenNode = document.createElement("div"); + xTokenNode.className = "token " + oToken.sType; + let xTokenValue = document.createElement("b"); + xTokenValue.className = oToken.sType; + xTokenValue.textContent = oToken.sValue; + xTokenNode.appendChild(xTokenValue); + let xSep = document.createElement("s"); + xSep.textContent = " : "; + xTokenNode.appendChild(xSep); + if (oToken.aLabel.length === 1) { + xTokenNode.appendChild(document.createTextNode(oToken.aLabel[0])); + } else { + let xTokenList = document.createElement("ul"); + for (let sLabel of oToken.aLabel) { + let xTokenLine = document.createElement("li"); + xTokenLine.textContent = sLabel; + xTokenList.appendChild(xTokenLine); + } + xTokenNode.appendChild(xTokenList); + } + return xTokenNode; +} + // display selection function displayClasses () { setHidden("ok", document.getElementById("ok").checked); Index: gc_lang/fr/xpi/data/test_panel.js ================================================================== --- gc_lang/fr/xpi/data/test_panel.js +++ gc_lang/fr/xpi/data/test_panel.js @@ -2,13 +2,13 @@ /* Events */ -self.port.on("addElem", function (sHtml) { - let xElem = document.createElement("p"); - xElem.innerHTML = sHtml; +self.port.on("addElem", function (sText) { + let xElem = document.createElement("pre"); + xElem.textContent = sText; document.getElementById("results").appendChild(xElem); }); self.port.on("clear", function () { document.getElementById("results").textContent = ""; Index: gc_lang/fr/xpi/gce_worker.js ================================================================== --- gc_lang/fr/xpi/gce_worker.js +++ gc_lang/fr/xpi/gce_worker.js @@ -84,36 +84,14 @@ return JSON.stringify(aGrammErr); } function parseAndSpellcheck (sText, sLang, bDebug, bContext) { let aGrammErr = gce.parse(sText, sLang, bDebug, bContext); - let aSpellErr = []; - for (let oToken of oTokenizer.genTokens(sText)) { - if (oToken.sType === 'WORD' && !oDict.isValidToken(oToken.sValue)) { - aSpellErr.push(oToken); - } - } + let aSpellErr = oTokenizer.getSpellingErrors(sText, oDict); return JSON.stringify({ aGrammErr: aGrammErr, aSpellErr: aSpellErr }); } -function parseAndTag (sText, iParagraph, sLang, bDebug) { - sText = text.addHtmlEntities(sText); - let aSpellErr = []; - for (let oToken of oTokenizer.genTokens(sText)) { - if (oToken.sType === 'WORD' && !oDict.isValidToken(oToken.sValue)) { - aSpellErr.push(oToken); - } - } - let aGrammErr = gce.parse(sText, sLang, bDebug); - let sHtml = text.tagParagraph(sText, iParagraph, aGrammErr, aSpellErr); - return sHtml; -} - -function parseAndGenerateParagraph (sText, iParagraph, sLang, bDebug) { - return text.createHTMLBlock(parseAndTag(sText, iParagraph, sLang, bDebug), iParagraph); -} - function getOptions () { return gce.getOptions()._toString(); } function getDefaultOptions () { @@ -146,17 +124,30 @@ let tests = require("resource://grammalecte/tests.js"); let oTest = new tests.TestGrammarChecking(gce); let sAllRes = ""; for (let sRes of oTest.testParse()) { dump(sRes+"\n"); - sAllRes += sRes+"
"; + sAllRes += sRes+"\n"; } gce.setOptions(dMemoOptions); return sAllRes; } // Lexicographer -function analyzeWords (sText) { - return oLxg.analyzeText(sText); +function getListOfElements (sText) { + try { + let aElem = []; + let aRes = null; + for (let oToken of oTokenizer.genTokens(sText)) { + aRes = oLxg.getInfoForToken(oToken); + if (aRes) { + aElem.push(aRes); + } + } + return JSON.stringify(aElem); + } + catch (e) { + helpers.logerror(e); + } } Index: gc_lang/fr/xpi/package.json ================================================================== --- gc_lang/fr/xpi/package.json +++ gc_lang/fr/xpi/package.json @@ -1,10 +1,10 @@ { "name": "grammalecte-fr", "title": "Grammalecte [fr]", "id": "French-GC@grammalecte.net", - "version": "0.5.17.2", + "version": "0.5.18", "description": "Correcteur grammatical pour le français", "homepage": "http://www.dicollecte.org/grammalecte", "main": "ui.js", "icon": "data/img/icon-48.png", "scripts": { Index: gc_lang/fr/xpi/ui.js ================================================================== --- gc_lang/fr/xpi/ui.js +++ gc_lang/fr/xpi/ui.js @@ -347,14 +347,14 @@ } return true; } function checkAndSendToPanel (sIdParagraph, sText) { - let xPromise = xGCEWorker.post('parseAndTag', [sText, parseInt(sIdParagraph), "FR", false]); + let xPromise = xGCEWorker.post('parseAndSpellcheck', [sText, "FR", false, false]); xPromise.then( function (aVal) { - xGCPanel.port.emit("refreshParagraph", sIdParagraph, aVal); + xGCPanel.port.emit("refreshParagraph", sText, sIdParagraph, aVal); }, function (aReason) { console.error('Promise rejected - ', aReason); } ).catch( @@ -416,20 +416,20 @@ let sRes = ""; try { sText = sText.normalize("NFC"); // remove combining diacritics for (let sParagraph of text.getParagraph(sText)) { if (sParagraph.trim() !== "") { - sRes = await xGCEWorker.post('parseAndGenerateParagraph', [sParagraph, iParagraph, "FR", false]) - xGCPanel.port.emit("addElem", sRes); + sRes = await xGCEWorker.post('parseAndSpellcheck', [sParagraph, "FR", false, false]); + xGCPanel.port.emit("addParagraph", sParagraph, iParagraph, sRes); nParagraph += 1; } iParagraph += 1; } - xGCPanel.port.emit("addElem", '

' + _("numberOfParagraphs") + " " + nParagraph + '

'); + xGCPanel.port.emit("addMessage", 'message', _("numberOfParagraphs") + " " + nParagraph); } catch (e) { - xGCPanel.port.emit("addElem", '

' + e.message + '

'); + xGCPanel.port.emit("addMessage", 'bug', e.message); } xGCPanel.port.emit("end"); } @@ -569,19 +569,19 @@ let nParagraph = 0; // non empty paragraphs let sRes = ""; try { for (let sParagraph of text.getParagraph(sText)) { if (sParagraph.trim() !== "") { - sRes = await xGCEWorker.post('analyzeWords', [sParagraph]) - xLxgPanel.port.emit("addElem", sRes); + sRes = await xGCEWorker.post('getListOfElements', [sParagraph]); + xLxgPanel.port.emit("addParagraphElems", sRes); nParagraph += 1; } } - xLxgPanel.port.emit("addElem", '

' + _("numberOfParagraphs") + " " + nParagraph + '

'); + xLxgPanel.port.emit("addMessage", 'message', _("numberOfParagraphs") + " " + nParagraph); } catch (e) { - xLxgPanel.port.emit("addElem", '

'+e.message+"

"); + xLxgPanel.port.emit("addMessage", 'bug', e.message); } xLxgPanel.port.emit("stopWaitIcon"); }