Index: compile_rules.py ================================================================== --- compile_rules.py +++ compile_rules.py @@ -517,10 +517,11 @@ def prepareOptions (lOptionLines): "returns a dictionary with data about options" sLang = "" + sDefaultUILang = "" lStructOpt = [] lOpt = [] dOptLabel = {} dOptPriority = {} for sLine in lOptionLines: @@ -539,18 +540,21 @@ dOptPriority[m.group(1)] = int(m.group(2)) elif sLine.startswith("OPTLANG/"): m = re.match("OPTLANG/([a-z][a-z](?:_[A-Z][A-Z]|)):(.+)$", sLine) sLang = m.group(1)[:2] dOptLabel[sLang] = { "__optiontitle__": m.group(2).strip() } + elif sLine.startswith("OPTDEFAULTUILANG:"): + m = re.match("OPTDEFAULTUILANG: *([a-z][a-z](?:_[A-Z][A-Z]|))$", sLine) + sDefaultUILang = m.group(1)[:2] elif sLine.startswith("OPTLABEL/"): m = re.match("OPTLABEL/([a-z0-9]+):(.+)$", sLine) dOptLabel[sLang][m.group(1)] = list(map(str.strip, m.group(2).split("|"))) if "|" in m.group(2) else [m.group(2).strip(), ""] else: print("# Error. Wrong option line in:\n ") print(sLine) print(" options defined for: " + ", ".join([ t[0] for t in lOpt ])) - dOptions = { "lStructOpt": lStructOpt, "dOptLabel": dOptLabel } + dOptions = { "lStructOpt": lStructOpt, "dOptLabel": dOptLabel, "sDefaultUILang": sDefaultUILang } dOptions.update({ "dOpt"+k: v for k, v in lOpt }) return dOptions, dOptPriority def printBookmark (nLevel, sComment, nLine): @@ -585,11 +589,11 @@ print(sLine.strip()) elif sLine.startswith("TEST:"): lTest.append("{:<8}".format(i) + " " + sLine[5:].strip()) elif sLine.startswith("TODO:"): pass - elif sLine.startswith(("OPTGROUP/", "OPTSOFTWARE:", "OPT/", "OPTLANG/", "OPTLABEL/", "OPTPRIORITY/")): + elif sLine.startswith(("OPTGROUP/", "OPTSOFTWARE:", "OPT/", "OPTLANG/", "OPTDEFAULTUILANG:", "OPTLABEL/", "OPTPRIORITY/")): lOpt.append(sLine) elif re.match("[  \t]*$", sLine): pass elif sLine.startswith("!!"): m = zBookmark.search(sLine) 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/build.py ================================================================== --- gc_lang/fr/build.py +++ gc_lang/fr/build.py @@ -18,13 +18,27 @@ "create Web-extension" print("Building WebExtension") helpers.createCleanFolder("_build/webext/"+sLang) dir_util.copy_tree("gc_lang/"+sLang+"/webext/", "_build/webext/"+sLang) dir_util.copy_tree("grammalecte-js", "_build/webext/"+sLang+"/grammalecte") + dVars['webextOptionsHTML'] = _createOptionsForWebExtension(dVars) + helpers.copyAndFileTemplate("_build/webext/"+sLang+"/panel/main.html", "_build/webext/"+sLang+"/panel/main.html", dVars) with helpers.cd("_build/webext/"+sLang): os.system("web-ext build") + +def _createOptionsForWebExtension (dVars): + sHTML = "" + sLang = dVars['sDefaultUILang'] + for sSection, lOpt in dVars['lStructOpt']: + sHTML += f'\n
\n

{dVars["dOptLabel"][sLang][sSection][0]}

\n' + for lLineOpt in lOpt: + for sOpt in lLineOpt: + sHTML += f'

\n' + sHTML += '
\n' + return sHTML + def createFirefoxExtension (sLang, dVars): "create extension for Firefox" print("Building extension for Firefox") helpers.createCleanFolder("_build/xpi/"+sLang) @@ -52,11 +66,11 @@ sHTML += '

\n' sHTML += '\n' # Creating translation data dProperties = {} for sLang in dVars['dOptLabel'].keys(): - dProperties[sLang] = "\n".join( [ "option_" + sOpt + " = " + dVars['dOptLabel'][sLang][sOpt][0].replace(" [!]", " [!]") for sOpt in dVars['dOptLabel'][sLang] ] ) + dProperties[sLang] = "\n".join( [ "option_" + sOpt + " = " + dVars['dOptLabel'][sLang][sOpt][0].replace(" [!]", " [!]") for sOpt in dVars['dOptLabel'][sLang] ] ) return sHTML, dProperties def createThunderbirdExtension (sLang, dVars, spLangPack): "create extension for Thunderbird" 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/rules.grx ================================================================== --- gc_lang/fr/rules.grx +++ gc_lang/fr/rules.grx @@ -98,10 +98,11 @@ # Priority: from 0 to 9. Default priority is 4. OPTPRIORITY/tu: 6 OPTPRIORITY/conf: 5 OPTPRIORITY/ocr: 3 +OPTDEFAULTUILANG: fr_FR # Labels OPTLANG/fr_FR: Grammalecte (Français) OPTLABEL/basic: Typographie OPTLABEL/typo: Signes typographiques 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/3rd/font-awesome-4.7.0/css/font-awesome.css Index: gc_lang/fr/webext/3rd/font-awesome-4.7.0/css/font-awesome.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/3rd/font-awesome-4.7.0/css/font-awesome.css @@ -0,0 +1,2337 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.7.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.fa-pull-left { + float: left; +} +.fa-pull-right { + float: right; +} +.fa.fa-pull-left { + margin-right: .3em; +} +.fa.fa-pull-right { + margin-left: .3em; +} +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper-pp:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-resistance:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} +.fa-buysellads:before { + content: "\f20d"; +} +.fa-connectdevelop:before { + content: "\f20e"; +} +.fa-dashcube:before { + content: "\f210"; +} +.fa-forumbee:before { + content: "\f211"; +} +.fa-leanpub:before { + content: "\f212"; +} +.fa-sellsy:before { + content: "\f213"; +} +.fa-shirtsinbulk:before { + content: "\f214"; +} +.fa-simplybuilt:before { + content: "\f215"; +} +.fa-skyatlas:before { + content: "\f216"; +} +.fa-cart-plus:before { + content: "\f217"; +} +.fa-cart-arrow-down:before { + content: "\f218"; +} +.fa-diamond:before { + content: "\f219"; +} +.fa-ship:before { + content: "\f21a"; +} +.fa-user-secret:before { + content: "\f21b"; +} +.fa-motorcycle:before { + content: "\f21c"; +} +.fa-street-view:before { + content: "\f21d"; +} +.fa-heartbeat:before { + content: "\f21e"; +} +.fa-venus:before { + content: "\f221"; +} +.fa-mars:before { + content: "\f222"; +} +.fa-mercury:before { + content: "\f223"; +} +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} +.fa-transgender-alt:before { + content: "\f225"; +} +.fa-venus-double:before { + content: "\f226"; +} +.fa-mars-double:before { + content: "\f227"; +} +.fa-venus-mars:before { + content: "\f228"; +} +.fa-mars-stroke:before { + content: "\f229"; +} +.fa-mars-stroke-v:before { + content: "\f22a"; +} +.fa-mars-stroke-h:before { + content: "\f22b"; +} +.fa-neuter:before { + content: "\f22c"; +} +.fa-genderless:before { + content: "\f22d"; +} +.fa-facebook-official:before { + content: "\f230"; +} +.fa-pinterest-p:before { + content: "\f231"; +} +.fa-whatsapp:before { + content: "\f232"; +} +.fa-server:before { + content: "\f233"; +} +.fa-user-plus:before { + content: "\f234"; +} +.fa-user-times:before { + content: "\f235"; +} +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} +.fa-viacoin:before { + content: "\f237"; +} +.fa-train:before { + content: "\f238"; +} +.fa-subway:before { + content: "\f239"; +} +.fa-medium:before { + content: "\f23a"; +} +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} +.fa-optin-monster:before { + content: "\f23c"; +} +.fa-opencart:before { + content: "\f23d"; +} +.fa-expeditedssl:before { + content: "\f23e"; +} +.fa-battery-4:before, +.fa-battery:before, +.fa-battery-full:before { + content: "\f240"; +} +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} +.fa-mouse-pointer:before { + content: "\f245"; +} +.fa-i-cursor:before { + content: "\f246"; +} +.fa-object-group:before { + content: "\f247"; +} +.fa-object-ungroup:before { + content: "\f248"; +} +.fa-sticky-note:before { + content: "\f249"; +} +.fa-sticky-note-o:before { + content: "\f24a"; +} +.fa-cc-jcb:before { + content: "\f24b"; +} +.fa-cc-diners-club:before { + content: "\f24c"; +} +.fa-clone:before { + content: "\f24d"; +} +.fa-balance-scale:before { + content: "\f24e"; +} +.fa-hourglass-o:before { + content: "\f250"; +} +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} +.fa-hourglass:before { + content: "\f254"; +} +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} +.fa-hand-scissors-o:before { + content: "\f257"; +} +.fa-hand-lizard-o:before { + content: "\f258"; +} +.fa-hand-spock-o:before { + content: "\f259"; +} +.fa-hand-pointer-o:before { + content: "\f25a"; +} +.fa-hand-peace-o:before { + content: "\f25b"; +} +.fa-trademark:before { + content: "\f25c"; +} +.fa-registered:before { + content: "\f25d"; +} +.fa-creative-commons:before { + content: "\f25e"; +} +.fa-gg:before { + content: "\f260"; +} +.fa-gg-circle:before { + content: "\f261"; +} +.fa-tripadvisor:before { + content: "\f262"; +} +.fa-odnoklassniki:before { + content: "\f263"; +} +.fa-odnoklassniki-square:before { + content: "\f264"; +} +.fa-get-pocket:before { + content: "\f265"; +} +.fa-wikipedia-w:before { + content: "\f266"; +} +.fa-safari:before { + content: "\f267"; +} +.fa-chrome:before { + content: "\f268"; +} +.fa-firefox:before { + content: "\f269"; +} +.fa-opera:before { + content: "\f26a"; +} +.fa-internet-explorer:before { + content: "\f26b"; +} +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} +.fa-contao:before { + content: "\f26d"; +} +.fa-500px:before { + content: "\f26e"; +} +.fa-amazon:before { + content: "\f270"; +} +.fa-calendar-plus-o:before { + content: "\f271"; +} +.fa-calendar-minus-o:before { + content: "\f272"; +} +.fa-calendar-times-o:before { + content: "\f273"; +} +.fa-calendar-check-o:before { + content: "\f274"; +} +.fa-industry:before { + content: "\f275"; +} +.fa-map-pin:before { + content: "\f276"; +} +.fa-map-signs:before { + content: "\f277"; +} +.fa-map-o:before { + content: "\f278"; +} +.fa-map:before { + content: "\f279"; +} +.fa-commenting:before { + content: "\f27a"; +} +.fa-commenting-o:before { + content: "\f27b"; +} +.fa-houzz:before { + content: "\f27c"; +} +.fa-vimeo:before { + content: "\f27d"; +} +.fa-black-tie:before { + content: "\f27e"; +} +.fa-fonticons:before { + content: "\f280"; +} +.fa-reddit-alien:before { + content: "\f281"; +} +.fa-edge:before { + content: "\f282"; +} +.fa-credit-card-alt:before { + content: "\f283"; +} +.fa-codiepie:before { + content: "\f284"; +} +.fa-modx:before { + content: "\f285"; +} +.fa-fort-awesome:before { + content: "\f286"; +} +.fa-usb:before { + content: "\f287"; +} +.fa-product-hunt:before { + content: "\f288"; +} +.fa-mixcloud:before { + content: "\f289"; +} +.fa-scribd:before { + content: "\f28a"; +} +.fa-pause-circle:before { + content: "\f28b"; +} +.fa-pause-circle-o:before { + content: "\f28c"; +} +.fa-stop-circle:before { + content: "\f28d"; +} +.fa-stop-circle-o:before { + content: "\f28e"; +} +.fa-shopping-bag:before { + content: "\f290"; +} +.fa-shopping-basket:before { + content: "\f291"; +} +.fa-hashtag:before { + content: "\f292"; +} +.fa-bluetooth:before { + content: "\f293"; +} +.fa-bluetooth-b:before { + content: "\f294"; +} +.fa-percent:before { + content: "\f295"; +} +.fa-gitlab:before { + content: "\f296"; +} +.fa-wpbeginner:before { + content: "\f297"; +} +.fa-wpforms:before { + content: "\f298"; +} +.fa-envira:before { + content: "\f299"; +} +.fa-universal-access:before { + content: "\f29a"; +} +.fa-wheelchair-alt:before { + content: "\f29b"; +} +.fa-question-circle-o:before { + content: "\f29c"; +} +.fa-blind:before { + content: "\f29d"; +} +.fa-audio-description:before { + content: "\f29e"; +} +.fa-volume-control-phone:before { + content: "\f2a0"; +} +.fa-braille:before { + content: "\f2a1"; +} +.fa-assistive-listening-systems:before { + content: "\f2a2"; +} +.fa-asl-interpreting:before, +.fa-american-sign-language-interpreting:before { + content: "\f2a3"; +} +.fa-deafness:before, +.fa-hard-of-hearing:before, +.fa-deaf:before { + content: "\f2a4"; +} +.fa-glide:before { + content: "\f2a5"; +} +.fa-glide-g:before { + content: "\f2a6"; +} +.fa-signing:before, +.fa-sign-language:before { + content: "\f2a7"; +} +.fa-low-vision:before { + content: "\f2a8"; +} +.fa-viadeo:before { + content: "\f2a9"; +} +.fa-viadeo-square:before { + content: "\f2aa"; +} +.fa-snapchat:before { + content: "\f2ab"; +} +.fa-snapchat-ghost:before { + content: "\f2ac"; +} +.fa-snapchat-square:before { + content: "\f2ad"; +} +.fa-pied-piper:before { + content: "\f2ae"; +} +.fa-first-order:before { + content: "\f2b0"; +} +.fa-yoast:before { + content: "\f2b1"; +} +.fa-themeisle:before { + content: "\f2b2"; +} +.fa-google-plus-circle:before, +.fa-google-plus-official:before { + content: "\f2b3"; +} +.fa-fa:before, +.fa-font-awesome:before { + content: "\f2b4"; +} +.fa-handshake-o:before { + content: "\f2b5"; +} +.fa-envelope-open:before { + content: "\f2b6"; +} +.fa-envelope-open-o:before { + content: "\f2b7"; +} +.fa-linode:before { + content: "\f2b8"; +} +.fa-address-book:before { + content: "\f2b9"; +} +.fa-address-book-o:before { + content: "\f2ba"; +} +.fa-vcard:before, +.fa-address-card:before { + content: "\f2bb"; +} +.fa-vcard-o:before, +.fa-address-card-o:before { + content: "\f2bc"; +} +.fa-user-circle:before { + content: "\f2bd"; +} +.fa-user-circle-o:before { + content: "\f2be"; +} +.fa-user-o:before { + content: "\f2c0"; +} +.fa-id-badge:before { + content: "\f2c1"; +} +.fa-drivers-license:before, +.fa-id-card:before { + content: "\f2c2"; +} +.fa-drivers-license-o:before, +.fa-id-card-o:before { + content: "\f2c3"; +} +.fa-quora:before { + content: "\f2c4"; +} +.fa-free-code-camp:before { + content: "\f2c5"; +} +.fa-telegram:before { + content: "\f2c6"; +} +.fa-thermometer-4:before, +.fa-thermometer:before, +.fa-thermometer-full:before { + content: "\f2c7"; +} +.fa-thermometer-3:before, +.fa-thermometer-three-quarters:before { + content: "\f2c8"; +} +.fa-thermometer-2:before, +.fa-thermometer-half:before { + content: "\f2c9"; +} +.fa-thermometer-1:before, +.fa-thermometer-quarter:before { + content: "\f2ca"; +} +.fa-thermometer-0:before, +.fa-thermometer-empty:before { + content: "\f2cb"; +} +.fa-shower:before { + content: "\f2cc"; +} +.fa-bathtub:before, +.fa-s15:before, +.fa-bath:before { + content: "\f2cd"; +} +.fa-podcast:before { + content: "\f2ce"; +} +.fa-window-maximize:before { + content: "\f2d0"; +} +.fa-window-minimize:before { + content: "\f2d1"; +} +.fa-window-restore:before { + content: "\f2d2"; +} +.fa-times-rectangle:before, +.fa-window-close:before { + content: "\f2d3"; +} +.fa-times-rectangle-o:before, +.fa-window-close-o:before { + content: "\f2d4"; +} +.fa-bandcamp:before { + content: "\f2d5"; +} +.fa-grav:before { + content: "\f2d6"; +} +.fa-etsy:before { + content: "\f2d7"; +} +.fa-imdb:before { + content: "\f2d8"; +} +.fa-ravelry:before { + content: "\f2d9"; +} +.fa-eercast:before { + content: "\f2da"; +} +.fa-microchip:before { + content: "\f2db"; +} +.fa-snowflake-o:before { + content: "\f2dc"; +} +.fa-superpowers:before { + content: "\f2dd"; +} +.fa-wpexplorer:before { + content: "\f2de"; +} +.fa-meetup:before { + content: "\f2e0"; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} ADDED gc_lang/fr/webext/3rd/font-awesome-4.7.0/css/font-awesome.min.css Index: gc_lang/fr/webext/3rd/font-awesome-4.7.0/css/font-awesome.min.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/3rd/font-awesome-4.7.0/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} ADDED gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/FontAwesome.otf Index: gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/FontAwesome.otf ================================================================== --- /dev/null +++ gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/FontAwesome.otf cannot compute difference between binary files ADDED gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.eot Index: gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.eot ================================================================== --- /dev/null +++ gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.eot cannot compute difference between binary files ADDED gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.svg Index: gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.svg ================================================================== --- /dev/null +++ gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ADDED gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf Index: gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf ================================================================== --- /dev/null +++ gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf cannot compute difference between binary files ADDED gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.woff Index: gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.woff ================================================================== --- /dev/null +++ gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.woff cannot compute difference between binary files ADDED gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 Index: gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 ================================================================== --- /dev/null +++ gc_lang/fr/webext/3rd/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 cannot compute difference between binary files 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,262 @@ +// 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": + browser.storage.local.set({"gc_options": result}); + break; + case "parse": + case "parseAndSpellcheck": + case "parseAndSpellcheck1": + case "getListOfTokens": + // send result to content script + if (typeof(dInfo.iReturnPort) === "number") { + let xPort = dConnx.get(dInfo.iReturnPort); + xPort.postMessage(e.data); + } else { + console.log("[background] don’t know where to send results"); + console.log(e.data); + } + break; + case "textToTest": + case "fullTests": + case "getOptions": + case "getDefaultOptions": + // send result to panel + browser.runtime.sendMessage(e.data); + break; + case "setOptions": + case "setOption": + browser.storage.local.set({"gc_options": result}); + break; + default: + console.log("Unknown command: " + sActionDone); + console.log(result); + } + } + catch (e) { + showError(e); + } +}; + + +function init () { + let xPromise = browser.storage.local.get("gc_options"); + xPromise.then( + function (dSavedOptions) { + let dOptions = (dSavedOptions.hasOwnProperty("gc_options")) ? dSavedOptions.gc_options : null; + xGCEWorker.postMessage({ + sCommand: "init", + dParam: {sExtensionPath: browser.extension.getURL("."), dOptions: dOptions, sContext: "Firefox"}, + dInfo: {} + }); + }, + function (e) { + showError(e); + xGCEWorker.postMessage({ + sCommand: "init", + dParam: {sExtensionPath: browser.extension.getURL("."), dOptions: null, sContext: "Firefox"}, + dInfo: {} + }); + } + ); +} + +init(); + + +/* + Ports from content-scripts +*/ + +let dConnx = new Map(); + + +/* + Messages from the extension (not the Worker) +*/ +function handleMessage (oRequest, xSender, sendResponse) { + //console.log(xSender); + switch (oRequest.sCommand) { + case "parse": + case "parseAndSpellcheck": + case "parseAndSpellcheck1": + case "getListOfTokens": + case "textToTest": + case "getOptions": + case "getDefaultOptions": + case "setOptions": + case "setOption": + case "fullTests": + xGCEWorker.postMessage(oRequest); + break; + default: + console.log("[background] Unknown command: " + oRequest.sCommand); + } + //sendResponse({response: "response from background script"}); +} + +browser.runtime.onMessage.addListener(handleMessage); + + +function handleConnexion (xPort) { + let iPortId = xPort.sender.tab.id; // identifier for the port: each port can be found at dConnx[iPortId] + dConnx.set(iPortId, xPort); + xPort.onMessage.addListener(function (oRequest) { + switch (oRequest.sCommand) { + case "parse": + case "parseAndSpellcheck": + case "parseAndSpellcheck1": + case "getListOfTokens": + oRequest.dInfo.iReturnPort = iPortId; // we pass the id of the return port to receive answer + xGCEWorker.postMessage(oRequest); + break; + default: + console.log("[background] Unknown command: " + oRequest.sCommand); + console.log(oRequest); + } + }); + xPort.postMessage({sActionDone: "newId", result: iPortId}); +} + +browser.runtime.onConnect.addListener(handleConnexion); + + +/* + Context Menu +*/ +browser.contextMenus.create({ + id: "parseAndSpellcheck", + title: "Correction grammaticale", + contexts: ["selection"] +}); + +browser.contextMenus.create({ + id: "getListOfTokens", + title: "Lexicographe", + contexts: ["selection"] +}); + +browser.contextMenus.create({ + id: "whatever", + type: "separator", + contexts: ["selection"] +}); + +browser.contextMenus.create({ + id: "conjugueur_window", + title: "Conjugueur [fenêtre]", + contexts: ["all"] +}); + +browser.contextMenus.create({ + id: "conjugueur_tab", + title: "Conjugueur [onglet]", + contexts: ["all"] +}); + + +browser.contextMenus.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); + // confusing: no way to get the node where we click?! + switch (xInfo.menuItemId) { + case "parseAndSpellcheck": + parseAndSpellcheckSelectedText(xTab.id, xInfo.selectionText); + break; + case "getListOfTokens": + getListOfTokensFromSelectedText(xTab.id, xInfo.selectionText); + break; + case "conjugueur_window": + openConjugueurWindow(); + break; + case "conjugueur_tab": + openConjugueurTab(); + break; + } +}); + + +/* + Keyboard shortcuts +*/ +browser.commands.onCommand.addListener(function (sCommand) { + switch (sCommand) { + case "conjugueur_tab": + openConjugueurTab(); + break; + case "conjugueur_window": + openConjugueurWindow(); + break; + } +}); + + +/* + Actions +*/ +function parseAndSpellcheckSelectedText (iTab, sText) { + // send message to the tab + let xTabPort = dConnx.get(iTab); + xTabPort.postMessage({sActionDone: "openGCPanel", result: null, dInfo: null, bEnd: false, bError: false}); + // send command to the worker + xGCEWorker.postMessage({ + sCommand: "parseAndSpellcheck", + dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {iReturnPort: iTab} + }); +} + +function getListOfTokensFromSelectedText (iTab, sText) { + // send message to the tab + let xTabPort = dConnx.get(iTab); + xTabPort.postMessage({sActionDone: "openLxgPanel", result: null, dInfo: null, bEnd: false, bError: false}); + // send command to the worker + xGCEWorker.postMessage({ + sCommand: "getListOfTokens", + dParam: {sText: sText}, + dInfo: {iReturnPort: iTab} + }); +} + +function openConjugueurTab () { + let xConjTab = browser.tabs.create({ + url: browser.extension.getURL("panel/conjugueur.html") + }); + xConjTab.then(onCreated, onError); +} + +function openConjugueurWindow () { + let xConjWindow = browser.windows.create({ + url: browser.extension.getURL("panel/conjugueur.html"), + type: "detached_panel", + width: 710, + height: 980 + }); + xConjWindow.then(onCreated, onError); +} + + +function onCreated (windowInfo) { + console.log(`Created window: ${windowInfo.id}`); +} + +function onError (error) { + console.log(`Error: ${error}`); +} 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,186 @@ +// 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"; + + +function showError (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); +} + + +const oGrammalecte = { + + nWrapper: 1, + + oConjPanel: null, + oTFPanel: null, + oLxgPanel: null, + oGCPanel: null, + + wrapTextareas: function () { + let lNode = document.getElementsByTagName("textarea"); + for (let xNode of lNode) { + this._createWrapper(xNode); + } + }, + + _createWrapper (xTextArea) { + try { + let xParentElement = xTextArea.parentElement; + let xWrapper = document.createElement("div"); + xWrapper.className = "grammalecte_wrapper"; + xWrapper.id = "grammalecte_wrapper" + this.nWrapper; + this.nWrapper += 1; + xParentElement.insertBefore(xWrapper, xTextArea); + xWrapper.appendChild(xTextArea); // move textarea in wrapper + xWrapper.appendChild(this._createWrapperToolbar(xTextArea)); + } + catch (e) { + showError(e); + } + }, + + _createWrapperToolbar: function (xTextArea) { + try { + let xToolbar = createNode("div", {className: "grammalecte_wrapper_toolbar"}); + let xConjButton = createNode("div", {className: "grammalecte_wrapper_button", textContent: "Conjuguer"}); + xConjButton.onclick = function () { + this.createConjPanel(); + //this.oConjPanel.show(); + }.bind(this); + let xTFButton = createNode("div", {className: "grammalecte_wrapper_button", textContent: "Formater"}); + xTFButton.onclick = function () { + this.createTFPanel(); + this.oTFPanel.start(xTextArea); + this.oTFPanel.show(); + }.bind(this); + let xLxgButton = createNode("div", {className: "grammalecte_wrapper_button", textContent: "Analyser"}); + xLxgButton.onclick = function () { + this.createLxgPanel(); + this.oLxgPanel.clear(); + this.oLxgPanel.show(); + this.oLxgPanel.startWaitIcon(); + xPort.postMessage({ + sCommand: "getListOfTokens", + dParam: {sText: xTextArea.value}, + dInfo: {sTextAreaId: xTextArea.id} + }); + }.bind(this); + let xGCButton = createNode("div", {className: "grammalecte_wrapper_button", textContent: "Corriger"}); + xGCButton.onclick = function () { + this.createGCPanel(); + this.oGCPanel.clear(); + this.oGCPanel.show(); + this.oGCPanel.start(xTextArea); + this.oGCPanel.startWaitIcon(); + xPort.postMessage({ + sCommand: "parseAndSpellcheck", + dParam: {sText: xTextArea.value, sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {sTextAreaId: xTextArea.id} + }); + }.bind(this); + // 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); + } + }, + + createConjPanel: function () { + if (this.oConjPanel === null) { + this.oConjPanel = new GrammalectePanel("grammalecte_conj_panel", "Conjugueur", 600, 600); + this.oConjPanel.insertIntoPage(); + } + }, + + createTFPanel: function () { + if (this.oTFPanel === null) { + this.oTFPanel = new GrammalecteTextFormatter("grammalecte_tf_panel", "Formateur de texte", 800, 620, false); + //this.oTFPanel.logInnerHTML(); + this.oTFPanel.insertIntoPage(); + } + }, + + createLxgPanel: function () { + if (this.oLxgPanel === null) { + this.oLxgPanel = new GrammalecteLexicographer("grammalecte_lxg_panel", "Lexicographe", 500, 700); + this.oLxgPanel.insertIntoPage(); + } + }, + + createGCPanel: function () { + if (this.oGCPanel === null) { + this.oGCPanel = new GrammalecteGrammarChecker("grammalecte_gc_panel", "Grammalecte", 500, 700); + //this.oGCPanel.logInnerHTML(); + this.oGCPanel.insertIntoPage(); + } + } +} + + +/* + Connexion to the background +*/ +let xPort = browser.runtime.connect({name: "content-script port"}); + +xPort.onMessage.addListener(function (oMessage) { + let {sActionDone, result, dInfo, bEnd, bError} = oMessage; + switch (sActionDone) { + case "parseAndSpellcheck": + if (!bEnd) { + oGrammalecte.oGCPanel.addParagraphResult(result); + } else { + oGrammalecte.oGCPanel.stopWaitIcon(); + } + break; + case "parseAndSpellcheck1": + oGrammalecte.oGCPanel.refreshParagraph(dInfo.sParagraphId, result); + break; + case "getListOfTokens": + if (!bEnd) { + oGrammalecte.oLxgPanel.addListOfTokens(result); + } else { + oGrammalecte.oLxgPanel.stopWaitIcon(); + } + break; + // Design WTF: context menus are made in background, not in content-script. + // Commands from context menu received here to initialize panels + case "openGCPanel": + oGrammalecte.createGCPanel(); + oGrammalecte.oGCPanel.clear(); + oGrammalecte.oGCPanel.show(); + oGrammalecte.oGCPanel.start(); + oGrammalecte.oGCPanel.startWaitIcon(); + break; + case "openLxgPanel": + oGrammalecte.createLxgPanel(); + oGrammalecte.oLxgPanel.clear(); + oGrammalecte.oLxgPanel.show(); + oGrammalecte.oLxgPanel.startWaitIcon(); + break; + default: + console.log("[Content script] Unknown command: " + sActionDone); + } +}); + + +/* + Start +*/ +oGrammalecte.wrapTextareas(); 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,363 @@ +/* + Grammar checker +*/ +#grammalecte_gc_panel_content { + margin: 0; + padding: 5px; +} + +.grammalecte_paragraph_block { + margin: 0 0 5px 0; +} + +.grammalecte_paragraph { + margin: 0; + padding: 10px; + background-color: hsl(0, 0%, 96%); + 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; +} + + +/* + Action buttons +*/ + +.grammalecte_paragraph_actions { + float: right; + margin: 0 0 5px 10px; +} + +.grammalecte_paragraph_actions .button { + display: inline-block; + background-color: hsl(0, 0%, 50%); + text-align: center; + margin-left: 2px; + padding: 1px 4px 3px 4px; + cursor: pointer; + font-size: 14px; + color: hsl(0, 0%, 96%); + border-radius: 2px; +} +.grammalecte_paragraph_actions .button:hover { + background-color: hsl(0, 0%, 40%); + color: hsl(0, 0%, 100%); +} + +.grammalecte_paragraph_actions .green { + background-color: hsl(120, 30%, 50%); + color: hsl(0, 0%, 96%); +} +.grammalecte_paragraph_actions .green:hover { + background-color: hsl(120, 50%, 40%); + color: hsl(0, 0%, 100%); +} +.grammalecte_paragraph_actions .red { + background-color: hsl(0, 30%, 50%); + color: hsl(0, 0%, 96%); +} +.grammalecte_paragraph_actions .red:hover { + background-color: hsl(0, 50%, 40%); + color: hsl(0, 0%, 100%); +} +.grammalecte_paragraph_actions .orange { + background-color: hsl(30, 50%, 50%); + color: hsl(30, 0%, 96%); +} +.grammalecte_paragraph_actions .orange:hover { + background-color: hsl(30, 70%, 40%); + color: hsl(30, 0%, 100%); +} +.grammalecte_paragraph_actions .bold { + font-weight: bold; +} + + + +/* + TOOLTIP +*/ +#grammalecte_tooltip_arrow { + position: absolute; + display: none; +} + +#grammalecte_tooltip { + position: absolute; + margin: 0; + 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; + border: 3px solid hsl(210, 50%, 30%); + color: hsla(210, 10%, 20%, 1); + z-index: 1000; +} + +#grammalecte_tooltip_message_block { + margin: 0; + padding: 5px 10px 10px 10px; + background-color: hsl(210, 50%, 30%); + color: hsl(210, 50%, 96%); +} +#grammalecte_tooltip_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; +} +#grammalecte_tooltip_message { + font-size: 15px; + margin: 0 0 5px 0; +} +a#grammalecte_tooltip_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#grammalecte_tooltip_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#grammalecte_tooltip_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#grammalecte_tooltip_url:hover { + background-color: hsla(210, 50%, 60%, 1); + color: hsla(0, 0%, 100%, 1); + text-shadow: 0 0 3px hsl(210, 30%, 60%); +} +#grammalecte_tooltip_sugg_title { + padding: 0 10px; + background-color: hsl(210, 10%, 90%); + color: hsl(210, 50%, 30%); + font-size: 10px; + font-weight: bold; +} +#grammalecte_tooltip_sugg_block { + padding: 10px; + background-color: hsl(210, 10%, 96%); + border-radius: 0 0 2px 2px; + line-height: 20px; +} +#grammalecte_tooltip_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; +} +#grammalecte_tooltip_sugg_block a.sugg:hover { + background-color: hsla(180, 70%, 45%, 1); + color: hsla(0, 0%, 100%, 1); +} + + +/* + ERRORS +*/ + +.grammalecte_paragraph .error { + /* default color */ + background-color: hsl(240, 10%, 50%); + color: hsl(240, 0%, 96%); +} +.grammalecte_paragraph .error:hover { + background-color: hsl(240, 10%, 40%); + color: hsl(240, 0%, 100%); +} + +/* elems */ +.grammalecte_paragraph .WORD { + background-color: hsl(0, 50%, 50%); + color: hsl(0, 0%, 96%); + /*text-decoration: underline wavy hsl(0, 50%, 50%);*/ +} +.grammalecte_paragraph .WORD:hover { + background-color: hsl(0, 60%, 40%); + color: hsl(0, 0%, 100%); +} + +/* elems */ +.grammalecte_paragraph .typo, +.grammalecte_paragraph .esp, +.grammalecte_paragraph .nbsp, +.grammalecte_paragraph .eif, +.grammalecte_paragraph .maj, +.grammalecte_paragraph .virg, +.grammalecte_paragraph .tu, +.grammalecte_paragraph .num, +.grammalecte_paragraph .unit, +.grammalecte_paragraph .nf, +.grammalecte_paragraph .liga, +.grammalecte_paragraph .mapos, +.grammalecte_paragraph .chim { + background-color: hsl(30, 70%, 50%); + color: hsl(30, 10%, 96%); + /*text-decoration: underline wavy hsl(30, 70%, 50%);*/ +} + +.grammalecte_paragraph .typo:hover, +.grammalecte_paragraph .esp:hover, +.grammalecte_paragraph .nbsp:hover, +.grammalecte_paragraph .eif:hover, +.grammalecte_paragraph .maj:hover, +.grammalecte_paragraph .virg:hover, +.grammalecte_paragraph .tu:hover, +.grammalecte_paragraph .num:hover, +.grammalecte_paragraph .unit:hover, +.grammalecte_paragraph .nf:hover, +.grammalecte_paragraph .liga:hover, +.grammalecte_paragraph .mapos:hover, +.grammalecte_paragraph .chim:hover { + background-color: hsl(30, 80%, 45%); + color: hsl(30, 10%, 96%); +} + +/* elems */ +.grammalecte_paragraph .apos { + background-color: hsl(40, 90%, 50%); + color: hsl(40, 10%, 96%); + /*text-decoration: underline wavy hsl(40, 70%, 45%);*/ +} +.grammalecte_paragraph .apos:hover { + background-color: hsl(40, 100%, 45%); + color: hsl(40, 10%, 96%); +} + +/* elems */ +.grammalecte_paragraph .gn, +.grammalecte_paragraph .sgpl { + background-color: hsl(210, 50%, 50%); + color: hsl(210, 10%, 96%); + /*text-decoration: underline wavy hsl(210, 50%, 50%);*/ +} +.grammalecte_paragraph .gn:hover, +.grammalecte_paragraph .sgpl:hover { + background-color: hsl(210, 60%, 40%); + color: hsl(210, 10%, 96%); +} + +/* elems */ +.grammalecte_paragraph .conj, +.grammalecte_paragraph .infi, +.grammalecte_paragraph .imp, +.grammalecte_paragraph .inte, +.grammalecte_paragraph .ppas, +.grammalecte_paragraph .vmode { + background-color: hsl(300, 30%, 40%); + color: hsl(300, 10%, 96%); + /*text-decoration: underline wavy hsl(300, 40%, 40%);*/ +} + +.grammalecte_paragraph .conj:hover, +.grammalecte_paragraph .infi:hover, +.grammalecte_paragraph .imp:hover, +.grammalecte_paragraph .inte:hover, +.grammalecte_paragraph .ppas:hover, +.grammalecte_paragraph .vmode:hover { + background-color: hsl(300, 40%, 30%); + color: hsl(300, 10%, 96%); +} + +/* elems */ +.grammalecte_paragraph .conf, +.grammalecte_paragraph .ocr { + background-color: hsl(270, 40%, 30%); + color: hsl(270, 10%, 96%); + /*text-decoration: underline wavy hsl(270, 40%, 30%);*/ +} + +.grammalecte_paragraph .conf:hover, +.grammalecte_paragraph .ocr:hover { + background-color: hsl(270, 50%, 20%); + color: hsl(270, 10%, 96%); +} + +/* elems */ +.grammalecte_paragraph .bs, +.grammalecte_paragraph .pleo, +.grammalecte_paragraph .neg, +.grammalecte_paragraph .redon1, +.grammalecte_paragraph .redon2, +.grammalecte_paragraph .mc, +.grammalecte_paragraph .date, +.grammalecte_paragraph .notype { + background-color: hsl(180, 50%, 40%); + color: hsl(180, 10%, 96%); + /*text-decoration: underline wavy hsl(180, 50%, 40%);*/ +} + +.grammalecte_paragraph .bs:hover, +.grammalecte_paragraph .pleo:hover, +.grammalecte_paragraph .neg:hover, +.grammalecte_paragraph .redon1:hover, +.grammalecte_paragraph .redon2:hover, +.grammalecte_paragraph .mc:hover, +.grammalecte_paragraph .date:hover, +.grammalecte_paragraph .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,449 @@ +// JavaScript + +"use strict"; + +function onGrammalecteGCPanelClick (xEvent) { + try { + let xElem = xEvent.target; + if (xElem.id) { + if (xElem.id.startsWith("grammalecte_sugg")) { + oGrammalecte.oGCPanel.applySuggestion(xElem.id); + } else if (xElem.id === "grammalecte_tooltip_ignore") { + oGrammalecte.oGCPanel.ignoreError(xElem.id); + } else if (xElem.id.startsWith("grammalecte_check")) { + oGrammalecte.oGCPanel.recheckParagraph(parseInt(xElem.dataset.para_num)); + } else if (xElem.id.startsWith("grammalecte_hide")) { + xElem.parentNode.parentNode.style.display = "none"; + } else if (xElem.tagName === "U" && xElem.id.startsWith("grammalecte_err") + && xElem.className !== "corrected" && xElem.className !== "ignored") { + oGrammalecte.oGCPanel.oTooltip.show(xElem.id); + } else if (xElem.id === "grammalecte_tooltip_url") { + oGrammalecte.oGCPanel.openURL(xElem.getAttribute("href")); + } else { + oGrammalecte.oGCPanel.oTooltip.hide(); + } + } else if (xElem.tagName === "A") { + oGrammalecte.oGCPanel.openURL(xElem.getAttribute("href")); + } else { + oGrammalecte.oGCPanel.oTooltip.hide(); + } + } + catch (e) { + showError(e); + } +} + + +class GrammalecteGrammarChecker extends GrammalectePanel { + /* + KEYS for identifiers: + grammalecte_paragraph{Id} : [paragraph number] + grammalecte_check{Id} : [paragraph number] + grammalecte_hide{Id} : [paragraph number] + grammalecte_error{Id} : [paragraph number]-[error_number] + grammalecte_sugg{Id} : [paragraph number]-[error_number]--[suggestion_number] + */ + + constructor (...args) { + super(...args); + this.aIgnoredErrors = new Set(); + this.xContentNode = createNode("div", {id: "grammalecte_gc_panel_content"}); + this.xParagraphList = createNode("div", {id: "grammalecte_paragraph_list"}); + this.xContentNode.appendChild(this.xParagraphList); + this.xPanelContent.addEventListener("click", onGrammalecteGCPanelClick, false); + this.oTooltip = new GrammalecteTooltip(this.xContentNode); + this.xPanelContent.appendChild(this.xContentNode); + this.oTAC = new GrammalecteTextAreaControl(); + } + + start (xTextArea=null) { + this.clear(); + if (xTextArea) { + this.oTAC.setTextArea(xTextArea); + } + } + + clear () { + while (this.xParagraphList.firstChild) { + this.xParagraphList.removeChild(this.xParagraphList.firstChild); + } + this.aIgnoredErrors.clear(); + } + + hide () { + this.xPanelNode.style.display = "none"; + this.oTAC.clear(); + } + + addParagraphResult (oResult) { + try { + if (oResult && oResult.sParagraph.trim() !== "" && (oResult.aGrammErr.length > 0 || oResult.aSpellErr.length > 0)) { + let xNodeDiv = createNode("div", {className: "grammalecte_paragraph_block"}); + // actions + let xActionsBar = createNode("div", {className: "grammalecte_paragraph_actions"}); + xActionsBar.appendChild(createNode("div", {id: "grammalecte_check" + oResult.iParaNum, className: "button green", textContent: "Réanalyser"}, {para_num: oResult.iParaNum})); + xActionsBar.appendChild(createNode("div", {id: "grammalecte_hide" + oResult.iParaNum, className: "button red bold", textContent: "×"})); + // paragraph + let xParagraph = createNode("p", {id: "grammalecte_paragraph"+oResult.iParaNum, lang: "fr", contentEditable: "true"}, {para_num: oResult.iParaNum}); + xParagraph.setAttribute("spellcheck", "false"); // doesn’t seem possible to use “spellcheck” as a common attribute. + xParagraph.className = (oResult.aGrammErr.length || oResult.aSpellErr.length) ? "grammalecte_paragraph softred" : "grammalecte_paragraph"; + xParagraph.addEventListener("keyup", function (xEvent) { + this.oTAC.setParagraph(parseInt(xEvent.target.dataset.para_num), this.purgeText(xEvent.target.textContent)); + this.oTAC.write(); + }.bind(this) + , true); + this._tagParagraph(xParagraph, oResult.sParagraph, oResult.iParaNum, oResult.aGrammErr, oResult.aSpellErr); + // creation + xNodeDiv.appendChild(xActionsBar); + xNodeDiv.appendChild(xParagraph); + this.xParagraphList.appendChild(xNodeDiv); + } + } + catch (e) { + showError(e); + } + } + + recheckParagraph (iParaNum) { + let sParagraphId = "grammalecte_paragraph" + iParaNum; + let xParagraph = document.getElementById(sParagraphId); + this.blockParagraph(xParagraph); + let sText = this.purgeText(xParagraph.textContent); + xPort.postMessage({ + sCommand: "parseAndSpellcheck1", + dParam: {sText: sText, sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {sParagraphId: sParagraphId} + }); + this.oTAC.setParagraph(iParaNum, sText); + this.oTAC.write(); + } + + refreshParagraph (sParagraphId, oResult) { + try { + let xParagraph = document.getElementById(sParagraphId); + xParagraph.className = (oResult.aGrammErr.length || oResult.aSpellErr.length) ? "grammalecte_paragraph softred" : "grammalecte_paragraph"; + xParagraph.textContent = ""; + this._tagParagraph(xParagraph, oResult.sParagraph, sParagraphId.slice(21), oResult.aGrammErr, oResult.aSpellErr); + this.freeParagraph(xParagraph); + } + catch (e) { + showError(e); + } + } + + _tagParagraph (xParagraph, sParagraph, iParaNum, 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'] = iParaNum + "-" + nErr.toString(); // error identifier + oErr['sIgnoredKey'] = iParaNum + ":" + nStart.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 (sUnderlined, oErr) { + let xNodeErr = document.createElement("u"); + xNodeErr.id = "grammalecte_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; + } + + blockParagraph (xParagraph) { + xParagraph.contentEditable = "false"; + document.getElementById("grammalecte_check"+xParagraph.dataset.para_num).textContent = "Analyse…"; + } + + freeParagraph (xParagraph) { + xParagraph.contentEditable = "true"; + document.getElementById("grammalecte_check"+xParagraph.dataset.para_num).textContent = "Réanalyser"; + } + + applySuggestion (sNodeSuggId) { // sugg + try { + console.log(sNodeSuggId); + let sErrorId = document.getElementById(sNodeSuggId).dataset.error_id; + //let sParaNum = sErrorId.slice(0, sErrorId.indexOf("-")); + console.log("grammalecte_err"+sErrorId); + let xNodeErr = document.getElementById("grammalecte_err" + sErrorId); + xNodeErr.textContent = document.getElementById(sNodeSuggId).textContent; + xNodeErr.className = "corrected"; + xNodeErr.removeAttribute("style"); + this.oTooltip.hide(); + this.recheckParagraph(parseInt(sErrorId.slice(0, sErrorId.indexOf("-")))); + } + catch (e) { + showError(e); + } + } + + ignoreError (sIgnoreButtonId) { // ignore + try { + console.log(sIgnoreButtonId); + let sErrorId = document.getElementById(sIgnoreButtonId).dataset.error_id; + console.log("grammalecte_err"+sErrorId); + let xNodeErr = document.getElementById("grammalecte_err" + sErrorId); + this.aIgnoredErrors.add(xNodeErr.dataset.ignored_key); + xNodeErr.className = "ignored"; + this.oTooltip.hide(); + } + catch (e) { + showError(e); + } + } + + purgeText (sText) { + return sText.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"); + } + + addSummary () { + // todo + } + + addMessage (sMessage) { + let xNode = createNode("div", {className: "grammalecte_gc_panel_message", textContent: sMessage}); + this.xParagraphList.appendChild(xNode); + } + + copyToClipboard () { + this.startWaitIcon(); + try { + let xClipboardButton = document.getElementById("grammalecte_clipboard_button"); + xClipboardButton.textContent = "copie en cours…"; + let sText = ""; + for (let xNode of document.getElementById("grammalecte_paragraph_list").getElementsByClassName("grammalecte_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); + } + this.stopWaitIcon(); + } +} + + + +class GrammalecteTooltip { + + constructor (xContentNode) { + this.xTooltip = createNode("div", {id: "grammalecte_tooltip"}); + this.xTooltipArrow = createNode("img", { + id: "grammalecte_tooltip_arrow", + src: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAECAYAAACzzX7wAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwQAADsEBuJFr7QAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xNkRpr/UAAAAlSURBVBhXY/j//z8cq/kW/wdhZDEMSXRFWCVhGKwAmwQCF/8HAGUkScGH4cM8AAAAAElFTkSuQmCC", + alt: "^" + }); + this.xTooltipSuggBlock = createNode("div", {id: "grammalecte_tooltip_sugg_block"}); + let xMessageBlock = createNode("div", {id: "grammalecte_tooltip_message_block"}); + xMessageBlock.appendChild(createNode("p", {id: "grammalecte_tooltip_rule_id"})); + xMessageBlock.appendChild(createNode("p", {id: "grammalecte_tooltip_message", textContent: "Erreur."})); + xMessageBlock.appendChild(createNode("a", {id: "grammalecte_tooltip_ignore", href: "#", onclick: "return false;", textContent: "Ignorer"})); + xMessageBlock.appendChild(createNode("a", {id: "grammalecte_tooltip_url", href: "#", onclick: "return false;", textContent: "Voulez-vous en savoir plus ?…"})); + this.xTooltip.appendChild(xMessageBlock); + this.xTooltip.appendChild(createNode("div", {id: "grammalecte_tooltip_sugg_title", textContent: "SUGGESTIONS :"})); + this.xTooltip.appendChild(this.xTooltipSuggBlock); + xContentNode.appendChild(this.xTooltip); + xContentNode.appendChild(this.xTooltipArrow); + } + + show (sNodeErrorId) { // err + try { + let xNodeErr = document.getElementById(sNodeErrorId); + let nLimit = 500 - 330; // paragraph width - tooltip width + this.xTooltipArrow.style.top = (xNodeErr.offsetTop + 16) + "px"; + this.xTooltipArrow.style.left = (xNodeErr.offsetLeft + Math.floor((xNodeErr.offsetWidth / 2))-4) + "px"; // 4 is half the width of the arrow. + this.xTooltip.style.top = (xNodeErr.offsetTop + 20) + "px"; + this.xTooltip.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("grammalecte_tooltip_message").textContent = xNodeErr.dataset.gc_message.slice(0, n); + document.getElementById("grammalecte_tooltip_rule_id").textContent = "Règle : " + xNodeErr.dataset.gc_message.slice(n+2); + document.getElementById("grammalecte_tooltip_rule_id").style.display = "block"; + } else { + document.getElementById("grammalecte_tooltip_message").textContent = xNodeErr.dataset.gc_message; + document.getElementById("grammalecte_tooltip_rule_id").style.display = "none"; + } + if (xNodeErr.dataset.gc_url != "") { + document.getElementById("grammalecte_tooltip_url").style.display = "inline"; + document.getElementById("grammalecte_tooltip_url").setAttribute("href", xNodeErr.dataset.gc_url); + } else { + document.getElementById("grammalecte_tooltip_url").style.display = "none"; + } + document.getElementById("grammalecte_tooltip_ignore").dataset.error_id = xNodeErr.dataset.error_id; + let iSugg = 0; + let xGCSugg = document.getElementById("grammalecte_tooltip_sugg_block"); + xGCSugg.textContent = ""; + if (xNodeErr.dataset.suggestions.length > 0) { + for (let sSugg of xNodeErr.dataset.suggestions.split("|")) { + xGCSugg.appendChild(this._createSuggestion(xNodeErr.dataset.error_id, iSugg, sSugg)); + xGCSugg.appendChild(document.createTextNode(" ")); + iSugg += 1; + } + } else { + xGCSugg.textContent = "Aucune."; + } + } + this.xTooltipArrow.style.display = "block"; + this.xTooltip.style.display = "block"; + if (xNodeErr.dataset.error_type === "spelling") { + // spelling mistake + document.getElementById("grammalecte_tooltip_message").textContent = "Mot inconnu du dictionnaire."; + document.getElementById("grammalecte_tooltip_ignore").dataset.error_id = xNodeErr.dataset.error_id; + while (this.xTooltipSuggBlock.firstChild) { + this.xTooltipSuggBlock.removeChild(this.xTooltipSuggBlock.firstChild); + } + //console.log("getSuggFor: " + xNodeErr.textContent.trim() + " // error_id: " + xNodeErr.dataset.error_id); + //self.port.emit("getSuggestionsForTo", xNodeErr.textContent.trim(), xNodeErr.dataset.error_id); + this.setSpellSuggestionsFor(xNodeErr.textContent.trim(), "", xNodeErr.dataset.error_id); + } + } + catch (e) { + showError(e); + } + } + + setTooltipColor () { + // todo + } + + hide () { + this.xTooltipArrow.style.display = "none"; + this.xTooltip.style.display = "none"; + } + + _createSuggestion (sErrId, iSugg, sSugg) { + let xNodeSugg = document.createElement("a"); + xNodeSugg.id = "grammalecte_sugg" + sErrId + "--" + iSugg.toString(); + xNodeSugg.className = "sugg"; + xNodeSugg.dataset.error_id = sErrId; + xNodeSugg.textContent = sSugg; + return xNodeSugg; + } + + setSpellSuggestionsFor (sWord, sSuggestions, sErrId) { + // spell checking suggestions + try { + // console.log("setSuggestionsFor: " + sWord + " > " + sSuggestions + " // " + sErrId); + let xSuggBlock = document.getElementById("grammalecte_tooltip_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); + } + } +} + + +class GrammalecteTextAreaControl { + + constructor () { + this._xTextArea = null; + this._dParagraph = new Map(); + } + + setTextArea (xNode) { + this.clear(); + this._xTextArea = xNode; + this._xTextArea.disabled = true; + this._loadText(); + } + + clear () { + if (this._xTextArea !== null) { + this._xTextArea.disabled = false; + this._xTextArea = null; + } + this._dParagraph.clear(); + } + + setParagraph (iParagraph, sText) { + if (this._xTextArea !== null) { + this._dParagraph.set(iParagraph, sText); + } + } + + _loadText () { + let sText = this._xTextArea.value; + let i = 0; + let iStart = 0; + let iEnd = 0; + sText = sText.replace("\r\n", "\n").replace("\r", "\n"); + while ((iEnd = sText.indexOf("\n", iStart)) !== -1) { + this._dParagraph.set(i, sText.slice(iStart, iEnd)); + i++; + iStart = iEnd+1; + } + this._dParagraph.set(i, sText.slice(iStart)); + console.log("Paragraphs number: " + (i+1)); + } + + write () { + if (this._xTextArea !== null) { + let sText = ""; + this._dParagraph.forEach(function (val, key) { + sText += val + "\n"; + }); + this._xTextArea.value = sText.slice(0,-1); + } + } +} 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,69 @@ +// JavaScript + +"use strict"; + +class GrammalecteLexicographer extends GrammalectePanel { + + constructor (...args) { + super(...args); + this._nCount = 0; + this._xContentNode = createNode("div", {id: "grammalecte_lxg_panel_content"}); + this.xPanelContent.appendChild(this._xContentNode); + } + + clear () { + this._nCount = 0; + while (this._xContentNode.firstChild) { + this._xContentNode.removeChild(this._xContentNode.firstChild); + } + } + + addSeparator (sText) { + if (this._xContentNode.textContent !== "") { + this._xContentNode.appendChild(createNode("div", {className: "grammalecte_lxg_separator", textContent: sText})); + } + } + + addMessage (sClass, sText) { + this._xContentNode.appendChild(createNode("div", {className: sClass, textContent: sText})); + } + + addListOfTokens (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 (oToken) { + let xTokenNode = createNode("div", {className: "grammalecte_token"}); + 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 (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/panels_content.css Index: gc_lang/fr/webext/content_scripts/panels_content.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/panels_content.css @@ -0,0 +1,147 @@ +/* + CSS + Content panels for Grammalecte +*/ + + +/* + Wrapper +*/ +.grammalecte_wrapper { + padding: 5px; + border-radius: 3px; + background-color: hsl(210, 40%, 96%); + border: 1px solid hsl(210, 40%, 92%); + box-shadow: 0 0 2px hsla(210, 40%, 0%, .5); + font-family: "Trebuchet MS", "Liberation Sans", sans-serif; + color: hsl(210, 40%, 50%); +} + +.grammalecte_wrapper_button { + display: inline-block; + padding: 0 5px; + margin-left: 5px; + background-color: hsl(210, 60%, 80%); + border-radius: 2px; + border: 1px solid hsl(210, 60%, 70%); + color: hsl(210, 40%, 40%); + cursor: pointer; +} +.grammalecte_wrapper_button:hover { + background-color: hsl(210, 80%, 70%); + border: 1px solid hsl(210, 80%, 60%); + box-shadow: 0 0 1px 1px hsl(210, 80%, 80%); + color: hsl(210, 80%, 30%); +} +.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% - 55px); /* panel height - title_bar */ + overflow: auto; +} + + +/* + CSS Spinner + Double bounce + http://tobiasahlin.com/spinkit/ +*/ +.grammalecte_spinner { + visibility: hidden; + width: 40px; + height: 40px; + position: absolute; + top: 2px; + right: 150px; +} +.grammalecte_spinner .bounce1, +.grammalecte_spinner .bounce2 { + width: 100%; + height: 100%; + border-radius: 50%; + background-color: hsl(0, 50%, 75%); + opacity: 0.6; + position: absolute; + top: 0; + left: 0; + animation: grammalecte-sk-bounce 2.0s infinite ease-in-out; +} +.grammalecte_spinner .bounce2 { + animation-delay: -1.0s; +} + +@keyframes grammalecte-sk-bounce { + 0%, 100% { + transform: scale(0.0); + } 50% { + transform: scale(1.0); + } +} ADDED gc_lang/fr/webext/content_scripts/panels_content.js Index: gc_lang/fr/webext/content_scripts/panels_content.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/content_scripts/panels_content.js @@ -0,0 +1,168 @@ +// JavaScript +// Panel creator + +"use strict"; + +console.log("[Content script] Panel creator"); + + +class GrammalectePanel { + + constructor (sId, sTitle, nWidth, nHeight, bFlexible=true) { + this.sId = sId; + this.sContentId = sId+"_content"; + this.nWidth = nWidth; + this.nHeight = nHeight; + this.bFlexible = bFlexible; + this.xPanelContent = createNode("div", {className: "grammalecte_panel_content"}); + this.xWaitIcon = this._createWaitIcon(); + 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: sTitle})); + xBar.appendChild(xTitle); + xPanel.appendChild(xBar); + xPanel.appendChild(this.xPanelContent); + return xPanel; + } + catch (e) { + showError(e); + } + } + + _createButtons () { + let xButtonLine = createNode("div", {className: "grammalecte_panel_commands"}); + xButtonLine.appendChild(this.xWaitIcon); + 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; + } + + _createWaitIcon () { + let xWaitIcon = createNode("div", {className: "grammalecte_spinner"}); + xWaitIcon.appendChild(createNode("div", {className: "bounce1"})); + xWaitIcon.appendChild(createNode("div", {className: "bounce2"})); + return xWaitIcon; + } + + _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; + } + + insertIntoPage () { + document.body.appendChild(this.xPanelNode); + } + + show () { + this.xPanelNode.style.display = "block"; + } + + hide () { + this.xPanelNode.style.display = "none"; + } + + center () { + let nHeight = (this.bFlexible) ? window.innerHeight-100 : this.nHeight; + 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 = (this.bFlexible) ? window.innerHeight-100 : this.nHeight; + this.xPanelNode.style = `top: 50%; left: -2px; width: ${this.nWidth}px; height: ${nHeight}px; margin-top: -${nHeight/2}px;`; + } + + stickToRight () { + let nHeight = (this.bFlexible) ? window.innerHeight-100 : this.nHeight; + this.xPanelNode.style = `top: 50%; right: -2px; width: ${this.nWidth}px; height: ${nHeight}px; margin-top: -${nHeight/2}px;`; + } + + stickToTop () { + let nWidth = (this.bFlexible) ? Math.floor(window.innerWidth/2) : this.nWidth; + let nHeight = (this.bFlexible) ? Math.floor(window.innerHeight*0.45) : this.nHeight; + this.xPanelNode.style = `top: -2px; left: 50%; width: ${nWidth}px; height: ${nHeight}px; margin-left: -${nWidth/2}px;`; + } + + stickToBottom () { + let nWidth = (this.bFlexible) ? Math.floor(window.innerWidth/2) : this.nWidth; + let nHeight = (this.bFlexible) ? Math.floor(window.innerHeight*0.45) : this.nHeight; + this.xPanelNode.style = `bottom: -2px; left: 50%; width: ${nWidth}px; height: ${nHeight}px; margin-left: -${nWidth/2}px;`; + } + + reduce () { + // todo + } + + logInnerHTML () { + // for debugging + console.log(this.xPanelNode.innerHTML); + } + + startWaitIcon () { + this.xWaitIcon.style.visibility = "visible"; + } + + stopWaitIcon () { + this.xWaitIcon.style.visibility = "hidden"; + } + + openURL (sURL) { + // todo + } +} + + +/* + 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,104 @@ +/* + CSS for the Text Formatter +*/ + + +/* + Options +*/ +#grammalecte_tf_options { + display: flex; + padding: 10px; +} +.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 .underline:hover { + background-color: hsl(210, 10%, 86%); + border-radius: 2px; +} +#grammalecte_tf_options .blockopt .option { + margin: 4px 5px 0 6px; + float: left; +} +#grammalecte_tf_options .grammalecte_tf_result { + float: right; + margin: 2px 3px 0 0; + font-size: 13px; +} +#grammalecte_tf_options .indent { + padding-left: 20px; +} +#grammalecte_tf_options .inlineblock { + display: inline-block; + margin-right: 10px; +} +#grammalecte_tf_options .rmg10 { + margin-right: 10px; +} + + + +/* + Actions +*/ +#grammalecte_tf_actions { + /*background-color: hsl(120, 10%, 92%);*/ + display: flex; + justify-content: space-between; + padding: 15px 15px 10px 15px; + border-top: 1px solid hsl(210, 10%, 90%); +} + +.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: 400px; +} +#grammalecte_tf_time_res { + width: 60px; + padding: 5px 10px; +} +#grammalecte_tf_apply { + background-color: hsl(120, 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,553 @@ +// JavaScript +// Text formatter + +"use strict"; + + +class GrammalecteTextFormatter extends GrammalectePanel { + + constructor (...args) { + super(...args); + this.xTFNode = this._createTextFormatter(); + this.xPanelContent.appendChild(this.xTFNode); + this.xTextArea = null; + } + + _createTextFormatter () { + 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 = this._createFieldset("group_ssp", true, "Espaces surnuméraires"); + xSSP.appendChild(this._createSimpleOption("o_start_of_paragraph", true, "En début de paragraphe")); + xSSP.appendChild(this._createSimpleOption("o_end_of_paragraph", true, "En fin de paragraphe")); + xSSP.appendChild(this._createSimpleOption("o_between_words", true, "Entre les mots")); + xSSP.appendChild(this._createSimpleOption("o_before_punctuation", true, "Avant les points (.), les virgules (,)")); + xSSP.appendChild(this._createSimpleOption("o_within_parenthesis", true, "À l’intérieur des parenthèses")); + xSSP.appendChild(this._createSimpleOption("o_within_square_brackets", true, "À l’intérieur des crochets")); + xSSP.appendChild(this._createSimpleOption("o_within_quotation_marks", true, "À l’intérieur des guillemets “ et ”")); + let xSpace = this._createFieldset("group_space", true, "Espaces manquants"); + xSpace.appendChild(this._createSimpleOption("o_add_space_after_punctuation", true, "Après , ; : ? ! . …")); + xSpace.appendChild(this._createSimpleOption("o_add_space_around_hyphens", true, "Autour des tirets d’incise")); + let xNBSP = this._createFieldset("group_nbsp", true, "Espaces insécables"); + xNBSP.appendChild(this._createSimpleOption("o_nbsp_before_punctuation", true, "Avant : ; ? et !")); + xNBSP.appendChild(this._createSimpleOption("o_nbsp_within_quotation_marks", true, "Avec les guillemets « et »")); + xNBSP.appendChild(this._createSimpleOption("o_nbsp_before_symbol", true, "Avant % ‰ € $ £ ¥ ˚C")); + xNBSP.appendChild(this._createSimpleOption("o_nbsp_within_numbers", true, "À l’intérieur des nombres")); + xNBSP.appendChild(this._createSimpleOption("o_nbsp_before_units", true, "Avant les unités de mesure")); + let xDelete = this._createFieldset("group_delete", true, "Suppressions"); + xDelete.appendChild(this._createSimpleOption("o_erase_non_breaking_hyphens", true, "Tirets conditionnels")); + let xColumn2 = createNode("div", {className: "grammalecte_tf_column"}); + let xTypo = this._createFieldset("group_typo", true, "Signes typographiques"); + xTypo.appendChild(this._createSimpleOption("o_ts_apostrophe", true, "Apostrophe (’)")); + xTypo.appendChild(this._createSimpleOption("o_ts_ellipsis", true, "Points de suspension (…)")); + xTypo.appendChild(this._createSimpleOption("o_ts_dash_middle", true, "Tirets d’incise :")); + xTypo.appendChild(this._createRadioBoxHyphens("hyphen1", "o_ts_m_dash_middle", "o_ts_n_dash_middle", false)); + xTypo.appendChild(this._createSimpleOption("o_ts_dash_start", true, "Tirets en début de paragraphe :")); + xTypo.appendChild(this._createRadioBoxHyphens("hyphen2", "o_ts_m_dash_start", "o_ts_n_dash_start", true)); + xTypo.appendChild(this._createSimpleOption("o_ts_quotation_marks", true, "Modifier les guillemets droits (\" et ')")); + xTypo.appendChild(this._createSimpleOption("o_ts_units", true, "Points médians des unités (N·m, Ω·m…)")); + xTypo.appendChild(this._createSimpleOption("o_ts_spell", true, "Ligatures (cœur…) et diacritiques (ça, État…)")); + xTypo.appendChild(this._createRadioBoxLigatures()); + xTypo.appendChild(this._createLigaturesSelection()); + let xMisc = this._createFieldset("group_misc", true, "Divers"); + xMisc.appendChild(this._createOrdinalOptions()); + xMisc.appendChild(this._createSimpleOption("o_etc", true, "Et cætera, etc.")); + xMisc.appendChild(this._createSimpleOption("o_missing_hyphens", true, "Traits d’union manquants")); + xMisc.appendChild(this._createSimpleOption("o_ma_word", true, "Apostrophes manquantes")); + xMisc.appendChild(this._createSingleLetterOptions()); + let xStruct = this._createFieldset("group_struct", false, "Restructuration [!]"); + xStruct.appendChild(this._createSimpleOption("o_remove_hyphens_at_end_of_paragraphs", false, "Enlever césures en fin de ligne/paragraphe [!]")); + xStruct.appendChild(this._createSimpleOption("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"}); + let xDefaultButton = createNode("div", {id: "grammalecte_tf_reset", textContent: "Par défaut", className: "grammalecte_button", style: "background-color: hsl(210, 50%, 50%)"}); + xDefaultButton.addEventListener("click", () => { this.reset(); }); + let xApplyButton = createNode("div", {id: "grammalecte_tf_apply", textContent: "Appliquer", className: "grammalecte_button", style: "background-color: hsl(180, 50%, 50%)"}); + xApplyButton.addEventListener("click", () => { this.saveOptions(); this.apply(); }); + xActions.appendChild(xDefaultButton); + xActions.appendChild(createNode("progress", {id: "grammalecte_tf_progressbar"})); + xActions.appendChild(createNode("span", {id: "grammalecte_tf_time_res", textContent: "…"})); + xActions.appendChild(xApplyButton); + //xActions.appendChild(createNode("div", {id: "grammalecte_infomsg", textContent: "blabla"})); + // create result + xTFNode.appendChild(xOptions); + xTFNode.appendChild(xActions); + } + catch (e) { + showError(e); + } + return xTFNode; + } + + // Common options + _createFieldset (sId, bDefault, sLabel) { + let xFieldset = createNode("fieldset", {id: sId, className: "groupblock"}); + let xLegend = document.createElement("legend"); + let xGroupOption = createNode("input", {type: "checkbox", id: "o_"+sId, className: "option"}, {default: bDefault}); + xGroupOption.addEventListener("click", (xEvent) => { this.switchGroup(xEvent.target.id); }); + xLegend.appendChild(xGroupOption); + xLegend.appendChild(createNode("label", {htmlFor: "o_"+sId, textContent: sLabel})); + xFieldset.appendChild(xLegend); + return xFieldset; + } + + _createSimpleOption (sId, bDefault, sLabel) { + let xLine = createNode("div", {className: "blockopt underline"}); + xLine.appendChild(createNode("input", {type: "checkbox", id: sId, className: "option"}, {default: bDefault})); + xLine.appendChild(createNode("label", {htmlFor: sId, textContent: sLabel, className: "opt_lbl largew"})); + xLine.appendChild(createNode("div", {id: "res_"+sId, className: "grammalecte_tf_result", textContent: "·"})); + return xLine; + } + + // Hyphens + _createRadioBoxHyphens (sName, sIdEmDash, sIdEnDash, bDefaultEmDash) { + let xLine = createNode("div", {className: "blockopt indent"}); + xLine.appendChild(this._createInlineRadioOption(sName, sIdEmDash, "cadratin (—)", bDefaultEmDash)); + xLine.appendChild(this._createInlineRadioOption(sName, sIdEnDash, "demi-cadratin (—)", !bDefaultEmDash)); + return xLine; + } + + // Ligatures + _createRadioBoxLigatures () { + let xLine = createNode("div", {className: "blockopt underline"}); + xLine.appendChild(createNode("div", {id: "res_"+"o_ts_ligature", className: "grammalecte_tf_result", textContent: "·"})); + xLine.appendChild(this._createInlineCheckboxOption("o_ts_ligature", "Ligatures", true)); + xLine.appendChild(this._createInlineRadioOption("liga", "o_ts_ligature_do", "faire", false)); + xLine.appendChild(this._createInlineRadioOption("liga", "o_ts_ligature_undo", "défaire", true)); + return xLine; + } + + _createLigaturesSelection () { + let xLine = createNode("div", {className: "blockopt indent"}); + xLine.appendChild(this._createInlineCheckboxOption("o_ts_ligature_ff", "ff", true)); + xLine.appendChild(this._createInlineCheckboxOption("o_ts_ligature_fi", "fi", true)); + xLine.appendChild(this._createInlineCheckboxOption("o_ts_ligature_ffi", "ffi", true)); + xLine.appendChild(this._createInlineCheckboxOption("o_ts_ligature_fl", "fl", true)); + xLine.appendChild(this._createInlineCheckboxOption("o_ts_ligature_ffl", "ffl", true)); + xLine.appendChild(this._createInlineCheckboxOption("o_ts_ligature_ft", "ft", true)); + xLine.appendChild(this._createInlineCheckboxOption("o_ts_ligature_st", "st", false)); + return xLine; + } + + // Apostrophes + _createSingleLetterOptions () { + let xLine = createNode("div", {className: "blockopt indent"}); + xLine.appendChild(this._createInlineCheckboxOption("o_ma_1letter_lowercase", "lettres isolées (j’ n’ m’ t’ s’ c’ d’ l’)", false)); + xLine.appendChild(this._createInlineCheckboxOption("o_ma_1letter_uppercase", "Maj.", false)); + return xLine; + } + + // Ordinals + _createOrdinalOptions () { + let xLine = createNode("div", {className: "blockopt underline"}); + xLine.appendChild(createNode("div", {id: "res_"+"o_ordinals_no_exponant", className: "grammalecte_tf_result", textContent: "·"})); + xLine.appendChild(this._createInlineCheckboxOption("o_ordinals_no_exponant", "Ordinaux (15e, XXIe…)", true)); + xLine.appendChild(this._createInlineCheckboxOption("o_ordinals_exponant", "e → ᵉ", true)); + return xLine; + } + + + // Inline option block + _createInlineCheckboxOption (sId, sLabel, bDefault) { + let xInlineBlock = createNode("div", {className: "inlineblock"}); + xInlineBlock.appendChild(createNode("input", {type: "checkbox", id: sId, className: "option"}, {default: bDefault})); + xInlineBlock.appendChild(createNode("label", {htmlFor: sId, textContent: sLabel, className: "opt_lbl"})); + return xInlineBlock; + } + + _createInlineRadioOption (sName, sId, sLabel, bDefault) { + let xInlineBlock = createNode("div", {className: "inlineblock"}); + xInlineBlock.appendChild(createNode("input", {type: "radio", id: sId, name: sName, className:"option"}, {default: bDefault})); + xInlineBlock.appendChild(createNode("label", {htmlFor: sId, className: "opt_lbl", textContent: sLabel})); + return xInlineBlock; + } + + + /* + Actions + */ + start (xTextArea) { + this.xTextArea = xTextArea; + let xPromise = browser.storage.local.get("tf_options"); + xPromise.then(this.setOptions.bind(this), this.reset.bind(this)); + } + + switchGroup (sOptName) { + if (document.getElementById(sOptName).checked) { + document.getElementById(sOptName.slice(2)).style.opacity = 1; + } else { + document.getElementById(sOptName.slice(2)).style.opacity = 0.3; + } + this.resetProgressBar(); + } + + reset () { + this.resetProgressBar(); + for (let xNode of document.getElementsByClassName("option")) { + xNode.checked = (xNode.dataset.default === "true"); + if (xNode.id.startsWith("o_group_")) { + this.switchGroup(xNode.id); + } + } + } + + resetProgressBar () { + document.getElementById('grammalecte_tf_progressbar').value = 0; + document.getElementById('grammalecte_tf_time_res').textContent = ""; + } + + setOptions (oOptions) { + if (oOptions.hasOwnProperty("tf_options")) { + oOptions = oOptions.tf_options; + } + for (let xNode of document.getElementsByClassName("option")) { + //console.log(xNode.id + " > " + oOptions.hasOwnProperty(xNode.id) + ": " + oOptions[xNode.id] + " [" + xNode.dataset.default + "]"); + xNode.checked = (oOptions.hasOwnProperty(xNode.id)) ? oOptions[xNode.id] : (xNode.dataset.default === "true"); + if (document.getElementById("res_"+xNode.id) !== null) { + document.getElementById("res_"+xNode.id).textContent = ""; + } + if (xNode.id.startsWith("o_group_")) { + this.switchGroup(xNode.id); + } + } + } + + saveOptions () { + let oOptions = {}; + for (let xNode of document.getElementsByClassName("option")) { + oOptions[xNode.id] = xNode.checked; + console.log(xNode.id + ": " + xNode.checked); + } + browser.storage.local.set({"tf_options": oOptions}); + } + + apply () { + try { + const t0 = Date.now(); + //window.setCursor("wait"); // change pointer + this.resetProgressBar(); + let sText = this.xTextArea.value; + document.getElementById('grammalecte_tf_progressbar').max = 7; + let n1 = 0, n2 = 0, n3 = 0, n4 = 0, n5 = 0, n6 = 0, n7 = 0; + + // Restructuration + if (document.getElementById("o_group_struct").checked) { + if (document.getElementById("o_remove_hyphens_at_end_of_paragraphs").checked) { + [sText, n1] = this.removeHyphenAtEndOfParagraphs(sText); + document.getElementById('res_o_remove_hyphens_at_end_of_paragraphs').textContent = n1; + } + if (document.getElementById("o_merge_contiguous_paragraphs").checked) { + [sText, n1] = this.mergeContiguousParagraphs(sText); + document.getElementById('res_o_merge_contiguous_paragraphs').textContent = n1; + } + document.getElementById("o_group_struct").checked = false; + this.switchGroup("o_group_struct"); + } + document.getElementById('grammalecte_tf_progressbar').value = 1; + + // espaces surnuméraires + if (document.getElementById("o_group_ssp").checked) { + if (document.getElementById("o_end_of_paragraph").checked) { + [sText, n1] = this.formatText(sText, "end_of_paragraph"); + document.getElementById('res_o_end_of_paragraph').textContent = n1; + } + if (document.getElementById("o_between_words").checked) { + [sText, n1] = this.formatText(sText, "between_words"); + document.getElementById('res_o_between_words').textContent = n1; + } + if (document.getElementById("o_start_of_paragraph").checked) { + [sText, n1] = this.formatText(sText, "start_of_paragraph"); + document.getElementById('res_o_start_of_paragraph').textContent = n1; + } + if (document.getElementById("o_before_punctuation").checked) { + [sText, n1] = this.formatText(sText, "before_punctuation"); + document.getElementById('res_o_before_punctuation').textContent = n1; + } + if (document.getElementById("o_within_parenthesis").checked) { + [sText, n1] = this.formatText(sText, "within_parenthesis"); + document.getElementById('res_o_within_parenthesis').textContent = n1; + } + if (document.getElementById("o_within_square_brackets").checked) { + [sText, n1] = this.formatText(sText, "within_square_brackets"); + document.getElementById('res_o_within_square_brackets').textContent = n1; + } + if (document.getElementById("o_within_quotation_marks").checked) { + [sText, n1] = this.formatText(sText, "within_quotation_marks"); + document.getElementById('res_o_within_quotation_marks').textContent = n1; + } + document.getElementById("o_group_ssp").checked = false; + this.switchGroup("o_group_ssp"); + } + document.getElementById('grammalecte_tf_progressbar').value = 2; + + // espaces typographiques + if (document.getElementById("o_group_nbsp").checked) { + if (document.getElementById("o_nbsp_before_punctuation").checked) { + [sText, n1] = this.formatText(sText, "nbsp_before_punctuation"); + [sText, n2] = this.formatText(sText, "nbsp_repair"); + document.getElementById('res_o_nbsp_before_punctuation').textContent = n1 - n2; + } + if (document.getElementById("o_nbsp_within_quotation_marks").checked) { + [sText, n1] = this.formatText(sText, "nbsp_within_quotation_marks"); + document.getElementById('res_o_nbsp_within_quotation_marks').textContent = n1; + } + if (document.getElementById("o_nbsp_before_symbol").checked) { + [sText, n1] = this.formatText(sText, "nbsp_before_symbol"); + document.getElementById('res_o_nbsp_before_symbol').textContent = n1; + } + if (document.getElementById("o_nbsp_within_numbers").checked) { + [sText, n1] = this.formatText(sText, "nbsp_within_numbers"); + document.getElementById('res_o_nbsp_within_numbers').textContent = n1; + } + if (document.getElementById("o_nbsp_before_units").checked) { + [sText, n1] = this.formatText(sText, "nbsp_before_units"); + document.getElementById('res_o_nbsp_before_units').textContent = n1; + } + document.getElementById("o_group_nbsp").checked = false; + this.switchGroup("o_group_nbsp"); + } + document.getElementById('grammalecte_tf_progressbar').value = 3; + + // espaces manquants + if (document.getElementById("o_group_typo").checked) { + if (document.getElementById("o_ts_units").checked) { + [sText, n1] = this.formatText(sText, "ts_units"); + document.getElementById('res_o_ts_units').textContent = n1; + } + } + if (document.getElementById("o_group_space").checked) { + if (document.getElementById("o_add_space_after_punctuation").checked) { + [sText, n1] = this.formatText(sText, "add_space_after_punctuation"); + [sText, n2] = this.formatText(sText, "add_space_repair"); + document.getElementById('res_o_add_space_after_punctuation').textContent = n1 - n2; + } + if (document.getElementById("o_add_space_around_hyphens").checked) { + [sText, n1] = this.formatText(sText, "add_space_around_hyphens"); + document.getElementById('res_o_add_space_around_hyphens').textContent = n1; + } + document.getElementById("o_group_space").checked = false; + this.switchGroup("o_group_space"); + } + document.getElementById('grammalecte_tf_progressbar').value = 4; + + // suppression + if (document.getElementById("o_group_delete").checked) { + if (document.getElementById("o_erase_non_breaking_hyphens").checked) { + [sText, n1] = this.formatText(sText, "erase_non_breaking_hyphens"); + document.getElementById('res_o_erase_non_breaking_hyphens').textContent = n1; + } + document.getElementById("o_group_delete").checked = false; + this.switchGroup("o_group_delete"); + } + document.getElementById('grammalecte_tf_progressbar').value = 5; + + // signes typographiques + if (document.getElementById("o_group_typo").checked) { + if (document.getElementById("o_ts_apostrophe").checked) { + [sText, n1] = this.formatText(sText, "ts_apostrophe"); + document.getElementById('res_o_ts_apostrophe').textContent = n1; + } + if (document.getElementById("o_ts_ellipsis").checked) { + [sText, n1] = this.formatText(sText, "ts_ellipsis"); + document.getElementById('res_o_ts_ellipsis').textContent = n1; + } + if (document.getElementById("o_ts_dash_start").checked) { + if (document.getElementById("o_ts_m_dash_start").checked) { + [sText, n1] = this.formatText(sText, "ts_m_dash_start"); + } else { + [sText, n1] = this.formatText(sText, "ts_n_dash_start"); + } + document.getElementById('res_o_ts_dash_start').textContent = n1; + } + if (document.getElementById("o_ts_dash_middle").checked) { + if (document.getElementById("o_ts_m_dash_middle").checked) { + [sText, n1] = this.formatText(sText, "ts_m_dash_middle"); + } else { + [sText, n1] = this.formatText(sText, "ts_n_dash_middle"); + } + document.getElementById('res_o_ts_dash_middle').textContent = n1; + } + if (document.getElementById("o_ts_quotation_marks").checked) { + [sText, n1] = this.formatText(sText, "ts_quotation_marks"); + document.getElementById('res_o_ts_quotation_marks').textContent = n1; + } + if (document.getElementById("o_ts_spell").checked) { + [sText, n1] = this.formatText(sText, "ts_spell"); + document.getElementById('res_o_ts_spell').textContent = n1; + } + if (document.getElementById("o_ts_ligature").checked) { + // ligatures typographiques : fi, fl, ff, ffi, ffl, ft, st + if (document.getElementById("o_ts_ligature_do").checked) { + if (document.getElementById("o_ts_ligature_ffi").checked) { + [sText, n1] = this.formatText(sText, "ts_ligature_ffi_do"); + } + if (document.getElementById("o_ts_ligature_ffl").checked) { + [sText, n2] = this.formatText(sText, "ts_ligature_ffl_do"); + } + if (document.getElementById("o_ts_ligature_fi").checked) { + [sText, n3] = this.formatText(sText, "ts_ligature_fi_do"); + } + if (document.getElementById("o_ts_ligature_fl").checked) { + [sText, n4] = this.formatText(sText, "ts_ligature_fl_do"); + } + if (document.getElementById("o_ts_ligature_ff").checked) { + [sText, n5] = this.formatText(sText, "ts_ligature_ff_do"); + } + if (document.getElementById("o_ts_ligature_ft").checked) { + [sText, n6] = this.formatText(sText, "ts_ligature_ft_do"); + } + if (document.getElementById("o_ts_ligature_st").checked) { + [sText, n7] = this.formatText(sText, "ts_ligature_st_do"); + } + } + if (document.getElementById("o_ts_ligature_undo").checked) { + if (document.getElementById("o_ts_ligature_ffi").checked) { + [sText, n1] = this.formatText(sText, "ts_ligature_ffi_undo"); + } + if (document.getElementById("o_ts_ligature_ffl").checked) { + [sText, n2] = this.formatText(sText, "ts_ligature_ffl_undo"); + } + if (document.getElementById("o_ts_ligature_fi").checked) { + [sText, n3] = this.formatText(sText, "ts_ligature_fi_undo"); + } + if (document.getElementById("o_ts_ligature_fl").checked) { + [sText, n4] = this.formatText(sText, "ts_ligature_fl_undo"); + } + if (document.getElementById("o_ts_ligature_ff").checked) { + [sText, n5] = this.formatText(sText, "ts_ligature_ff_undo"); + } + if (document.getElementById("o_ts_ligature_ft").checked) { + [sText, n6] = this.formatText(sText, "ts_ligature_ft_undo"); + } + if (document.getElementById("o_ts_ligature_st").checked) { + [sText, n7] = this.formatText(sText, "ts_ligature_st_undo"); + } + } + document.getElementById('res_o_ts_ligature').textContent = n1 + n2 + n3 + n4 + n5 + n6 + n7; + } + document.getElementById("o_group_typo").checked = false; + this.switchGroup("o_group_typo"); + } + document.getElementById('grammalecte_tf_progressbar').value = 6; + + // divers + if (document.getElementById("o_group_misc").checked) { + if (document.getElementById("o_ordinals_no_exponant").checked) { + if (document.getElementById("o_ordinals_exponant").checked) { + [sText, n1] = this.formatText(sText, "ordinals_exponant"); + } else { + [sText, n1] = this.formatText(sText, "ordinals_no_exponant"); + } + document.getElementById('res_o_ordinals_no_exponant').textContent = n1; + } + if (document.getElementById("o_etc").checked) { + [sText, n1] = this.formatText(sText, "etc"); + document.getElementById('res_o_etc').textContent = n1; + } + if (document.getElementById("o_missing_hyphens").checked) { + [sText, n1] = this.formatText(sText, "missing_hyphens"); + document.getElementById('res_o_missing_hyphens').textContent = n1; + } + if (document.getElementById("o_ma_word").checked) { + [sText, n1] = this.formatText(sText, "ma_word"); + if (document.getElementById("o_ma_1letter_lowercase").checked) { + [sText, n1] = this.formatText(sText, "ma_1letter_lowercase"); + if (document.getElementById("o_ma_1letter_uppercase").checked) { + [sText, n1] = this.formatText(sText, "ma_1letter_uppercase"); + } + } + document.getElementById('res_o_ma_word').textContent = n1; + } + document.getElementById("o_group_misc").checked = false; + this.switchGroup("o_group_misc"); + } + document.getElementById('grammalecte_tf_progressbar').value = document.getElementById('grammalecte_tf_progressbar').max; + // end of processing + + //window.setCursor("auto"); // restore pointer + + const t1 = Date.now(); + document.getElementById('grammalecte_tf_time_res').textContent = this.getTimeRes((t1-t0)/1000); + this.xTextArea.value = sText; + } + catch (e) { + showError(e); + } + } + + formatText (sText, sOptName) { + let nCount = 0; + try { + if (!oReplTable.hasOwnProperty(sOptName)) { + console.log("# Error. TF: there is no option “" + sOptName+ "”."); + return [sText, nCount]; + } + for (let [zRgx, sRep] of oReplTable[sOptName]) { + nCount += (sText.match(zRgx) || []).length; + sText = sText.replace(zRgx, sRep); + } + } + catch (e) { + showError(e); + } + return [sText, nCount]; + } + + removeHyphenAtEndOfParagraphs (sText) { + let nCount = (sText.match(/-[  ]*\n/gm) || []).length; + sText = sText.replace(/-[  ]*\n/gm, ""); + return [sText, nCount]; + } + + mergeContiguousParagraphs (sText) { + let nCount = 0; + sText = sText.replace(/^[  ]+$/gm, ""); // clear empty paragraphs + let s = ""; + for (let sParagraph of this.getParagraph(sText)) { + if (sParagraph === "") { + s += "\n"; + } else { + s += sParagraph + " "; + nCount += 1; + } + } + s = s.replace(/ +/gm, " ").replace(/ $/gm, ""); + return [s, nCount]; + } + + * getParagraph (sText) { + // generator: returns paragraphs of text + let iStart = 0; + let iEnd = 0; + while ((iEnd = sText.indexOf("\n", iStart)) !== -1) { + yield sText.slice(iStart, iEnd); + iStart = iEnd + 1; + } + yield sText.slice(iStart); + } + + getTimeRes (n) { + // returns duration in seconds as string + if (n < 10) { + return n.toFixed(3).toString() + " s"; + } + if (n < 100) { + return n.toFixed(2).toString() + " s"; + } + if (n < 1000) { + return n.toFixed(1).toString() + " s"; + } + return n.toFixed().toString() + " s"; + } +} ADDED gc_lang/fr/webext/gce_sharedworker.js Index: gc_lang/fr/webext/gce_sharedworker.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/gce_sharedworker.js @@ -0,0 +1,271 @@ +/* + WORKER: + https://developer.mozilla.org/en-US/docs/Web/API/Worker + https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope + + + JavaScript sucks. + No module available in WebExtension at the moment! :( + No require, no import/export. + + In Worker, we have importScripts() which imports everything in this scope. + + In order to use the same base of code with XUL-addon for Thunderbird and SDK-addon for Firefox, + all modules have been “objectified”. And while they are still imported via “require” + in the previous extensions, they are loaded as background scripts in WebExtension sharing + the same memory space… + + When JavaScript become a modern language, “deobjectify” the modules… + + ATM, import/export are not available by default: + — Chrome 60 – behind the Experimental Web Platform flag in chrome:flags. + — Firefox 54 – behind the dom.moduleScripts.enabled setting in about:config. + — Edge 15 – behind the Experimental JavaScript Features setting in about:flags. + + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export +*/ + +"use strict"; + + +console.log("GC Engine SharedWorker [start]"); +//console.log(self); + +importScripts("grammalecte/helpers.js"); +importScripts("grammalecte/str_transform.js"); +importScripts("grammalecte/ibdawg.js"); +importScripts("grammalecte/text.js"); +importScripts("grammalecte/tokenizer.js"); +importScripts("grammalecte/fr/conj.js"); +importScripts("grammalecte/fr/mfsp.js"); +importScripts("grammalecte/fr/phonet.js"); +importScripts("grammalecte/fr/cregex.js"); +importScripts("grammalecte/fr/gc_options.js"); +importScripts("grammalecte/fr/gc_rules.js"); +importScripts("grammalecte/fr/gc_engine.js"); +importScripts("grammalecte/fr/lexicographe.js"); +importScripts("grammalecte/tests.js"); +/* + Warning. + Initialization can’t be completed at startup of the worker, + for we need the path of the extension to load data stored in JSON files. + This path is retrieved in background.js and passed with the event “init”. +*/ + + +/* + Message Event Object + https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent +*/ + +let xPort = null; + +function createResponse (sActionDone, result, dInfo, bError=false) { + return { + "sActionDone": sActionDone, + "result": result, // can be of any type + "dInfo": dInfo, + "bError": bError + }; +} + +function createErrorResult (e, sDescr="no description") { + return { + "sType": "error", + "sDescription": sDescr, + "sMessage": e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message + }; +} + +function showData (e) { + for (let sParam in e) { + console.log(sParam); + console.log(e[sParam]); + } +} + +onconnect = function (e) { + console.log("[Sharedworker] START CONNECTION"); + xPort = e.ports[0]; + + xPort.onmessage = function (e) { + console.log("[Sharedworker] ONMESSAGE"); + let {sCommand, dParam, dInfo} = e.data; + console.log(e.data); + switch (sCommand) { + case "init": + init(dParam.sExtensionPath, dParam.sOptions, dParam.sContext, dInfo); + break; + case "parse": + parse(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); + break; + case "parseAndSpellcheck": + parseAndSpellcheck(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); + break; + case "getOptions": + getOptions(dInfo); + break; + case "getDefaultOptions": + getDefaultOptions(dInfo); + break; + case "setOptions": + setOptions(dParam.sOptions, dInfo); + break; + case "setOption": + setOption(dParam.sOptName, dParam.bValue, dInfo); + break; + case "resetOptions": + resetOptions(dInfo); + break; + case "textToTest": + textToTest(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); + break; + case "fullTests": + fullTests('{"nbsp":true, "esp":true, "unit":true, "num":true}', dInfo); + break; + case "getListOfTokens": + getListOfTokens(dParam.sText, dInfo); + break; + default: + console.log("Unknown command: " + sCommand); + showData(e.data); + } + } + //xPort.start(); +} + +let bInitDone = false; + +let oDict = null; +let oTokenizer = null; +let oLxg = null; +let oTest = null; + + +function init (sExtensionPath, sGCOptions="", sContext="JavaScript", dInfo={}) { + try { + if (!bInitDone) { + console.log("[Sharedworker] Loading… Extension path: " + sExtensionPath); + conj.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/conj_data.json")); + phonet.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/phonet_data.json")); + mfsp.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/mfsp_data.json")); + console.log("[Sharedworker] Modules have been initialized…"); + gc_engine.load(sContext, sExtensionPath+"grammalecte/_dictionaries"); + oDict = gc_engine.getDictionary(); + oTest = new TestGrammarChecking(gc_engine, sExtensionPath+"/grammalecte/fr/tests_data.json"); + oLxg = new Lexicographe(oDict); + if (sGCOptions !== "") { + gc_engine.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); + } + oTokenizer = new Tokenizer("fr"); + //tests(); + bInitDone = true; + } else { + console.log("[Sharedworker] Already initialized…") + } + // we always retrieve options from the gc_engine, for setOptions filters obsolete options + xPort.postMessage(createResponse("init", gc_engine.getOptions().gl_toString(), dInfo)); + } + catch (e) { + helpers.logerror(e); + xPort.postMessage(createResponse("init", createErrorResult(e, "init failed"), dInfo, true)); + } +} + +function parse (sText, sCountry, bDebug, bContext, dInfo={}) { + let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); + xPort.postMessage({sActionDone: "parse", result: aGrammErr, dInfo: dInfo}); +} + +function parseAndSpellcheck (sText, sCountry, bDebug, bContext, dInfo={}) { + let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); + let aSpellErr = oTokenizer.getSpellingErrors(sText, oDict); + xPort.postMessage(createResponse("parseAndSpellcheck", {aGrammErr: aGrammErr, aSpellErr: aSpellErr}, dInfo)); +} + +function getOptions (dInfo={}) { + xPort.postMessage(createResponse("getOptions", gc_engine.getOptions().gl_toString(), dInfo)); +} + +function getDefaultOptions (dInfo={}) { + xPort.postMessage(createResponse("getDefaultOptions", gc_engine.getDefaultOptions().gl_toString(), dInfo)); +} + +function setOptions (sGCOptions, dInfo={}) { + gc_engine.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); + xPort.postMessage(createResponse("setOptions", gc_engine.getOptions().gl_toString(), dInfo)); +} + +function setOption (sOptName, bValue, dInfo={}) { + gc_engine.setOptions(new Map([ [sOptName, bValue] ])); + xPort.postMessage(createResponse("setOption", gc_engine.getOptions().gl_toString(), dInfo)); +} + +function resetOptions (dInfo={}) { + gc_engine.resetOptions(); + xPort.postMessage(createResponse("resetOptions", gc_engine.getOptions().gl_toString(), dInfo)); +} + +function tests () { + console.log(conj.getConj("devenir", ":E", ":2s")); + console.log(mfsp.getMasForm("emmerdeuse", true)); + console.log(mfsp.getMasForm("pointilleuse", false)); + console.log(phonet.getSimil("est")); + let aRes = gc_engine.parse("Je suit..."); + for (let oErr of aRes) { + console.log(text.getReadableError(oErr)); + } +} + +function textToTest (sText, sCountry, bDebug, bContext, dInfo={}) { + if (!gc_engine || !oDict) { + xPort.postMessage(createResponse("textToTest", "# Grammar checker or dictionary not loaded.", dInfo)); + return; + } + let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); + let sMsg = ""; + for (let oErr of aGrammErr) { + sMsg += text.getReadableError(oErr) + "\n"; + } + xPort.postMessage(createResponse("textToTest", sMsg, dInfo)); +} + +function fullTests (sGCOptions="", dInfo={}) { + if (!gc_engine || !oDict) { + xPort.postMessage(createResponse("fullTests", "# Grammar checker or dictionary not loaded.", dInfo)); + return; + } + let dMemoOptions = gc_engine.getOptions(); + if (sGCOptions) { + gc_engine.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); + } + let sMsg = ""; + for (let sRes of oTest.testParse()) { + sMsg += sRes + "\n"; + console.log(sRes); + } + gc_engine.setOptions(dMemoOptions); + xPort.postMessage(createResponse("fullTests", sMsg, dInfo)); +} + + +// Lexicographer + +function getListOfTokens (sText, dInfo={}) { + try { + let aElem = []; + let aRes = null; + for (let oToken of oTokenizer.genTokens(sText)) { + aRes = oLxg.getInfoForToken(oToken); + if (aRes) { + aElem.push(aRes); + } + } + xPort.postMessage(createResponse("getListOfTokens", aElem, dInfo)); + } + catch (e) { + helpers.logerror(e); + xPort.postMessage(createResponse("getListOfTokens", createErrorResult(e, "no tokens"), dInfo, true)); + } +} Index: gc_lang/fr/webext/gce_worker.js ================================================================== --- gc_lang/fr/webext/gce_worker.js +++ gc_lang/fr/webext/gce_worker.js @@ -1,139 +1,297 @@ +/* + WORKER: + https://developer.mozilla.org/en-US/docs/Web/API/Worker + https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope + + + JavaScript sucks. + No module available in WebExtension at the moment! :( + No require, no import/export. + + In Worker, we have importScripts() which imports everything in this scope. + + In order to use the same base of code with XUL-addon for Thunderbird and SDK-addon for Firefox, + all modules have been “objectified”. And while they are still imported via “require” + in the previous extensions, they are loaded as background scripts in WebExtension sharing + the same memory space… + + When JavaScript become a modern language, “deobjectify” the modules… + + ATM, import/export are not available by default: + — Chrome 60 – behind the Experimental Web Platform flag in chrome:flags. + — Firefox 54 – behind the dom.moduleScripts.enabled setting in about:config. + — Edge 15 – behind the Experimental JavaScript Features setting in about:flags. + + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export +*/ + +"use strict"; + + +//console.log("[Worker] GC Engine Worker [start]"); +//console.log(self); + +importScripts("grammalecte/helpers.js"); +importScripts("grammalecte/str_transform.js"); +importScripts("grammalecte/ibdawg.js"); +importScripts("grammalecte/text.js"); +importScripts("grammalecte/tokenizer.js"); +importScripts("grammalecte/fr/conj.js"); +importScripts("grammalecte/fr/mfsp.js"); +importScripts("grammalecte/fr/phonet.js"); +importScripts("grammalecte/fr/cregex.js"); +importScripts("grammalecte/fr/gc_options.js"); +importScripts("grammalecte/fr/gc_rules.js"); +importScripts("grammalecte/fr/gc_engine.js"); +importScripts("grammalecte/fr/lexicographe.js"); +importScripts("grammalecte/tests.js"); +/* + Warning. + Initialization can’t be completed at startup of the worker, + for we need the path of the extension to load data stored in JSON files. + This path is retrieved in background.js and passed with the event “init”. +*/ + + +function createResponse (sActionDone, result, dInfo, bEnd, bError=false) { + return { + "sActionDone": sActionDone, + "result": result, // can be of any type + "dInfo": dInfo, + "bEnd": bEnd, + "bError": bError + }; +} + +function createErrorResult (e, sDescr="no description") { + return { + "sType": "error", + "sDescription": sDescr, + "sMessage": e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message + }; +} + +function showData (e) { + for (let sParam in e) { + console.log(sParam); + console.log(e[sParam]); + } +} + + +/* + Message Event Object + https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent +*/ +onmessage = function (e) { + let {sCommand, dParam, dInfo} = e.data; + switch (sCommand) { + case "init": + init(dParam.sExtensionPath, dParam.sOptions, dParam.sContext, dInfo); + break; + case "parse": + parse(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); + break; + case "parseAndSpellcheck": + parseAndSpellcheck(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); + break; + case "parseAndSpellcheck1": + parseAndSpellcheck1(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); + case "getOptions": + getOptions(dInfo); + break; + case "getDefaultOptions": + getDefaultOptions(dInfo); + break; + case "setOptions": + setOptions(dParam.sOptions, dInfo); + break; + case "setOption": + setOption(dParam.sOptName, dParam.bValue, dInfo); + break; + case "resetOptions": + resetOptions(dInfo); + break; + case "textToTest": + textToTest(dParam.sText, dParam.sCountry, dParam.bDebug, dParam.bContext, dInfo); + break; + case "fullTests": + fullTests('{"nbsp":true, "esp":true, "unit":true, "num":true}', dInfo); + break; + case "getListOfTokens": + getListOfTokens(dParam.sText, dInfo); + break; + default: + console.log("Unknown command: " + sCommand); + showData(e.data); + } +} + + + +let bInitDone = false; + +let oDict = null; +let oTokenizer = null; +let oLxg = null; +let oTest = null; /* -try { - console.log("BEFORE"); - //var myhelpers = require('./grammalecte/helpers.js'); - require(['./grammalecte/helpers.js'], function (foo) { - console.log("LOADING"); - echo("MODULE LOADED2"); - }); - console.log("AFTER"); -} -catch (e) { - console.log("\n" + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); - console.error(e); -}*/ - -echo("VA TE FAIRE FOUTRE"); - - - -let gce = null; // module: grammar checker engine -let text = null; -let tkz = null; // module: tokenizer -let lxg = null; // module: lexicographer -let helpers = null; - -let oTokenizer = null; -let oDict = null; -let oLxg = null; - -function loadGrammarChecker (sGCOptions="", sContext="JavaScript") { - if (gce === null) { - try { - gce = require("resource://grammalecte/fr/gc_engine.js"); - helpers = require("resource://grammalecte/helpers.js"); - text = require("resource://grammalecte/text.js"); - tkz = require("resource://grammalecte/tokenizer.js"); - lxg = require("resource://grammalecte/fr/lexicographe.js"); - oTokenizer = new tkz.Tokenizer("fr"); - helpers.setLogOutput(console.log); - gce.load(sContext); - oDict = gce.getDictionary(); - oLxg = new lxg.Lexicographe(oDict); - if (sGCOptions !== "") { - gce.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); - } - // we always retrieve options from the gce, for setOptions filters obsolete options - return gce.getOptions()._toString(); - } - catch (e) { - console.log("# Error: " + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); - } - } -} - -function parse (sText, sLang, bDebug, bContext) { - let aGrammErr = gce.parse(sText, sLang, bDebug, bContext); - return JSON.stringify(aGrammErr); -} - -function parseAndSpellcheck (sText, sLang, bDebug, bContext) { - let aGrammErr = gce.parse(sText, sLang, bDebug, bContext); - let aSpellErr = oTokenizer.getSpellingErrors(sText, oDict); - return JSON.stringify({ aGrammErr: aGrammErr, aSpellErr: aSpellErr }); -} - -function getOptions () { - return gce.getOptions()._toString(); -} - -function getDefaultOptions () { - return gce.getDefaultOptions()._toString(); -} - -function setOptions (sGCOptions) { - gce.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); - return gce.getOptions()._toString(); -} - -function setOption (sOptName, bValue) { - gce.setOptions(new Map([ [sOptName, bValue] ])); - return gce.getOptions()._toString(); -} - -function resetOptions () { - gce.resetOptions(); - return gce.getOptions()._toString(); -} - -function fullTests (sGCOptions="") { - if (!gce || !oDict) { - return "# Error: grammar checker or dictionary not loaded." - } - let dMemoOptions = gce.getOptions(); - if (sGCOptions) { - gce.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); - } - let tests = require("resource://grammalecte/tests.js"); - let oTest = new tests.TestGrammarChecking(gce); - let sAllRes = ""; - for (let sRes of oTest.testParse()) { - dump(sRes+"\n"); - sAllRes += sRes+"\n"; - } - gce.setOptions(dMemoOptions); - return sAllRes; -} + Technical note: + This worker don’t work as a PromiseWorker (which returns a promise), so when we send request + to this worker, we can’t wait the return of the answer just after the request made. + The answer is received by the background in another function (onmessage). + That’s why the full text to analyze is send in one block, but analyse is returned paragraph + by paragraph. +*/ + +function init (sExtensionPath, dOptions=null, sContext="JavaScript", dInfo={}) { + try { + if (!bInitDone) { + //console.log("[Worker] Loading… Extension path: " + sExtensionPath); + conj.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/conj_data.json")); + phonet.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/phonet_data.json")); + mfsp.init(helpers.loadFile(sExtensionPath + "/grammalecte/fr/mfsp_data.json")); + //console.log("[Worker] Modules have been initialized…"); + gc_engine.load(sContext, sExtensionPath+"grammalecte/_dictionaries"); + oDict = gc_engine.getDictionary(); + oTest = new TestGrammarChecking(gc_engine, sExtensionPath+"/grammalecte/fr/tests_data.json"); + oLxg = new Lexicographe(oDict); + if (dOptions !== null) { + gc_engine.setOptions(dOptions); + } + oTokenizer = new Tokenizer("fr"); + //tests(); + bInitDone = true; + } else { + console.log("[Worker] Already initialized…") + } + // we always retrieve options from the gc_engine, for setOptions filters obsolete options + postMessage(createResponse("init", gc_engine.getOptions(), dInfo, true)); + } + catch (e) { + helpers.logerror(e); + postMessage(createResponse("init", createErrorResult(e, "init failed"), dInfo, true, true)); + } +} + + +function parse (sText, sCountry, bDebug, bContext, dInfo={}) { + for (let sParagraph of text.getParagraph(sText)) { + let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, bContext); + postMessage(createResponse("parse", aGrammErr, dInfo, false)); + } + postMessage(createResponse("parse", null, dInfo, true)); +} + +function parseAndSpellcheck (sText, sCountry, bDebug, bContext, dInfo={}) { + let i = 0; + for (let sParagraph of text.getParagraph(sText)) { + let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, bContext); + let aSpellErr = oTokenizer.getSpellingErrors(sParagraph, oDict); + postMessage(createResponse("parseAndSpellcheck", {sParagraph: sParagraph, iParaNum: i, aGrammErr: aGrammErr, aSpellErr: aSpellErr}, dInfo, false)); + i += 1; + } + postMessage(createResponse("parseAndSpellcheck", null, dInfo, true)); +} + +function parseAndSpellcheck1 (sParagraph, sCountry, bDebug, bContext, dInfo={}) { + let aGrammErr = gc_engine.parse(sParagraph, sCountry, bDebug, bContext); + let aSpellErr = oTokenizer.getSpellingErrors(sParagraph, oDict); + postMessage(createResponse("parseAndSpellcheck1", {sParagraph: sParagraph, aGrammErr: aGrammErr, aSpellErr: aSpellErr}, dInfo, true)); +} + +function getOptions (dInfo={}) { + postMessage(createResponse("getOptions", gc_engine.getOptions(), dInfo, true)); +} + +function getDefaultOptions (dInfo={}) { + postMessage(createResponse("getDefaultOptions", gc_engine.getDefaultOptions(), dInfo, true)); +} + +function setOptions (dOptions, dInfo={}) { + gc_engine.setOptions(dOptions); + postMessage(createResponse("setOptions", gc_engine.getOptions(), dInfo, true)); +} + +function setOption (sOptName, bValue, dInfo={}) { + console.log(sOptName+": "+bValue); + gc_engine.setOptions(new Map([ [sOptName, bValue] ])); + postMessage(createResponse("setOption", gc_engine.getOptions(), dInfo, true)); +} + +function resetOptions (dInfo={}) { + gc_engine.resetOptions(); + postMessage(createResponse("resetOptions", gc_engine.getOptions(), dInfo, true)); +} + +function tests () { + console.log(conj.getConj("devenir", ":E", ":2s")); + console.log(mfsp.getMasForm("emmerdeuse", true)); + console.log(mfsp.getMasForm("pointilleuse", false)); + console.log(phonet.getSimil("est")); + let aRes = gc_engine.parse("Je suit..."); + for (let oErr of aRes) { + console.log(text.getReadableError(oErr)); + } +} + +function textToTest (sText, sCountry, bDebug, bContext, dInfo={}) { + if (!gc_engine || !oDict) { + postMessage(createResponse("textToTest", "# Grammar checker or dictionary not loaded.", dInfo, true)); + return; + } + let aGrammErr = gc_engine.parse(sText, sCountry, bDebug, bContext); + let sMsg = ""; + for (let oErr of aGrammErr) { + sMsg += text.getReadableError(oErr) + "\n"; + } + postMessage(createResponse("textToTest", sMsg, dInfo, true)); +} + +function fullTests (sGCOptions="", dInfo={}) { + if (!gc_engine || !oDict) { + postMessage(createResponse("fullTests", "# Grammar checker or dictionary not loaded.", dInfo, true)); + return; + } + let dMemoOptions = gc_engine.getOptions(); + if (sGCOptions) { + gc_engine.setOptions(helpers.objectToMap(JSON.parse(sGCOptions))); + } + let sMsg = ""; + for (let sRes of oTest.testParse()) { + sMsg += sRes + "\n"; + console.log(sRes); + } + gc_engine.setOptions(dMemoOptions); + postMessage(createResponse("fullTests", sMsg, dInfo, true)); +} + // Lexicographer -function getListOfElements (sText) { +function getListOfTokens (sText, dInfo={}) { try { - let aElem = []; - let aRes = null; - for (let oToken of oTokenizer.genTokens(sText)) { - aRes = oLxg.getInfoForToken(oToken); - if (aRes) { - aElem.push(aRes); + for (let sParagraph of text.getParagraph(sText)) { + if (sParagraph.trim() !== "") { + let aElem = []; + let aRes = null; + for (let oToken of oTokenizer.genTokens(sParagraph)) { + aRes = oLxg.getInfoForToken(oToken); + if (aRes) { + aElem.push(aRes); + } + } + postMessage(createResponse("getListOfTokens", aElem, dInfo, false)); } } - return JSON.stringify(aElem); + postMessage(createResponse("getListOfTokens", null, dInfo, true)); } catch (e) { helpers.logerror(e); - } - return JSON.stringify([]); -} - - -function handleMessage (oRequest, xSender, sendResponse) { - console.log(`[background] received: ${oRequest.content}`); - sendResponse({response: "response from background script"}); -} - -browser.runtime.onMessage.addListener(handleMessage); - - + postMessage(createResponse("getListOfTokens", createErrorResult(e, "no tokens"), dInfo, true, true)); + } +} ADDED gc_lang/fr/webext/img/Algoo_logo.png Index: gc_lang/fr/webext/img/Algoo_logo.png ================================================================== --- /dev/null +++ gc_lang/fr/webext/img/Algoo_logo.png cannot compute difference between binary files ADDED gc_lang/fr/webext/img/LaMouette.png Index: gc_lang/fr/webext/img/LaMouette.png ================================================================== --- /dev/null +++ gc_lang/fr/webext/img/LaMouette.png cannot compute difference between binary files ADDED gc_lang/fr/webext/img/lines.png Index: gc_lang/fr/webext/img/lines.png ================================================================== --- /dev/null +++ gc_lang/fr/webext/img/lines.png cannot compute difference between binary files ADDED gc_lang/fr/webext/img/logo-80.png Index: gc_lang/fr/webext/img/logo-80.png ================================================================== --- /dev/null +++ gc_lang/fr/webext/img/logo-80.png cannot compute difference between binary files ADDED gc_lang/fr/webext/img/logo120_text.png Index: gc_lang/fr/webext/img/logo120_text.png ================================================================== --- /dev/null +++ gc_lang/fr/webext/img/logo120_text.png cannot compute difference between binary files Index: gc_lang/fr/webext/manifest.json ================================================================== --- gc_lang/fr/webext/manifest.json +++ gc_lang/fr/webext/manifest.json @@ -5,39 +5,88 @@ "version": "0.6", "applications": { "gecko": { "id": "French-GC@grammalecte.net", - "strict_min_version": "54.0" + "strict_min_version": "55.0" } }, "author": "Olivier R.", "homepage_url": "https://grammalecte.net", - "offline_enabled": true, "description": "Correcteur grammatical pour le français.", "icons": { "16": "img/logo-16.png", "32": "img/logo-32.png", "48": "img/logo-48.png", "64": "img/logo-64.png", + "80": "img/logo-80.png", "96": "img/logo-96.png" }, "browser_action": { "default_icon": "img/logo-32.png", "default_popup": "panel/main.html", "default_title": "Grammalecte [fr]", "browser_style": false }, "background": { - "scripts": ["require.js", "grammalecte/helpers.js", "gce_worker.js"] + "scripts": [ + "background.js" + ] + }, + "content_scripts": [ + { + "matches": [""], + "css": [ + "content_scripts/panels_content.css", + "content_scripts/tf_content.css", + "content_scripts/gc_content.css", + "content_scripts/lxg_content.css", + "3rd/font-awesome-4.7.0/css/font-awesome.min.css" + ], + "js": [ + "content_scripts/panels_content.js", + "grammalecte/fr/textformatter.js", + "content_scripts/tf_content.js", + "content_scripts/gc_content.js", + "content_scripts/lxg_content.js", + "content_scripts/content_modifier.js" + ] + } + ], + "commands": { + "conjugueur_tab": { + "suggested_key": { + "default": "Ctrl+Shift+F6" + }, + "description": "Ouvre le conjugueur dans un onglet" + }, + "conjugueur_window": { + "suggested_key": { + "default": "Ctrl+Shift+F7" + }, + "description": "Ouvre le conjugueur dans une fenêtre" + } }, "web_accessible_resources": [ - "beasts/frog.jpg", - "beasts/turtle.jpg", - "beasts/snake.jpg" + "grammalecte/_dictionaries/French.json", + "grammalecte/fr/conj_data.json", + "grammalecte/fr/mfsp_data.json", + "grammalecte/fr/phonet_data.json", + "grammalecte/fr/tests_data.json", + "img/logo-16.png" ], "permissions": [ - "activeTab" - ] + "activeTab", + "contextMenus", + "storage" + ], + "chrome_settings_overrides": { + "search_provider": { + "name": "Grammalecte", + "search_url": "https://www.dicollecte.org/dictionary.php?prj=fr&lemma={searchTerms}", + "keyword": "disc", + "favicon_url": "https://www.dicollecte.org/favicon.ico" + } + } } ADDED gc_lang/fr/webext/panel/conjugueur.css Index: gc_lang/fr/webext/panel/conjugueur.css ================================================================== --- /dev/null +++ gc_lang/fr/webext/panel/conjugueur.css @@ -0,0 +1,157 @@ +/* + CSS Document + White + Design par Olivier R. +*/ + +* { margin: 0; padding: 0; } +img { border: none; } + + +/* Generic classes */ + +.fleft { + float: left; +} +.fright { + float: right; +} +.center { + text-align: center; +} +.right { + text-align: right; +} +.left { + text-align: left; +} +.justify { + text-align: justify; +} +.hidden { + display: none; +} + +.clearer { + clear: both; + font-size: 0; + height: 0; +} + +body { + background: #FFFFFF url(../img/lines.png); + font: normal 16px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; + color: #505050; + /*text-rendering: optimizeLegibility;*/ +} + +.mainflow { + width: 600px; + margin: 0 auto 0 auto; +} + +.logo { + position: absolute; + background-color: #FFFFFF; + border: 3px solid #F0F0F0; + border-radius: 96px; + padding: 4px 4px 0 4px; +} + + +/* MAIN ====================================================================== */ + +#main .inbox { + width: 600px; + margin: 20px auto 10px auto; + padding: 10px 30px 30px 30px; + background: #FFFFFF; + border: 2px solid #F0F0F0; + border-radius: 20px; +} + +#main h1 { + margin: 5px 0 2px 0; + color: hsl(210, 50%, 50%); + font: bold 30px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; +} +#main h2 { + margin: 5px 0 2px 0; + color: hsl(0, 50%, 50%); + font: bold 16px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; +} +#main h3 { + margin: 5px 0 2px 0; + color: hsl(210, 50%, 50%); + font: bold 14px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; +} + +#main .container { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: center; + align-items: flex-start; +} + +#main .colonne { + width: 280px; + padding: 0 10px; +} + +.colonne p { + font-size: 12px; +} + +/*.box { + border-left: 2px solid #8BC5EF; + border-bottom: 2px solid #8BC5EF; + border-radius: 5px; + padding: 0 0 2px 10px; +}*/ + +input#verb { + display: inline-block; + width: 230px; + margin-left: 5px; + padding: 5px 10px; + border: 2px solid hsl(0, 0%, 80%); + border-radius: 3px; + height: 24px; + background: transparent; + font: normal 20px Tahoma, "Ubuntu Condensed"; + color: hsl(0, 0%, 30%); +} +input[placeholder]#verb { + color: hsl(0, 0%, 70%); +} + +a#conjugate { + display: inline-block; + padding: 7px 10px; + font-size: 20px; + background-color: hsl(0, 30%, 30%); + color: hsl(0, 30%, 60%); + border-radius: 3px; + text-transform: uppercase; + text-align: center; + text-decoration: none; +} +a#conjugate:hover { + background-color: hsl(0, 60%, 40%); + color: hsl(0, 60%, 70%); + box-shadow: 0 0 2px hsl(0, 60%, 50%); +} + +#options { + margin: 10px 0 0 5px; + font-size: 16px; + text-align: center; +} + +#smallnote { + margin: 10px 0 10px 0; + font-size: 11px; + color: hsl(0, 0%, 60%); + text-align: center; +} ADDED gc_lang/fr/webext/panel/conjugueur.html Index: gc_lang/fr/webext/panel/conjugueur.html ================================================================== --- /dev/null +++ gc_lang/fr/webext/panel/conjugueur.html @@ -0,0 +1,175 @@ + + + + + Grammalecte · Conjugueur + + + + +
+
+ +
+
+ +
+
+ +

+ + Conjuguer +

+ +

 

+

 

+ +

+ + · + · + · + · +

+

+ +
+ + +
+
+
+

Infinitif

+

 

+
+
+

Impératif

+

Présent

+

 

+

 

+

 

+
+
+ +
+
+

Participe présent

+

 

+
+
+

Participes passés

+

 

+

 

+

 

+

 

+
+
+
+ +
+ + +
+
+
+

Indicatif

+
+

Présent

+

 

+

 

+

 

+

 

+

 

+

 

+
+
+

Imparfait

+

 

+

 

+

 

+

 

+

 

+

 

+
+
+

Passé simple

+

 

+

 

+

 

+

 

+

 

+

 

+
+
+

Futur

+

 

+

 

+

 

+

 

+

 

+

 

+
+
+
+ +
+
+

Subjonctif

+
+

Présent

+

 

+

 

+

 

+

 

+

 

+

 

+
+
+

Imparfait

+

 

+

 

+

 

+

 

+

 

+

 

+
+
+
+

Conditionnel

+
+

Présent

+

 

+

 

+

 

+

 

+

 

+

 

+
+
+

 

+

 

+

 

+

 

+

 

+

 

+

 

+
+
+
+
+ +
+ +
+ + +
+ + + + + + + ADDED gc_lang/fr/webext/panel/conjugueur.js Index: gc_lang/fr/webext/panel/conjugueur.js ================================================================== --- /dev/null +++ gc_lang/fr/webext/panel/conjugueur.js @@ -0,0 +1,255 @@ +// JavaScript + +let oVerb = null; + +// button +document.getElementById('conjugate').addEventListener("click", function (event) { + createVerbAndConjugate(document.getElementById('verb').value); +}); + +// text field +document.getElementById('verb').addEventListener("change", function (event) { + createVerbAndConjugate(document.getElementById('verb').value); +}); + +// options +document.getElementById('oneg').addEventListener("click", function (event) { + _displayResults(); +}); +document.getElementById('opro').addEventListener("click", function (event) { + _displayResults(); +}); +document.getElementById('oint').addEventListener("click", function (event) { + _displayResults(); +}); +document.getElementById('ofem').addEventListener("click", function (event) { + _displayResults(); +}); +document.getElementById('otco').addEventListener("click", function (event) { + _displayResults(); +}); + +function createVerbAndConjugate (sVerb) { + try { + document.getElementById('oneg').checked = false; + document.getElementById('opro').checked = false; + document.getElementById('oint').checked = false; + document.getElementById('otco').checked = false; + document.getElementById('ofem').checked = false; + + // request analyzing + sVerb = sVerb.trim().toLowerCase().replace(/’/g, "'").replace(/ +/g, " "); + if (sVerb) { + if (sVerb.startsWith("ne pas ")) { + document.getElementById('oneg').checked = true; + sVerb = sVerb.slice(7); + } + if (sVerb.startsWith("se ")) { + document.getElementById('opro').checked = true; + sVerb = sVerb.slice(3); + } else if (sVerb.startsWith("s'")) { + document.getElementById('opro').checked = true; + sVerb = sVerb.slice(2); + } + if (sVerb.endsWith("?")) { + document.getElementById('oint').checked = true; + sVerb = sVerb.slice(0,-1).trim(); + } + + if (!conj.isVerb(sVerb)) { + document.getElementById('verb').style = "color: #BB4411;"; + } else { + document.getElementById('verb_title').textContent = sVerb; + document.getElementById('verb').style = "color: #999999;"; + document.getElementById('verb').value = ""; + oVerb = new Verb(sVerb); + let sRawInfo = oVerb._sRawInfo; + document.getElementById('info').textContent = oVerb.sInfo; + document.getElementById('opro').textContent = "pronominal"; + if (sRawInfo.endsWith("zz")) { + document.getElementById('opro').checked = false; + document.getElementById('opro').disabled = true; + document.getElementById('opro_lbl').style = "color: #CCC;"; + document.getElementById('otco').checked = false; + document.getElementById('otco').disabled = true; + document.getElementById('otco_lbl').style = "color: #CCC;"; + document.getElementById('smallnote').textContent = "Ce verbe n’a pas encore été vérifié. C’est pourquoi les options “pronominal” et “temps composés” sont désactivées."; + } else { + document.getElementById('smallnote').textContent = "❦"; + if (sRawInfo[5] == "_") { + document.getElementById('opro').checked = false; + document.getElementById('opro').disabled = true; + document.getElementById('opro_lbl').style = "color: #CCC;"; + } else if (["q", "u", "v", "e"].includes(sRawInfo[5])) { + document.getElementById('opro').checked = false; + document.getElementById('opro').disabled = false; + document.getElementById('opro_lbl').style = "color: #000;"; + } else if (sRawInfo[5] == "p" || sRawInfo[5] == "r") { + document.getElementById('opro').checked = true; + document.getElementById('opro').disabled = true; + document.getElementById('opro_lbl').style = "color: #CCC;"; + } else if (sRawInfo[5] == "x") { + document.getElementById('opro').textContent = "cas particuliers"; + document.getElementById('opro').checked = false; + document.getElementById('opro').disabled = true; + document.getElementById('opro_lbl').style = "color: #CCC;"; + } else { + document.getElementById('opro').textContent = "# erreur #"; + document.getElementById('opro').checked = false; + document.getElementById('opro').disabled = true; + document.getElementById('opro_lbl').style = "color: #CCC;"; + } + document.getElementById('otco').disabled = false; + document.getElementById('otco_lbl').style = "color: #000;"; + } + _displayResults(); + } + } + } + catch (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); + } +} + +function _displayResults () { + if (oVerb === null) { + return; + } + try { + let opro = document.getElementById('opro').checked; + let oneg = document.getElementById('oneg').checked; + let otco = document.getElementById('otco').checked; + let oint = document.getElementById('oint').checked; + let ofem = document.getElementById('ofem').checked; + // titles + _setTitles() + // participes passés + document.getElementById('ppas1').textContent = oVerb.participePasse(":Q1") || " "; // something or nbsp + document.getElementById('ppas2').textContent = oVerb.participePasse(":Q2") || " "; + document.getElementById('ppas3').textContent = oVerb.participePasse(":Q3") || " "; + document.getElementById('ppas4').textContent = oVerb.participePasse(":Q4") || " "; + // infinitif + document.getElementById('infi').textContent = oVerb.infinitif(opro, oneg, otco, oint, ofem); + // participe présent + document.getElementById('ppre').textContent = oVerb.participePresent(opro, oneg, otco, oint, ofem) || " "; + // conjugaisons + document.getElementById('ipre1').textContent = oVerb.conjugue(":Ip", ":1s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipre2').textContent = oVerb.conjugue(":Ip", ":2s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipre3').textContent = oVerb.conjugue(":Ip", ":3s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipre4').textContent = oVerb.conjugue(":Ip", ":1p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipre5').textContent = oVerb.conjugue(":Ip", ":2p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipre6').textContent = oVerb.conjugue(":Ip", ":3p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('iimp1').textContent = oVerb.conjugue(":Iq", ":1s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('iimp2').textContent = oVerb.conjugue(":Iq", ":2s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('iimp3').textContent = oVerb.conjugue(":Iq", ":3s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('iimp4').textContent = oVerb.conjugue(":Iq", ":1p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('iimp5').textContent = oVerb.conjugue(":Iq", ":2p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('iimp6').textContent = oVerb.conjugue(":Iq", ":3p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipsi1').textContent = oVerb.conjugue(":Is", ":1s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipsi2').textContent = oVerb.conjugue(":Is", ":2s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipsi3').textContent = oVerb.conjugue(":Is", ":3s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipsi4').textContent = oVerb.conjugue(":Is", ":1p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipsi5').textContent = oVerb.conjugue(":Is", ":2p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ipsi6').textContent = oVerb.conjugue(":Is", ":3p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ifut1').textContent = oVerb.conjugue(":If", ":1s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ifut2').textContent = oVerb.conjugue(":If", ":2s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ifut3').textContent = oVerb.conjugue(":If", ":3s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ifut4').textContent = oVerb.conjugue(":If", ":1p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ifut5').textContent = oVerb.conjugue(":If", ":2p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('ifut6').textContent = oVerb.conjugue(":If", ":3p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('conda1').textContent = oVerb.conjugue(":K", ":1s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('conda2').textContent = oVerb.conjugue(":K", ":2s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('conda3').textContent = oVerb.conjugue(":K", ":3s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('conda4').textContent = oVerb.conjugue(":K", ":1p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('conda5').textContent = oVerb.conjugue(":K", ":2p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('conda6').textContent = oVerb.conjugue(":K", ":3p", opro, oneg, otco, oint, ofem) || " "; + if (!oint) { + document.getElementById('spre1').textContent = oVerb.conjugue(":Sp", ":1s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('spre2').textContent = oVerb.conjugue(":Sp", ":2s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('spre3').textContent = oVerb.conjugue(":Sp", ":3s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('spre4').textContent = oVerb.conjugue(":Sp", ":1p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('spre5').textContent = oVerb.conjugue(":Sp", ":2p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('spre6').textContent = oVerb.conjugue(":Sp", ":3p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('simp1').textContent = oVerb.conjugue(":Sq", ":1s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('simp2').textContent = oVerb.conjugue(":Sq", ":2s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('simp3').textContent = oVerb.conjugue(":Sq", ":3s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('simp4').textContent = oVerb.conjugue(":Sq", ":1p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('simp5').textContent = oVerb.conjugue(":Sq", ":2p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('simp6').textContent = oVerb.conjugue(":Sq", ":3p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('impe1').textContent = oVerb.imperatif(":2s", opro, oneg, otco, ofem) || " "; + document.getElementById('impe2').textContent = oVerb.imperatif(":1p", opro, oneg, otco, ofem) || " "; + document.getElementById('impe3').textContent = oVerb.imperatif(":2p", opro, oneg, otco, ofem) || " "; + } else { + document.getElementById('spre_temps').textContent = " "; + document.getElementById('spre1').textContent = " "; + document.getElementById('spre2').textContent = " "; + document.getElementById('spre3').textContent = " "; + document.getElementById('spre4').textContent = " "; + document.getElementById('spre5').textContent = " "; + document.getElementById('spre6').textContent = " "; + document.getElementById('simp_temps').textContent = " "; + document.getElementById('simp1').textContent = " "; + document.getElementById('simp2').textContent = " "; + document.getElementById('simp3').textContent = " "; + document.getElementById('simp4').textContent = " "; + document.getElementById('simp5').textContent = " "; + document.getElementById('simp6').textContent = " "; + document.getElementById('impe_temps').textContent = " "; + document.getElementById('impe1').textContent = " "; + document.getElementById('impe2').textContent = " "; + document.getElementById('impe3').textContent = " "; + } + if (otco) { + document.getElementById('condb1').textContent = oVerb.conjugue(":Sq", ":1s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('condb2').textContent = oVerb.conjugue(":Sq", ":2s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('condb3').textContent = oVerb.conjugue(":Sq", ":3s", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('condb4').textContent = oVerb.conjugue(":Sq", ":1p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('condb5').textContent = oVerb.conjugue(":Sq", ":2p", opro, oneg, otco, oint, ofem) || " "; + document.getElementById('condb6').textContent = oVerb.conjugue(":Sq", ":3p", opro, oneg, otco, oint, ofem) || " "; + } else { + document.getElementById('condb1').textContent = " "; + document.getElementById('condb2').textContent = " "; + document.getElementById('condb3').textContent = " "; + document.getElementById('condb4').textContent = " "; + document.getElementById('condb5').textContent = " "; + document.getElementById('condb6').textContent = " "; + } + document.getElementById('verb').Text = ""; + } + catch (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); + } +} + +function _setTitles () { + try { + if (!document.getElementById('otco').checked) { + document.getElementById('ipre_temps').textContent = "Présent"; + document.getElementById('ifut_temps').textContent = "Futur"; + document.getElementById('iimp_temps').textContent = "Imparfait"; + document.getElementById('ipsi_temps').textContent = "Passé simple"; + document.getElementById('spre_temps').textContent = "Présent"; + document.getElementById('simp_temps').textContent = "Imparfait"; + document.getElementById('conda_temps').textContent = "Présent"; + document.getElementById('condb_temps').textContent = " "; + document.getElementById('impe_temps').textContent = "Présent"; + } else { + document.getElementById('ipre_temps').textContent = "Passé composé"; + document.getElementById('ifut_temps').textContent = "Futur antérieur"; + document.getElementById('iimp_temps').textContent = "Plus-que-parfait"; + document.getElementById('ipsi_temps').textContent = "Passé antérieur"; + document.getElementById('spre_temps').textContent = "Passé"; + document.getElementById('simp_temps').textContent = "Plus-que-parfait"; + document.getElementById('conda_temps').textContent = "Passé (1ʳᵉ forme)"; + document.getElementById('condb_temps').textContent = "Passé (2ᵉ forme)"; + document.getElementById('impe_temps').textContent = "Passé"; + } + } + catch (e) { + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); + } +} + +createVerbAndConjugate("être"); + +document.getElementById("verb").focus(); Index: gc_lang/fr/webext/panel/main.css ================================================================== --- gc_lang/fr/webext/panel/main.css +++ gc_lang/fr/webext/panel/main.css @@ -51,11 +51,11 @@ /* links */ a:link, a:visited { color: hsl(210, 70%, 40%); - /*text-decoration: none;*/ + text-decoration: none; } a:hover, a:active { text-shadow: 0 0 2px hsl(210, 80%, 60%); } @@ -66,362 +66,209 @@ /* Main classes */ html { box-sizing: border-box; - width: 530px; - height: 880px; + width: 400px; + height: 500px; font-family: "Trebuchet MS", "Liberation Sans", sans-serif; } body { - width: 530px; - height: 880px; + width: 400px; + height: 500px; } +/* + Maximal height of a panel in WebExtention seems to be 500px. + When going over this limit, a scrollbar appears which destructs the + horizontal balance of elements. + --> vertical scrolling is done with overflow in #page. + #page must have the same height than body. +*/ #main { display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: stretch; background-color: hsl(210, 0%, 100%); min-height: 100%; } -#left { - width: 54px; - background-color: hsl(210, 10%, 96%); +#menu { + width: 50px; border-right: solid 1px hsl(210, 0%, 70%); + background-color: hsl(210, 10%, 96%); color: hsl(210, 10%, 96%); } #logo { - padding: 10px; -} -#left li { - padding: 10px 5px; - border-bottom: 1px solid hsl(210, 10%, 90%); - text-align: center; - cursor: pointer; - color: hsl(210, 10%, 50%); - list-style-type: none; -} -#left li:hover { - background-color: hsl(210, 10%, 92%); - -} + padding: 10px; +} +#menu li { + padding: 10px 5px; + border-bottom: 1px solid hsl(210, 10%, 90%); + text-align: center; + cursor: pointer; + color: hsl(210, 10%, 50%); + list-style-type: none; + font-size: 20px; +} +#menu li:hover { + background-color: hsl(210, 10%, 92%); + color: hsl(210, 80%, 50%); + text-shadow: 0px 0px 1px hsl(210, 100%, 70%); +} + #page { background-color: hsl(210, 0%, 100%); + height: 500px; + overflow: auto; } #page h1 { margin: 0 0 10px 0; color: hsl(210, 70%, 70%); font: bold 30px 'Yanone Kaffeesatz', "Liberation Sans Narrow", sans-serif; } -#page p { - margin: 10px 0 5px 0; -} + +/* + Home +*/ #home_page { - display: block; - padding: 20px; -} - -#tf_page { - display: none; - padding: 20px; -} -#gc_page { - display: none; - padding: 20px 20px 30px 20px; -} + display: block; + background-color: hsl(210, 20%, 92%); + min-height: 100%; +} +#home_page #mainlogo { + padding: 5px 20px 20px 20px; + background-color: hsl(210, 20%, 96%); +} +#home_page .notice { + margin-top: 5px; + font-size: 12px; + font-variant-caps: small-caps; + color: hsl(210, 20%, 50%); +} +#home_page #thanks { + padding: 20px 0 0 0; + border-top: 1px solid hsl(210, 20%, 88%); + font-size: 14px; +} + +/* + Help +*/ +#help_page { + display: none; + padding: 20px; + background-color: hsl(0, 50%, 90%); +} + + +/* + Grammar checking options +*/ #gc_options_page { - display: none; - padding: 20px; -} -#sc_options_page { - display: none; - padding: 20px; -} -#lxg_page { - display: none; - padding: 20px; + display: none; + padding: 20px; +} +.opt_subsection { + margin: 5px 0; + padding: 10px; + background-color: hsl(210, 20%, 95%); + border-radius: 3px; +} +.opt_subsection h2 { + color: hsl(210, 30%, 50%); + margin-bottom: 3px; + font-size: 16px; + font-family: "Trebuchet MS", sans-serif; +} +.opt_subsection p { + padding: 2px 3px; + display: flex; + align-items: center; +} +.opt_subsection p:hover { + background-color: hsl(210, 20%, 90%); + border-radius: 2px; +} +.opt_subsection label { + flex: 1; + font-size: 12px; +} +.opt_subsection input { + order: 1 } /* - Conjugueur page -*/ - -#conj_page { - display: none; - padding: 10px; -} - -#conj_page h2 { - margin: 5px 0 2px 0; - color: hsl(210, 50%, 50%); - font: bold 30px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; -} -#conj_page h3 { - margin: 5px 0 2px 0; - color: hsl(0, 50%, 50%); - font: bold 16px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; -} -#conj_page h4 { - margin: 5px 0 2px 0; - color: hsl(210, 50%, 50%); - font: bold 14px Tahoma, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif; -} - -#conj_page .colonne { - float: left; - width: 240px; -} -#conj_page .colsep { - float: left; - width: 20px; -} - -#conj_page .colonne p { - font-size: 12px; -} - - -#conj_page input#verb { - display: inline-block; - width: 230px; - margin-left: 5px; - padding: 5px 10px; - border: 2px solid hsl(0, 0%, 80%); - border-radius: 3px; - height: 24px; - background: transparent; - font: normal 20px Tahoma, "Ubuntu Condensed"; - color: hsl(0, 0%, 30%); -} -#conj_page input[placeholder]#verb { - color: hsl(0, 0%, 70%); -} - -#conj_page a#conjugate { - display: inline-block; - padding: 7px 10px; - font-size: 20px; - background-color: hsl(0, 30%, 30%); - color: hsl(0, 30%, 60%); - border-radius: 3px; - text-transform: uppercase; - text-align: center; - text-decoration: none; -} -#conj_page a#conjugate:hover { - background-color: hsl(0, 60%, 40%); - color: hsl(0, 60%, 70%); - box-shadow: 0 0 2px hsl(0, 60%, 50%); -} - -#conj_options { - margin: 10px 5px 0 5px; - font-size: 16px; - text-align: center; -} - -#conj_smallnote { - float: right; - width: 190px; - margin: 15px 0 0 0; - padding: 0 5px; - font-size: 8.5px; - color: hsl(0, 0%, 60%); - text-align: center; + Spell checking options +*/ +#sc_options_page { + display: none; + padding: 20px; +} +#dictionaries_info { + margin: 10px 0; + padding: 10px; + border-radius: 3px; + background-color: hsl(0, 50%, 40%); + color: hsl(0, 10%, 96%); +} +.dict_section { + padding: 10px; + margin-top: 10px; + border-radius: 5px; + background-color: hsl(210, 20%, 96%); +} +.dict_section label { + font-size: 16px; + line-height: 20px; + color: hsl(210, 20%, 50%); + font-weight: bold; +} +.dict_description { + padding: 0 0 0 20px; + color: hsl(0, 0%, 0%); + font-size: 12px; } /* Test page */ - #test_page { - display: none; + display: none; + background-color: hsl(210, 20%, 96%); + min-height: 100%; } #test_cmd { padding: 15px; - background-color: hsl(0, 0%, 92%); - border-bottom: 1px solid hsl(0, 0%, 86%); + background-color: hsl(210, 20%, 92%); + border-bottom: 1px solid hsl(210, 20%, 90%); } #test_cmd textarea { - width: 100%; - border: 2px solid hsl(0, 0%, 89%); + width: 95%; + padding: 5px; + border: 2px solid hsl(210, 20%, 80%); + background-color: hsl(210, 20%, 98%); border-radius: 3px; resize: vertical; } -#test_results { +#tests_result { padding: 15px; - background-color: hsl(0, 0%, 96%); } #test_page .button { display: inline-block; - padding: 5px 10px; - width: 120px; + padding: 5px; + width: 100px; border-radius: 3px; font-size: 12px; text-align: center; cursor: pointer; } - -/* - Text formatter -*/ - -#tf_options { - -} - -#tf_options fieldset { - margin: 5px 0; - padding: 5px 10px 10px 10px; - background-color: hsl(0, 0%, 92%); - border-radius: 3px; -} - -#tf_options legend { - font-size: 20px; - color: hsla(210, 20%, 50%, .8); - font-weight: bold; -} -#tf_options legend span { - display: none; -} - -#tf_options fieldset h2 { - color: hsl(210, 80%, 40%); -} - -#tf_options fieldset .blockopt { - padding: 2px 3px; - font-size: 12.5px; -} -#tf_options fieldset .underline:hover { - background-color: hsl(180, 10%, 86%); - border-radius: 2px; -} - -#tf_options fieldset .option { - margin: 1px 3px 0 0; - float: left; -} -#tf_options legend .option { - margin: 7px 5px 0 3px; - float: left; -} - -#tf_options fieldset .opt_lbl { - display: inline-block; - color: hsl(0, 0%, 20%); -} - - -#tf_options fieldset .largew { - width: 300px; -} -#tf_options fieldset .reducedw { - width: 200px; -} -#tf_options fieldset .smallw { - width: 90px; -} - -#tf_options fieldset .secondoption { - display: inline-block; -} - -#tf_options fieldset label span { - display: none; -} - -#tf_options .groupblock { - opacity: 0.3; -} - -#tf_options .inlineblock { - display: inline-block; -} -#tf_options .indent { - margin-left: 15px; -} - -#tf_actions { - background-color: hsl(120, 10%, 92%); - padding: 15px; - border-top: 1px solid hsl(120, 20%, 86%); -} - -#tf_options .button { - display: inline-block; - padding: 5px 10px; - width: 100px; - border-radius: 3px; - font-size: 16px; - font-weight: bold; - text-align: center; - cursor: pointer; -} - -#tf_progressbarbox { - display: inline-block; - padding: 10px 20px; -} - - -/* - Other elements -*/ - -#movewindow { - position: fixed; - right: 0; - top: 50; - width: 16px; - margin-top: 60px; - z-index: 100; -} -#movewindow .arrow { - background-color: hsl(180, 60%, 50%); - cursor: pointer; - padding: 1px 3px; - font-size: 10px; - font-weight: bold; - text-align: center; - color: hsl(180, 50%, 90%); -} -#movewindow .arrow:hover { - background-color: hsl(180, 70%, 40%); - cursor: hsl(180, 50%, 96%); -} - -#rightcorner { - position: absolute; - top: 0; - right: 0; -} -a.rightcornerbutton1 { - float: right; - padding: 2px 10px 5px 10px; - border-radius: 0 0 0 3px; - font-size: 18px; - text-decoration: none; -} -a.rightcornerbutton { - float: right; - padding: 2px 10px 5px 10px; - font-size: 18px; - text-decoration: none; -} - /* CSS Spinner Double bounce http://tobiasahlin.com/spinkit/ Index: gc_lang/fr/webext/panel/main.html ================================================================== --- gc_lang/fr/webext/panel/main.html +++ gc_lang/fr/webext/panel/main.html @@ -2,494 +2,125 @@ +
-
-
-

GRAMMALECTE

-
+ + + +
+

Grammalecte remercie

+

+ + La Mouette + +

+

+ + Algoo + +

+

+ + et tous ceux qui l’ont soutenu + +

+
+ -
-

CORRECTEUR GRAMMATICAL

-
-
+
+

AIDE

+
+

+

+

+

+

+

+
+

OPTIONS GRAMMATICALES

-
+
+ ${webextOptionsHTML} +
+

Options par défaut

+

OPTIONS ORTHOGRAPHIQUES

-
- -
-

LEXICOGRAPHE

-
-
+
+

*** non actif ***

+
+

+

Ce dictionnaire propose l’orthographe telle qu’elle est écrite aujourd’hui le plus couramment. C’est le dictionnaire recommandé si le français n’est pas votre langue maternelle ou si vous ne désirez qu’une seule graphie correcte par mot.

+
+
+

+

Il s’agit du dictionnaire “Moderne”, avec des graphies classiques en sus, certaines encore communément utilisées, d’autres désuètes. C’est le dictionnaire recommandé si le français est votre langue maternelle.

+
+
+

+

Avec ce dictionnaire, seule l’orthographe réformée est reconnue. Attendu que bon nombre de graphies réformées sont considérées comme erronées par beaucoup, ce dictionnaire est déconseillé. Les graphies passées dans l’usage sont déjà incluses dans le dictionnaire “Moderne”.

+
+
+

+

Ce dictionnaire contient les variantes graphiques, classiques, réformées, ainsi que d’autres plus rares encore. Ce dictionnaire est déconseillé à ceux qui ne connaissent pas très bien la langue française.

+
+
+

TESTS

- -
Tests complets
Analyser
-
-
-
-
- -
-

CONJUGUEUR

-

- - Conjuguer -

- -

- - -

- - · - · -
- · -

- -

 

-

 

- - -
-
-

Infinitif

-

 

-
-
-

Impératif

-

Présent

-

 

-

 

-

 

-
-
- -
 
- -
-
-

Participe présent

-

 

-
-
-

Participes passés

-

 

-

 

-

 

-

 

-
-
- -
- - -
-
-

Indicatif

-
-

Présent

-

 

-

 

-

 

-

 

-

 

-

 

-
-
-

Imparfait

-

 

-

 

-

 

-

 

-

 

-

 

-
-
-

Passé simple

-

 

-

 

-

 

-

 

-

 

-

 

-
-
-

Futur

-

 

-

 

-

 

-

 

-

 

-

 

-
-
-
- -
 
- -
-
-

Subjonctif

-
-

Présent

-

 

-

 

-

 

-

 

-

 

-

 

-
-
-

Imparfait

-

 

-

 

-

 

-

 

-

 

-

 

-
-
-
-

Conditionnel

-
-

Présent

-

 

-

 

-

 

-

 

-

 

-

 

-
-
-

 

-

 

-

 

-

 

-

 

-

 

-

 

-
-
-
- -
-
- -
-

FORMATEUR DE TEXTE

-
- - -
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
- -
-
-
- - -
-
-
- - -
-
-
- - -
- -
-
-
- - - -
-
-
- - - -
-
-
- - -
-
-
- - - -
-
-
- - -
-
-
- - -
- -
-
-
- - -
-
-
- - -
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- -
-
- -
-
-
-
- - -
-
-
- -
-
- -
-
-
-
- - -
-
-
- - -
-
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
- -
-
-  
-  
-  
-  
-  
-  
-
-
-
- - -
- -
-
-
- - -
- - -
-
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - -
-
-
-
- - -
- -
-
-
- - -
-
-
- - -
-
-
-
- -
-
Par défaut
-
Appliquer
-
- -
-
+
Page for tests
+ +
Tests complets
Analyser
+
+

+         
 
       
+ Index: gc_lang/fr/webext/panel/main.js ================================================================== --- gc_lang/fr/webext/panel/main.js +++ gc_lang/fr/webext/panel/main.js @@ -1,70 +1,136 @@ +// Main panel + +"use strict"; + function showError (e) { - console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); + console.error(e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); } -function beastNameToURL(beastName) { - switch (beastName) { - case "Frog": - return browser.extension.getURL("beasts/frog.jpg"); - case "Snake": - return browser.extension.getURL("beasts/snake.jpg"); - case "Turtle": - return browser.extension.getURL("beasts/turtle.jpg"); - } -} - + +/* + Events +*/ window.addEventListener( - "click", - function (xEvent) { - let xElem = xEvent.target; - if (xElem.id) { - if (xElem.id) { - - } - } else if (xElem.className === "select") { - showPage(xElem.dataset.page); - } else if (xElem.tagName === "A") { - openURL(xElem.getAttribute("href")); - } - }, - false + "click", + function (xEvent) { + let xElem = xEvent.target; + if (xElem.id) { + if (xElem.id === "text_to_test") { + browser.runtime.sendMessage({ + sCommand: "textToTest", + dParam: {sText: document.getElementById("text_to_test").value, sCountry: "FR", bDebug: false, bContext: false}, + dInfo: {} + }); + } + else if (xElem.id === "fulltests") { + document.getElementById("tests_result").textContent = "Veuillez patienter…"; + browser.runtime.sendMessage({ + sCommand: "fullTests", + dParam: {}, + dInfo: {} + }); + } + else if (xElem.id.startsWith("option_")) { + browser.runtime.sendMessage({ + sCommand: "setOption", + dParam: {sOptName: xElem.dataset.option, bValue: xElem.checked}, + dInfo: {} + }); + } + } else if (xElem.className.startsWith("select")) { + showPage(xElem.dataset.page); + }/* else if (xElem.tagName === "A") { + openURL(xElem.getAttribute("href")); + }*/ + }, + false ); -function showPage (sPageName) { - try { - // hide them all - for (let xNodePage of document.getElementsByClassName("page")) { - xNodePage.style.display = "None"; - } - // show the one - document.getElementById(sPageName).style.display = "block"; - sendMessage("Mon message"); - // specific modifications - if (sPageName === "conj_page") { - document.body.style.width = "600px"; - document.documentElement.style.width = "600px"; - document.getElementById("movewindow").style.display = "none"; - } else { - document.body.style.width = "530px"; - document.documentElement.style.width = "530px"; - document.getElementById("movewindow").style.display = "block"; - } - } - catch (e) { - showError(e); - } -} - -function handleResponse(message) { - console.log(`[Panel] received: ${message.response}`); -} - -function handleError(error) { - console.log(`[Panel] Error: ${error}`); -} - -function sendMessage (sMessage) { - let sending = browser.runtime.sendMessage({content: sMessage}); - sending.then(handleResponse, handleError); + +/* + Message sender + and response handling +*/ +function handleResponse (oResponse) { + console.log(`[Panel] received:`); + console.log(oResponse); +} + +function handleError (error) { + console.log(`[Panel] Error:`); + console.log(error); +} + +function sendMessageAndWaitResponse (oData) { + let xPromise = browser.runtime.sendMessage(oData); + xPromise.then(handleResponse, handleError); +} + + +/* + Messages received +*/ +function handleMessage (oMessage, xSender, sendResponse) { + let {sActionDone, result, dInfo, bEnd, bError} = oMessage; + switch(sActionDone) { + case "textToTest": + case "fullTests": + showTestResult(result); + break; + case "getOptions": + case "getDefaultOptions": + break; + default: + console.log("GRAMMALECTE. Unknown command: " + oMessage.sCommand); + } + sendResponse({sCommand: "none", result: "done"}); +} + +browser.runtime.onMessage.addListener(handleMessage); + + +/* + Actions +*/ + +function showPage (sPageName) { + try { + // hide them all + for (let xNodePage of document.getElementsByClassName("page")) { + xNodePage.style.display = "none"; + } + // show the selected one + document.getElementById(sPageName).style.display = "block"; + if (sPageName == "gc_options_page") { + setGCOptions(); + } + } + catch (e) { + showError(e); + } +} + + +function showTestResult (sText) { + document.getElementById("tests_result").textContent = sText; +} + +function setGCOptions () { + let xPromise = browser.storage.local.get("gc_options"); + xPromise.then( + function (dSavedOptions) { + console.log(dSavedOptions); + if (dSavedOptions.hasOwnProperty("gc_options")) { + for (let [sOpt, bVal] of dSavedOptions.gc_options) { + if (document.getElementById("option_"+sOpt)) { + document.getElementById("option_"+sOpt).checked = bVal; + } + } + } + }, + function (e) { + showError(e); + } + ); } Index: gc_lang/fr/xpi/data/conj_panel.js ================================================================== --- gc_lang/fr/xpi/data/conj_panel.js +++ gc_lang/fr/xpi/data/conj_panel.js @@ -69,11 +69,11 @@ if (sVerb.endsWith("?")) { document.getElementById('oint').checked = true; sVerb = sVerb.slice(0,-1).trim(); } - if (!isVerb(sVerb)) { + if (!conj.isVerb(sVerb)) { document.getElementById('verb').style = "color: #BB4411;"; } else { self.port.emit("show"); document.getElementById('verb_title').textContent = sVerb; document.getElementById('verb').style = "color: #999999;"; Index: gc_lang/fr/xpi/data/gc_panel.js ================================================================== --- gc_lang/fr/xpi/data/gc_panel.js +++ gc_lang/fr/xpi/data/gc_panel.js @@ -199,11 +199,11 @@ function _tagParagraph (sParagraph, xParagraph, iParagraph, aSpellErr, aGrammErr) { try { if (aGrammErr.length === 0 && aSpellErr.length === 0) { xParagraph.textContent = sParagraph; - return + return; } aGrammErr.push(...aSpellErr); aGrammErr.sort(function (a, b) { if (a["nStart"] < b["nStart"]) return -1; Index: gc_lang/fr/xpi/gce_worker.js ================================================================== --- gc_lang/fr/xpi/gce_worker.js +++ gc_lang/fr/xpi/gce_worker.js @@ -77,17 +77,17 @@ worker.log("# Error: " + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message); } } } -function parse (sText, sLang, bDebug, bContext) { - let aGrammErr = gce.parse(sText, sLang, bDebug, bContext); +function parse (sText, sCountry, bDebug, bContext) { + let aGrammErr = gce.parse(sText, sCountry, bDebug, bContext); return JSON.stringify(aGrammErr); } -function parseAndSpellcheck (sText, sLang, bDebug, bContext) { - let aGrammErr = gce.parse(sText, sLang, bDebug, bContext); +function parseAndSpellcheck (sText, sCountry, bDebug, bContext) { + let aGrammErr = gce.parse(sText, sCountry, bDebug, bContext); let aSpellErr = oTokenizer.getSpellingErrors(sText, oDict); return JSON.stringify({ aGrammErr: aGrammErr, aSpellErr: aSpellErr }); } function getOptions () { Index: gc_lang/fr/xpi/package.json ================================================================== --- gc_lang/fr/xpi/package.json +++ gc_lang/fr/xpi/package.json @@ -1,10 +1,10 @@ { "name": "grammalecte-fr", "title": "Grammalecte [fr]", "id": "French-GC@grammalecte.net", - "version": "0.5.18", + "version": "0.5.19", "description": "Correcteur grammatical pour le français", "homepage": "http://www.dicollecte.org/grammalecte", "main": "ui.js", "icon": "data/img/icon-48.png", "scripts": { Index: make.py ================================================================== --- make.py +++ make.py @@ -11,10 +11,11 @@ import datetime import argparse import importlib import unittest import json +import platform from distutils import dir_util, file_util import dialog_bundled import compile_rules @@ -348,15 +349,17 @@ tests.perf(sVersion, hDst) # Firefox if xArgs.firefox: with helpers.cd("_build/xpi/"+sLang): - os.system("jpm run -b nightly") + spfFirefox = dVars['win_fx_dev_path'] if platform.system() == "Windows" else dVars['linux_fx_dev_path'] + os.system('jpm run -b "' + spfFirefox + '"') if xArgs.web_ext: with helpers.cd("_build/webext/"+sLang): - os.system(r'web-ext run --firefox="' + dVars['fx_beta_path'] + '" --browser-console') + spfFirefox = dVars['win_fx_nightly_path'] if platform.system() == "Windows" else dVars['linux_fx_nightly_path'] + os.system(r'web-ext run --firefox="' + spfFirefox + '" --browser-console') # Thunderbird if xArgs.thunderbird: os.system("thunderbird -jsconsole -P debug") else: