Index: gc_core/js/helpers.js ================================================================== --- gc_core/js/helpers.js +++ gc_core/js/helpers.js @@ -1,98 +1,101 @@ // HELPERS +/*jslint esversion: 6*/ +/*global console,require,exports,XMLHttpRequest*/ "use strict"; // In Firefox, there is no console.log in PromiseWorker, but there is worker.log. // In Thunderbird, you can’t access to console directly. So it’s required to pass a log function. let funcOutput = null; -function setLogOutput (func) { - funcOutput = func; -} - -function echo (obj) { - if (funcOutput !== null) { - funcOutput(obj); - } else { - console.log(obj); - } - return true; -} - -function logerror (e, bStack=false) { - let sMsg = "\n" + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message; - if (bStack) { - sMsg += "\n--- Stack ---\n" + e.stack; - } - if (funcOutput !== null) { - funcOutput(sMsg); - } 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) { - try { - let xRequest; - if (typeof XMLHttpRequest !== "undefined") { - xRequest = new XMLHttpRequest(); - } - else { - // JS bullshit again… necessary for Thunderbird - let { Cc, Ci } = require("chrome"); - xRequest = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); - xRequest.QueryInterface(Ci.nsIXMLHttpRequest); - } - xRequest.open('GET', spf, false); // 3rd arg is false for synchronous, sync is acceptable in workers - xRequest.send(); - return xRequest.responseText; - } - catch (e) { - logerror(e); - return null - } -} - - -// conversions -function objectToMap (obj) { - let m = new Map(); - for (let param in obj) { - //console.log(param + " " + obj[param]); - m.set(param, obj[param]); - } - return m; -} - -function mapToObject (m) { - let obj = {}; - for (let [k, v] of m) { - obj[k] = v; - } - return obj; -} +var helpers = { + + setLogOutput: function (func) { + funcOutput = func; + }, + + echo: function (obj) { + if (funcOutput !== null) { + funcOutput(obj); + } else { + console.log(obj); + } + return true; + }, + + logerror: function (e, bStack=false) { + let sMsg = "\n" + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message; + if (bStack) { + sMsg += "\n--- Stack ---\n" + e.stack; + } + if (funcOutput !== null) { + funcOutput(sMsg); + } else { + console.error(sMsg); + } + }, + + inspect: function (o) { + let sMsg = "__inspect__: " + typeof o; + for (let sParam in o) { + sMsg += "\n" + sParam + ": " + o.sParam; + } + sMsg += "\n" + JSON.stringify(o) + "\n__end__"; + this.echo(sMsg); + }, + + loadFile: function (spf) { + // 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 + try { + let xRequest; + if (typeof XMLHttpRequest !== "undefined") { + xRequest = new XMLHttpRequest(); + } else { + // JS bullshit again… necessary for Thunderbird + let { Cc, Ci } = require("chrome"); + xRequest = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); + xRequest.QueryInterface(Ci.nsIXMLHttpRequest); + } + xRequest.open('GET', spf, false); // 3rd arg is false for synchronous, sync is acceptable in workers + xRequest.overrideMimeType('text/json'); + xRequest.send(); + return xRequest.responseText; + } + catch (e) { + this.logerror(e); + return null; + } + }, + + // conversions + objectToMap: function (obj) { + let m = new Map(); + for (let param in obj) { + //console.log(param + " " + obj[param]); + m.set(param, obj[param]); + } + return m; + }, + + mapToObject: function (m) { + let obj = {}; + for (let [k, v] of m) { + obj[k] = v; + } + return obj; + } +}; if (typeof(exports) !== 'undefined') { - exports.setLogOutput = setLogOutput; - exports.echo = echo; - exports.logerror = logerror; - exports.inspect = inspect; - exports.loadFile = loadFile; - exports.objectToMap = objectToMap; - exports.mapToObject = mapToObject; + exports.setLogOutput = helpers.setLogOutput; + exports.echo = helpers.echo; + exports.logerror = helpers.logerror; + exports.inspect = helpers.inspect; + exports.loadFile = helpers.loadFile; + exports.objectToMap = helpers.objectToMap; + exports.mapToObject = helpers.mapToObject; } Index: gc_core/js/ibdawg.js ================================================================== --- gc_core/js/ibdawg.js +++ gc_core/js/ibdawg.js @@ -1,11 +1,16 @@ //// IBDAWG +/*jslint esversion: 6*/ +/*global console,require,exports*/ "use strict"; -const st = require("resource://grammalecte/str_transform.js"); -const helpers = require("resource://grammalecte/helpers.js"); + +if (typeof(require) !== 'undefined') { + var str_transform = require("resource://grammalecte/str_transform.js"); + var helpers = require("resource://grammalecte/helpers.js"); +} // String // Don’t remove. Necessary in TB. ${string} @@ -13,16 +18,15 @@ class IBDAWG { // INDEXABLE BINARY DIRECT ACYCLIC WORD GRAPH - constructor (sDicName) { + constructor (sDicName, sPath="") { try { - const dict = JSON.parse(helpers.loadFile("resource://grammalecte/_dictionaries/"+sDicName)); + let sURL = (sPath !== "") ? sPath + "/" + sDicName : "resource://grammalecte/_dictionaries/"+sDicName; + const dict = JSON.parse(helpers.loadFile(sURL)); Object.assign(this, dict); - //const dict = require("resource://grammalecte/"+sLang+"/dictionary.js"); - //Object.assign(this, dict.dictionary); } catch (e) { throw Error("# Error. File not found or not loadable.\n" + e.message + "\n"); } /* @@ -39,15 +43,15 @@ this.dChar = helpers.objectToMap(this.dChar); //this.byDic = new Uint8Array(this.byDic); // not quicker, even slower if (this.cStemming == "S") { - this.funcStemming = st.getStemFromSuffixCode; + this.funcStemming = str_transform.getStemFromSuffixCode; } else if (this.cStemming == "A") { - this.funcStemming = st.getStemFromAffixCode; + this.funcStemming = str_transform.getStemFromAffixCode; } else { - this.funcStemming = st.noStemming; + this.funcStemming = str_transform.noStemming; } // Configuring DAWG functions according to nVersion switch (this.nVersion) { case 1: @@ -72,18 +76,18 @@ throw ValueError("# Error: unknown code: " + this.nVersion); } //console.log(this.getInfo()); this.bOptNumSigle = true; this.bOptNumAtLast = false; - }; + } getInfo () { return ` Language: ${this.sLang} Version: ${this.nVersion} Stemming: ${this.cStemming}FX\n` + ` Arcs values: ${this.nArcVal} = ${this.nChar} characters, ${this.nAff} affixes, ${this.nTag} tags\n` + ` Dictionary: ${this.nEntries} entries, ${this.nNode} nodes, ${this.nArc} arcs\n` + ` Address size: ${this.nBytesNodeAddress} bytes, Arc size: ${this.nBytesArc} bytes\n`; - }; + } isValidToken (sToken) { // checks if sToken is valid (if there is hyphens in sToken, sToken is split, each part is checked) if (this.isValid(sToken)) { return true; @@ -93,11 +97,11 @@ return true; } return sToken.split("-").every(sWord => this.isValid(sWord)); } return false; - }; + } isValid (sWord) { // checks if sWord is valid (different casing tested if the first letter is a capital) if (!sWord) { return null; @@ -123,11 +127,11 @@ } else { return !!this.lookup(sWord.toLowerCase()); } } return false; - }; + } _convBytesToInteger (aBytes) { // Byte order = Big Endian (bigger first) let nVal = 0; let nWeight = (aBytes.length - 1) * 8; @@ -134,11 +138,11 @@ for (let n of aBytes) { nVal += n << nWeight; nWeight = nWeight - 8; } return nVal; - }; + } lookup (sWord) { // returns true if sWord in dictionary (strict verification) let iAddr = 0; for (let c of sWord) { @@ -149,11 +153,11 @@ if (iAddr === null) { return false; } } return Boolean(this._convBytesToInteger(this.byDic.slice(iAddr, iAddr+this.nBytesArc)) & this._finalNodeMask); - }; + } getMorph (sWord) { // retrieves morphologies list, different casing allowed let l = this.morph(sWord); if (sWord[0].gl_isUpperCase()) { @@ -161,15 +165,15 @@ if (sWord.gl_isUpperCase() && sWord.length > 1) { l = l.concat(this.morph(sWord.gl_toCapitalize())); } } return l; - }; + } // morph (sWord) { // is defined in constructor - // }; + // } // VERSION 1 _morph1 (sWord) { // returns morphologies of sWord let iAddr = 0; @@ -205,11 +209,11 @@ iAddr = iEndArcAddr + this.nBytesNodeAddress; } return l; } return []; - }; + } _stem1 (sWord) { // returns stems list of sWord let iAddr = 0; for (let c of sWord) { @@ -235,11 +239,11 @@ iAddr = iEndArcAddr + this.nBytesNodeAddress; } return l; } return []; - }; + } _lookupArcNode1 (nVal, iAddr) { // looks if nVal is an arc at the node at iAddr, if yes, returns address of next node else None while (true) { let iEndArcAddr = iAddr+this.nBytesArc; @@ -255,39 +259,39 @@ return null; } iAddr = iEndArcAddr + this.nBytesNodeAddress; } } - }; + } // VERSION 2 _morph2 (sWord) { // to do - }; + } _stem2 (sWord) { // to do - }; + } _lookupArcNode2 (nVal, iAddr) { // to do - }; + } // VERSION 3 _morph3 (sWord) { // to do - }; + } _stem3 (sWord) { // to do - }; + } _lookupArcNode3 (nVal, iAddr) { // to do - }; + } } if (typeof(exports) !== 'undefined') { exports.IBDAWG = IBDAWG; } Index: gc_core/js/jsex_map.js ================================================================== --- gc_core/js/jsex_map.js +++ gc_core/js/jsex_map.js @@ -1,47 +1,48 @@ // Map +/*jslint esversion: 6*/ if (Map.prototype.grammalecte === undefined) { Map.prototype.gl_shallowCopy = function () { let oNewMap = new Map(); for (let [key, val] of this.entries()) { oNewMap.set(key, val); } return oNewMap; - } + }; Map.prototype.gl_get = function (key, defaultValue) { let res = this.get(key); if (res !== undefined) { return res; } return defaultValue; - } + }; Map.prototype.gl_toString = function () { // Default .toString() gives nothing useful let sRes = "{ "; for (let [k, v] of this.entries()) { sRes += (typeof k === "string") ? '"' + k + '": ' : k.toString() + ": "; sRes += (typeof v === "string") ? '"' + v + '", ' : v.toString() + ", "; } - sRes = sRes.slice(0, -2) + " }" + sRes = sRes.slice(0, -2) + " }"; return sRes; - } + }; Map.prototype.gl_update = function (dDict) { for (let [k, v] of dDict.entries()) { this.set(k, v); } - } + }; Map.prototype.gl_updateOnlyExistingKeys = function (dDict) { for (let [k, v] of dDict.entries()) { if (this.has(k)){ this.set(k, v); } } - } + }; Map.prototype.grammalecte = true; } Index: gc_core/js/jsex_regex.js ================================================================== --- gc_core/js/jsex_regex.js +++ gc_core/js/jsex_regex.js @@ -1,7 +1,8 @@ // regex +/*jslint esversion: 6*/ if (RegExp.prototype.grammalecte === undefined) { RegExp.prototype.gl_exec2 = function (sText, aGroupsPos, aNegLookBefore=null) { let m; while ((m = this.exec(sText)) !== null) { @@ -40,23 +41,23 @@ // at the end of the pattern m.start.push(this.lastIndex - m[i].length); m.end.push(this.lastIndex); } else if (codePos === "w") { // word in the middle of the pattern - iPos = m[0].search("[ ’,()«»“”]"+m[i]+"[ ,’()«»“”]") + 1 + m.index + iPos = m[0].search("[ ’,()«»“”]"+m[i]+"[ ,’()«»“”]") + 1 + m.index; m.start.push(iPos); - m.end.push(iPos + m[i].length) + m.end.push(iPos + m[i].length); } else if (codePos === "*") { // anywhere iPos = m[0].indexOf(m[i]) + m.index; m.start.push(iPos); - m.end.push(iPos + m[i].length) + m.end.push(iPos + m[i].length); } else if (codePos === "**") { // anywhere after previous group iPos = m[0].indexOf(m[i], m.end[i-1]-m.index) + m.index; m.start.push(iPos); - m.end.push(iPos + m[i].length) + m.end.push(iPos + m[i].length); } else if (codePos.startsWith(">")) { // >x:_ // todo: look in substring x iPos = m[0].indexOf(m[i]) + m.index; m.start.push(iPos); @@ -81,9 +82,9 @@ } else { console.error(e); } } return m; - } + }; RegExp.prototype.grammalecte = true; } Index: gc_core/js/jsex_string.js ================================================================== --- gc_core/js/jsex_string.js +++ gc_core/js/jsex_string.js @@ -1,7 +1,8 @@ // String +/*jslint esversion: 6*/ if (String.prototype.grammalecte === undefined) { String.prototype.gl_count = function (sSearch, bOverlapping) { // http://jsperf.com/string-ocurrence-split-vs-match/8 if (sSearch.length <= 0) { @@ -13,45 +14,45 @@ while ((iPos = this.indexOf(sSearch, iPos)) >= 0) { nOccur++; iPos += nStep; } return nOccur; - } + }; String.prototype.gl_isDigit = function () { return (this.search(/^[0-9⁰¹²³⁴⁵⁶⁷⁸⁹]+$/) !== -1); - } + }; String.prototype.gl_isLowerCase = function () { return (this.search(/^[a-zà-öø-ÿ0-9-]+$/) !== -1); - } + }; String.prototype.gl_isUpperCase = function () { return (this.search(/^[A-ZÀ-ÖØ-ߌ0-9-]+$/) !== -1); - } + }; String.prototype.gl_isTitle = function () { return (this.search(/^[A-ZÀ-ÖØ-ߌ][a-zà-öø-ÿ'’-]+$/) !== -1); - } + }; String.prototype.gl_toCapitalize = function () { return this.slice(0,1).toUpperCase() + this.slice(1).toLowerCase(); - } + }; String.prototype.gl_expand = function (oMatch) { let sNew = this; for (let i = 0; i < oMatch.length ; i++) { let z = new RegExp("\\\\"+parseInt(i), "g"); sNew = sNew.replace(z, oMatch[i]); } return sNew; - } + }; String.prototype.gl_trimRight = function (sChars) { let z = new RegExp("["+sChars+"]+$"); return this.replace(z, ""); - } + }; String.prototype.gl_trimLeft = function (sChars) { let z = new RegExp("^["+sChars+"]+"); return this.replace(z, ""); - } + }; String.prototype.gl_trim = function (sChars) { let z1 = new RegExp("^["+sChars+"]+"); let z2 = new RegExp("["+sChars+"]+$"); return this.replace(z1, "").replace(z2, ""); - } + }; String.prototype.grammalecte = true; } 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 @@ -1,11 +1,30 @@ // Grammar checker engine +/*jslint esversion: 6*/ +/*global console,require,exports*/ + +"use strict"; ${string} ${regex} ${map} + +if (typeof(require) !== 'undefined') { + var helpers = require("resource://grammalecte/helpers.js"); + var gc_options = require("resource://grammalecte/${lang}/gc_options.js"); + var gc_rules = require("resource://grammalecte/${lang}/gc_rules.js"); + var cregex = require("resource://grammalecte/${lang}/cregex.js"); + var text = require("resource://grammalecte/text.js"); + var echo = helpers.echo; +} +else if (typeof(console) !== "undefined") { + var echo = function (o) { console.log(o); return true; }; +} +else { + var echo = function () { return true; } +} function capitalizeArray (aArray) { // can’t map on user defined function?? let aNew = []; for (let i = 0; i < aArray.length; i = i + 1) { @@ -12,407 +31,411 @@ aNew[i] = aArray[i].gl_toCapitalize(); } return aNew; } -const ibdawg = require("resource://grammalecte/ibdawg.js"); -const helpers = require("resource://grammalecte/helpers.js"); -const gc_options = require("resource://grammalecte/${lang}/gc_options.js"); -const cr = require("resource://grammalecte/${lang}/cregex.js"); -const text = require("resource://grammalecte/text.js"); -const echo = require("resource://grammalecte/helpers.js").echo; - -const lang = "${lang}"; -const locales = ${loc}; -const pkg = "${implname}"; -const name = "${name}"; -const version = "${version}"; -const author = "${author}"; - -// commons regexes -const _zEndOfSentence = new RegExp ('([.?!:;…][ .?!… »”")]*|.$)', "g"); -const _zBeginOfParagraph = new RegExp ("^[- –—.,;?!…]*", "ig"); -const _zEndOfParagraph = new RegExp ("[- .,;?!…–—]*$", "ig"); - -// grammar rules and dictionary -//const _rules = require("./gc_rules.js"); -let _sContext = ""; // what software is running -const _rules = require("resource://grammalecte/${lang}/gc_rules.js"); + +// data +let _sAppContext = ""; // what software is running let _dOptions = null; let _aIgnoredRules = new Set(); let _oDict = null; -let _dAnalyses = new Map(); // cache for data from dictionary - - -///// Parsing - -function parse (sText, sCountry="${country_default}", bDebug=false, bContext=false) { - // analyses the paragraph sText and returns list of errors - let dErrors; - let errs; - let sAlt = sText; - let dDA = new Map(); // Disamnbiguator - let dPriority = new Map(); // Key = position; value = priority - let sNew = ""; - - // parse paragraph - try { - [sNew, dErrors] = _proofread(sText, sAlt, 0, true, dDA, dPriority, sCountry, bDebug, bContext); - if (sNew) { - sText = sNew; - } - } - catch (e) { - helpers.logerror(e); - } - - // cleanup - if (sText.includes(" ")) { - sText = sText.replace(/ /g, ' '); // nbsp - } - if (sText.includes(" ")) { - sText = sText.replace(/ /g, ' '); // snbsp - } - if (sText.includes("'")) { - sText = sText.replace(/'/g, "’"); - } - if (sText.includes("‑")) { - sText = sText.replace(/‑/g, "-"); // nobreakdash - } - - // parse sentence - for (let [iStart, iEnd] of _getSentenceBoundaries(sText)) { - if (4 < (iEnd - iStart) < 2000) { - dDA.clear(); - //echo(sText.slice(iStart, iEnd)); - try { - [_, errs] = _proofread(sText.slice(iStart, iEnd), sAlt.slice(iStart, iEnd), iStart, false, dDA, dPriority, sCountry, bDebug, bContext); - dErrors.gl_update(errs); - } - catch (e) { - helpers.logerror(e); - } - } - } - return Array.from(dErrors.values()); -} - -function* _getSentenceBoundaries (sText) { - let mBeginOfSentence = _zBeginOfParagraph.exec(sText) - let iStart = _zBeginOfParagraph.lastIndex; - let m; - while ((m = _zEndOfSentence.exec(sText)) !== null) { - yield [iStart, _zEndOfSentence.lastIndex]; - iStart = _zEndOfSentence.lastIndex; - } -} - -function _proofread (s, sx, nOffset, bParagraph, dDA, dPriority, sCountry, bDebug, bContext) { - let dErrs = new Map(); - let bChange = false; - let bIdRule = option('idrule'); - let m; - let bCondMemo; - let nErrorStart; - - for (let [sOption, lRuleGroup] of _getRules(bParagraph)) { - if (!sOption || option(sOption)) { - for (let [zRegex, bUppercase, sLineId, sRuleId, nPriority, lActions, lGroups, lNegLookBefore] of lRuleGroup) { - if (!_aIgnoredRules.has(sRuleId)) { - while ((m = zRegex.gl_exec2(s, lGroups, lNegLookBefore)) !== null) { - bCondMemo = null; - /*if (bDebug) { - echo(">>>> Rule # " + sLineId + " - Text: " + s + " opt: "+ sOption); - }*/ - for (let [sFuncCond, cActionType, sWhat, ...eAct] of lActions) { - // action in lActions: [ condition, action type, replacement/suggestion/action[, iGroup[, message, URL]] ] - try { - //echo(oEvalFunc[sFuncCond]); - bCondMemo = (!sFuncCond || oEvalFunc[sFuncCond](s, sx, m, dDA, sCountry, bCondMemo)) - if (bCondMemo) { - switch (cActionType) { - case "-": - // grammar error - //echo("-> error detected in " + sLineId + "\nzRegex: " + zRegex.source); - nErrorStart = nOffset + m.start[eAct[0]]; - if (!dErrs.has(nErrorStart) || nPriority > dPriority.get(nErrorStart)) { - dErrs.set(nErrorStart, _createError(s, sx, sWhat, nOffset, m, eAct[0], sLineId, sRuleId, bUppercase, eAct[1], eAct[2], bIdRule, sOption, bContext)); - dPriority.set(nErrorStart, nPriority); - } - break; - case "~": - // text processor - //echo("-> text processor by " + sLineId + "\nzRegex: " + zRegex.source); - s = _rewrite(s, sWhat, eAct[0], m, bUppercase); - bChange = true; - if (bDebug) { - echo("~ " + s + " -- " + m[eAct[0]] + " # " + sLineId); - } - break; - case "=": - // disambiguation - //echo("-> disambiguation by " + sLineId + "\nzRegex: " + zRegex.source); - oEvalFunc[sWhat](s, m, dDA); - if (bDebug) { - echo("= " + m[0] + " # " + sLineId + "\nDA: " + dDA.gl_toString()); - } - break; - case ">": - // we do nothing, this test is just a condition to apply all following actions - break; - default: - echo("# error: unknown action at " + sLineId); - } - } else { - if (cActionType == ">") { - break; - } - } - } - catch (e) { - echo(s); - echo("# line id: " + sLineId + "\n# rule id: " + sRuleId); - helpers.logerror(e); +let _dAnalyses = new Map(); // cache for data from dictionary + + +var gc_engine = { + + //// Informations + + lang: "${lang}", + locales: ${loc}, + pkg: "${implname}", + name: "${name}", + version: "${version}", + author: "${author}", + + //// Parsing + + parse: function (sText, sCountry="${country_default}", bDebug=false, bContext=false) { + // analyses the paragraph sText and returns list of errors + let dErrors; + let errs; + let sAlt = sText; + let dDA = new Map(); // Disamnbiguator + let dPriority = new Map(); // Key = position; value = priority + let sNew = ""; + + // parse paragraph + try { + [sNew, dErrors] = this._proofread(sText, sAlt, 0, true, dDA, dPriority, sCountry, bDebug, bContext); + if (sNew) { + sText = sNew; + } + } + catch (e) { + helpers.logerror(e); + } + + // cleanup + if (sText.includes(" ")) { + sText = sText.replace(/ /g, ' '); // nbsp + } + if (sText.includes(" ")) { + sText = sText.replace(/ /g, ' '); // snbsp + } + if (sText.includes("'")) { + sText = sText.replace(/'/g, "’"); + } + if (sText.includes("‑")) { + sText = sText.replace(/‑/g, "-"); // nobreakdash + } + + // parse sentence + for (let [iStart, iEnd] of this._getSentenceBoundaries(sText)) { + if (4 < (iEnd - iStart) < 2000) { + dDA.clear(); + //helpers.echo(sText.slice(iStart, iEnd)); + try { + [, errs] = this._proofread(sText.slice(iStart, iEnd), sAlt.slice(iStart, iEnd), iStart, false, dDA, dPriority, sCountry, bDebug, bContext); + dErrors.gl_update(errs); + } + catch (e) { + helpers.logerror(e); + } + } + } + return Array.from(dErrors.values()); + }, + + _zEndOfSentence: new RegExp ('([.?!:;…][ .?!… »”")]*|.$)', "g"), + _zBeginOfParagraph: new RegExp ("^[- –—.,;?!…]*", "ig"), + _zEndOfParagraph: new RegExp ("[- .,;?!…–—]*$", "ig"), + + _getSentenceBoundaries: function* (sText) { + let mBeginOfSentence = this._zBeginOfParagraph.exec(sText); + let iStart = this._zBeginOfParagraph.lastIndex; + let m; + while ((m = this._zEndOfSentence.exec(sText)) !== null) { + yield [iStart, this._zEndOfSentence.lastIndex]; + iStart = this._zEndOfSentence.lastIndex; + } + }, + + _proofread: function (s, sx, nOffset, bParagraph, dDA, dPriority, sCountry, bDebug, bContext) { + let dErrs = new Map(); + let bChange = false; + let bIdRule = option('idrule'); + let m; + let bCondMemo; + let nErrorStart; + + for (let [sOption, lRuleGroup] of this._getRules(bParagraph)) { + if (!sOption || option(sOption)) { + for (let [zRegex, bUppercase, sLineId, sRuleId, nPriority, lActions, lGroups, lNegLookBefore] of lRuleGroup) { + if (!_aIgnoredRules.has(sRuleId)) { + while ((m = zRegex.gl_exec2(s, lGroups, lNegLookBefore)) !== null) { + bCondMemo = null; + /*if (bDebug) { + helpers.echo(">>>> Rule # " + sLineId + " - Text: " + s + " opt: "+ sOption); + }*/ + for (let [sFuncCond, cActionType, sWhat, ...eAct] of lActions) { + // action in lActions: [ condition, action type, replacement/suggestion/action[, iGroup[, message, URL]] ] + try { + //helpers.echo(oEvalFunc[sFuncCond]); + bCondMemo = (!sFuncCond || oEvalFunc[sFuncCond](s, sx, m, dDA, sCountry, bCondMemo)); + if (bCondMemo) { + switch (cActionType) { + case "-": + // grammar error + //helpers.echo("-> error detected in " + sLineId + "\nzRegex: " + zRegex.source); + nErrorStart = nOffset + m.start[eAct[0]]; + if (!dErrs.has(nErrorStart) || nPriority > dPriority.get(nErrorStart)) { + dErrs.set(nErrorStart, this._createError(s, sx, sWhat, nOffset, m, eAct[0], sLineId, sRuleId, bUppercase, eAct[1], eAct[2], bIdRule, sOption, bContext)); + dPriority.set(nErrorStart, nPriority); + } + break; + case "~": + // text processor + //helpers.echo("-> text processor by " + sLineId + "\nzRegex: " + zRegex.source); + s = this._rewrite(s, sWhat, eAct[0], m, bUppercase); + bChange = true; + if (bDebug) { + helpers.echo("~ " + s + " -- " + m[eAct[0]] + " # " + sLineId); + } + break; + case "=": + // disambiguation + //helpers.echo("-> disambiguation by " + sLineId + "\nzRegex: " + zRegex.source); + oEvalFunc[sWhat](s, m, dDA); + if (bDebug) { + helpers.echo("= " + m[0] + " # " + sLineId + "\nDA: " + dDA.gl_toString()); + } + break; + case ">": + // we do nothing, this test is just a condition to apply all following actions + break; + default: + helpers.echo("# error: unknown action at " + sLineId); + } + } else { + if (cActionType == ">") { + break; + } + } + } + catch (e) { + helpers.echo(s); + helpers.echo("# line id: " + sLineId + "\n# rule id: " + sRuleId); + helpers.logerror(e); + } } } } } } } - } - if (bChange) { - return [s, dErrs]; - } - return [false, dErrs]; -} - -function _createError (s, sx, sRepl, nOffset, m, iGroup, sLineId, sRuleId, bUppercase, sMsg, sURL, bIdRule, sOption, bContext) { - let oErr = {}; - oErr["nStart"] = nOffset + m.start[iGroup]; - oErr["nEnd"] = nOffset + m.end[iGroup]; - oErr["sLineId"] = sLineId; - oErr["sRuleId"] = sRuleId; - oErr["sType"] = (sOption) ? sOption : "notype"; - // suggestions - if (sRepl[0] === "=") { - let sugg = oEvalFunc[sRepl.slice(1)](s, m); - if (sugg) { - if (bUppercase && m[iGroup].slice(0,1).gl_isUpperCase()) { - oErr["aSuggestions"] = capitalizeArray(sugg.split("|")); - } else { - oErr["aSuggestions"] = sugg.split("|"); - } - } else { - oErr["aSuggestions"] = []; - } - } else if (sRepl == "_") { - oErr["aSuggestions"] = []; - } else { - if (bUppercase && m[iGroup].slice(0,1).gl_isUpperCase()) { - oErr["aSuggestions"] = capitalizeArray(sRepl.gl_expand(m).split("|")); - } else { - oErr["aSuggestions"] = sRepl.gl_expand(m).split("|"); - } - } - // Message - if (sMsg[0] === "=") { - sMessage = oEvalFunc[sMsg.slice(1)](s, m) - } else { - sMessage = sMsg.gl_expand(m); - } - if (bIdRule) { - sMessage += " ##" + sLineId + " #" + sRuleId; - } - oErr["sMessage"] = sMessage; - // URL - oErr["URL"] = sURL || ""; - // Context - if (bContext) { - oErr["sUnderlined"] = sx.slice(m.start[iGroup], m.end[iGroup]); - oErr["sBefore"] = sx.slice(Math.max(0, m.start[iGroup]-80), m.start[iGroup]); - oErr["sAfter"] = sx.slice(m.end[iGroup], m.end[iGroup]+80); - } - return oErr; -} - -function _rewrite (s, sRepl, iGroup, m, bUppercase) { - // text processor: write sRepl in s at iGroup position" - let ln = m.end[iGroup] - m.start[iGroup]; - let sNew = ""; - if (sRepl === "*") { - sNew = " ".repeat(ln); - } else if (sRepl === ">" || sRepl === "_" || sRepl === "~") { - sNew = sRepl + " ".repeat(ln-1); - } else if (sRepl === "@") { - sNew = "@".repeat(ln); - } else if (sRepl.slice(0,1) === "=") { - sNew = oEvalFunc[sRepl.slice(1)](s, m); - sNew = sNew + " ".repeat(ln-sNew.length); - if (bUppercase && m[iGroup].slice(0,1).gl_isUpperCase()) { - sNew = sNew.gl_toCapitalize(); - } - } else { - sNew = sRepl.gl_expand(m); - sNew = sNew + " ".repeat(ln-sNew.length); - } - //echo("\n"+s+"\nstart: "+m.start[iGroup]+" end:"+m.end[iGroup]) - return s.slice(0, m.start[iGroup]) + sNew + s.slice(m.end[iGroup]); -} - -function ignoreRule (sRuleId) { - _aIgnoredRules.add(sRuleId); -} - -function resetIgnoreRules () { - _aIgnoredRules.clear(); -} - -function reactivateRule (sRuleId) { - _aIgnoredRules.delete(sRuleId); -} - -function listRules (sFilter=null) { - // generator: returns tuple (sOption, sLineId, sRuleId) - try { - for ([sOption, lRuleGroup] of _getRules(true)) { - for ([_, _, sLineId, sRuleId, _, _] of lRuleGroup) { - if (!sFilter || sRuleId.test(sFilter)) { - yield [sOption, sLineId, sRuleId]; - } - } - } - for ([sOption, lRuleGroup] of _getRules(false)) { - for ([_, _, sLineId, sRuleId, _, _] of lRuleGroup) { - if (!sFilter || sRuleId.test(sFilter)) { - yield [sOption, sLineId, sRuleId]; - } - } - } - } - catch (e) { - helpers.logerror(e); - } -} - - -//////// init - -function load (sContext="JavaScript") { - try { - _oDict = new ibdawg.IBDAWG("${dic_name}.json"); - _sContext = sContext; - _dOptions = gc_options.getOptions(sContext).gl_shallowCopy(); // duplication necessary, to be able to reset to default - } - catch (e) { - helpers.logerror(e); - } -} - -function setOption (sOpt, bVal) { - if (_dOptions.has(sOpt)) { - _dOptions.set(sOpt, bVal); - } -} - -function setOptions (dOpt) { - _dOptions.gl_updateOnlyExistingKeys(dOpt); -} - -function getOptions () { - return _dOptions; -} - -function getDefaultOptions () { - return gc_options.getOptions(_sContext).gl_shallowCopy(); -} - -function resetOptions () { - _dOptions = gc_options.getOptions(_sContext).gl_shallowCopy(); -} - -function getDictionary () { - return _oDict; -} - -function _getRules (bParagraph) { - if (!bParagraph) { - return _rules.lSentenceRules; - } - return _rules.lParagraphRules; -} - - - -//////// common functions + if (bChange) { + return [s, dErrs]; + } + return [false, dErrs]; + }, + + _createError: function (s, sx, sRepl, nOffset, m, iGroup, sLineId, sRuleId, bUppercase, sMsg, sURL, bIdRule, sOption, bContext) { + let oErr = {}; + oErr["nStart"] = nOffset + m.start[iGroup]; + oErr["nEnd"] = nOffset + m.end[iGroup]; + oErr["sLineId"] = sLineId; + oErr["sRuleId"] = sRuleId; + oErr["sType"] = (sOption) ? sOption : "notype"; + // suggestions + if (sRepl.slice(0,1) === "=") { + let sugg = oEvalFunc[sRepl.slice(1)](s, m); + if (sugg) { + if (bUppercase && m[iGroup].slice(0,1).gl_isUpperCase()) { + oErr["aSuggestions"] = capitalizeArray(sugg.split("|")); + } else { + oErr["aSuggestions"] = sugg.split("|"); + } + } else { + oErr["aSuggestions"] = []; + } + } else if (sRepl == "_") { + oErr["aSuggestions"] = []; + } else { + if (bUppercase && m[iGroup].slice(0,1).gl_isUpperCase()) { + oErr["aSuggestions"] = capitalizeArray(sRepl.gl_expand(m).split("|")); + } else { + oErr["aSuggestions"] = sRepl.gl_expand(m).split("|"); + } + } + // Message + let sMessage = ""; + if (sMsg.slice(0,1) === "=") { + sMessage = oEvalFunc[sMsg.slice(1)](s, m); + } else { + sMessage = sMsg.gl_expand(m); + } + if (bIdRule) { + sMessage += " ##" + sLineId + " #" + sRuleId; + } + oErr["sMessage"] = sMessage; + // URL + oErr["URL"] = sURL || ""; + // Context + if (bContext) { + oErr["sUnderlined"] = sx.slice(m.start[iGroup], m.end[iGroup]); + oErr["sBefore"] = sx.slice(Math.max(0, m.start[iGroup]-80), m.start[iGroup]); + oErr["sAfter"] = sx.slice(m.end[iGroup], m.end[iGroup]+80); + } + return oErr; + }, + + _rewrite: function (s, sRepl, iGroup, m, bUppercase) { + // text processor: write sRepl in s at iGroup position" + let ln = m.end[iGroup] - m.start[iGroup]; + let sNew = ""; + if (sRepl === "*") { + sNew = " ".repeat(ln); + } else if (sRepl === ">" || sRepl === "_" || sRepl === "~") { + sNew = sRepl + " ".repeat(ln-1); + } else if (sRepl === "@") { + sNew = "@".repeat(ln); + } else if (sRepl.slice(0,1) === "=") { + sNew = oEvalFunc[sRepl.slice(1)](s, m); + sNew = sNew + " ".repeat(ln-sNew.length); + if (bUppercase && m[iGroup].slice(0,1).gl_isUpperCase()) { + sNew = sNew.gl_toCapitalize(); + } + } else { + sNew = sRepl.gl_expand(m); + sNew = sNew + " ".repeat(ln-sNew.length); + } + //helpers.echo("\n"+s+"\nstart: "+m.start[iGroup]+" end:"+m.end[iGroup]) + return s.slice(0, m.start[iGroup]) + sNew + s.slice(m.end[iGroup]); + }, + + // Actions on rules + + ignoreRule: function (sRuleId) { + _aIgnoredRules.add(sRuleId); + }, + + resetIgnoreRules: function () { + _aIgnoredRules.clear(); + }, + + reactivateRule: function (sRuleId) { + _aIgnoredRules.delete(sRuleId); + }, + + listRules: function* (sFilter=null) { + // generator: returns tuple (sOption, sLineId, sRuleId) + try { + for (let [sOption, lRuleGroup] of this._getRules(true)) { + for (let [,, sLineId, sRuleId,,] of lRuleGroup) { + if (!sFilter || sRuleId.test(sFilter)) { + yield [sOption, sLineId, sRuleId]; + } + } + } + for (let [sOption, lRuleGroup] of this._getRules(false)) { + for (let [,, sLineId, sRuleId,,] of lRuleGroup) { + if (!sFilter || sRuleId.test(sFilter)) { + yield [sOption, sLineId, sRuleId]; + } + } + } + } + catch (e) { + helpers.logerror(e); + } + }, + + _getRules: function (bParagraph) { + if (!bParagraph) { + return gc_rules.lSentenceRules; + } + return gc_rules.lParagraphRules; + }, + + //// Initialization + + load: function (sContext="JavaScript", sPath="") { + try { + if (typeof(require) !== 'undefined') { + var ibdawg = require("resource://grammalecte/ibdawg.js"); + _oDict = new ibdawg.IBDAWG("${dic_name}.json"); + } else { + _oDict = new IBDAWG("${dic_name}.json", sPath); + } + _sAppContext = sContext; + _dOptions = gc_options.getOptions(sContext).gl_shallowCopy(); // duplication necessary, to be able to reset to default + } + catch (e) { + helpers.logerror(e); + } + }, + + getDictionary: function () { + return _oDict; + }, + + //// Options + + setOption: function (sOpt, bVal) { + if (_dOptions.has(sOpt)) { + _dOptions.set(sOpt, bVal); + } + }, + + setOptions: function (dOpt) { + _dOptions.gl_updateOnlyExistingKeys(dOpt); + }, + + getOptions: function () { + return _dOptions; + }, + + getDefaultOptions: function () { + return gc_options.getOptions(_sAppContext).gl_shallowCopy(); + }, + + resetOptions: function () { + _dOptions = gc_options.getOptions(_sAppContext).gl_shallowCopy(); + } +}; + + +//////// Common functions function option (sOpt) { // return true if option sOpt is active return _dOptions.get(sOpt); } function displayInfo (dDA, aWord) { // for debugging: info of word if (!aWord) { - echo("> nothing to find"); + helpers.echo("> nothing to find"); return true; } if (!_dAnalyses.has(aWord[1]) && !_storeMorphFromFSA(aWord[1])) { - echo("> not in FSA"); + helpers.echo("> not in FSA"); return true; } if (dDA.has(aWord[0])) { - echo("DA: " + dDA.get(aWord[0])); + helpers.echo("DA: " + dDA.get(aWord[0])); } - echo("FSA: " + _dAnalyses.get(aWord[1])); + helpers.echo("FSA: " + _dAnalyses.get(aWord[1])); return true; } function _storeMorphFromFSA (sWord) { // retrieves morphologies list from _oDict -> _dAnalyses - //echo("register: "+sWord + " " + _oDict.getMorph(sWord).toString()) + //helpers.echo("register: "+sWord + " " + _oDict.getMorph(sWord).toString()) _dAnalyses.set(sWord, _oDict.getMorph(sWord)); return !!_dAnalyses.get(sWord); } function morph (dDA, aWord, sPattern, bStrict=true, bNoWord=false) { // analyse a tuple (position, word), return true if sPattern in morphologies (disambiguation on) if (!aWord) { - //echo("morph: noword, returns " + bNoWord); + //helpers.echo("morph: noword, returns " + bNoWord); return bNoWord; } - //echo("aWord: "+aWord.toString()); + //helpers.echo("aWord: "+aWord.toString()); if (!_dAnalyses.has(aWord[1]) && !_storeMorphFromFSA(aWord[1])) { return false; } let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _dAnalyses.get(aWord[1]); - //echo("lMorph: "+lMorph.toString()); + //helpers.echo("lMorph: "+lMorph.toString()); if (lMorph.length === 0) { return false; } - //echo("***"); + //helpers.echo("***"); if (bStrict) { return lMorph.every(s => (s.search(sPattern) !== -1)); } return lMorph.some(s => (s.search(sPattern) !== -1)); } function morphex (dDA, aWord, sPattern, sNegPattern, bNoWord=false) { // analyse a tuple (position, word), returns true if not sNegPattern in word morphologies and sPattern in word morphologies (disambiguation on) if (!aWord) { - //echo("morph: noword, returns " + bNoWord); + //helpers.echo("morph: noword, returns " + bNoWord); return bNoWord; } - //echo("aWord: "+aWord.toString()); + //helpers.echo("aWord: "+aWord.toString()); if (!_dAnalyses.has(aWord[1]) && !_storeMorphFromFSA(aWord[1])) { return false; } let lMorph = dDA.has(aWord[0]) ? dDA.get(aWord[0]) : _dAnalyses.get(aWord[1]); - //echo("lMorph: "+lMorph.toString()); + //helpers.echo("lMorph: "+lMorph.toString()); if (lMorph.length === 0) { return false; } - //echo("***"); + //helpers.echo("***"); // check negative condition if (lMorph.some(s => (s.search(sNegPattern) !== -1))) { return false; } // search sPattern @@ -449,60 +472,56 @@ return []; } if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) { return []; } - return [ for (s of _dAnalyses.get(sWord)) s.slice(1, s.indexOf(" ")) ]; + return _dAnalyses.get(sWord).map( s => s.slice(1, s.indexOf(" ")) ); } //// functions to get text outside pattern scope // warning: check compile_rules.py to understand how it works function nextword (s, iStart, n) { // get the nth word of the input string or empty string - let z = new RegExp("^( +[a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st%_-]+){" + (n-1).toString() + "} +([a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st%_-]+)", "i"); + let z = new RegExp("^(?: +[a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st%_-]+){" + (n-1).toString() + "} +([a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st%_-]+)", "ig"); let m = z.exec(s.slice(iStart)); if (!m) { return null; } - return [iStart + RegExp.lastIndex - m[2].length, m[2]]; + return [iStart + z.lastIndex - m[1].length, m[1]]; } function prevword (s, iEnd, n) { // get the (-)nth word of the input string or empty string - let z = new RegExp("([a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st%_-]+) +([a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st%_-]+ +){" + (n-1).toString() + "}$", "i"); + let z = new RegExp("([a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st%_-]+) +(?:[a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st%_-]+ +){" + (n-1).toString() + "}$", "i"); let m = z.exec(s.slice(0, iEnd)); if (!m) { return null; } return [m.index, m[1]]; } -const _zNextWord = new RegExp ("^ +([a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st_][a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st_-]*)", "i"); -const _zPrevWord = new RegExp ("([a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st_][a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st_-]*) +$", "i"); - function nextword1 (s, iStart) { // get next word (optimization) + let _zNextWord = new RegExp ("^ +([a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st_][a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st_-]*)", "ig"); let m = _zNextWord.exec(s.slice(iStart)); if (!m) { return null; } - return [iStart + RegExp.lastIndex - m[1].length, m[1]]; + return [iStart + _zNextWord.lastIndex - m[1].length, m[1]]; } + +const _zPrevWord = new RegExp ("([a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st_][a-zà-öA-Zø-ÿÀ-Ö0-9Ø-ßĀ-ʯfi-st_-]*) +$", "i"); function prevword1 (s, iEnd) { // get previous word (optimization) - //echo("prev1, s:"+s); - //echo("prev1, s.slice(0, iEnd):"+s.slice(0, iEnd)); let m = _zPrevWord.exec(s.slice(0, iEnd)); - //echo("prev1, m:"+m); if (!m) { return null; } - //echo("prev1: " + m.index + " " + m[1]); return [m.index, m[1]]; } function look (s, zPattern, zNegPattern=null) { // seek zPattern in s (before/after/fulltext), if antipattern zNegPattern not in s @@ -549,16 +568,14 @@ return true; } if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) { return true; } - //echo("morph: "+_dAnalyses.get(sWord).toString()); if (_dAnalyses.get(sWord).length === 1) { return true; } - let lSelect = [ for (sMorph of _dAnalyses.get(sWord)) if (sMorph.search(sPattern) !== -1) sMorph ]; - //echo("lSelect: "+lSelect.toString()); + let lSelect = _dAnalyses.get(sWord).filter( sMorph => sMorph.search(sPattern) !== -1 ); if (lSelect.length > 0) { if (lSelect.length != _dAnalyses.get(sWord).length) { dDA.set(nPos, lSelect); } } else if (lDefault) { @@ -578,12 +595,11 @@ return true; } if (_dAnalyses.get(sWord).length === 1) { return true; } - let lSelect = [ for (sMorph of _dAnalyses.get(sWord)) if (sMorph.search(sPattern) === -1) sMorph ]; - //echo("lSelect: "+lSelect.toString()); + let lSelect = _dAnalyses.get(sWord).filter( sMorph => sMorph.search(sPattern) === -1 ); if (lSelect.length > 0) { if (lSelect.length != _dAnalyses.get(sWord).length) { dDA.set(nPos, lSelect); } } else if (lDefault) { @@ -594,10 +610,11 @@ function define (dDA, nPos, lMorph) { dDA.set(nPos, lMorph); return true; } + //////// GRAMMAR CHECKER PLUGINS ${pluginsJS} @@ -605,20 +622,32 @@ ${callablesJS} if (typeof(exports) !== 'undefined') { - exports.load = load; - exports.parse = parse; - exports.lang = lang; - exports.version = version; - exports.getDictionary = getDictionary; - exports.setOption = setOption; - exports.setOptions = setOptions; - exports.getOptions = getOptions; - exports.getDefaultOptions = getDefaultOptions; - exports.resetOptions = resetOptions; - exports.ignoreRule = ignoreRule; - exports.reactivateRule = reactivateRule; - exports.resetIgnoreRules = resetIgnoreRules; - exports.listRules = listRules; + exports.lang = gc_engine.lang; + exports.locales = gc_engine.locales; + exports.pkg = gc_engine.pkg; + exports.name = gc_engine.name; + exports.version = gc_engine.version; + exports.author = gc_engine.author; + exports.parse = gc_engine.parse; + exports._zEndOfSentence = gc_engine._zEndOfSentence; + exports._zBeginOfParagraph = gc_engine._zBeginOfParagraph; + exports._zEndOfParagraph = gc_engine._zEndOfParagraph; + exports._getSentenceBoundaries = gc_engine._getSentenceBoundaries; + exports._proofread = gc_engine._proofread; + exports._createError = gc_engine._createError; + exports._rewrite = gc_engine._rewrite; + exports.ignoreRule = gc_engine.ignoreRule; + exports.resetIgnoreRules = gc_engine.resetIgnoreRules; + exports.reactivateRule = gc_engine.reactivateRule; + exports.listRules = gc_engine.listRules; + exports._getRules = gc_engine._getRules; + exports.load = gc_engine.load; + exports.getDictionary = gc_engine.getDictionary; + exports.setOption = gc_engine.setOption; + exports.setOptions = gc_engine.setOptions; + exports.getOptions = gc_engine.getOptions; + exports.getDefaultOptions = gc_engine.getDefaultOptions; + exports.resetOptions = gc_engine.resetOptions; } Index: gc_core/js/lang_core/gc_options.js ================================================================== --- gc_core/js/lang_core/gc_options.js +++ gc_core/js/lang_core/gc_options.js @@ -1,27 +1,33 @@ // Options for Grammalecte +/*jslint esversion: 6*/ +/*global exports*/ ${map} -function getOptions (sContext="JavaScript") { - if (dOpt.hasOwnProperty(sContext)) { - return dOpt[sContext]; - } - return dOpt["JavaScript"]; -} - -const lStructOpt = ${lStructOpt}; - -const dOpt = { - "JavaScript": new Map (${dOptJavaScript}), - "Firefox": new Map (${dOptFirefox}), - "Thunderbird": new Map (${dOptThunderbird}), -} - -const dOptLabel = ${dOptLabel}; + +var gc_options = { + getOptions: function (sContext="JavaScript") { + if (this.dOpt.hasOwnProperty(sContext)) { + return this.dOpt[sContext]; + } + return this.dOpt["JavaScript"]; + }, + + lStructOpt: ${lStructOpt}, + + dOpt: { + "JavaScript": new Map (${dOptJavaScript}), + "Firefox": new Map (${dOptFirefox}), + "Thunderbird": new Map (${dOptThunderbird}), + }, + + dOptLabel: ${dOptLabel} +} if (typeof(exports) !== 'undefined') { - exports.getOptions = getOptions; - exports.lStructOpt = lStructOpt; - exports.dOptLabel = dOptLabel; + exports.getOptions = gc_options.getOptions; + exports.lStructOpt = gc_options.lStructOpt; + exports.dOpt = gc_options.dOpt; + exports.dOptLabel = gc_options.dOptLabel; } Index: gc_core/js/lang_core/gc_rules.js ================================================================== --- gc_core/js/lang_core/gc_rules.js +++ gc_core/js/lang_core/gc_rules.js @@ -1,15 +1,20 @@ // Grammar checker rules +/*jslint esversion: 6*/ +/*global exports*/ + "use strict"; ${string} ${regex} -const lParagraphRules = ${paragraph_rules_JS}; +var gc_rules = { + lParagraphRules: ${paragraph_rules_JS}, -const lSentenceRules = ${sentence_rules_JS}; + lSentenceRules: ${sentence_rules_JS} +} if (typeof(exports) !== 'undefined') { - exports.lParagraphRules = lParagraphRules; - exports.lSentenceRules = lSentenceRules; + exports.lParagraphRules = gc_rules.lParagraphRules; + exports.lSentenceRules = gc_rules.lSentenceRules; } Index: gc_core/js/str_transform.js ================================================================== --- gc_core/js/str_transform.js +++ gc_core/js/str_transform.js @@ -1,60 +1,33 @@ //// STRING TRANSFORMATION - -var dSimilarChars = new Map ([ - ["a", "aàâáä"], - ["à", "aàâáä"], - ["â", "aàâáä"], - ["á", "aàâáä"], - ["ä", "aàâáä"], - ["c", "cç"], - ["ç", "cç"], - ["e", "eéêèë"], - ["é", "eéêèë"], - ["ê", "eéêèë"], - ["è", "eéêèë"], - ["ë", "eéêèë"], - ["i", "iîïíì"], - ["î", "iîïíì"], - ["ï", "iîïíì"], - ["í", "iîïíì"], - ["ì", "iîïíì"], - ["o", "oôóòö"], - ["ô", "oôóòö"], - ["ó", "oôóòö"], - ["ò", "oôóòö"], - ["ö", "oôóòö"], - ["u", "uûùüú"], - ["û", "uûùüú"], - ["ù", "uûùüú"], - ["ü", "uûùüú"], - ["ú", "uûùüú"] -]); +/*jslint esversion: 6*/ // Note: 48 is the ASCII code for "0" -// Suffix only -function getStemFromSuffixCode (sFlex, sSfxCode) { - if (sSfxCode == "0") { - return sFlex; - } - return sSfxCode[0] == '0' ? sFlex + sSfxCode.slice(1) : sFlex.slice(0, -(sSfxCode.charCodeAt(0)-48)) + sSfxCode.slice(1); -} - -// Prefix and suffix -function getStemFromAffixCode (sFlex, sAffCode) { - if (sAffCode == "0") { - return sFlex; - } - if (!sAffCode.includes("/")) { - return "# error #"; - } - var [sPfxCode, sSfxCode] = sAffCode.split('/'); - sFlex = sPfxCode.slice(1) + sFlex.slice(sPfxCode.charCodeAt(0)-48); - return sSfxCode[0] == '0' ? sFlex + sSfxCode.slice(1) : sFlex.slice(0, -(sSfxCode.charCodeAt(0)-48)) + sSfxCode.slice(1); -} +var str_transform = { + getStemFromSuffixCode: function (sFlex, sSfxCode) { + // Suffix only + if (sSfxCode == "0") { + return sFlex; + } + return sSfxCode[0] == '0' ? sFlex + sSfxCode.slice(1) : sFlex.slice(0, -(sSfxCode.charCodeAt(0)-48)) + sSfxCode.slice(1); + }, + + getStemFromAffixCode: function (sFlex, sAffCode) { + // Prefix and suffix + if (sAffCode == "0") { + return sFlex; + } + if (!sAffCode.includes("/")) { + return "# error #"; + } + let [sPfxCode, sSfxCode] = sAffCode.split('/'); + sFlex = sPfxCode.slice(1) + sFlex.slice(sPfxCode.charCodeAt(0)-48); + return sSfxCode[0] == '0' ? sFlex + sSfxCode.slice(1) : sFlex.slice(0, -(sSfxCode.charCodeAt(0)-48)) + sSfxCode.slice(1); + } +}; if (typeof(exports) !== 'undefined') { - exports.getStemFromSuffixCode = getStemFromSuffixCode; - exports.getStemFromAffixCode = getStemFromAffixCode; + exports.getStemFromSuffixCode = str_transform.getStemFromSuffixCode; + exports.getStemFromAffixCode = str_transform.getStemFromAffixCode; } Index: gc_core/js/tests.js ================================================================== --- gc_core/js/tests.js +++ gc_core/js/tests.js @@ -1,26 +1,31 @@ // JavaScript +/*jslint esversion: 6*/ +/*global console,require,exports*/ "use strict"; -const helpers = require("resource://grammalecte/helpers.js"); +if (typeof(require) !== 'undefined') { + var helpers = require("resource://grammalecte/helpers.js"); +} class TestGrammarChecking { - constructor (gce) { + constructor (gce, spfTests="") { this.gce = gce; + this.spfTests = spfTests; this._aRuleTested = new Set(); - }; + } * testParse (bDebug=false) { const t0 = Date.now(); - const aData = JSON.parse(helpers.loadFile("resource://grammalecte/"+this.gce.lang+"/tests_data.json")).aData; - //const aData = require("resource://grammalecte/"+this.gce.lang+"/tests_data.js").aData; - let nInvalid = 0 - let nTotal = 0 + let sURL = (this.spfTests !== "") ? this.spfTests : "resource://grammalecte/"+this.gce.lang+"/tests_data.json"; + const aData = JSON.parse(helpers.loadFile(sURL)).aData; + let nInvalid = 0; + let nTotal = 0; let sErrorText; let sSugg; let sExpectedErrors; let sTextToCheck; let sFoundErrors; @@ -59,11 +64,11 @@ "\n# Line num: " + sLineNum + "\n> to check: " + sTextToCheck + "\n expected: " + sExpectedErrors + "\n found: " + sFoundErrors + "\n errors: \n" + sListErr; - nInvalid = nInvalid + 1 + nInvalid = nInvalid + 1; } nTotal = nTotal + 1; } i = i + 1; if (i % 1000 === 0) { @@ -76,11 +81,11 @@ helpers.logerror(e); } if (bShowUntested) { i = 0; - for (let [sOpt, sLineId, sRuleId] of gce.listRules()) { + for (let [sOpt, sLineId, sRuleId] of this.gce.listRules()) { if (!this._aRuleTested.has(sLineId) && !/^[0-9]+[sp]$|^[pd]_/.test(sRuleId)) { sUntestedRules += sRuleId + ", "; i += 1; } } @@ -90,11 +95,11 @@ } const t1 = Date.now(); yield "Tests parse finished in " + ((t1-t0)/1000).toString() + " s\nTotal errors: " + nInvalid.toString() + " / " + nTotal.toString(); - }; + } _getExpectedErrors (sLine) { try { let sRes = " ".repeat(sLine.length); let z = /\{\{.+?\}\}/g; @@ -116,19 +121,19 @@ } catch (e) { helpers.logerror(e); } return " ".repeat(sLine.length); - }; + } _getFoundErrors (sLine, bDebug, sOption) { try { let aErrs = []; if (sOption) { - gce.setOption(sOption, true); + this.gce.setOption(sOption, true); aErrs = this.gce.parse(sLine, "FR", bDebug); - gce.setOption(sOption, false); + this.gce.setOption(sOption, false); } else { aErrs = this.gce.parse(sLine, "FR", bDebug); } let sRes = " ".repeat(sLine.length); let sListErr = ""; @@ -141,13 +146,13 @@ } catch (e) { helpers.logerror(e); } return [" ".repeat(sLine.length), ""]; - }; + } } if (typeof(exports) !== 'undefined') { exports.TestGrammarChecking = TestGrammarChecking; } Index: gc_core/js/text.js ================================================================== --- gc_core/js/text.js +++ gc_core/js/text.js @@ -1,64 +1,71 @@ // JavaScript +/*jslint esversion: 6*/ +/*global require,exports*/ "use strict"; -const helpers = require("resource://grammalecte/helpers.js"); - - -function* getParagraph (sText) { - // generator: returns paragraphs of text - let iStart = 0; - let iEnd = 0; - sText = sText.replace("\r", ""); - while ((iEnd = sText.indexOf("\n", iStart)) !== -1) { - yield sText.slice(iStart, iEnd); - iStart = iEnd + 1; - } - yield sText.slice(iStart); -} - -function* wrap (sText, nWidth=80) { - // generator: returns text line by line - while (sText) { - if (sText.length >= nWidth) { - let nEnd = sText.lastIndexOf(" ", nWidth) + 1; - if (nEnd > 0) { - yield sText.slice(0, nEnd); - sText = sText.slice(nEnd); - } else { - yield sText.slice(0, nWidth); - sText = sText.slice(nWidth); - } - } else { - break; - } - } - yield sText; -} - -function getReadableError (oErr) { - // Returns an error oErr as a readable error - try { - let sResult = "\n* " + oErr['nStart'] + ":" + oErr['nEnd'] - + " # " + oErr['sLineId'] + " # " + oErr['sRuleId'] + ":\n"; - sResult += " " + oErr["sMessage"]; - if (oErr["aSuggestions"].length > 0) { - sResult += "\n > Suggestions : " + oErr["aSuggestions"].join(" | "); - } - if (oErr["URL"] !== "") { - sResult += "\n > URL: " + oErr["URL"]; - } - return sResult; - } - catch (e) { - helpers.logerror(e); - return "\n# Error. Data: " + oErr.toString(); - } -} + +if (typeof(require) !== 'undefined') { + var helpers = require("resource://grammalecte/helpers.js"); +} + + +var text = { + getParagraph: function* (sText) { + // generator: returns paragraphs of text + let iStart = 0; + let iEnd = 0; + sText = sText.replace("\r\n", "\n").replace("\r", "\n"); + while ((iEnd = sText.indexOf("\n", iStart)) !== -1) { + yield sText.slice(iStart, iEnd); + iStart = iEnd + 1; + } + yield sText.slice(iStart); + }, + + wrap: function* (sText, nWidth=80) { + // generator: returns text line by line + while (sText) { + if (sText.length >= nWidth) { + let nEnd = sText.lastIndexOf(" ", nWidth) + 1; + if (nEnd > 0) { + yield sText.slice(0, nEnd); + sText = sText.slice(nEnd); + } else { + yield sText.slice(0, nWidth); + sText = sText.slice(nWidth); + } + } else { + break; + } + } + yield sText; + }, + + getReadableError: function (oErr) { + // Returns an error oErr as a readable error + try { + let sResult = "\n* " + oErr['nStart'] + ":" + oErr['nEnd'] + + " # " + oErr['sLineId'] + " # " + oErr['sRuleId'] + ":\n"; + sResult += " " + oErr["sMessage"]; + if (oErr["aSuggestions"].length > 0) { + sResult += "\n > Suggestions : " + oErr["aSuggestions"].join(" | "); + } + if (oErr["URL"] !== "") { + sResult += "\n > URL: " + oErr["URL"]; + } + return sResult; + } + catch (e) { + helpers.logerror(e); + return "\n# Error. Data: " + oErr.toString(); + } + } +}; if (typeof(exports) !== 'undefined') { - exports.getParagraph = getParagraph; - exports.wrap = wrap; - exports.getReadableError = getReadableError; + exports.getParagraph = text.getParagraph; + exports.wrap = text.wrap; + exports.getReadableError = text.getReadableError; } Index: gc_core/js/tokenizer.js ================================================================== --- gc_core/js/tokenizer.js +++ gc_core/js/tokenizer.js @@ -1,13 +1,19 @@ // JavaScript // Very simple tokenizer +/*jslint esversion: 6*/ +/*global require,exports*/ "use strict"; -const helpers = require("resource://grammalecte/helpers.js"); + +if (typeof(require) !== 'undefined') { + var helpers = require("resource://grammalecte/helpers.js"); +} + -const aPatterns = { +const aTkzPatterns = { // All regexps must start with ^. "default": [ [/^[ \t]+/, 'SPACE'], [/^[,.;:!?…«»“”‘’"(){}\[\]/·–—]+/, 'SEPARATOR'], @@ -33,22 +39,22 @@ [/^\d\d?[hm]\d\d\b/, 'HOUR'], [/^\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'] ] -} +}; class Tokenizer { constructor (sLang) { this.sLang = sLang; - if (!aPatterns.hasOwnProperty(sLang)) { + if (!aTkzPatterns.hasOwnProperty(sLang)) { this.sLang = "default"; } - this.aRules = aPatterns[this.sLang]; - }; + this.aRules = aTkzPatterns[this.sLang]; + } * genTokens (sText) { let m; let i = 0; while (sText) { @@ -72,11 +78,11 @@ } } 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)) { Index: gc_core/py/lang_core/gc_engine.py ================================================================== --- gc_core/py/lang_core/gc_engine.py +++ gc_core/py/lang_core/gc_engine.py @@ -472,19 +472,19 @@ # warning: check compile_rules.py to understand how it works def nextword (s, iStart, n): "get the nth word of the input string or empty string" - m = re.match("( +[\\w%-]+){" + str(n-1) + "} +([\\w%-]+)", s[iStart:]) + m = re.match("(?: +[\\w%-]+){" + str(n-1) + "} +([\\w%-]+)", s[iStart:]) if not m: return None - return (iStart+m.start(2), m.group(2)) + return (iStart+m.start(1), m.group(1)) def prevword (s, iEnd, n): "get the (-)nth word of the input string or empty string" - m = re.search("([\\w%-]+) +([\\w%-]+ +){" + str(n-1) + "}$", s[:iEnd]) + m = re.search("([\\w%-]+) +(?:[\\w%-]+ +){" + str(n-1) + "}$", s[:iEnd]) if not m: return None return (m.start(1), m.group(1)) Index: gc_core/py/text.py ================================================================== --- gc_core/py/text.py +++ gc_core/py/text.py @@ -5,10 +5,11 @@ def getParagraph (sText): "generator: returns paragraphs of text" iStart = 0 + sText = sText.replace("\r\n", "\n").replace("\r", "\n") iEnd = sText.find("\n", iStart) while iEnd != -1: yield sText[iStart:iEnd] iStart = iEnd + 1 iEnd = sText.find("\n", iStart) 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.18 +version = 0.6 author = Olivier R. provider = Dicollecte link = http://grammalecte.net description = Correcteur grammatical pour le français. extras = README_fr.txt @@ -29,13 +29,14 @@ # Firefox fx_identifier = French-GC@grammalecte.net fx_name = Grammalecte [fr] -fx_standard_path = C:\Program Files\Mozilla Firefox\firefox.exe -fx_beta_path = C:\Program Files\Mozilla Firefox Beta\firefox.exe -fx_nightly_path = C:\Program Files (x86)\Nightly\firefox.exe +win_fx_dev_path = C:\Program Files\Firefox Developer Edition\firefox.exe +win_fx_nightly_path = C:\Program Files (x86)\Nightly\firefox.exe +linux_fx_dev_path = /usr/bin/firefox +linux_fx_nightly_path = /usr/bin/firefox # Thunderbird tb_identifier = French-GC-TB@grammalecte.net tb_name = Grammalecte [fr] Index: gc_lang/fr/modules-js/conj.js ================================================================== --- gc_lang/fr/modules-js/conj.js +++ gc_lang/fr/modules-js/conj.js @@ -1,190 +1,186 @@ // Grammalecte - Conjugueur // License: GPL 3 +/*jslint esversion: 6*/ +/*global console,require,exports,self,browser*/ "use strict"; ${map} -let helpers = null; // module not loaded in Firefox content script - -let _oData = {}; -let _lVtyp = null; -let _lTags = null; -let _dPatternConj = {}; -let _dVerb = {}; - - -if (typeof(exports) !== 'undefined') { - // used within Grammalecte library - helpers = require("resource://grammalecte/helpers.js"); - _oData = JSON.parse(helpers.loadFile("resource://grammalecte/fr/conj_data.json")); - _lVtyp = _oData.lVtyp; - _lTags = _oData.lTags; - _dPatternConj = _oData.dPatternConj; - _dVerb = _oData.dVerb; -} else { - // used within Firefox content script (conjugation panel). - // can’t load JSON from here, so we do it in ui.js and send it here. - self.port.on("provideConjData", function (sData) { - _oData = JSON.parse(sData); - _lVtyp = _oData.lVtyp; - _lTags = _oData.lTags; - _dPatternConj = _oData.dPatternConj; - _dVerb = _oData.dVerb; - }); -} - - -const _zStartVoy = new RegExp("^[aeéiouœê]"); -const _zNeedTeuph = new RegExp("[tdc]$"); - -const _dProSuj = new Map ([ [":1s", "je"], [":1ś", "je"], [":2s", "tu"], [":3s", "il"], [":1p", "nous"], [":2p", "vous"], [":3p", "ils"] ]); -const _dProObj = new Map ([ [":1s", "me "], [":1ś", "me "], [":2s", "te "], [":3s", "se "], [":1p", "nous "], [":2p", "vous "], [":3p", "se "] ]); -const _dProObjEl = new Map ([ [":1s", "m’"], [":1ś", "m’"], [":2s", "t’"], [":3s", "s’"], [":1p", "nous "], [":2p", "vous "], [":3p", "s’"] ]); -const _dImpePro = new Map ([ [":2s", "-toi"], [":1p", "-nous"], [":2p", "-vous"] ]); -const _dImpeProNeg = new Map ([ [":2s", "ne te "], [":1p", "ne nous "], [":2p", "ne vous "] ]); -const _dImpeProEn = new Map ([ [":2s", "-t’en"], [":1p", "-nous-en"], [":2p", "-vous-en"] ]); -const _dImpeProNegEn = new Map ([ [":2s", "ne t’en "], [":1p", "ne nous en "], [":2p", "ne vous en "] ]); - -const _dGroup = new Map ([ ["0", "auxiliaire"], ["1", "1ᵉʳ groupe"], ["2", "2ᵉ groupe"], ["3", "3ᵉ groupe"] ]); - -const _dTenseIdx = new Map ([ [":PQ", 0], [":Ip", 1], [":Iq", 2], [":Is", 3], [":If", 4], [":K", 5], [":Sp", 6], [":Sq", 7], [":E", 8] ]); - - -function isVerb (sVerb) { - return _dVerb.hasOwnProperty(sVerb); -} - -function getConj (sVerb, sTense, sWho) { - // returns conjugation (can be an empty string) - if (!_dVerb.hasOwnProperty(sVerb)) { - return null; - } - if (!_dPatternConj[sTense][_lTags[_dVerb[sVerb][1]][_dTenseIdx.get(sTense)]].hasOwnProperty(sWho)) { - return ""; - } - return _modifyStringWithSuffixCode(sVerb, _dPatternConj[sTense][_lTags[_dVerb[sVerb][1]][_dTenseIdx.get(sTense)]][sWho]); -} - -function hasConj (sVerb, sTense, sWho) { - // returns false if no conjugation (also if empty) else true - if (!_dVerb.hasOwnProperty(sVerb)) { - return false; - } - if (_dPatternConj[sTense][_lTags[_dVerb[sVerb][1]][_dTenseIdx.get(sTense)]].hasOwnProperty(sWho) - && _dPatternConj[sTense][_lTags[_dVerb[sVerb][1]][_dTenseIdx.get(sTense)]][sWho]) { - return true; - } - return false; -} - -function getVtyp (sVerb) { - // returns raw informations about sVerb - if (!_dVerb.hasOwnProperty(sVerb)) { - return null; - } - return _lVtyp[_dVerb[sVerb][0]]; -} - -function getSimil (sWord, sMorph, sFilter=null) { - if (!sMorph.includes(":V")) { - return new Set(); - } - let sInfi = sMorph.slice(1, sMorph.indexOf(" ")); - let tTags = _getTags(sInfi); - let aSugg = new Set(); - if (sMorph.includes(":Q") || sMorph.includes(":Y")) { - // we suggest conjugated forms - if (sMorph.includes(":V1")) { - aSugg.add(sInfi); - aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":3s")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":2p")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":Iq", ":1s")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":Iq", ":3s")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":Iq", ":3p")); - } else if (sMorph.includes(":V2")) { - aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":1s")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":3s")); - } else if (sMorph.includes(":V3")) { - aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":1s")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":Ip", ":3s")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":Is", ":1s")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":Is", ":3s")); - } else if (isMorph.includes(":V0a")) { - aSugg.add("eus"); - aSugg.add("eut"); - } else { - aSugg.add("étais"); - aSugg.add("était"); - } - aSugg.delete(""); - } else { - // we suggest past participles - aSugg.add(_getConjWithTags(sInfi, tTags, ":PQ", ":Q1")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":PQ", ":Q2")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":PQ", ":Q3")); - aSugg.add(_getConjWithTags(sInfi, tTags, ":PQ", ":Q4")); - aSugg.delete(""); - // if there is only one past participle (epi inv), unreliable. - if (aSugg.size === 1) { - aSugg.clear(); - } - if (sMorph.includes(":V1")) { - aSugg.add(sInfi); - } - } - return aSugg; -} - - -function _getTags (sVerb) { - // returns tuple of tags (usable with functions _getConjWithTags and _hasConjWithTags) - if (!_dVerb.hasOwnProperty(sVerb)) { - return null; - } - return _lTags[_dVerb[sVerb][1]]; -} - -function _getConjWithTags (sVerb, tTags, sTense, sWho) { - // returns conjugation (can be an empty string) - if (!_dPatternConj[sTense][tTags[_dTenseIdx.get(sTense)]].hasOwnProperty(sWho)) { - return ""; - } - return _modifyStringWithSuffixCode(sVerb, _dPatternConj[sTense][tTags[_dTenseIdx.get(sTense)]][sWho]); -} - -function _hasConjWithTags (tTags, sTense, sWho) { - // returns false if no conjugation (also if empty) else true - if (_dPatternConj[sTense][tTags[_dTenseIdx.get(sTense)]].hasOwnProperty(sWho) - && _dPatternConj[sTense][tTags[_dTenseIdx.get(sTense)]][sWho]) { - return true; - } - return false; -} - -function _modifyStringWithSuffixCode (sWord, sSfx) { - // returns sWord modified by sSfx - if (sSfx === "") { - return ""; - } - if (sSfx === "0") { - return sWord; - } - try { - if (sSfx[0] !== '0') { - return sWord.slice(0, -(sSfx.charCodeAt(0)-48)) + sSfx.slice(1); // 48 is the ASCII code for "0" - } else { - return sWord + sSfx.slice(1); - } - } - catch (e) { - console.log(e); - return "## erreur, code : " + sSfx + " ##"; - } -} +if (typeof(require) !== 'undefined') { + var helpers = require("resource://grammalecte/helpers.js"); +} + +var conj = { + _lVtyp: [], + _lTags: [], + _dPatternConj: {}, + _dVerb: {}, + + bInit: false, + init: function (sJSONData) { + try { + let _oData = JSON.parse(sJSONData); + this._lVtyp = _oData.lVtyp; + this._lTags = _oData.lTags; + this._dPatternConj = _oData.dPatternConj; + this._dVerb = _oData.dVerb; + this.bInit = true; + } + catch (e) { + console.error(e); + } + }, + + _zStartVoy: new RegExp("^[aeéiouœê]"), + _zNeedTeuph: new RegExp("[tdc]$"), + + _dProSuj: new Map ([ [":1s", "je"], [":1ś", "je"], [":2s", "tu"], [":3s", "il"], [":1p", "nous"], [":2p", "vous"], [":3p", "ils"] ]), + _dProObj: new Map ([ [":1s", "me "], [":1ś", "me "], [":2s", "te "], [":3s", "se "], [":1p", "nous "], [":2p", "vous "], [":3p", "se "] ]), + _dProObjEl: new Map ([ [":1s", "m’"], [":1ś", "m’"], [":2s", "t’"], [":3s", "s’"], [":1p", "nous "], [":2p", "vous "], [":3p", "s’"] ]), + _dImpePro: new Map ([ [":2s", "-toi"], [":1p", "-nous"], [":2p", "-vous"] ]), + _dImpeProNeg: new Map ([ [":2s", "ne te "], [":1p", "ne nous "], [":2p", "ne vous "] ]), + _dImpeProEn: new Map ([ [":2s", "-t’en"], [":1p", "-nous-en"], [":2p", "-vous-en"] ]), + _dImpeProNegEn: new Map ([ [":2s", "ne t’en "], [":1p", "ne nous en "], [":2p", "ne vous en "] ]), + + _dGroup: new Map ([ ["0", "auxiliaire"], ["1", "1ᵉʳ groupe"], ["2", "2ᵉ groupe"], ["3", "3ᵉ groupe"] ]), + + _dTenseIdx: new Map ([ [":PQ", 0], [":Ip", 1], [":Iq", 2], [":Is", 3], [":If", 4], [":K", 5], [":Sp", 6], [":Sq", 7], [":E", 8] ]), + + isVerb: function (sVerb) { + return this._dVerb.hasOwnProperty(sVerb); + }, + + getConj: function (sVerb, sTense, sWho) { + // returns conjugation (can be an empty string) + if (!this._dVerb.hasOwnProperty(sVerb)) { + return null; + } + if (!this._dPatternConj[sTense][this._lTags[this._dVerb[sVerb][1]][this._dTenseIdx.get(sTense)]].hasOwnProperty(sWho)) { + return ""; + } + return this._modifyStringWithSuffixCode(sVerb, this._dPatternConj[sTense][this._lTags[this._dVerb[sVerb][1]][this._dTenseIdx.get(sTense)]][sWho]); + }, + + hasConj: function (sVerb, sTense, sWho) { + // returns false if no conjugation (also if empty) else true + if (!this._dVerb.hasOwnProperty(sVerb)) { + return false; + } + if (this._dPatternConj[sTense][this._lTags[this._dVerb[sVerb][1]][this._dTenseIdx.get(sTense)]].hasOwnProperty(sWho) + && this._dPatternConj[sTense][this._lTags[this._dVerb[sVerb][1]][this._dTenseIdx.get(sTense)]][sWho]) { + return true; + } + return false; + }, + + getVtyp: function (sVerb) { + // returns raw informations about sVerb + if (!this._dVerb.hasOwnProperty(sVerb)) { + return null; + } + return this._lVtyp[this._dVerb[sVerb][0]]; + }, + + getSimil: function (sWord, sMorph, sFilter=null) { + if (!sMorph.includes(":V")) { + return new Set(); + } + let sInfi = sMorph.slice(1, sMorph.indexOf(" ")); + let tTags = this._getTags(sInfi); + let aSugg = new Set(); + if (sMorph.includes(":Q") || sMorph.includes(":Y")) { + // we suggest conjugated forms + if (sMorph.includes(":V1")) { + aSugg.add(sInfi); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":3s")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":2p")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Iq", ":1s")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Iq", ":3s")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Iq", ":3p")); + } else if (sMorph.includes(":V2")) { + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":1s")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":3s")); + } else if (sMorph.includes(":V3")) { + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":1s")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Ip", ":3s")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Is", ":1s")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":Is", ":3s")); + } else if (sMorph.includes(":V0a")) { + aSugg.add("eus"); + aSugg.add("eut"); + } else { + aSugg.add("étais"); + aSugg.add("était"); + } + aSugg.delete(""); + } else { + // we suggest past participles + aSugg.add(this._getConjWithTags(sInfi, tTags, ":PQ", ":Q1")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":PQ", ":Q2")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":PQ", ":Q3")); + aSugg.add(this._getConjWithTags(sInfi, tTags, ":PQ", ":Q4")); + aSugg.delete(""); + // if there is only one past participle (epi inv), unreliable. + if (aSugg.size === 1) { + aSugg.clear(); + } + if (sMorph.includes(":V1")) { + aSugg.add(sInfi); + } + } + return aSugg; + }, + + _getTags: function (sVerb) { + // returns tuple of tags (usable with functions _getConjWithTags and _hasConjWithTags) + if (!this._dVerb.hasOwnProperty(sVerb)) { + return null; + } + return this._lTags[this._dVerb[sVerb][1]]; + }, + + _getConjWithTags: function (sVerb, tTags, sTense, sWho) { + // returns conjugation (can be an empty string) + if (!this._dPatternConj[sTense][tTags[this._dTenseIdx.get(sTense)]].hasOwnProperty(sWho)) { + return ""; + } + return this._modifyStringWithSuffixCode(sVerb, this._dPatternConj[sTense][tTags[this._dTenseIdx.get(sTense)]][sWho]); + }, + + _hasConjWithTags: function (tTags, sTense, sWho) { + // returns false if no conjugation (also if empty) else true + if (this._dPatternConj[sTense][tTags[this._dTenseIdx.get(sTense)]].hasOwnProperty(sWho) + && this._dPatternConj[sTense][tTags[this._dTenseIdx.get(sTense)]][sWho]) { + return true; + } + return false; + }, + + _modifyStringWithSuffixCode: function (sWord, sSfx) { + // returns sWord modified by sSfx + if (sSfx === "") { + return ""; + } + if (sSfx === "0") { + return sWord; + } + try { + if (sSfx[0] !== '0') { + return sWord.slice(0, -(sSfx.charCodeAt(0)-48)) + sSfx.slice(1); // 48 is the ASCII code for "0" + } else { + return sWord + sSfx.slice(1); + } + } + catch (e) { + console.log(e); + return "## erreur, code : " + sSfx + " ##"; + } + } +}; class Verb { constructor (sVerb) { @@ -191,107 +187,107 @@ if (typeof sVerb !== "string" || sVerb === "") { throw new TypeError ("The value should be a non-empty string"); } this.sVerb = sVerb; this.sVerbAux = ""; - this._sRawInfo = getVtyp(this.sVerb); + this._sRawInfo = conj.getVtyp(this.sVerb); this.sInfo = this._readableInfo(this._sRawInfo); - this._tTags = _getTags(sVerb); - this._tTagsAux = _getTags(this.sVerbAux); + this._tTags = conj._getTags(sVerb); + this._tTagsAux = conj._getTags(this.sVerbAux); this.bProWithEn = (this._sRawInfo[5] === "e"); this.dConj = new Map ([ [":Y", new Map ([ ["label", "Infinitif"], [":Y", sVerb] ])], [":PQ", new Map ([ ["label", "Participes passés et présent"], - [":Q1", _getConjWithTags(sVerb, this._tTags, ":PQ", ":Q1")], - [":Q2", _getConjWithTags(sVerb, this._tTags, ":PQ", ":Q2")], - [":Q3", _getConjWithTags(sVerb, this._tTags, ":PQ", ":Q3")], - [":Q4", _getConjWithTags(sVerb, this._tTags, ":PQ", ":Q4")], - [":P", _getConjWithTags(sVerb, this._tTags, ":PQ", ":P")] + [":Q1", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":Q1")], + [":Q2", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":Q2")], + [":Q3", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":Q3")], + [":Q4", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":Q4")], + [":P", conj._getConjWithTags(sVerb, this._tTags, ":PQ", ":P")] ])], [":Ip", new Map ([ ["label", "Présent"], - [":1s", _getConjWithTags(sVerb, this._tTags, ":Ip", ":1s")], - [":1ś", _getConjWithTags(sVerb, this._tTags, ":Ip", ":1ś")], - [":2s", _getConjWithTags(sVerb, this._tTags, ":Ip", ":2s")], - [":3s", _getConjWithTags(sVerb, this._tTags, ":Ip", ":3s")], - [":1p", _getConjWithTags(sVerb, this._tTags, ":Ip", ":1p")], - [":2p", _getConjWithTags(sVerb, this._tTags, ":Ip", ":2p")], - [":3p", _getConjWithTags(sVerb, this._tTags, ":Ip", ":3p")] + [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":1s")], + [":1ś", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":1ś")], + [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":2s")], + [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":3s")], + [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":1p")], + [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":2p")], + [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Ip", ":3p")] ])], [":Iq", new Map ([ ["label", "Imparfait"], - [":1s", _getConjWithTags(sVerb, this._tTags, ":Iq", ":1s")], - [":2s", _getConjWithTags(sVerb, this._tTags, ":Iq", ":2s")], - [":3s", _getConjWithTags(sVerb, this._tTags, ":Iq", ":3s")], - [":1p", _getConjWithTags(sVerb, this._tTags, ":Iq", ":1p")], - [":2p", _getConjWithTags(sVerb, this._tTags, ":Iq", ":2p")], - [":3p", _getConjWithTags(sVerb, this._tTags, ":Iq", ":3p")] + [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":1s")], + [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":2s")], + [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":3s")], + [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":1p")], + [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":2p")], + [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Iq", ":3p")] ])], [":Is", new Map ([ ["label", "Passé simple"], - [":1s", _getConjWithTags(sVerb, this._tTags, ":Is", ":1s")], - [":2s", _getConjWithTags(sVerb, this._tTags, ":Is", ":2s")], - [":3s", _getConjWithTags(sVerb, this._tTags, ":Is", ":3s")], - [":1p", _getConjWithTags(sVerb, this._tTags, ":Is", ":1p")], - [":2p", _getConjWithTags(sVerb, this._tTags, ":Is", ":2p")], - [":3p", _getConjWithTags(sVerb, this._tTags, ":Is", ":3p")] + [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":1s")], + [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":2s")], + [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":3s")], + [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":1p")], + [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":2p")], + [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Is", ":3p")] ])], [":If", new Map ([ ["label", "Futur"], - [":1s", _getConjWithTags(sVerb, this._tTags, ":If", ":1s")], - [":2s", _getConjWithTags(sVerb, this._tTags, ":If", ":2s")], - [":3s", _getConjWithTags(sVerb, this._tTags, ":If", ":3s")], - [":1p", _getConjWithTags(sVerb, this._tTags, ":If", ":1p")], - [":2p", _getConjWithTags(sVerb, this._tTags, ":If", ":2p")], - [":3p", _getConjWithTags(sVerb, this._tTags, ":If", ":3p")] + [":1s", conj._getConjWithTags(sVerb, this._tTags, ":If", ":1s")], + [":2s", conj._getConjWithTags(sVerb, this._tTags, ":If", ":2s")], + [":3s", conj._getConjWithTags(sVerb, this._tTags, ":If", ":3s")], + [":1p", conj._getConjWithTags(sVerb, this._tTags, ":If", ":1p")], + [":2p", conj._getConjWithTags(sVerb, this._tTags, ":If", ":2p")], + [":3p", conj._getConjWithTags(sVerb, this._tTags, ":If", ":3p")] ])], [":Sp", new Map ([ ["label", "Présent subjonctif"], - [":1s", _getConjWithTags(sVerb, this._tTags, ":Sp", ":1s")], - [":1ś", _getConjWithTags(sVerb, this._tTags, ":Sp", ":1ś")], - [":2s", _getConjWithTags(sVerb, this._tTags, ":Sp", ":2s")], - [":3s", _getConjWithTags(sVerb, this._tTags, ":Sp", ":3s")], - [":1p", _getConjWithTags(sVerb, this._tTags, ":Sp", ":1p")], - [":2p", _getConjWithTags(sVerb, this._tTags, ":Sp", ":2p")], - [":3p", _getConjWithTags(sVerb, this._tTags, ":Sp", ":3p")] + [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":1s")], + [":1ś", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":1ś")], + [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":2s")], + [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":3s")], + [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":1p")], + [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":2p")], + [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Sp", ":3p")] ])], [":Sq", new Map ([ ["label", "Imparfait subjonctif"], - [":1s", _getConjWithTags(sVerb, this._tTags, ":Sq", ":1s")], - [":1ś", _getConjWithTags(sVerb, this._tTags, ":Sq", ":1ś")], - [":2s", _getConjWithTags(sVerb, this._tTags, ":Sq", ":2s")], - [":3s", _getConjWithTags(sVerb, this._tTags, ":Sq", ":3s")], - [":1p", _getConjWithTags(sVerb, this._tTags, ":Sq", ":1p")], - [":2p", _getConjWithTags(sVerb, this._tTags, ":Sq", ":2p")], - [":3p", _getConjWithTags(sVerb, this._tTags, ":Sq", ":3p")] + [":1s", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":1s")], + [":1ś", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":1ś")], + [":2s", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":2s")], + [":3s", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":3s")], + [":1p", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":1p")], + [":2p", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":2p")], + [":3p", conj._getConjWithTags(sVerb, this._tTags, ":Sq", ":3p")] ])], [":K", new Map ([ ["label", "Conditionnel"], - [":1s", _getConjWithTags(sVerb, this._tTags, ":K", ":1s")], - [":2s", _getConjWithTags(sVerb, this._tTags, ":K", ":2s")], - [":3s", _getConjWithTags(sVerb, this._tTags, ":K", ":3s")], - [":1p", _getConjWithTags(sVerb, this._tTags, ":K", ":1p")], - [":2p", _getConjWithTags(sVerb, this._tTags, ":K", ":2p")], - [":3p", _getConjWithTags(sVerb, this._tTags, ":K", ":3p")] + [":1s", conj._getConjWithTags(sVerb, this._tTags, ":K", ":1s")], + [":2s", conj._getConjWithTags(sVerb, this._tTags, ":K", ":2s")], + [":3s", conj._getConjWithTags(sVerb, this._tTags, ":K", ":3s")], + [":1p", conj._getConjWithTags(sVerb, this._tTags, ":K", ":1p")], + [":2p", conj._getConjWithTags(sVerb, this._tTags, ":K", ":2p")], + [":3p", conj._getConjWithTags(sVerb, this._tTags, ":K", ":3p")] ])], [":E", new Map ([ ["label", "Impératif"], - [":2s", _getConjWithTags(sVerb, this._tTags, ":E", ":2s")], - [":1p", _getConjWithTags(sVerb, this._tTags, ":E", ":1p")], - [":2p", _getConjWithTags(sVerb, this._tTags, ":E", ":2p")] + [":2s", conj._getConjWithTags(sVerb, this._tTags, ":E", ":2s")], + [":1p", conj._getConjWithTags(sVerb, this._tTags, ":E", ":1p")], + [":2p", conj._getConjWithTags(sVerb, this._tTags, ":E", ":2p")] ])] ]); - }; + } _readableInfo () { // returns readable infos this.sVerbAux = (this._sRawInfo.slice(7,8) == "e") ? "être" : "avoir"; - let sGroup = _dGroup.get(this._sRawInfo[0]); + let sGroup = conj._dGroup.get(this._sRawInfo[0]); let sInfo = ""; if (this._sRawInfo.slice(3,4) == "t") { sInfo = "transitif"; } else if (this._sRawInfo.slice(4,5) == "n") { sInfo = "transitif indirect"; @@ -310,11 +306,11 @@ } if (sInfo === "") { sInfo = "# erreur - code : " + this._sRawInfo; } return sGroup + " · " + sInfo; - }; + } infinitif (bPro, bNeg, bTpsCo, bInt, bFem) { let sInfi; if (bTpsCo) { sInfi = (bPro) ? "être" : this.sVerbAux; @@ -323,11 +319,11 @@ } if (bPro) { if (this.bProWithEn) { sInfi = "s’en " + sInfi; } else { - sInfi = (_zStartVoy.test(sInfi)) ? "s’" + sInfi : "se " + sInfi; + sInfi = (conj._zStartVoy.test(sInfi)) ? "s’" + sInfi : "se " + sInfi; } } if (bNeg) { sInfi = "ne pas " + sInfi; } @@ -336,30 +332,30 @@ } if (bInt) { sInfi += " … ?"; } return sInfi; - }; + } participePasse (sWho) { return this.dConj.get(":PQ").get(sWho); - }; + } participePresent (bPro, bNeg, bTpsCo, bInt, bFem) { if (!this.dConj.get(":PQ").get(":P")) { return ""; } let sPartPre; if (bTpsCo) { - sPartPre = (!bPro) ? _getConjWithTags(this.sVerbAux, this._tTagsAux, ":PQ", ":P") : getConj("être", ":PQ", ":P"); + sPartPre = (!bPro) ? conj._getConjWithTags(this.sVerbAux, this._tTagsAux, ":PQ", ":P") : conj.getConj("être", ":PQ", ":P"); } else { sPartPre = this.dConj.get(":PQ").get(":P"); } if (sPartPre === "") { return ""; } - let bEli = _zStartVoy.test(sPartPre); + let bEli = conj._zStartVoy.test(sPartPre); if (bPro) { if (this.bProWithEn) { sPartPre = "s’en " + sPartPre; } else { sPartPre = (bEli) ? "s’" + sPartPre : "se " + sPartPre; @@ -373,11 +369,11 @@ } if (bInt) { sPartPre += " … ?"; } return sPartPre; - }; + } conjugue (sTemps, sWho, bPro, bNeg, bTpsCo, bInt, bFem) { if (!this.dConj.get(sTemps).get(sWho)) { return ""; } @@ -384,30 +380,30 @@ let sConj; if (!bTpsCo && bInt && sWho == ":1s" && this.dConj.get(sTemps).gl_get(":1ś", false)) { sWho = ":1ś"; } if (bTpsCo) { - sConj = (!bPro) ? _getConjWithTags(this.sVerbAux, this._tTagsAux, sTemps, sWho) : getConj("être", sTemps, sWho); + sConj = (!bPro) ? conj._getConjWithTags(this.sVerbAux, this._tTagsAux, sTemps, sWho) : conj.getConj("être", sTemps, sWho); } else { sConj = this.dConj.get(sTemps).get(sWho); } if (sConj === "") { return ""; } - let bEli = _zStartVoy.test(sConj); + let bEli = conj._zStartVoy.test(sConj); if (bPro) { if (!this.bProWithEn) { - sConj = (bEli) ? _dProObjEl.get(sWho) + sConj : _dProObj.get(sWho) + sConj; + sConj = (bEli) ? conj._dProObjEl.get(sWho) + sConj : conj._dProObj.get(sWho) + sConj; } else { - sConj = _dProObjEl.get(sWho) + "en " + sConj; + sConj = conj._dProObjEl.get(sWho) + "en " + sConj; } } if (bNeg) { sConj = (bEli && !bPro) ? "n’" + sConj : "ne " + sConj; } if (bInt) { - if (sWho == ":3s" && !_zNeedTeuph.test(sConj)) { + if (sWho == ":3s" && !conj._zNeedTeuph.test(sConj)) { sConj += "-t"; } sConj += "-" + this._getPronom(sWho, bFem); } else { if (sWho == ":1s" && bEli && !bNeg && !bPro) { @@ -424,11 +420,11 @@ } if (bInt) { sConj += " … ?"; } return sConj; - }; + } _getPronom (sWho, bFem) { if (sWho == ":3s") { if (this._sRawInfo[5] == "r") { return "on"; @@ -436,45 +432,45 @@ return "elle"; } } else if (sWho == ":3p" && bFem) { return "elles"; } - return _dProSuj.get(sWho); - }; + return conj._dProSuj.get(sWho); + } imperatif (sWho, bPro, bNeg, bTpsCo, bFem) { if (!this.dConj.get(":E").get(sWho)) { return ""; } let sImpe; if (bTpsCo) { - sImpe = (!bPro) ? _getConjWithTags(this.sVerbAux, this._tTagsAux, ":E", sWho) : getConj("être", ":E", sWho); + sImpe = (!bPro) ? conj._getConjWithTags(this.sVerbAux, this._tTagsAux, ":E", sWho) : conj.getConj("être", ":E", sWho); } else { sImpe = this.dConj.get(":E").get(sWho); } if (sImpe === "") { return ""; } - let bEli = _zStartVoy.test(sImpe); + let bEli = conj._zStartVoy.test(sImpe); if (bNeg) { if (bPro) { if (!this.bProWithEn) { - sImpe = (bEli && sWho == ":2s") ? "ne t’" + sImpe + " pas" : _dImpeProNeg.get(sWho) + sImpe + " pas"; + sImpe = (bEli && sWho == ":2s") ? "ne t’" + sImpe + " pas" : conj._dImpeProNeg.get(sWho) + sImpe + " pas"; } else { - sImpe = _dImpeProNegEn.get(sWho) + sImpe + " pas"; + sImpe = conj._dImpeProNegEn.get(sWho) + sImpe + " pas"; } } else { sImpe = (bEli) ? "n’" + sImpe + " pas" : "ne " + sImpe + " pas"; } } else if (bPro) { - sImpe = (this.bProWithEn) ? sImpe + _dImpeProEn.get(sWho) : sImpe + _dImpePro.get(sWho); + sImpe = (this.bProWithEn) ? sImpe + conj._dImpeProEn.get(sWho) : sImpe + conj._dImpePro.get(sWho); } if (bTpsCo) { return sImpe + " " + this._seekPpas(bPro, bFem, sWho.endsWith("p") || this._sRawInfo[5] == "r"); } return sImpe; - }; + } _seekPpas (bPro, bFem, bPlur) { if (!bPro && this.sVerbAux == "avoir") { return this.dConj.get(":PQ").get(":Q1"); } @@ -486,19 +482,54 @@ } return (this.dConj.get(":PQ").get(":Q4")) ? this.dConj.get(":PQ").get(":Q4") : this.dConj.get(":PQ").get(":Q1"); } } + +// Initialization +if (!conj.bInit && typeof(browser) !== 'undefined') { + // WebExtension (but not in Worker) + conj.init(helpers.loadFile(browser.extension.getURL("grammalecte/fr/conj_data.json"))); +} else if (!conj.bInit && typeof(require) !== 'undefined') { + // Add-on SDK and Thunderbird + conj.init(helpers.loadFile("resource://grammalecte/fr/conj_data.json")); +} else if ( !conj.bInit && typeof(self) !== 'undefined' && typeof(self.port) !== 'undefined' && typeof(self.port.on) !== "undefined") { + // used within Firefox content script (conjugation panel). + // can’t load JSON from here, so we do it in ui.js and send it here. + self.port.on("provideConjData", function (sJSONData) { + conj.init(sJSONData); + }); +} else if (conj.bInit){ + console.log("Module conj déjà initialisé"); +} else { + console.log("Module conj non initialisé"); +} + if (typeof(exports) !== 'undefined') { - // Used for Grammalecte library. - // In content scripts, these variable are directly reachable + exports._lVtyp = conj._lVtyp; + exports._lTags = conj._lTags; + exports._dPatternConj = conj._dPatternConj; + exports._dVerb = conj._dVerb; + exports.init = conj.init; + exports._zStartVoy = conj._zStartVoy; + exports._zNeedTeuph = conj._zNeedTeuph; + exports._dProSuj = conj._dProSuj; + exports._dProObj = conj._dProObj; + exports._dProObjEl = conj._dProObjEl; + exports._dImpePro = conj._dImpePro; + exports._dImpeProNeg = conj._dImpeProNeg; + exports._dImpeProEn = conj._dImpeProEn; + exports._dImpeProNegEn = conj._dImpeProNegEn; + exports._dGroup = conj._dGroup; + exports._dTenseIdx = conj._dTenseIdx; + exports.isVerb = conj.isVerb; + exports.getConj = conj.getConj; + exports.hasConj = conj.hasConj; + exports.getVtyp = conj.getVtyp; + exports.getSimil = conj.getSimil; + exports._getTags = conj._getTags; + exports._getConjWithTags = conj._getConjWithTags; + exports._hasConjWithTags = conj._hasConjWithTags; + exports._modifyStringWithSuffixCode = conj._modifyStringWithSuffixCode; exports.Verb = Verb; - exports.isVerb = isVerb; - exports.getConj = getConj; - exports.hasConj = hasConj; - exports.getVtyp = getVtyp; - exports.getSimil = getSimil; - exports._getTags = _getTags; - exports._hasConjWithTags = _hasConjWithTags; - exports._getConjWithTags = _getConjWithTags; } Index: gc_lang/fr/modules-js/cregex.js ================================================================== --- gc_lang/fr/modules-js/cregex.js +++ gc_lang/fr/modules-js/cregex.js @@ -1,301 +1,349 @@ //// Grammalecte - Compiled regular expressions - - -///// Lemme -const zLemma = new RegExp("^>([a-zà-öø-ÿ0-9Ā-ʯ][a-zà-öø-ÿ0-9Ā-ʯ-]+)"); - -///// Masculin / féminin / singulier / pluriel -const zGender = new RegExp(":[mfe]"); -const zNumber = new RegExp(":[spi]"); - -///// Nom et adjectif -const zNA = new RegExp(":[NA]"); - -//// nombre -const zNAs = new RegExp(":[NA].*:s"); -const zNAp = new RegExp(":[NA].*:p"); -const zNAi = new RegExp(":[NA].*:i"); -const zNAsi = new RegExp(":[NA].*:[si]"); -const zNApi = new RegExp(":[NA].*:[pi]"); - -//// genre -const zNAm = new RegExp(":[NA].*:m"); -const zNAf = new RegExp(":[NA].*:f"); -const zNAe = new RegExp(":[NA].*:e"); -const zNAme = new RegExp(":[NA].*:[me]"); -const zNAfe = new RegExp(":[NA].*:[fe]"); - -//// nombre et genre -// singuilier -const zNAms = new RegExp(":[NA].*:m.*:s"); -const zNAfs = new RegExp(":[NA].*:f.*:s"); -const zNAes = new RegExp(":[NA].*:e.*:s"); -const zNAmes = new RegExp(":[NA].*:[me].*:s"); -const zNAfes = new RegExp(":[NA].*:[fe].*:s"); - -// singulier et invariable -const zNAmsi = new RegExp(":[NA].*:m.*:[si]"); -const zNAfsi = new RegExp(":[NA].*:f.*:[si]"); -const zNAesi = new RegExp(":[NA].*:e.*:[si]"); -const zNAmesi = new RegExp(":[NA].*:[me].*:[si]"); -const zNAfesi = new RegExp(":[NA].*:[fe].*:[si]"); - -// pluriel -const zNAmp = new RegExp(":[NA].*:m.*:p"); -const zNAfp = new RegExp(":[NA].*:f.*:p"); -const zNAep = new RegExp(":[NA].*:e.*:p"); -const zNAmep = new RegExp(":[NA].*:[me].*:p"); -const zNAfep = new RegExp(":[NA].*:[me].*:p"); - -// pluriel et invariable -const zNAmpi = new RegExp(":[NA].*:m.*:[pi]"); -const zNAfpi = new RegExp(":[NA].*:f.*:[pi]"); -const zNAepi = new RegExp(":[NA].*:e.*:[pi]"); -const zNAmepi = new RegExp(":[NA].*:[me].*:[pi]"); -const zNAfepi = new RegExp(":[NA].*:[fe].*:[pi]"); - -//// Divers -const zAD = new RegExp(":[AB]"); - -///// Verbe -const zVconj = new RegExp(":[123][sp]"); -const zVconj123 = new RegExp(":V[123].*:[123][sp]"); - -///// Nom | Adjectif | Verbe -const zNVconj = new RegExp(":(?:N|[123][sp])"); -const zNAVconj = new RegExp(":(?:N|A|[123][sp])"); - -///// Spécifique -const zNnotA = new RegExp(":N(?!:A)"); -const zPNnotA = new RegExp(":(?:N(?!:A)|Q)"); - -///// Noms propres -const zNP = new RegExp(":(?:M[12P]|T)"); -const zNPm = new RegExp(":(?:M[12P]|T):m"); -const zNPf = new RegExp(":(?:M[12P]|T):f"); -const zNPe = new RegExp(":(?:M[12P]|T):e"); - - -///// FONCTIONS - -function getLemmaOfMorph (sMorph) { - return zLemma.exec(sMorph)[1]; -} - -function checkAgreement (l1, l2) { - // check number agreement - if (!mbInv(l1) && !mbInv(l2)) { - if (mbSg(l1) && !mbSg(l2)) { - return false; - } - if (mbPl(l1) && !mbPl(l2)) { - return false; - } - } - // check gender agreement - if (mbEpi(l1) || mbEpi(l2)) { - return true; - } - if (mbMas(l1) && !mbMas(l2)) { - return false; - } - if (mbFem(l1) && !mbFem(l2)) { - return false; - } - return true; -} - -function checkConjVerb (lMorph, sReqConj) { - return lMorph.some(s => s.includes(sReqConj)); -} - -function getGender (lMorph) { - // returns gender of word (':m', ':f', ':e' or empty string). - let sGender = ""; - for (let sMorph of lMorph) { - let m = zGender.exec(sMorph); - if (m) { - if (!sGender) { - sGender = m[0]; - } else if (sGender != m[0]) { - return ":e"; - } - } - } - return sGender; -} - -function getNumber (lMorph) { - // returns number of word (':s', ':p', ':i' or empty string). - let sNumber = ""; - for (let sMorph of lMorph) { - let m = zNumber.exec(sWord); - if (m) { - if (!sNumber) { - sNumber = m[0]; - } else if (sNumber != m[0]) { - return ":i"; - } - } - } - return sNumber; -} - -// NOTE : isWhat (lMorph) returns true if lMorph contains nothing else than What -// mbWhat (lMorph) returns true if lMorph contains What at least once - -//// isXXX = it’s certain - -function isNom (lMorph) { - return lMorph.every(s => s.includes(":N")); -} - -function isNomNotAdj (lMorph) { - return lMorph.every(s => zNnotA.test(s)); -} - -function isAdj (lMorph) { - return lMorph.every(s => s.includes(":A")); -} - -function isNomAdj (lMorph) { - return lMorph.every(s => zNA.test(s)); -} - -function isNomVconj (lMorph) { - return lMorph.every(s => zNVconj.test(s)); -} - -function isInv (lMorph) { - return lMorph.every(s => s.includes(":i")); -} -function isSg (lMorph) { - return lMorph.every(s => s.includes(":s")); -} -function isPl (lMorph) { - return lMorph.every(s => s.includes(":p")); -} -function isEpi (lMorph) { - return lMorph.every(s => s.includes(":e")); -} -function isMas (lMorph) { - return lMorph.every(s => s.includes(":m")); -} -function isFem (lMorph) { - return lMorph.every(s => s.includes(":f")); -} - - -//// mbXXX = MAYBE XXX - -function mbNom (lMorph) { - return lMorph.some(s => s.includes(":N")); -} - -function mbAdj (lMorph) { - return lMorph.some(s => s.includes(":A")); -} - -function mbAdjNb (lMorph) { - return lMorph.some(s => zAD.test(s)); -} - -function mbNomAdj (lMorph) { - return lMorph.some(s => zNA.test(s)); -} - -function mbNomNotAdj (lMorph) { - let b = false; - for (let s of lMorph) { - if (s.includes(":A")) { - return false; - } - if (s.includes(":N")) { - b = true; - } - } - return b; -} - -function mbPpasNomNotAdj (lMorph) { - return lMorph.some(s => zPNnotA.test(s)); -} - -function mbVconj (lMorph) { - return lMorph.some(s => zVconj.test(s)); -} - -function mbVconj123 (lMorph) { - return lMorph.some(s => zVconj123.test(s)); -} - -function mbMG (lMorph) { - return lMorph.some(s => s.includes(":G")); -} - -function mbInv (lMorph) { - return lMorph.some(s => s.includes(":i")); -} -function mbSg (lMorph) { - return lMorph.some(s => s.includes(":s")); -} -function mbPl (lMorph) { - return lMorph.some(s => s.includes(":p")); -} -function mbEpi (lMorph) { - return lMorph.some(s => s.includes(":e")); -} -function mbMas (lMorph) { - return lMorph.some(s => s.includes(":m")); -} -function mbFem (lMorph) { - return lMorph.some(s => s.includes(":f")); -} - -function mbNpr (lMorph) { - return lMorph.some(s => zNP.test(s)); -} - -function mbNprMasNotFem (lMorph) { - if (lMorph.some(s => zNPf.test(s))) { - return false; - } - return lMorph.some(s => zNPm.test(s)); -} +/*jslint esversion: 6*/ + + +var cregex = { + ///// Lemme + _zLemma: new RegExp(">([a-zà-öø-ÿ0-9Ā-ʯ][a-zà-öø-ÿ0-9Ā-ʯ-]+)"), + + ///// Masculin / féminin / singulier / pluriel + _zGender: new RegExp(":[mfe]"), + _zNumber: new RegExp(":[spi]"), + + ///// Nom et adjectif + _zNA: new RegExp(":[NA]"), + + //// nombre + _zNAs: new RegExp(":[NA].*:s"), + _zNAp: new RegExp(":[NA].*:p"), + _zNAi: new RegExp(":[NA].*:i"), + _zNAsi: new RegExp(":[NA].*:[si]"), + _zNApi: new RegExp(":[NA].*:[pi]"), + + //// genre + _zNAm: new RegExp(":[NA].*:m"), + _zNAf: new RegExp(":[NA].*:f"), + _zNAe: new RegExp(":[NA].*:e"), + _zNAme: new RegExp(":[NA].*:[me]"), + _zNAfe: new RegExp(":[NA].*:[fe]"), + + //// nombre et genre + // singuilier + _zNAms: new RegExp(":[NA].*:m.*:s"), + _zNAfs: new RegExp(":[NA].*:f.*:s"), + _zNAes: new RegExp(":[NA].*:e.*:s"), + _zNAmes: new RegExp(":[NA].*:[me].*:s"), + _zNAfes: new RegExp(":[NA].*:[fe].*:s"), + + // singulier et invariable + _zNAmsi: new RegExp(":[NA].*:m.*:[si]"), + _zNAfsi: new RegExp(":[NA].*:f.*:[si]"), + _zNAesi: new RegExp(":[NA].*:e.*:[si]"), + _zNAmesi: new RegExp(":[NA].*:[me].*:[si]"), + _zNAfesi: new RegExp(":[NA].*:[fe].*:[si]"), + + // pluriel + _zNAmp: new RegExp(":[NA].*:m.*:p"), + _zNAfp: new RegExp(":[NA].*:f.*:p"), + _zNAep: new RegExp(":[NA].*:e.*:p"), + _zNAmep: new RegExp(":[NA].*:[me].*:p"), + _zNAfep: new RegExp(":[NA].*:[me].*:p"), + + // pluriel et invariable + _zNAmpi: new RegExp(":[NA].*:m.*:[pi]"), + _zNAfpi: new RegExp(":[NA].*:f.*:[pi]"), + _zNAepi: new RegExp(":[NA].*:e.*:[pi]"), + _zNAmepi: new RegExp(":[NA].*:[me].*:[pi]"), + _zNAfepi: new RegExp(":[NA].*:[fe].*:[pi]"), + + //// Divers + _zAD: new RegExp(":[AB]"), + + ///// Verbe + _zVconj: new RegExp(":[123][sp]"), + _zVconj123: new RegExp(":V[123].*:[123][sp]"), + + ///// Nom | Adjectif | Verbe + _zNVconj: new RegExp(":(?:N|[123][sp])"), + _zNAVconj: new RegExp(":(?:N|A|[123][sp])"), + + ///// Spécifique + _zNnotA: new RegExp(":N(?!:A)"), + _zPNnotA: new RegExp(":(?:N(?!:A)|Q)"), + + ///// Noms propres + _zNP: new RegExp(":(?:M[12P]|T)"), + _zNPm: new RegExp(":(?:M[12P]|T):m"), + _zNPf: new RegExp(":(?:M[12P]|T):f"), + _zNPe: new RegExp(":(?:M[12P]|T):e"), + + + ///// FONCTIONS + + getLemmaOfMorph: function (sMorph) { + return this._zLemma.exec(sMorph)[1]; + }, + + checkAgreement: function (l1, l2) { + // check number agreement + if (!this.mbInv(l1) && !this.mbInv(l2)) { + if (this.mbSg(l1) && !this.mbSg(l2)) { + return false; + } + if (this.mbPl(l1) && !this.mbPl(l2)) { + return false; + } + } + // check gender agreement + if (this.mbEpi(l1) || this.mbEpi(l2)) { + return true; + } + if (this.mbMas(l1) && !this.mbMas(l2)) { + return false; + } + if (this.mbFem(l1) && !this.mbFem(l2)) { + return false; + } + return true; + }, + + checkConjVerb: function (lMorph, sReqConj) { + return lMorph.some(s => s.includes(sReqConj)); + }, + + getGender: function (lMorph) { + // returns gender of word (':m', ':f', ':e' or empty string). + let sGender = ""; + for (let sMorph of lMorph) { + let m = this._zGender.exec(sMorph); + if (m) { + if (!sGender) { + sGender = m[0]; + } else if (sGender != m[0]) { + return ":e"; + } + } + } + return sGender; + }, + + getNumber: function (lMorph) { + // returns number of word (':s', ':p', ':i' or empty string). + let sNumber = ""; + for (let sMorph of lMorph) { + let m = this._zNumber.exec(sWord); + if (m) { + if (!sNumber) { + sNumber = m[0]; + } else if (sNumber != m[0]) { + return ":i"; + } + } + } + return sNumber; + }, + + // NOTE : isWhat (lMorph) returns true if lMorph contains nothing else than What + // mbWhat (lMorph) returns true if lMorph contains What at least once + + //// isXXX = it’s certain + + isNom: function (lMorph) { + return lMorph.every(s => s.includes(":N")); + }, + + isNomNotAdj: function (lMorph) { + return lMorph.every(s => this._zNnotA.test(s)); + }, + + isAdj: function (lMorph) { + return lMorph.every(s => s.includes(":A")); + }, + + isNomAdj: function (lMorph) { + return lMorph.every(s => this._zNA.test(s)); + }, + + isNomVconj: function (lMorph) { + return lMorph.every(s => this._zNVconj.test(s)); + }, + + isInv: function (lMorph) { + return lMorph.every(s => s.includes(":i")); + }, + isSg: function (lMorph) { + return lMorph.every(s => s.includes(":s")); + }, + isPl: function (lMorph) { + return lMorph.every(s => s.includes(":p")); + }, + isEpi: function (lMorph) { + return lMorph.every(s => s.includes(":e")); + }, + isMas: function (lMorph) { + return lMorph.every(s => s.includes(":m")); + }, + isFem: function (lMorph) { + return lMorph.every(s => s.includes(":f")); + }, + + + //// mbXXX = MAYBE XXX + + mbNom: function (lMorph) { + return lMorph.some(s => s.includes(":N")); + }, + + mbAdj: function (lMorph) { + return lMorph.some(s => s.includes(":A")); + }, + + mbAdjNb: function (lMorph) { + return lMorph.some(s => this._zAD.test(s)); + }, + + mbNomAdj: function (lMorph) { + return lMorph.some(s => this._zNA.test(s)); + }, + + mbNomNotAdj: function (lMorph) { + let b = false; + for (let s of lMorph) { + if (s.includes(":A")) { + return false; + } + if (s.includes(":N")) { + b = true; + } + } + return b; + }, + + mbPpasNomNotAdj: function (lMorph) { + return lMorph.some(s => this._zPNnotA.test(s)); + }, + + mbVconj: function (lMorph) { + return lMorph.some(s => this._zVconj.test(s)); + }, + + mbVconj123: function (lMorph) { + return lMorph.some(s => this._zVconj123.test(s)); + }, + + mbMG: function (lMorph) { + return lMorph.some(s => s.includes(":G")); + }, + + mbInv: function (lMorph) { + return lMorph.some(s => s.includes(":i")); + }, + mbSg: function (lMorph) { + return lMorph.some(s => s.includes(":s")); + }, + mbPl: function (lMorph) { + return lMorph.some(s => s.includes(":p")); + }, + mbEpi: function (lMorph) { + return lMorph.some(s => s.includes(":e")); + }, + mbMas: function (lMorph) { + return lMorph.some(s => s.includes(":m")); + }, + mbFem: function (lMorph) { + return lMorph.some(s => s.includes(":f")); + }, + + mbNpr: function (lMorph) { + return lMorph.some(s => this._zNP.test(s)); + }, + + mbNprMasNotFem: function (lMorph) { + if (lMorph.some(s => this._zNPf.test(s))) { + return false; + } + return lMorph.some(s => this._zNPm.test(s)); + } +}; if (typeof(exports) !== 'undefined') { - exports.getLemmaOfMorph = getLemmaOfMorph; - exports.checkAgreement = checkAgreement; - exports.checkConjVerb = checkConjVerb; - exports.getGender = getGender; - exports.getNumber = getNumber; - exports.isNom = isNom; - exports.isNomNotAdj = isNomNotAdj; - exports.isAdj = isAdj; - exports.isNomAdj = isNomAdj; - exports.isNomVconj = isNomVconj; - exports.isInv = isInv; - exports.isSg = isSg; - exports.isPl = isPl; - exports.isEpi = isEpi; - exports.isMas = isMas; - exports.isFem = isFem; - exports.mbNom = mbNom; - exports.mbAdj = mbAdj; - exports.mbAdjNb = mbAdjNb; - exports.mbNomAdj = mbNomAdj; - exports.mbNomNotAdj = mbNomNotAdj; - exports.mbPpasNomNotAdj = mbPpasNomNotAdj; - exports.mbVconj = mbVconj; - exports.mbVconj123 = mbVconj123; - exports.mbMG = mbMG; - exports.mbInv = mbInv; - exports.mbSg = mbSg; - exports.mbPl = mbPl; - exports.mbEpi = mbEpi; - exports.mbMas = mbMas; - exports.mbFem = mbFem; - exports.mbNpr = mbNpr; - exports.mbNprMasNotFem = mbNprMasNotFem; + exports._zLemma = cregex._zLemma; + exports._zGender = cregex._zGender; + exports._zNumber = cregex._zNumber; + exports._zNA = cregex._zNA; + exports._zNAs = cregex._zNAs; + exports._zNAp = cregex._zNAp; + exports._zNAi = cregex._zNAi; + exports._zNAsi = cregex._zNAsi; + exports._zNApi = cregex._zNApi; + exports._zNAm = cregex._zNAm; + exports._zNAf = cregex._zNAf; + exports._zNAe = cregex._zNAe; + exports._zNAme = cregex._zNAme; + exports._zNAfe = cregex._zNAfe; + exports._zNAms = cregex._zNAms; + exports._zNAfs = cregex._zNAfs; + exports._zNAes = cregex._zNAes; + exports._zNAmes = cregex._zNAmes; + exports._zNAfes = cregex._zNAfes; + exports._zNAmsi = cregex._zNAmsi; + exports._zNAfsi = cregex._zNAfsi; + exports._zNAesi = cregex._zNAesi; + exports._zNAmesi = cregex._zNAmesi; + exports._zNAfesi = cregex._zNAfesi; + exports._zNAmp = cregex._zNAmp; + exports._zNAfp = cregex._zNAfp; + exports._zNAep = cregex._zNAep; + exports._zNAmep = cregex._zNAmep; + exports._zNAfep = cregex._zNAfep; + exports._zNAmpi = cregex._zNAmpi; + exports._zNAfpi = cregex._zNAfpi; + exports._zNAepi = cregex._zNAepi; + exports._zNAmepi = cregex._zNAmepi; + exports._zNAfepi = cregex._zNAfepi; + exports._zAD = cregex._zAD; + exports._zVconj = cregex._zVconj; + exports._zVconj123 = cregex._zVconj123; + exports._zNVconj = cregex._zNVconj; + exports._zNAVconj = cregex._zNAVconj; + exports._zNnotA = cregex._zNnotA; + exports._zPNnotA = cregex._zPNnotA; + exports._zNP = cregex._zNP; + exports._zNPm = cregex._zNPm; + exports._zNPf = cregex._zNPf; + exports._zNPe = cregex._zNPe; + exports.getLemmaOfMorph = cregex.getLemmaOfMorph; + exports.checkAgreement = cregex.checkAgreement; + exports.checkConjVerb = cregex.checkConjVerb; + exports.getGender = cregex.getGender; + exports.getNumber = cregex.getNumber; + exports.isNom = cregex.isNom; + exports.isNomNotAdj = cregex.isNomNotAdj; + exports.isAdj = cregex.isAdj; + exports.isNomAdj = cregex.isNomAdj; + exports.isNomVconj = cregex.isNomVconj; + exports.isInv = cregex.isInv; + exports.isSg = cregex.isSg; + exports.isPl = cregex.isPl; + exports.isEpi = cregex.isEpi; + exports.isMas = cregex.isMas; + exports.isFem = cregex.isFem; + exports.mbNom = cregex.mbNom; + exports.mbAdj = cregex.mbAdj; + exports.mbAdjNb = cregex.mbAdjNb; + exports.mbNomAdj = cregex.mbNomAdj; + exports.mbNomNotAdj = cregex.mbNomNotAdj; + exports.mbPpasNomNotAdj = cregex.mbPpasNomNotAdj; + exports.mbVconj = cregex.mbVconj; + exports.mbVconj123 = cregex.mbVconj123; + exports.mbMG = cregex.mbMG; + exports.mbInv = cregex.mbInv; + exports.mbSg = cregex.mbSg; + exports.mbPl = cregex.mbPl; + exports.mbEpi = cregex.mbEpi; + exports.mbMas = cregex.mbMas; + exports.mbFem = cregex.mbFem; + exports.mbNpr = cregex.mbNpr; + exports.mbNprMasNotFem = cregex.mbNprMasNotFem; } Index: gc_lang/fr/modules-js/gce_analyseur.js ================================================================== --- gc_lang/fr/modules-js/gce_analyseur.js +++ gc_lang/fr/modules-js/gce_analyseur.js @@ -1,6 +1,7 @@ //// GRAMMAR CHECKING ENGINE PLUGIN: Parsing functions for French language +/*jslint esversion: 6*/ function rewriteSubject (s1, s2) { // s1 is supposed to be prn/patr/npr (M[12P]) if (s2 == "lui") { return "ils"; @@ -20,11 +21,11 @@ if (s2 == "eux") { return "ils"; } if (s2 == "elle" || s2 == "elles") { // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - if (cr.mbNprMasNotFem(_dAnalyses.gl_get(s1, ""))) { + if (cregex.mbNprMasNotFem(_dAnalyses.gl_get(s1, ""))) { return "ils"; } // si épicène, indéterminable, mais OSEF, le féminin l’emporte return "elles"; } @@ -32,22 +33,22 @@ } function apposition (sWord1, sWord2) { // returns true if nom + nom (no agreement required) // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - return cr.mbNomNotAdj(_dAnalyses.gl_get(sWord2, "")) && cr.mbPpasNomNotAdj(_dAnalyses.gl_get(sWord1, "")); + return cregex.mbNomNotAdj(_dAnalyses.gl_get(sWord2, "")) && cregex.mbPpasNomNotAdj(_dAnalyses.gl_get(sWord1, "")); } function isAmbiguousNAV (sWord) { // words which are nom|adj and verb are ambiguous (except être and avoir) if (!_dAnalyses.has(sWord) && !_storeMorphFromFSA(sWord)) { return false; } - if (!cr.mbNomAdj(_dAnalyses.gl_get(sWord, "")) || sWord == "est") { + if (!cregex.mbNomAdj(_dAnalyses.gl_get(sWord, "")) || sWord == "est") { return false; } - if (cr.mbVconj(_dAnalyses.gl_get(sWord, "")) && !cr.mbMG(_dAnalyses.gl_get(sWord, ""))) { + if (cregex.mbVconj(_dAnalyses.gl_get(sWord, "")) && !cregex.mbMG(_dAnalyses.gl_get(sWord, ""))) { return true; } return false; } @@ -56,64 +57,64 @@ // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before let a2 = _dAnalyses.gl_get(sWord2, null); if (!a2 || a2.length === 0) { return false; } - if (cr.checkConjVerb(a2, sReqMorphConj)) { + if (cregex.checkConjVerb(a2, sReqMorphConj)) { // verb word2 is ok return false; } let a1 = _dAnalyses.gl_get(sWord1, null); if (!a1 || a1.length === 0) { return false; } - if (cr.checkAgreement(a1, a2) && (cr.mbAdj(a2) || cr.mbAdj(a1))) { + if (cregex.checkAgreement(a1, a2) && (cregex.mbAdj(a2) || cregex.mbAdj(a1))) { return false; } return true; } function isVeryAmbiguousAndWrong (sWord1, sWord2, sReqMorphNA, sReqMorphConj, bLastHopeCond) { //// use it if sWord1 can be also a verb; word2 is assumed to be true via isAmbiguousNAV // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - let a2 = _dAnalyses.gl_get(sWord2, null) + let a2 = _dAnalyses.gl_get(sWord2, null); if (!a2 || a2.length === 0) { return false; } - if (cr.checkConjVerb(a2, sReqMorphConj)) { + if (cregex.checkConjVerb(a2, sReqMorphConj)) { // verb word2 is ok return false; } let a1 = _dAnalyses.gl_get(sWord1, null); if (!a1 || a1.length === 0) { return false; } - if (cr.checkAgreement(a1, a2) && (cr.mbAdj(a2) || cr.mbAdjNb(a1))) { + if (cregex.checkAgreement(a1, a2) && (cregex.mbAdj(a2) || cregex.mbAdjNb(a1))) { return false; } // now, we know there no agreement, and conjugation is also wrong - if (cr.isNomAdj(a1)) { + if (cregex.isNomAdj(a1)) { return true; } - //if cr.isNomAdjVerb(a1): # considered true + //if cregex.isNomAdjVerb(a1): # considered true if (bLastHopeCond) { return true; } return false; } function checkAgreement (sWord1, sWord2) { // We don’t check if word exists in _dAnalyses, for it is assumed it has been done before - let a2 = _dAnalyses.gl_get(sWord2, null) + let a2 = _dAnalyses.gl_get(sWord2, null); if (!a2 || a2.length === 0) { return true; } let a1 = _dAnalyses.gl_get(sWord1, null); if (!a1 || a1.length === 0) { return true; } - return cr.checkAgreement(a1, a2); + return cregex.checkAgreement(a1, a2); } function mbUnit (s) { if (/[µ\/⁰¹²³⁴⁵⁶⁷⁸⁹Ωℓ·]/.test(s)) { return true; Index: gc_lang/fr/modules-js/gce_date_verif.js ================================================================== --- gc_lang/fr/modules-js/gce_date_verif.js +++ gc_lang/fr/modules-js/gce_date_verif.js @@ -1,6 +1,7 @@ //// GRAMMAR CHECKING ENGINE PLUGIN +/*jslint esversion: 6*/ // Check date validity // WARNING: when creating a Date, month must be between 0 and 11 Index: gc_lang/fr/modules-js/gce_suggestions.js ================================================================== --- gc_lang/fr/modules-js/gce_suggestions.js +++ gc_lang/fr/modules-js/gce_suggestions.js @@ -1,10 +1,14 @@ //// GRAMMAR CHECKING ENGINE PLUGIN: Suggestion mechanisms +/*jslint esversion: 6*/ +/*global require*/ -const conj = require("resource://grammalecte/fr/conj.js"); -const mfsp = require("resource://grammalecte/fr/mfsp.js"); -const phonet = require("resource://grammalecte/fr/phonet.js"); +if (typeof(require) !== 'undefined') { + var conj = require("resource://grammalecte/fr/conj.js"); + var mfsp = require("resource://grammalecte/fr/mfsp.js"); + var phonet = require("resource://grammalecte/fr/phonet.js"); +} //// verbs function suggVerb (sFlex, sWho, funcSugg2=null) { @@ -15,12 +19,12 @@ if (tTags) { // we get the tense let aTense = new Set(); for (let sMorph of _dAnalyses.gl_get(sFlex, [])) { let m; - let zVerb = new RegExp (sStem+" .*?(:(?:Y|I[pqsf]|S[pq]|K))", "g"); - while (m = zVerb.exec(sMorph)) { + let zVerb = new RegExp (">"+sStem+" .*?(:(?:Y|I[pqsf]|S[pq]|K))", "g"); + while ((m = zVerb.exec(sMorph)) !== null) { // stem must be used in regex to prevent confusion between different verbs (e.g. sauras has 2 stems: savoir and saurer) if (m) { if (m[1] === ":Y") { aTense.add(":Ip"); aTense.add(":Iq"); @@ -139,12 +143,11 @@ } return ""; } function suggVerbInfi (sFlex) { - //return stem(sFlex).join("|"); - return [ for (sStem of stem(sFlex)) if (conj.isVerb(sStem)) sStem ].join("|"); + return stem(sFlex).filter(sStem => conj.isVerb(sStem)).join("|"); } const _dQuiEst = new Map ([ ["je", ":1s"], ["j’", ":1s"], ["j’en", ":1s"], ["j’y", ":1s"], @@ -195,11 +198,11 @@ // returns plural forms assuming sFlex is singular if (sWordToAgree) { if (!_dAnalyses.has(sWordToAgree) && !_storeMorphFromFSA(sWordToAgree)) { return ""; } - let sGender = cr.getGender(_dAnalyses.gl_get(sWordToAgree, [])); + let sGender = cregex.getGender(_dAnalyses.gl_get(sWordToAgree, [])); if (sGender == ":m") { return suggMasPlur(sFlex); } else if (sGender == ":f") { return suggFemPlur(sFlex); } @@ -261,18 +264,18 @@ if (!sMorph.includes(":V")) { // not a verb if (sMorph.includes(":m") || sMorph.includes(":e")) { aSugg.add(suggSing(sFlex)); } else { - let sStem = cr.getLemmaOfMorph(sMorph); + let sStem = cregex.getLemmaOfMorph(sMorph); if (mfsp.isFemForm(sStem)) { mfsp.getMasForm(sStem, false).forEach(function(x) { aSugg.add(x); }); } } } else { // a verb - let sVerb = cr.getLemmaOfMorph(sMorph); + let sVerb = cregex.getLemmaOfMorph(sMorph); if (conj.hasConj(sVerb, ":PQ", ":Q1") && conj.hasConj(sVerb, ":PQ", ":Q3")) { // We also check if the verb has a feminine form. // If not, we consider it’s better to not suggest the masculine one, as it can be considered invariable. aSugg.add(conj.getConj(sVerb, ":PQ", ":Q1")); } @@ -297,18 +300,18 @@ if (!sMorph.includes(":V")) { // not a verb if (sMorph.includes(":m") || sMorph.includes(":e")) { aSugg.add(suggPlur(sFlex)); } else { - let sStem = cr.getLemmaOfMorph(sMorph); + let sStem = cregex.getLemmaOfMorph(sMorph); if (mfsp.isFemForm(sStem)) { mfsp.getMasForm(sStem, true).forEach(function(x) { aSugg.add(x); }); } } } else { // a verb - let sVerb = cr.getLemmaOfMorph(sMorph); + let sVerb = cregex.getLemmaOfMorph(sMorph); if (conj.hasConj(sVerb, ":PQ", ":Q2")) { aSugg.add(conj.getConj(sVerb, ":PQ", ":Q2")); } else if (conj.hasConj(sVerb, ":PQ", ":Q1")) { let sSugg = conj.getConj(sVerb, ":PQ", ":Q1"); // it is necessary to filter these flexions, like “succédé” or “agi” that are not masculine plural @@ -338,18 +341,18 @@ if (!sMorph.includes(":V")) { // not a verb if (sMorph.includes(":f") || sMorph.includes(":e")) { aSugg.add(suggSing(sFlex)); } else { - let sStem = cr.getLemmaOfMorph(sMorph); + let sStem = cregex.getLemmaOfMorph(sMorph); if (mfsp.isFemForm(sStem)) { aSugg.add(sStem); } } } else { // a verb - let sVerb = cr.getLemmaOfMorph(sMorph); + let sVerb = cregex.getLemmaOfMorph(sMorph); if (conj.hasConj(sVerb, ":PQ", ":Q3")) { aSugg.add(conj.getConj(sVerb, ":PQ", ":Q3")); } } } @@ -372,18 +375,18 @@ if (!sMorph.includes(":V")) { // not a verb if (sMorph.includes(":f") || sMorph.includes(":e")) { aSugg.add(suggPlur(sFlex)); } else { - let sStem = cr.getLemmaOfMorph(sMorph); + let sStem = cregex.getLemmaOfMorph(sMorph); if (mfsp.isFemForm(sStem)) { aSugg.add(sStem+"s"); } } } else { // a verb - let sVerb = cr.getLemmaOfMorph(sMorph); + let sVerb = cregex.getLemmaOfMorph(sMorph); if (conj.hasConj(sVerb, ":PQ", ":Q4")) { aSugg.add(conj.getConj(sVerb, ":PQ", ":Q4")); } } } Index: gc_lang/fr/modules-js/lexicographe.js ================================================================== --- gc_lang/fr/modules-js/lexicographe.js +++ gc_lang/fr/modules-js/lexicographe.js @@ -1,17 +1,19 @@ // Grammalecte - Lexicographe // License: MPL 2 +/*jslint esversion: 6*/ +/*global require,exports*/ "use strict"; ${string} ${map} -const helpers = require("resource://grammalecte/helpers.js"); -const tkz = require("resource://grammalecte/tokenizer.js"); - +if (typeof(require) !== 'undefined') { + var helpers = require("resource://grammalecte/helpers.js"); +} const _dTAGS = new Map ([ [':G', "[mot grammatical]"], [':N', " nom,"], [':A', " adjectif,"], @@ -197,11 +199,11 @@ constructor (oDict) { this.oDict = oDict; 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"); - }; + } getInfoForToken (oToken) { // Token: .sType, .sValue, .nStart, .nEnd // return a list [type, token_string, values] let m = null; @@ -224,17 +226,23 @@ if (oToken.sValue.gl_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) ]; + let aElem = []; + for (let s of lMorph){ + if (s.includes(":")) aElem.push( 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) ]; + let aElem = []; + for (let s of lMorph){ + if (s.includes(":")) aElem.push( 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"] }; @@ -244,11 +252,11 @@ } catch (e) { helpers.logerror(e); } return null; - }; + } _formatTags (sTags) { let sRes = ""; sTags = sTags.replace(/V([0-3][ea]?)[itpqnmr_eaxz]+/, "V$1"); let m; @@ -265,11 +273,11 @@ sRes = "#Erreur. Étiquette inconnue : [" + sTags + "]"; helpers.echo(sRes); return sRes; } return sRes.gl_trimRight(","); - }; + } _formatSuffix (s) { if (s.startsWith("t-")) { return "“t” euphonique +" + _dAD.get(s.slice(2)); } @@ -279,12 +287,12 @@ if (s.endsWith("ous")) { s += '2'; } let nPos = s.indexOf("-"); return _dAD.get(s.slice(0, nPos)) + " +" + _dAD.get(s.slice(nPos+1)); - }; + } } if (typeof(exports) !== 'undefined') { exports.Lexicographe = Lexicographe; } Index: gc_lang/fr/modules-js/mfsp.js ================================================================== --- gc_lang/fr/modules-js/mfsp.js +++ gc_lang/fr/modules-js/mfsp.js @@ -1,87 +1,132 @@ // Grammalecte +/*jslint esversion: 6*/ +/*global console,require,exports,browser*/ "use strict"; -const helpers = require("resource://grammalecte/helpers.js"); -const echo = helpers.echo; - -const oData = JSON.parse(helpers.loadFile("resource://grammalecte/fr/mfsp_data.json")); - -// list of affix codes -const _lTagMiscPlur = oData.lTagMiscPlur; -const _lTagMasForm = oData.lTagMasForm; - -// dictionary of words with uncommon plurals (-x, -ux, english, latin and italian plurals) and tags to generate them -const _dMiscPlur = helpers.objectToMap(oData.dMiscPlur); - -// dictionary of feminine forms and tags to generate masculine forms (singular and plural) -const _dMasForm = helpers.objectToMap(oData.dMasForm); - - - -function isFemForm (sWord) { - // returns True if sWord exists in _dMasForm - return _dMasForm.has(sWord); -} - -function getMasForm (sWord, bPlur) { - // returns masculine form with feminine form - if (_dMasForm.has(sWord)) { - return [ for (sTag of _whatSuffixCodes(sWord, bPlur)) _modifyStringWithSuffixCode(sWord, sTag) ]; - } - return []; -} - -function hasMiscPlural (sWord) { - // returns True if sWord exists in dMiscPlur - return _dMiscPlur.has(sWord); -} - -function getMiscPlural (sWord) { - // returns plural form with singular form - if (_dMiscPlur.has(sWord)) { - return [ for (sTag of _lTagMiscPlur[_dMiscPlur.get(sWord)].split("|")) _modifyStringWithSuffixCode(sWord, sTag) ]; - } - return []; -} - -function _whatSuffixCodes (sWord, bPlur) { - // necessary only for dMasFW - let sSfx = _lTagMasForm[_dMasForm.get(sWord)]; - if (sSfx.includes("/")) { - if (bPlur) { - return sSfx.slice(sSfx.indexOf("/")+1).split("|"); - } - return sSfx.slice(0, sSfx.indexOf("/")).split("|"); - } - return sSfx.split("|"); -} - -function _modifyStringWithSuffixCode (sWord, sSfx) { - // returns sWord modified by sSfx - if (!sWord) { - return ""; - } - if (sSfx === "0") { - return sWord; - } - try { - if (sSfx[0] !== '0') { - return sWord.slice(0, -(sSfx.charCodeAt(0)-48)) + sSfx.slice(1); // 48 is the ASCII code for "0" - } else { - return sWord + sSfx.slice(1); - } - } - catch (e) { - console.log(e); - return "## erreur, code : " + sSfx + " ##"; - } + +if (typeof(require) !== 'undefined') { + var helpers = require("resource://grammalecte/helpers.js"); +} + + +var mfsp = { + // list of affix codes + _lTagMiscPlur: [], + _lTagMasForm: [], + // dictionary of words with uncommon plurals (-x, -ux, english, latin and italian plurals) and tags to generate them + _dMiscPlur: new Map(), + // dictionary of feminine forms and tags to generate masculine forms (singular and plural) + _dMasForm: new Map(), + + bInit: false, + init: function (sJSONData) { + try { + let _oData = JSON.parse(sJSONData); + this._lTagMiscPlur = _oData.lTagMiscPlur; + this._lTagMasForm = _oData.lTagMasForm; + this._dMiscPlur = helpers.objectToMap(_oData.dMiscPlur); + this._dMasForm = helpers.objectToMap(_oData.dMasForm); + this.bInit = true; + } + catch (e) { + console.error(e); + } + }, + + isFemForm: function (sWord) { + // returns True if sWord exists in this._dMasForm + return this._dMasForm.has(sWord); + }, + + getMasForm: function (sWord, bPlur) { + // returns masculine form with feminine form + if (this._dMasForm.has(sWord)) { + let aMasForm = []; + for (let sTag of this._whatSuffixCode(sWord, bPlur)){ + aMasForm.push( this._modifyStringWithSuffixCode(sWord, sTag) ); + } + return aMasForm; + } + return []; + }, + + hasMiscPlural: function (sWord) { + // returns True if sWord exists in dMiscPlur + return this._dMiscPlur.has(sWord); + }, + + getMiscPlural: function (sWord) { + // returns plural form with singular form + if (this._dMiscPlur.has(sWord)) { + let aMiscPlural = []; + for (let sTag of this._lTagMiscPlur[this._dMiscPlur.get(sWord)].split("|")){ + aMiscPlural.push( this._modifyStringWithSuffixCode(sWord, sTag) ); + } + return aMiscPlural; + } + return []; + }, + + _whatSuffixCode: function (sWord, bPlur) { + // necessary only for dMasFW + let sSfx = this._lTagMasForm[this._dMasForm.get(sWord)]; + if (sSfx.includes("/")) { + if (bPlur) { + return sSfx.slice(sSfx.indexOf("/")+1).split("|"); + } + return sSfx.slice(0, sSfx.indexOf("/")).split("|"); + } + return sSfx.split("|"); + }, + + _modifyStringWithSuffixCode: function (sWord, sSfx) { + // returns sWord modified by sSfx + if (!sWord) { + return ""; + } + if (sSfx === "0") { + return sWord; + } + try { + if (sSfx[0] !== '0') { + return sWord.slice(0, -(sSfx.charCodeAt(0)-48)) + sSfx.slice(1); // 48 is the ASCII code for "0" + } else { + return sWord + sSfx.slice(1); + } + } + catch (e) { + console.log(e); + return "## erreur, code : " + sSfx + " ##"; + } + } +}; + + +// Initialization +if (!mfsp.bInit && typeof(browser) !== 'undefined') { + // WebExtension + mfsp.init(helpers.loadFile(browser.extension.getURL("grammalecte/fr/mfsp_data.json"))); +} else if (!mfsp.bInit && typeof(require) !== 'undefined') { + // Add-on SDK and Thunderbird + mfsp.init(helpers.loadFile("resource://grammalecte/fr/mfsp_data.json")); +} else if (mfsp.bInit){ + console.log("Module mfsp déjà initialisé"); +} else { + console.log("Module mfsp non initialisé"); } if (typeof(exports) !== 'undefined') { - exports.isFemForm = isFemForm; - exports.getMasForm = getMasForm; - exports.hasMiscPlural = hasMiscPlural; - exports.getMiscPlural = getMiscPlural; + exports._lTagMiscPlur = mfsp._lTagMiscPlur; + exports._lTagMasForm = mfsp._lTagMasForm; + exports._dMiscPlur = mfsp._dMiscPlur; + exports._dMasForm = mfsp._dMasForm; + exports.init = mfsp.init; + exports.isFemForm = mfsp.isFemForm; + exports.getMasForm = mfsp.getMasForm; + exports.hasMiscPlural = mfsp.hasMiscPlural; + exports.getMiscPlural = mfsp.getMiscPlural; + exports._whatSuffixCode = mfsp._whatSuffixCode; + exports._modifyStringWithSuffixCode = mfsp._modifyStringWithSuffixCode; } Index: gc_lang/fr/modules-js/phonet.js ================================================================== --- gc_lang/fr/modules-js/phonet.js +++ gc_lang/fr/modules-js/phonet.js @@ -1,75 +1,108 @@ // Grammalecte - Suggestion phonétique - -const helpers = require("resource://grammalecte/helpers.js"); -const echo = helpers.echo; - -const oData = JSON.parse(helpers.loadFile("resource://grammalecte/fr/phonet_data.json")); - -const _dWord = helpers.objectToMap(oData.dWord); -const _lSet = oData.lSet; -const _dMorph = helpers.objectToMap(oData.dMorph); - - - -function hasSimil (sWord, sPattern=null) { - // return True if there is list of words phonetically similar to sWord - if (!sWord) { - return false; - } - if (_dWord.has(sWord)) { - if (sPattern) { - return getSimil(sWord).some(sSimil => _dMorph.gl_get(sSimil, []).some(sMorph => sMorph.search(sPattern) >= 0)); - } - return true; - } - if (sWord.slice(0,1).gl_isUpperCase()) { - sWord = sWord.toLowerCase(); - if (_dWord.has(sWord)) { - if (sPattern) { - return getSimil(sWord).some(sSimil => _dMorph.gl_get(sSimil, []).some(sMorph => sMorph.search(sPattern) >= 0)); - } - return true; - } - } - return false; -} - -function getSimil (sWord) { - // return list of words phonetically similar to sWord - if (!sWord) { - return []; - } - if (_dWord.has(sWord)) { - return _lSet[_dWord.get(sWord)]; - } - if (sWord.slice(0,1).gl_isUpperCase()) { - sWord = sWord.toLowerCase(); - if (_dWord.has(sWord)) { - return _lSet[_dWord.get(sWord)]; - } - } - return []; -} - -function selectSimil (sWord, sPattern) { - // return list of words phonetically similar to sWord and whom POS is matching sPattern - if (!sPattern) { - return new Set(getSimil(sWord)); - } - let aSelect = new Set(); - for (let sSimil of getSimil(sWord)) { - for (let sMorph of _dMorph.gl_get(sSimil, [])) { - if (sMorph.search(sPattern) >= 0) { - aSelect.add(sSimil); - } - } - } - return aSelect; +/*jslint esversion: 6*/ + +if (typeof(require) !== 'undefined') { + var helpers = require("resource://grammalecte/helpers.js"); +} + + +var phonet = { + _dWord: new Map(), + _lSet: [], + _dMorph: new Map(), + + bInit: false, + init: function (sJSONData) { + try { + let _oData = JSON.parse(sJSONData); + this._dWord = helpers.objectToMap(_oData.dWord); + this._lSet = _oData.lSet; + this._dMorph = helpers.objectToMap(_oData.dMorph); + this.bInit = true; + } + catch (e) { + console.error(e); + } + }, + + hasSimil: function (sWord, sPattern=null) { + // return True if there is list of words phonetically similar to sWord + if (!sWord) { + return false; + } + if (this._dWord.has(sWord)) { + if (sPattern) { + return this.getSimil(sWord).some(sSimil => this._dMorph.gl_get(sSimil, []).some(sMorph => sMorph.search(sPattern) >= 0)); + } + return true; + } + if (sWord.slice(0,1).gl_isUpperCase()) { + sWord = sWord.toLowerCase(); + if (this._dWord.has(sWord)) { + if (sPattern) { + return this.getSimil(sWord).some(sSimil => this._dMorph.gl_get(sSimil, []).some(sMorph => sMorph.search(sPattern) >= 0)); + } + return true; + } + } + return false; + }, + + getSimil: function (sWord) { + // return list of words phonetically similar to sWord + if (!sWord) { + return []; + } + if (this._dWord.has(sWord)) { + return this._lSet[this._dWord.get(sWord)]; + } + if (sWord.slice(0,1).gl_isUpperCase()) { + sWord = sWord.toLowerCase(); + if (this._dWord.has(sWord)) { + return this._lSet[this._dWord.get(sWord)]; + } + } + return []; + }, + + selectSimil: function (sWord, sPattern) { + // return list of words phonetically similar to sWord and whom POS is matching sPattern + if (!sPattern) { + return new Set(this.getSimil(sWord)); + } + let aSelect = new Set(); + for (let sSimil of this.getSimil(sWord)) { + for (let sMorph of this._dMorph.gl_get(sSimil, [])) { + if (sMorph.search(sPattern) >= 0) { + aSelect.add(sSimil); + } + } + } + return aSelect; + } +}; + + +// Initialization +if (!phonet.bInit && typeof(browser) !== 'undefined') { + // WebExtension + phonet.init(helpers.loadFile(browser.extension.getURL("grammalecte/fr/phonet_data.json"))); +} else if (!phonet.bInit && typeof(require) !== 'undefined') { + // Add-on SDK and Thunderbird + phonet.init(helpers.loadFile("resource://grammalecte/fr/phonet_data.json")); +} else if (phonet.bInit){ + console.log("Module phonet déjà initialisé"); +} else { + console.log("Module phonet non initialisé"); } if (typeof(exports) !== 'undefined') { - exports.hasSimil = hasSimil; - exports.getSimil = getSimil; - exports.selectSimil = selectSimil; + exports._dWord = phonet._dWord; + exports._lSet = phonet._lSet; + exports._dMorph = phonet._dMorph; + exports.init = phonet.init; + exports.hasSimil = phonet.hasSimil; + exports.getSimil = phonet.getSimil; + exports.selectSimil = phonet.selectSimil; } Index: gc_lang/fr/modules-js/textformatter.js ================================================================== --- gc_lang/fr/modules-js/textformatter.js +++ gc_lang/fr/modules-js/textformatter.js @@ -1,6 +1,8 @@ // Grammalecte - text formatter +/*jslint esversion: 6*/ +/*global exports*/ "use strict"; ${map} @@ -196,11 +198,11 @@ "ma_1letter_uppercase": [ [/[ ]([LDJNMTSCÇ]) (?=[aàeéêiîoôuyhAÀEÉÊIÎOÔUYH])/g, "$1’"], [/^([LDJNMTSCÇ]) (?=[aàeéêiîoôuyhAÀEÉÊIÎOÔUYH])/g, "$1’"] ] }; -const dDefaultOptions = new Map ([ +const dTFDefaultOptions = new Map ([ ["ts_units", true], ["start_of_paragraph", true], ["end_of_paragraph", true], ["between_words", true], ["before_punctuation", true], @@ -249,38 +251,38 @@ ["ma_word", true], ["ma_1letter_lowercase", false], ["ma_1letter_uppercase", false] ]); -const dOptions = dDefaultOptions.gl_shallowCopy(); +const dTFOptions = dTFDefaultOptions.gl_shallowCopy(); class TextFormatter { constructor () { this.sLang = "fr"; - }; + } formatText (sText, dOpt=null) { if (dOpt !== null) { - dOptions.gl_updateOnlyExistingKeys(dOpt); + dTFOptions.gl_updateOnlyExistingKeys(dOpt); } - for (let [sOptName, bVal] of dOptions) { + for (let [sOptName, bVal] of dTFOptions) { if (bVal && oReplTable.has(sOptName)) { for (let [zRgx, sRep] of oReplTable[sOptName]) { sText = sText.replace(zRgx, sRep); } } } return sText; - }; + } getDefaultOptions () { - return dDefaultOptions; + return dTFDefaultOptions; } } if (typeof(exports) !== 'undefined') { exports.TextFormatter = TextFormatter; exports.oReplTable = oReplTable; } Index: gc_lang/fr/modules/gce_suggestions.py ================================================================== --- gc_lang/fr/modules/gce_suggestions.py +++ gc_lang/fr/modules/gce_suggestions.py @@ -13,11 +13,11 @@ tTags = conj._getTags(sStem) if tTags: # we get the tense aTense = set() for sMorph in _dAnalyses.get(sFlex, []): # we don’t check if word exists in _dAnalyses, for it is assumed it has been done before - for m in re.finditer(sStem+" .*?(:(?:Y|I[pqsf]|S[pq]|K|P))", sMorph): + for m in re.finditer(">"+sStem+" .*?(:(?:Y|I[pqsf]|S[pq]|K|P))", sMorph): # stem must be used in regex to prevent confusion between different verbs (e.g. sauras has 2 stems: savoir and saurer) if m: if m.group(1) == ":Y": aTense.add(":Ip") aTense.add(":Iq") Index: gc_lang/fr/tb/content/overlay.js ================================================================== --- gc_lang/fr/tb/content/overlay.js +++ gc_lang/fr/tb/content/overlay.js @@ -910,10 +910,11 @@ }, false); window.addEventListener("load", function (xEvent) { oDictIgniter.init(); oGrammarChecker.loadGC(); + //oGrammarChecker.fullTests(); }, false); window.addEventListener("compose-window-init", function (xEvent) { oGrammarChecker.loadUI(); oGrammarChecker.closePanel(); ADDED gc_lang/fr/webext/background.js Index: gc_lang/fr/webext/background.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/background.js @@ -0,0 +1,220 @@ +// Background + +"use strict"; + +function showError (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); +} + +/* + Worker (separate thread to avoid freezing Firefox) +*/ +let xGCEWorker = new Worker("gce_worker.js"); + +xGCEWorker.onmessage = function (e) { + // https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent + try { + let {sActionDone, result, dInfo} = e.data; + switch (sActionDone) { + case "init": + console.log("INIT DONE"); + break; + case "parse": + case "parseAndSpellcheck": + case "getListOfTokens": + console.log("Action done: " + sActionDone); + if (typeof(dInfo.iReturnPort) === "number") { + let xPort = aConnx[dInfo.iReturnPort]; + xPort.postMessage(e.data); + } else { + console.log("[background] don’t know where to send results"); + console.log(e.data); + } + break; + case "textToTest": + console.log("TEXT TO TEXT RESULTS"); + browser.runtime.sendMessage({sCommand: "text_to_test_result", sResult: result}); + break; + case "fullTests": + console.log("FULL TESTS RESULTS"); + browser.runtime.sendMessage({sCommand: "fulltests_result", sResult: result}); + break; + case "getOptions": + case "getDefaultOptions": + case "setOptions": + case "setOption": + console.log("OPTIONS"); + break; + default: + console.log("Unknown command: " + sActionDone); + console.log(result); + } + } + catch (e) { + showError(e); + } +}; + + +xGCEWorker.postMessage({sCommand: "init", dParam: {sExtensionPath: browser.extension.getURL("."), sOptions: "", sContext: "Firefox"}, dInfo: {}}); + + +/* + Ports from content-scripts +*/ + +let aConnx = []; + + +/* + Messages from the extension (not the Worker) +*/ +function handleMessage (oRequest, xSender, sendResponse) { + //console.log(xSender); + console.log("[background] received:"); + console.log(oRequest); + switch (oRequest.sCommand) { + case "parse": + case "parseAndSpellcheck": + case "getListOfTokens": + case "textToTest": + case "getOptions": + case "getDefaultOptions": + case "setOptions": + case "setOption": + case "fullTests": + xGCEWorker.postMessage(oRequest); + break; + } + //sendResponse({response: "response from background script"}); +} + +browser.runtime.onMessage.addListener(handleMessage); + + +function handleConnexion (p) { + var xPort = p; + let iPortId = aConnx.length; // identifier for the port: each port can be found at aConnx[iPortId] + aConnx.push(xPort); + console.log("Port: " + p.name + ", id: " + iPortId); + xPort.onMessage.addListener(function (oRequest) { + console.log("[background] message via connexion:"); + console.log(oRequest); + switch (oRequest.sCommand) { + case "getCurrentTabId": + xPort.postMessage({sActionDone: "getCurrentTabId", result: "getCurrentTabId()", dInfo: null, bError: false}); + break; + case "parse": + case "parseAndSpellcheck": + case "getListOfTokens": + oRequest.dInfo.iReturnPort = iPortId; // we pass the id of the return port to receive answer + console.log(oRequest); + xGCEWorker.postMessage(oRequest); + break; + default: + console.log("[background] Unknown command: " + oRequest.sCommand); + } + }); + xPort.postMessage({sActionDone: "newId", result: iPortId}); +} + +browser.runtime.onConnect.addListener(handleConnexion); + + +/* + Context Menu +*/ +browser.contextMenus.create({ + id: "grammar_checking", + title: "Correction grammaticale", + contexts: ["selection", "editable", "page"] +}); + +browser.contextMenus.create({ + id: "lexicographer", + title: "Lexicographe", + contexts: ["selection", "editable", "page"] +}); + +browser.contextMenus.create({ + id: "conjugueur_panel", + title: "Conjugueur [fenêtre]", + contexts: ["all"] +}); +browser.contextMenus.create({ + id: "conjugueur_tab", + title: "Conjugueur [onglet]", + contexts: ["all"] +}); + +function onCreated(windowInfo) { + console.log(`Created window: ${windowInfo.id}`); +} + +function onError(error) { + console.log(`Error: ${error}`); +} + +let xConjWindow = null; +let xConjTab = null; + +browser.contextMenus.onClicked.addListener(function (xInfo, xTab) { + // xInfo = https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/contextMenus/OnClickData + // xTab = https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/Tab + console.log(xInfo); + console.log(xTab); + console.log("Item " + xInfo.menuItemId + " clicked in tab " + xTab.id); + console.log("editable: " + xInfo.editable + " · selected: " + xInfo.selectionText); + // confusing: no way to get the node where we click?! + switch (xInfo.menuItemId) { + case "grammar_checking": + break; + case "lexicographer": + if (xInfo.selectionText) { + xGCEWorker.postMessage(["getListOfTokens", {sText: xInfo.selectionText}]); + } + break; + case "conjugueur_panel": + xConjWindow = browser.windows.create({ + url: browser.extension.getURL("panel/conjugueur.html"), + type: "detached_panel", + width: 710, + height: 980 + }); + xConjWindow.then(onCreated, onError); + break; + case "conjugueur_tab": + xConjTab = browser.tabs.create({ + url: browser.extension.getURL("panel/conjugueur.html"), + pinned: true + }); + xConjTab.then(onCreated, onError); + break; + } +}); + + +async function getCurrentTabId () { + let xTab = await browser.tabs.getCurrent(); + return xTab.id; +} + +/* + TESTS ONLY +*/ +async function newwin () { + // test for popup window-like, which doesn’t close when losing the focus + console.log("Async on"); + const getActive = browser.tabs.query({ currentWindow: true, active: true, }); + const xWindowInfo = await browser.windows.getLastFocused(); + const width = 710, height = 980; // the maximum size for panels is somewhere around 700x800. Firefox needs some additional pixels: 14x42 for FF54 on Win 10 with dpi 1.25 + const left = Math.round(xWindowInfo.left + xWindowInfo.width - width - 25); + const top = Math.round(xWindowInfo.top + 74); // the actual frame height of the main window varies, but 74px should place the pop-up at the bottom if the button + const xWin = await browser.windows.create({ + type: 'panel', url: browser.extension.getURL("panel/conjugueur.html"), top: top, left: left, width: width, height: height, + }); + browser.windows.update(xWin.id, { top:top, left:left, }); // firefox currently ignores top and left in .create(), so move it here + console.log("Async done"); +} + +//newwin(); ADDED gc_lang/fr/webext/content_scripts/content_modifier.js Index: gc_lang/fr/webext/content_scripts/content_modifier.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/content_modifier.js @@ -0,0 +1,180 @@ +// Modify page + +/* + JS sucks (again and again and again and again…) + Not possible to load content from within the extension: + https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 + No SharedWorker, no images allowed for now… +*/ + +"use strict"; + +console.log("[Content script] Start"); + + +let nTadId = null; +let nWrapper = 0; + +let oConjPanel = null; +let oTFPanel = null; +let oLxgPanel = null; +let oGCPanel = null; + + +function showError (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); +} + +function wrapTextareas () { + let lNode = document.getElementsByTagName("textarea"); + for (let xNode of lNode) { + createWrapper(xNode); + } +} + +function createWrapper (xTextArea) { + try { + let xParentElement = xTextArea.parentElement; + let xWrapper = document.createElement("div"); + xWrapper.className = "grammalecte_wrapper"; + xWrapper.id = nWrapper + 1; + nWrapper += 1; + xParentElement.insertBefore(xWrapper, xTextArea); + xWrapper.appendChild(xTextArea); // move textarea in wrapper + let xToolbar = createWrapperToolbar(xTextArea); + xWrapper.appendChild(xToolbar); + } + catch (e) { + showError(e); + } +} + +function createWrapperToolbar (xTextArea) { + try { + let xToolbar = createNode("div", {className: "grammalecte_wrapper_toolbar"}); + let xConjButton = createNode("div", {className: "grammalecte_wrapper_button", textContent: "Conjuguer"}); + xConjButton.onclick = function() { createConjPanel(); }; + let xTFButton = createNode("div", {className: "grammalecte_wrapper_button", textContent: "Formater"}); + xTFButton.onclick = function() { createTFPanel(xTextArea); }; + let xLxgButton = createNode("div", {className: "grammalecte_wrapper_button", textContent: "Analyser"}); + xLxgButton.onclick = function() { + createLxgPanel(); + xPort.postMessage({sCommand: "getListOfTokens", dParam: {sText: xTextArea.value}, dInfo: {sTextAreaId: xTextArea.id}}); + }; + let xGCButton = createNode("div", {className: "grammalecte_wrapper_button", textContent: "Corriger"}); + xGCButton.onclick = function() { + createGCPanel(); + xPort.postMessage({sCommand: "parseAndSpellcheck", dParam: {sText: xTextArea.value, sCountry: "FR", bDebug: false, bContext: false}, dInfo: {sTextAreaId: xTextArea.id}}); + }; + // Create + //xToolbar.appendChild(createNode("img", {scr: browser.extension.getURL("img/logo-16.png")})); + // can’t work, due to content-script policy: https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 + xToolbar.appendChild(createLogo()); + xToolbar.appendChild(document.createTextNode("Grammalecte")); + xToolbar.appendChild(xConjButton); + xToolbar.appendChild(xTFButton); + xToolbar.appendChild(xLxgButton); + xToolbar.appendChild(xGCButton); + return xToolbar; + } + catch (e) { + showError(e); + } +} + +function createConjPanel () { + console.log("Conjugueur"); + if (oConjPanel !== null) { + oConjPanel.show(); + } else { + // create the panel + oConjPanel = new GrammalectePanel("grammalecte_conj_panel", "Conjugueur", 600, 600); + oConjPanel.insertIntoPage(); + } +} + +function createTFPanel (xTextArea) { + console.log("Formateur de texte"); + if (oTFPanel !== null) { + oTFPanel.show(); + } else { + // create the panel + oTFPanel = new GrammalectePanel("grammalecte_tf_panel", "Formateur de texte", 800, 600, false); + oTFPanel.logInnerHTML(); + oTFPanel.setContentNode(createTextFormatter(xTextArea)); + oTFPanel.insertIntoPage(); + } +} + +function createLxgPanel () { + console.log("Lexicographe"); + if (oLxgPanel !== null) { + oLxgPanelContent.clear(); + oLxgPanel.show(); + } else { + // create the panel + oLxgPanel = new GrammalectePanel("grammalecte_lxg_panel", "Lexicographe", 500, 700); + oLxgPanel.setContentNode(oLxgPanelContent.getNode()); + oLxgPanel.insertIntoPage(); + } +} + +function createGCPanel () { + console.log("Correction grammaticale"); + if (oGCPanel !== null) { + oGCPanelContent.clear(); + oGCPanel.show(); + } else { + // create the panel + oGCPanel = new GrammalectePanel("grammalecte_gc_panel", "Correcteur", 500, 700); + oGCPanel.setContentNode(oGCPanelContent.getNode()); + oGCPanel.insertIntoPage(); + } +} + + +/* + Simple message +*/ +function handleMessage (oMessage, xSender, sendResponse) { + console.log("[Content script] received:"); + console.log(oMessage); + //change(request.myparam); + //browser.runtime.onMessage.removeListener(handleMessage); + sendResponse({response: "response from content script"}); +} + +browser.runtime.onMessage.addListener(handleMessage); + + +/* + Connexion +*/ +let xPort = browser.runtime.connect({name: "content-script port"}); +xPort.onMessage.addListener(function (oMessage) { + console.log("[Content script] received…"); + let {sActionDone, result, dInfo, bError} = oMessage; + switch (sActionDone) { + case "getCurrentTabId": + console.log("[Content script] tab id: " + result); + nTadId = result; + break; + case "parseAndSpellcheck": + console.log(result); + oGCPanelContent.addParagraphResult(result); + break; + case "getListOfTokens": + console.log(result); + oLxgPanelContent.addListOfTokens(result); + break; + default: + console.log("[Content script] Unknown command: " + sActionDone); + } +}); +xPort.postMessage({sCommand: "getCurrentTabId", dParam: {}, dInfo: {}}); + +/*document.body.addEventListener("click", function () { + xPort.postMessage({greeting: "they clicked the page!"}); +});*/ + +wrapTextareas(); ADDED gc_lang/fr/webext/content_scripts/content_panels.css Index: gc_lang/fr/webext/content_scripts/content_panels.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/content_panels.css @@ -0,0 +1,105 @@ +/* + CSS + Content panels for Grammalecte +*/ + + +/* + Wrapper +*/ +.grammalecte_wrapper { + padding: 5px; + border-radius: 3px; + background-color: hsl(210, 50%, 50%); + font-family: "Trebuchet MS", "Liberation Sans", sans-serif; + color: hsl(210, 10%, 90%); +} + +.grammalecte_wrapper_button { + display: inline-block; + padding: 0 5px; + margin-left: 5px; + background-color: hsl(210, 50%, 60%); + border-radius: 2px; + color: hsl(210, 0%, 96%); + cursor: pointer; +} +.grammalecte_wrapper_button:hover { + background-color: hsl(210, 50%, 55%); + box-shadow: 0 0 1px 1px hsl(210, 50%, 20%); + color: hsl(210, 0%, 100%); +} +.grammalecte_wrapper_toolbar { + display: flex; + justify-content: flex-end; + margin-top: 5px; + padding: 5px 10px; +} + +/* + Panels +*/ +.grammalecte_panel { + padding: 0; + margin: 0; + position: fixed; + z-index: 100; + border: 2px solid hsl(210, 0%, 50%); + border-radius: 10px 10px 10px 10px; + background-color: hsl(210, 20%, 100%); + color: hsl(210, 10%, 4%); + font-family: "Trebuchet MS", "Liberation Sans", sans-serif; + box-shadow: 0 0 4px 2px hsl(210, 0%, 50%); +} + +.grammalecte_panel_bar { + position: sticky; + width: 100%; + background-color: hsl(210, 0%, 90%); + border-radius: 10px 10px 0 0; + border-bottom: 1px solid hsl(210, 10%, 80%); + font-size: 20px; +} +.grammalecte_panel_title { + padding: 10px 20px; +} +.grammalecte_panel_label { + display: inline-block; + padding: 0 10px; +} + +.grammalecte_panel_commands { + float: right; +} +.grammalecte_move_button { + display: inline-block; + padding: 2px 5px; + background-color: hsl(180, 80%, 50%); + font-size: 22px; + font-weight: bold; + color: hsl(210, 0%, 100%); + text-align: center; + cursor: pointer; +} +.grammalecte_move_button:hover { + background-color: hsl(180, 100%, 60%); +} +.grammalecte_close_button { + display: inline-block; + padding: 2px 10px; + border-radius: 0 8px 0 0; + background-color: hsl(0, 80%, 50%); + font-size: 22px; + font-weight: bold; + color: hsl(210, 0%, 100%); + text-align: center; + cursor: pointer; +} +.grammalecte_close_button:hover { + background-color: hsl(0, 100%, 60%); +} + +.grammalecte_panel_content { + height: calc(100% - 65px); /* panel height - title_bar */ + overflow: auto; +} ADDED gc_lang/fr/webext/content_scripts/gc_content.css Index: gc_lang/fr/webext/content_scripts/gc_content.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/gc_content.css @@ -0,0 +1,335 @@ +/* + Grammar checker +*/ +#grammalecte_gc_panel_content { + padding: 5px; +} + +.grammalecte_paragraph_block { + margin: 0 0 10px 0; +} + +.grammalecte_paragraph { + background-color: hsla(0, 0%, 90%, 1); + padding: 10px; + border-radius: 2px; + font-size: 14px; + font-family : "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace; + color: hsl(0, 0%, 0%); +} + +.grammalecte_paragraph a { + background-color: hsla(210, 50%, 50%, 1); + padding: 1px 5px; + border-radius: 2px; + color: hsla(210, 0%, 96%, 1); + cursor: pointer; + text-decoration: none; +} +.grammalecte_paragraph a:hover { + background-color: hsla(210, 60%, 40%, 1); + color: hsla(0, 0%, 100%, 1); + text-shadow: 0 0 3px hsl(210, 30%, 60%); +} + +.grammalecte_paragraph u { + text-decoration: none; +} + +.grammalecte_paragraph u.corrected, +.grammalecte_paragraph u.ignored { + background-color: hsla(120, 50%, 70%, 1); + color: hsla(0, 0%, 4%, 1); + border-radius: 2px; + text-decoration: none; +} +.grammalecte_paragraph u.ignored { + background-color: hsla(30, 20%, 80%, 1); +} + +.grammalecte_paragraph u.error { + position: relative; + cursor: pointer; + border-radius: 2px; + text-decoration: none; /* to remove for wavy underlines */ +} +.grammalecte_paragraph u.error:hover { + cursor: pointer; +} + + +/* + 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; +} +#sc_sugg_block a.sugg:hover { + background-color: hsla(30, 100%, 50%, 1); + color: hsla(0, 0%, 100%, 1); +} + +/* + Action buttons +*/ + +.actions { + margin: 0 0 5px 10px; +} + +.actions .button { + background-color: hsl(0, 0%, 50%); + text-align: center; + float: right; + margin-left: 2px; + padding: 1px 4px 3px 4px; + /*width: 18px; + height: 18px;*/ + cursor: pointer; + font-size: 14px; + color: hsl(0, 0%, 96%); + border-radius: 2px; +} +.actions .button:hover { + background-color: hsl(0, 0%, 40%); + color: hsl(0, 0%, 100%); +} + +.actions .green { + background-color: hsl(120, 30%, 50%); + color: hsl(0, 0%, 96%); +} +.actions .green:hover { + background-color: hsl(120, 50%, 40%); + color: hsl(0, 0%, 100%); +} +.actions .red { + background-color: hsl(0, 30%, 50%); + color: hsl(0, 0%, 96%); +} +.actions .red:hover { + background-color: hsl(0, 50%, 40%); + color: hsl(0, 0%, 100%); +} +.actions .orange { + background-color: hsl(30, 50%, 50%); + color: hsl(30, 0%, 96%); +} +.actions .orange:hover { + background-color: hsl(30, 70%, 40%); + color: hsl(30, 0%, 100%); +} +.actions .bold { + font-weight: bold; +} + + +/* + ERRORS +*/ + +.error { + /* default color */ + background-color: hsl(240, 10%, 50%); + color: hsl(240, 0%, 96%); +} +.error:hover { + background-color: hsl(240, 10%, 40%); + color: hsl(240, 0%, 100%); +} + +/* elems */ +.WORD { + background-color: hsl(0, 50%, 50%); + color: hsl(0, 0%, 96%); + /*text-decoration: underline wavy hsl(0, 50%, 50%);*/ +} +.WORD:hover { + background-color: hsl(0, 60%, 40%); + color: hsl(0, 0%, 100%); +} + +/* elems */ +.typo, .esp, .nbsp, .eif, .maj, .virg, .tu, .num, .unit, .nf, .liga, .mapos, .chim { + background-color: hsl(30, 70%, 50%); + color: hsl(30, 10%, 96%); + /*text-decoration: underline wavy hsl(30, 70%, 50%);*/ +} +.typo:hover, .esp:hover, .nbsp:hover, .eif:hover, .maj:hover, .virg:hover, .tu:hover, .num:hover, .unit:hover, .nf:hover, .liga:hover, .mapos:hover, .chim:hover { + background-color: hsl(30, 80%, 45%); + color: hsl(30, 10%, 96%); +} + +/* elems */ +.apos { + background-color: hsl(40, 90%, 50%); + color: hsl(40, 10%, 96%); + /*text-decoration: underline wavy hsl(40, 70%, 45%);*/ +} +.apos:hover { + background-color: hsl(40, 100%, 45%); + color: hsl(40, 10%, 96%); +} + +/* elems */ +.gn, .sgpl { + background-color: hsl(210, 50%, 50%); + color: hsl(210, 10%, 96%); + /*text-decoration: underline wavy hsl(210, 50%, 50%);*/ +} +.gn:hover, .sgpl:hover { + background-color: hsl(210, 60%, 40%); + color: hsl(210, 10%, 96%); +} + +/* elems */ +.conj, .infi, .imp, .inte, .ppas, .vmode { + background-color: hsl(300, 30%, 40%); + color: hsl(300, 10%, 96%); + /*text-decoration: underline wavy hsl(300, 40%, 40%);*/ +} +.conj:hover, .infi:hover, .imp:hover, .inte:hover, .ppas:hover, .vmode:hover { + background-color: hsl(300, 40%, 30%); + color: hsl(300, 10%, 96%); +} + +/* elems */ +.conf, .ocr { + background-color: hsl(270, 40%, 30%); + color: hsl(270, 10%, 96%); + /*text-decoration: underline wavy hsl(270, 40%, 30%);*/ +} +.conf:hover, .ocr:hover { + background-color: hsl(270, 50%, 20%); + color: hsl(270, 10%, 96%); +} + +/* elems */ +.bs, .pleo, .neg, .redon1, .redon2, .mc, .date, .notype { + background-color: hsl(180, 50%, 40%); + color: hsl(180, 10%, 96%); + /*text-decoration: underline wavy hsl(180, 50%, 40%);*/ +} +.bs:hover, .pleo:hover, .neg:hover, .redon1:hover, .redon2:hover, .mc:hover, .date:hover, .notype:hover { + background-color: hsl(180, 60%, 30%); + color: hsl(180, 10%, 96%); +} ADDED gc_lang/fr/webext/content_scripts/gc_content.js Index: gc_lang/fr/webext/content_scripts/gc_content.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/gc_content.js @@ -0,0 +1,314 @@ +// JavaScript + +"use strict"; + +const oGCPanelContent = { + + _xContentNode: createNode("div", {id: "grammalecte_gc_panel_content"}), + + aIgnoredErrors: new Map(), + + getNode: function () { + return this._xContentNode; + }, + + clear: function () { + while (this._xContentNode.firstChild) { + this._xContentNode.removeChild(this._xContentNode.firstChild); + } + }, + + addParagraphResult: function (oResult) { + try { + if (oResult) { + let xNodeDiv = createNode("div", {className: "grammalecte_paragraph_block"}); + // actions + let xActionsBar = createNode("div", {className: "grammalecte_paragraph_actions"}); + let xCloseButton = createNode("div", {id: "end" + oResult.sParaNum, className: "grammalecte_paragraph_close_button", textContent: "×"}); + let xAnalyseButton = createNode("div", {id: "check" + oResult.sParaNum, className: "grammalecte_paragraph_analyse_button", textContent: "Réanalyser"}); + xActionsBar.appendChild(xAnalyseButton); + xActionsBar.appendChild(xCloseButton); + // paragraph + let xParagraph = createNode("p", {id: "paragr"+oResult.sParaNum, lang: "fr", spellcheck: "false", contenteditable: "true"}); + xParagraph.className = (oResult.aGrammErr.length || oResult.aSpellErr.length) ? "grammalecte_paragraph softred" : "grammalecte_paragraph"; + this._tagParagraph(xParagraph, oResult.sParagraph, oResult.sParaNum, oResult.aGrammErr, oResult.aSpellErr); + // creation + xNodeDiv.appendChild(xActionsBar); + xNodeDiv.appendChild(xParagraph); + this._xContentNode.appendChild(xNodeDiv); + } + } + catch (e) { + showError(e); + } + }, + + refreshParagraph: function (oResult) { + try { + let xParagraph = document.getElementById("paragr"+sIdParagr); + xParagraph.className = (oResult.aGrammErr.length || oResult.aSpellErr.length) ? "grammalecte_paragraph softred" : "grammalecte_paragraph"; + xParagraph.textContent = ""; + this._tagParagraph(xParagraph, oResult.sParagraph, oResult.sParaNum, oResult.aGrammErr, oResult.aSpellErr); + } + catch (e) { + showError(e); + } + }, + + _tagParagraph: function (xParagraph, sParagraph, sParaNum, 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'] = sParaNum + "_" + nErr.toString(); // error identifier + oErr['sIgnoredKey'] = sParaNum + ":" + nStart.toString() + ":" + nEnd.toString() + ":" + sParagraph.slice(nStart, nEnd); + if (nEndLastErr < nStart) { + xParagraph.appendChild(document.createTextNode(sParagraph.slice(nEndLastErr, nStart))); + } + xParagraph.appendChild(this._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); + } + }, + + _createError: function (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"; + 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 = (this.aIgnoredErrors.has(xNodeErr.dataset.ignored_key)) ? "ignored" : "error " + oErr['sType']; + return xNodeErr; + }, + + applySuggestion: function (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)); + this.hideAllTooltips(); + stopWaitIcon("paragr"+sIdParagr); + } + catch (e) { + showError(e); + } + }, + + ignoreError: function (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); + this.aIgnoredErrors.add(xNodeErr.dataset.ignored_key); + xNodeErr.className = "ignored"; + xNodeErr.removeAttribute("style"); + this.hideAllTooltips(); + } + catch (e) { + showError(e); + } + }, + + showTooltip: function (sNodeErrorId) { // err + try { + this.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(this._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); + } + }, + + _createSuggestion: function (sErrId, iSugg, sSugg) { + let xNodeSugg = document.createElement("a"); + xNodeSugg.id = "sugg" + sErrId + "-" + iSugg.toString(); + xNodeSugg.className = "sugg"; + xNodeSugg.dataset.error_id = sErrId; + xNodeSugg.textContent = sSugg; + return xNodeSugg; + }, + + setSpellSuggestionsFor: function (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(this._createSuggestion(sErrId, iSugg, sSugg)); + xSuggBlock.appendChild(document.createTextNode(" ")); + iSugg += 1; + } + } + } + catch (e) { + showError(e); + } + }, + + hideAllTooltips: function () { + 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"; + }, + + addSummary: function () { + // todo + }, + + addMessage: function (sMessage) { + let xNode = createNode("div", {className: "grammalecte_gc_panel_message", textContent: sMessage}); + this._xContentNode.appendChild(xNode); + } +} + + + + + + + + + + + +function sendBackAndCheck (sCheckButtonId) { // check + startWaitIcon(); + let sIdParagr = sCheckButtonId.slice(5); + self.port.emit("modifyAndCheck", sIdParagr, getPurgedTextOfParagraph("paragr"+sIdParagr)); + stopWaitIcon(); +} + + + + + +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 { + 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; +} + +function stopWaitIcon (sIdParagr=null) { + if (sIdParagr) { + document.getElementById(sIdParagr).disabled = false; + document.getElementById(sIdParagr).style.opacity = 1; + } + document.getElementById("waiticon").hidden = true; +} ADDED gc_lang/fr/webext/content_scripts/lxg_content.css Index: gc_lang/fr/webext/content_scripts/lxg_content.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/lxg_content.css @@ -0,0 +1,83 @@ +/* + Lexicographer +*/ +#grammalecte_lxg_panel_content { + padding: 5px; + font-size: 13px; +} + +.grammalecte_lxg_list_of_tokens { + margin: 5px 0 10px 0; + padding: 10px; + background-color: hsla(0, 0%, 96%, 1); + border-radius: 2px; +} +.grammalecte_lxg_list_of_tokens .num { + float: right; + margin: -12px 0 5px 10px; + padding: 5px 10px; + font-weight: bold; + border-radius: 0 0 4px 4px; + background-color: hsl(0, 50%, 50%); + color: hsl(0, 10%, 96%); +} +.grammalecte_token { + padding: 4px 0; +} +.grammalecte_token .separator { + margin: 20px 0; + padding: 5px 5px; + background-color: hsla(0, 0%, 75%, 1); + color: hsla(0, 0%, 96%, 1); + border-radius: 5px; + text-align: center; + font-size: 20px; +} +.grammalecte_token ul { + margin: 0 0 5px 5px; +} +.grammalecte_token b { + background-color: hsla(150, 10%, 50%, 1); + color: hsla(0, 0%, 96%, 1); + padding: 2px 5px; + border-radius: 2px; + text-decoration: none; +} +.grammalecte_token b.WORD { + background-color: hsla(150, 50%, 50%, 1); +} +.grammalecte_token b.ELPFX { + background-color: hsla(150, 30%, 50%, 1); +} +.grammalecte_token b.UNKNOWN { + background-color: hsla(0, 50%, 50%, 1); +} +.grammalecte_token b.NUM { + background-color: hsla(180, 50%, 50%, 1); +} +.grammalecte_token b.COMPLEX { + background-color: hsla(60, 50%, 50%, 1); +} +.grammalecte_token b.SEPARATOR { + background-color: hsla(210, 50%, 50%, 1); +} +.grammalecte_token b.LINK { + background-color: hsla(270, 50%, 50%, 1); +} +.grammalecte_token s { + color: hsla(0, 0%, 60%, 1); + text-decoration: none; +} +.grammalecte_token .textline { + text-decoration: bold; +} + +.grammalecte_token p.message { + margin-top: 20px; + padding: 10px 10px; + background-color: hsla(240, 10%, 50%, 1); + font-size: 18px; + color: hsla(240, 0%, 96%, 1); + border-radius: 3px; + text-align: center; +} ADDED gc_lang/fr/webext/content_scripts/lxg_content.js Index: gc_lang/fr/webext/content_scripts/lxg_content.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/lxg_content.js @@ -0,0 +1,70 @@ +// JavaScript + +"use strict"; + +const oLxgPanelContent = { + + _xContentNode: createNode("div", {id: "grammalecte_lxg_panel_content"}), + + _nCount: 0, + + getNode: function () { + return this._xContentNode; + }, + + clear: function () { + this._nCount = 0; + while (this._xContentNode.firstChild) { + this._xContentNode.removeChild(this._xContentNode.firstChild); + } + }, + + addSeparator: function (sText) { + if (this._xContentNode.textContent !== "") { + this._xContentNode.appendChild(createNode("div", {className: "grammalecte_lxg_separator", textContent: sText})); + } + }, + + addMessage: function (sClass, sText) { + this._xContentNode.appendChild(createNode("div", {className: sClass, textContent: sText})); + }, + + addListOfTokens: function (lTokens) { + try { + if (lTokens) { + this._nCount += 1; + let xNodeDiv = createNode("div", {className: "grammalecte_lxg_list_of_tokens"}); + xNodeDiv.appendChild(createNode("div", {className: "num", textContent: this._nCount})); + for (let oToken of lTokens) { + xNodeDiv.appendChild(this._createTokenNode(oToken)); + } + this._xContentNode.appendChild(xNodeDiv); + } + } + catch (e) { + showError(e); + } + }, + + _createTokenNode: function (oToken) { + let xTokenNode = createNode("div", {className: "grammalecte_token " + oToken.sType}); + xTokenNode.appendChild(createNode("b", {className: oToken.sType, textContent: oToken.sValue})); + xTokenNode.appendChild(createNode("s", {textContent: " : "})); + if (oToken.aLabel.length === 1) { + xTokenNode.appendChild(document.createTextNode(oToken.aLabel[0])); + } else { + let xTokenList = document.createElement("ul"); + for (let sLabel of oToken.aLabel) { + xTokenList.appendChild(createNode("li", {textContent: sLabel})); + } + xTokenNode.appendChild(xTokenList); + } + return xTokenNode; + }, + + setHidden: function (sClass, bHidden) { + for (let xNode of document.getElementsByClassName(sClass)) { + xNode.hidden = bHidden; + } + } +} DELETED gc_lang/fr/webext/content_scripts/modify_page.js Index: gc_lang/fr/webext/content_scripts/modify_page.js ================================================================== --- gc_lang/fr/webext/content_scripts/modify_page.js +++ /dev/null @@ -1,28 +0,0 @@ -import { echo } from "../mymodule"; - -echo("CONTENT SCRIPRT!!!"); - -function handleMessage2 (oRequest, xSender, sendResponse) { - console.log(`[Content script] received: ${oRequest.content}`); - change(request.myparam); - //browser.runtime.onMessage.removeListener(handleMessage); - sendResponse({response: "response from content script"}); -} - -function removeEverything () { - while (document.body.firstChild) { - document.body.firstChild.remove(); - } -} - -function change (param) { - document.getElementById("title").setAttribute("background-color", "#809060"); - console.log("param: " + param); - document.getElementById("title").setAttribute("background-color", "#FF0000"); -} - - -/* - Assign do_something() as a listener for messages from the extension. -*/ -browser.runtime.onMessage.addListener(handleMessage2); ADDED gc_lang/fr/webext/content_scripts/panel_creator.js Index: gc_lang/fr/webext/content_scripts/panel_creator.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/panel_creator.js @@ -0,0 +1,152 @@ +// JavaScript +// Panel creator + +"use strict"; + +console.log("[Content script] Panel creator"); + + +class GrammalectePanel { + + constructor (sId, sTitle, nWidth, nHeight, bMovable=true) { + this.sId = sId; + this.sContentId = sId+"_content"; + this.nWidth = nWidth; + this.nHeight = nHeight; + this.bMovable = bMovable; + this.xContentNode = createNode("div", {className: "grammalecte_panel_content"}); + this.xPanelNode = this._createPanel(sTitle); + this.center(); + } + + _createPanel (sTitle) { + try { + let xPanel = createNode("div", {id: this.sId, className: "grammalecte_panel"}); + let xBar = createNode("div", {className: "grammalecte_panel_bar"}); + xBar.appendChild(this._createButtons()); + let xTitle = createNode("div", {className: "grammalecte_panel_title"}); + xTitle.appendChild(createLogo()); + xTitle.appendChild(createNode("div", {className: "grammalecte_panel_label", textContent: "Grammalecte · " + sTitle})); + xBar.appendChild(xTitle); + xPanel.appendChild(xBar); + //xPanel.appendChild(createNode("div", {className: "grammalecte_empty_space_under_title_bar"})); + xPanel.appendChild(this.xContentNode); + return xPanel; + } + catch (e) { + showError(e); + } + } + + _createButtons () { + let xButtonLine = createNode("div", {className: "grammalecte_panel_commands"}); + if (this.bMovable) { + xButtonLine.appendChild(this._createMoveButton("stickToTop", "¯", "Coller en haut")); + xButtonLine.appendChild(this._createMoveButton("stickToLeft", "«", "Coller à gauche")); + xButtonLine.appendChild(this._createMoveButton("center", "•", "Centrer")); + xButtonLine.appendChild(this._createMoveButton("stickToRight", "»", "Coller à droite")); + xButtonLine.appendChild(this._createMoveButton("stickToBottom", "_", "Coller en bas")); + } + xButtonLine.appendChild(this._createCloseButton()); + return xButtonLine; + } + + _createMoveButton (sAction, sLabel, sTitle) { + let xButton = createNode("div", {className: "grammalecte_move_button", textContent: sLabel, title: sTitle}); + xButton.onclick = function () { this[sAction](); }.bind(this); + return xButton; + } + + _createCloseButton () { + let xButton = createNode("div", {className: "grammalecte_close_button", textContent: "×", title: "Fermer la fenêtre"}); + xButton.onclick = function () { this.hide(); }.bind(this); // better than writing “let that = this;” before the function? + return xButton; + } + + setContentNode (xNode) { + this.xContentNode.appendChild(xNode); + } + + insertIntoPage () { + document.body.appendChild(this.xPanelNode); + } + + show () { + this.xPanelNode.style.display = "block"; + } + + hide () { + this.xPanelNode.style.display = "none"; + } + + center () { + let nHeight = window.innerHeight-100; + this.xPanelNode.style = `top: 50%; left: 50%; width: ${this.nWidth}px; height: ${nHeight}px; margin-top: -${nHeight/2}px; margin-left: -${this.nWidth/2}px;`; + } + + stickToLeft () { + let nHeight = window.innerHeight-100; + this.xPanelNode.style = `top: 50%; left: -2px; width: ${this.nWidth}px; height: ${nHeight}px; margin-top: -${nHeight/2}px;`; + } + + stickToRight () { + let nHeight = window.innerHeight-100; + this.xPanelNode.style = `top: 50%; right: -2px; width: ${this.nWidth}px; height: ${nHeight}px; margin-top: -${nHeight/2}px;`; + } + + stickToTop () { + let nWidth = Math.floor(window.innerWidth/2); + this.xPanelNode.style = `top: -2px; left: 50%; width: ${nWidth}px; height: ${Math.floor(window.innerHeight*0.45)}px; margin-left: -${nWidth/2}px;`; + } + + stickToBottom () { + let nWidth = Math.floor(window.innerWidth/2); + this.xPanelNode.style = `bottom: -2px; left: 50%; width: ${nWidth}px; height: ${Math.floor(window.innerHeight*0.45)}px; margin-left: -${nWidth/2}px;`; + } + + reduce () { + // todo + } + + logInnerHTML () { + // for debugging + console.log(this.xPanelNode.innerHTML); + } +} + + +/* + Common functions +*/ +function createNode (sType, oAttr, oDataset=null) { + try { + let xNode = document.createElement(sType); + Object.assign(xNode, oAttr); + if (oDataset) { + Object.assign(xNode.dataset, oDataset); + } + return xNode; + } + catch (e) { + showError(e); + } +} + +function createLogo () { + let xImg = document.createElement("img"); + xImg.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAC8UlEQVQ4jX3TbUgTcRwH8P89ddu5u9tt082aZmpFEU4tFz0QGTUwCi0heniR9MSUIKRaD0RvIlKigsooo+iNFa0XJYuwIjEK19OcDtPElsG0ktyp591t7u7+vUh7MPX3+vf5/n8/+P0BmKJIPUUVlh2rdVVeesWlzEybqg+bFOsoylnqPmNavGFfknV2Omu2Lvja3vxAURKJib3opHizu8riLK6gjRyuKgmoSoMRFENRUqfXTzvBGK62LC2uoFkOl4RhjQ8+qWt7dPNE3sbdp+2LXbsGe9qb4rIo/BfwFy6nWQ4ThWGNDzbcfu29dMDh2nHU7CypYNLmzTda0/L5cNuzmDQi/A4Y27k6eQxLI79wS/11D0AAMNvs6XT6ojVJjJEgTbMy2BT77xBMp09KcpaWV1uc41jQoi0NdUHfjeOO9WWn7AVF7s7n986SithPJGeupBh2PCSP/xxqxAp3eq6wuUV7Wc6MSZIEhA8vHjbfOe/OcW3zmAuKy+nUzAyD2bow8ODaEROFq8AyZ5WBYdEZXGqGxZ61HJV+9HYCJRbTNA0QBA40HWunaKN5dKg/DBKxeCIe09Th/m4MJwiMSZmLEzMQAABQRuNqgu8NYX3doTcMpvCkLbtQZ2AJkrPOZG1zlnY13T+Hy9EehY90h57eqcorcZ/lctZuMzAsOjLEqwNv66/6vZcPYRBC+C3cGaBxhSet2av1BpYgTTY7k5y2JPT41slIR6Axv8R9nnOs+4Pf+2r992uOxGVJwgAAAEINfgt3BGgsESWtWas1iGDyl+CT/u7WpvxNFRc4x7qtBoZFhSFejb7z1fq9NYfjsiT+cwcQavBruCOgU4SIGo18amuoq3Js3FNlynVtH385+s53ze+t8cRkURx3yMTTRBAEQVAUXbFlf3XystJKA2NExeFBdWASDAAA+MQACCEEmqbJ0b6PMC7JwhDU8YFHV5u9NZ64LErT/oW/63tPV6uJwmKoOND78u7Fg5NhAAD4CVbzY9cwrWQrAAAAAElFTkSuQmCC"; + return xImg; +} + +function loadImage (sContainerClass, sImagePath) { + let xRequest = new XMLHttpRequest(); + xRequest.open('GET', browser.extension.getURL("")+sImagePath, false); + xRequest.responseType = "arraybuffer"; + xRequest.send(); + let blobTxt = new Blob([xRequest.response], {type: 'image/png'}); + let img = document.createElement('img'); + img.src = (URL || webkitURL).createObjectURL(blobTxt); // webkitURL is obsolete: https://bugs.webkit.org/show_bug.cgi?id=167518 + Array.filter(document.getElementsByClassName(sContainerClass), function (oElem) { + oElem.appendChild(img); + }); +} ADDED gc_lang/fr/webext/content_scripts/tf_content.css Index: gc_lang/fr/webext/content_scripts/tf_content.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/tf_content.css @@ -0,0 +1,79 @@ +/* + Text Formatter +*/ +#grammalecte_tf_options { + display: flex; + padding: 10px; +} +#grammalecte_tf_options .underline:hover { + background-color: hsl(210, 10%, 86%); + border-radius: 2px; +} +.grammalecte_tf_column { + flex-grow: 1; + width: 360px; + padding: 0 5px; +} +#grammalecte_tf_options legend label { + font-size: 20px; + color: hsla(210, 20%, 50%, .8); + font-weight: bold; +} +#grammalecte_tf_options fieldset { + padding: 2px 10px 10px 13px; + margin-bottom: 5px; + background-color: hsl(210, 10%, 96%); + border-color: hsl(210, 20%, 80%); + border-radius: 3px; +} +#grammalecte_tf_options legend .option { + margin: 7px 5px 0 4px; + float: left; +} +#grammalecte_tf_options label { + font-size: 13px; +} +#grammalecte_tf_options .grammalecte_tf_result { + float: right; + margin-right: 3px; + font-size: 13px; +} + +#grammalecte_tf_actions { + background-color: hsl(120, 10%, 92%); + padding: 15px; + border-top: 1px solid hsl(120, 20%, 86%); +} + +.grammalecte_button { + display: inline-block; + padding: 5px 10px; + width: 100px; + border-radius: 3px; + font-size: 16px; + font-weight: bold; + text-align: center; + cursor: pointer; +} + +#grammalecte_tf_reset { + background-color: hsl(210, 100%, 50%); + color: hsl(210, 0%, 100%); +} +#grammalecte_tf_progressbar { + width: 250px; +} +#grammalecte_tf_time_res { + width: 40px; + padding: 5px 10px; + width: 25px; +} +#grammalecte_tf_apply { + background-color: hsl(150, 100%, 50%); + color: hsl(150, 0%, 100%); +} + +#grammalecte_progressbarbox { + display: inline-block; + padding: 10px 20px; +} ADDED gc_lang/fr/webext/content_scripts/tf_content.js Index: gc_lang/fr/webext/content_scripts/tf_content.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/tf_content.js @@ -0,0 +1,167 @@ +// JavaScript +// Text formatter + +"use strict"; + +function createTextFormatter (xTextArea) { + let xTFNode = document.createElement("div"); + try { + // Options + let xOptions = createNode("div", {id: "grammalecte_tf_options"}); + let xColumn1 = createNode("div", {className: "grammalecte_tf_column"}); + let xSSP = createFieldset("group_ssp", true, "Espaces surnuméraires"); + xSSP.appendChild(createOptionInputAndLabel("o_start_of_paragraph", true, "En début de paragraphe")); + xSSP.appendChild(createOptionInputAndLabel("o_end_of_paragraph", true, "En fin de paragraphe")); + xSSP.appendChild(createOptionInputAndLabel("o_between_words", true, "Entre les mots")); + xSSP.appendChild(createOptionInputAndLabel("o_before_punctuation", true, "Avant les points (.), les virgules (,)")); + xSSP.appendChild(createOptionInputAndLabel("o_within_parenthesis", true, "À l’intérieur des parenthèses")); + xSSP.appendChild(createOptionInputAndLabel("o_within_square_brackets", true, "À l’intérieur des crochets")); + xSSP.appendChild(createOptionInputAndLabel("o_within_quotation_marks", true, "À l’intérieur des guillemets “ et ”")); + let xSpace = createFieldset("group_space", true, "Espaces manquants"); + xSpace.appendChild(createOptionInputAndLabel("o_add_space_after_punctuation", true, "Après , ; : ? ! . …")); + xSpace.appendChild(createOptionInputAndLabel("o_add_space_around_hyphens", true, "Autour des tirets d’incise")); + let xNBSP = createFieldset("group_nbsp", true, "Espaces insécables"); + xNBSP.appendChild(createOptionInputAndLabel("o_nbsp_before_punctuation", true, "Avant : ; ? et !")); + xNBSP.appendChild(createOptionInputAndLabel("o_nbsp_within_quotation_marks", true, "Avec les guillemets « et »")); + xNBSP.appendChild(createOptionInputAndLabel("o_nbsp_before_symbol", true, "Avant % ‰ € $ £ ¥ ˚C")); + xNBSP.appendChild(createOptionInputAndLabel("o_nbsp_within_numbers", true, "À l’intérieur des nombres")); + xNBSP.appendChild(createOptionInputAndLabel("o_nbsp_before_units", true, "Avant les unités de mesure")); + let xDelete = createFieldset("group_delete", true, "Suppressions"); + xDelete.appendChild(createOptionInputAndLabel("o_erase_non_breaking_hyphens", true, "Tirets conditionnels")); + let xColumn2 = createNode("div", {className: "grammalecte_tf_column"}); + let xTypo = createFieldset("group_typo", true, "Signes typographiques"); + xTypo.appendChild(createOptionInputAndLabel("o_ts_apostrophe", true, "Apostrophe (’)")); + xTypo.appendChild(createOptionInputAndLabel("o_ts_ellipsis", true, "Points de suspension (…)")); + xTypo.appendChild(createOptionInputAndLabel("o_ts_dash_middle", true, "Tirets d’incise :")); + xTypo.appendChild(createRadioBoxHyphens("hyphen1", "o_ts_m_dash_middle", "o_ts_n_dash_middle", false)); + xTypo.appendChild(createOptionInputAndLabel("o_ts_dash_start", true, "Tirets en début de paragraphe :")); + xTypo.appendChild(createRadioBoxHyphens("hyphen2", "o_ts_m_dash_start", "o_ts_n_dash_start", true)); + xTypo.appendChild(createOptionInputAndLabel("o_ts_quotation_marks", true, "Modifier les guillemets droits (\" et ')")); + xTypo.appendChild(createOptionInputAndLabel("o_ts_units", true, "Points médians des unités (N·m, Ω·m…)")); + xTypo.appendChild(createOptionInputAndLabel("o_ts_spell", true, "Ligatures (cœur…) et diacritiques (ça, État…)")); + xTypo.appendChild(createRadioBoxLigatures()); + xTypo.appendChild(createLigaturesSelection()); + let xMisc = createFieldset("group_misc", true, "Divers"); + xMisc.appendChild(createOptionInputAndLabel("o_ordinals_no_exponant", true, "Ordinaux (15e, XXIe…)")); + xMisc.appendChild(createOptionInputAndLabel("o_etc", true, "Et cætera, etc.")); + xMisc.appendChild(createOptionInputAndLabel("o_missing_hyphens", true, "Traits d’union manquants")); + xMisc.appendChild(createOptionInputAndLabel("o_ma_word", true, "Apostrophes manquantes")); + let xStruct = createFieldset("group_struct", false, "Restructuration [!]"); + xStruct.appendChild(createOptionInputAndLabel("o_remove_hyphens_at_end_of_paragraphs", false, "Enlever césures en fin de ligne/paragraphe [!]")); + xStruct.appendChild(createOptionInputAndLabel("o_merge_contiguous_paragraphs", false, "Fusionner les paragraphes contigus [!]")); + xColumn1.appendChild(xSSP); + xColumn1.appendChild(xSpace); + xColumn1.appendChild(xNBSP); + xColumn1.appendChild(xDelete); + xColumn2.appendChild(xTypo); + xColumn2.appendChild(xMisc); + xColumn2.appendChild(xStruct); + xOptions.appendChild(xColumn1); + xOptions.appendChild(xColumn2); + // Actions + let xActions = createNode("div", {id: "grammalecte_tf_actions"}); + xActions.appendChild(createNode("div", {id: "grammalecte_tf_reset", textContent: "Par défaut", className: "grammalecte_button", style: "background-color: hsl(210, 50%, 50%)"})); + xActions.appendChild(createNode("progress", {id: "grammalecte_tf_progressbar"})); + xActions.appendChild(createNode("span", {id: "grammalecte_tf_time_res"})); + xActions.appendChild(createNode("div", {id: "grammalecte_tf_apply", textContent: "Appliquer", className: "grammalecte_button", style: "background-color: hsl(180, 50%, 50%)"})); + //xActions.appendChild(createNode("div", {id: "grammalecte_infomsg", textContent: "blabla"})); + // create result + xTFNode.appendChild(xOptions); + xTFNode.appendChild(xActions); + } + catch (e) { + //console.error(e); + showError(e); + } + return xTFNode; +} + + +/* + Common options +*/ +function createFieldset (sId, bDefault, sLabel) { + let xFieldset = createNode("fieldset", {id: sId, className: "groupblock"}); + let xLegend = document.createElement("legend"); + xLegend.appendChild(createNode("input", {type: "checkbox", id: "o_"+sId, className: "option"}, {default: bDefault})); + xLegend.appendChild(createNode("label", {htmlFor: "o_"+sId, textContent: sLabel})); + xFieldset.appendChild(xLegend); + return xFieldset; +} + +function createOptionInputAndLabel (sId, bDefault, sLabel) { + let xOption = createNode("div", {className: "blockopt underline"}); + xOption.appendChild(createNode("input", {type: "checkbox", id: sId, className: "option"}, {default: bDefault})); + xOption.appendChild(createNode("label", {htmlFor: sId, textContent: sLabel, className: "opt_lbl largew"})); + xOption.appendChild(createNode("div", {id: "res_"+sId, className: "grammalecte_tf_result", textContent: "9999"})); + return xOption; +} + + +/* + Hyphens +*/ +function createRadioBoxHyphens (sName, sIdEmDash, sIdEnDash, bDefaultEmDash) { + let xLine = createNode("div", {className: "blockopt"}); + xLine.appendChild(createNode("input", {type: "radio", id: sIdEmDash, name: sName, className:"option"}, {default: bDefaultEmDash})); + xLine.appendChild(createNode("label", {htmlFor: sIdEmDash, className: "opt_lbl", textContent: "cadratin (—)"})); + xLine.appendChild(createNode("input", {type: "radio", id: sIdEnDash, name: sName, className:"option"}, {default: !bDefaultEmDash})); + xLine.appendChild(createNode("label", {htmlFor: sIdEnDash, className: "opt_lbl", textContent: "demi-cadratin (–)"})); + return xLine; +} + + +/* + Ligatures +*/ +function createRadioBoxLigatures () { + let xLine = createNode("div", {className: "blockopt underline"}); + xLine.appendChild(createOptionInputAndLabel("o_ts_ligature", true, "Ligatures")); + xLine.appendChild(createNode("input", {type: "radio", id: "o_ts_ligature_do", name: "liga", className:"option"}, {default: false})); + xLine.appendChild(createNode("label", {htmlFor: "o_ts_ligature_do", className: "opt_lbl", textContent: "faire"})); + xLine.appendChild(createNode("input", {type: "radio", id: "o_ts_ligature_undo", name: "liga", className:"option"}, {default: true})); + xLine.appendChild(createNode("label", {htmlFor: "o_ts_ligature_undo", className: "opt_lbl", textContent: "défaire"})); + return xLine; +} + +function createLigaturesSelection () { + let xLine = createNode("div", {className: "blockopt"}); + xLine.appendChild(createLigatureCheckboxAndLabel("o_ts_ligature_ff", "ff", true)); + xLine.appendChild(createLigatureCheckboxAndLabel("o_ts_ligature_fi", "fi", true)); + xLine.appendChild(createLigatureCheckboxAndLabel("o_ts_ligature_ffi", "ffi", true)); + xLine.appendChild(createLigatureCheckboxAndLabel("o_ts_ligature_fl", "fl", true)); + xLine.appendChild(createLigatureCheckboxAndLabel("o_ts_ligature_ffl", "ffl", true)); + xLine.appendChild(createLigatureCheckboxAndLabel("o_ts_ligature_ft", "ft", true)); + xLine.appendChild(createLigatureCheckboxAndLabel("o_ts_ligature_st", "st", false)); + return xLine; +} + +function createLigatureCheckboxAndLabel (sId, sLabel, bDefault) { + let xInlineBlock = createNode("div", {style: "display: inline-block;"}); + xInlineBlock.appendChild(createNode("input", {type: "checkbox", id: sId, className: "option"}, {default: bDefault})); + xInlineBlock.appendChild(createNode("label", {htmlFor: sId, className: "opt_lbl", textContent: sLabel})); + return xInlineBlock; +} + +let sTFinnerHTML = ' \ + \ +
+ + Conjuguer +
+ +
+ +
+ + · + · + · + · +
+❦
+ + + + ++
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+