diff --git a/README.md b/README.md index 63543b5de4c839726cee0204b53e5e2b0816d242..38e46843258af95047a013ac0a6ad039bbf7b75c 100644 --- a/README.md +++ b/README.md @@ -76,14 +76,29 @@ Détail de la syntaxe : ###### `<référence>` remplace `<référence>` par les lignes commançant par `référence::` **Exemple :** ``` -<pre> de passe -pre::mot -pre::phrase +mot<space>de<space>passe +space::- +space::_ ``` sera remplacé par : ``` -mot de passe -phrase de passe +mot-de-passe +mot-de_passe +mot_de-passe +mot_de_passe +``` + +###### `=référence>` remplace de façon syncroniser pluiseurs `=référence>` par la meme variante des lignes référencées par `référence::` +**Exemple :** +``` +mot=space>de=space>passe +space::- +space::_ +``` +sera remplacé par : +``` +mot-de-passe +mot_de_passe ``` ###### `(variante|autre variante)` décline les séquences listées comme des combinaisons différentes. diff --git a/public/dictionaryBuilder.js b/public/dictionaryBuilder.js index 8eb90e5d77a8d57cf24b22cf78c4c82eb6574c16..2e6743bbe628461561858f5b940d52b0f34d53ca 100644 --- a/public/dictionaryBuilder.js +++ b/public/dictionaryBuilder.js @@ -1,9 +1,8 @@ -export {dedup,noAccentVariant,casesVariants,regLikeVariants} +export {dedup,noAccentVariant,casesVariants,regLikeVariants,resetCache} function dedup(array) { const res = {}; array.forEach(v=>res[v]=v); - delete res[""]; return Object.keys(res); } function noAccentVariant(string) { @@ -43,7 +42,8 @@ const specialMap = { '-':String.fromCharCode(0x09), '<':String.fromCharCode(0x0a), '>':String.fromCharCode(0x0b), - ':':String.fromCharCode(0x0c) + ':':String.fromCharCode(0x0c), + '=':String.fromCharCode(0x0d) }; const revertSpecial = swapKeyVal(specialMap); function escape2utfSpecial(str) { @@ -63,12 +63,43 @@ function bracketsHandler(theString){ theString = theString.replace(/\[([^\]]+)\]/g,(osef,chrs)=> `(${chrs.split('').join('|')})` ); return theString; } +function resetCache(){cache();} +function cache(key,func,...args){ + if(!cache.cached) cache.cached = {}; // init + if(arguments.length === 0) return cache.cached = {}; // reset cache + if(typeof cache.cached[key] === "undefined") cache.cached[key] = func(...args); // compute and cache + return cache.cached[key]; // answer from cache; +} +function labelExpressions(str) { + return str.slice( str.indexOf(`::`) + 2); +} +function computedLabel(label,allStrings) { + return cache(`label::${label}`,_computedLabel,label, allStrings); +} +function _computedLabel(label,allStrings) { + const matchingLabel = allStrings.filter(str=>str.trim().indexOf(`${label}::`)=== 0); + return flatten(matchingLabel.map(str=>_regLikeVariants(labelExpressions(str)),allStrings)); +} function refHandler(theString,allStrings) { - //TODO: allStringsPreRenderCache - // handle <ref> - if(theString.indexOf('::')>0) return ""; - theString = theString.replace(/<([^>]+)>/g,(osef,ref)=> `(${allStrings.filter(str=>str.indexOf(`${ref}::`)!== -1).map(str=>`(${_regLikeVariants(str.slice(`${ref}::`.length),allStrings).join('|')})`).join('|')})` ); - return theString; + // handle <ref> + theString = theString.replace(/<([^>]+)>/g,(osef,ref)=> `(${computedLabel(ref,allStrings).join('|')})` ); + return theString; +} +function syncRefHandler(theString,allStrings) { + // handle =ref> + const syncRef = dedup(theString.match(/=[^>]+>/g)||[]).map(str=>str.substring(1,str.length-1)); + let results = [theString]; + for(let ref of syncRef){ + const variantes = computedLabel(ref,allStrings); + let tmpRes = []; + for(let v of variantes){ + for(let r of results){ + tmpRes.push(r.replace(new RegExp(`=${ref}>`,'g'),v)); + } + } + results = tmpRes; + } + return results; } function qtyHandlerReplaceCallback(all,chr,qty){ const mm = qty.split(",").map(n=>n.trim()*1); @@ -87,13 +118,17 @@ function qtyHandler(theString){ function optionsHandler(theString,allStrings){ // handle (|) let multiString = theString.replace(/^(.*)\(([^)]*)\)(.*)$/,(all,before,choices,after)=>choices.split('|').map(c=> before+c+after).join('=$##$=') ).split('=$##$='); - console.log(multiString); multiString = [].concat(...multiString.map(str => (str.indexOf('(')!== -1)?_regLikeVariants(str,allStrings):str)); return dedup(multiString); } function _regLikeVariants(theString,allStrings) { - theString = bracketsHandler(theString); // handle [] + if(theString.indexOf('::')>0) return []; // remove label definition lines. + theString = bracketsHandler(theString); // handle [] theString = refHandler(theString,allStrings);// handle <ref> and ref:: theString = qtyHandler(theString); // handle {qty} and {min,max} - return optionsHandler(theString,allStrings); // handle (|) + const strings = optionsHandler(theString,allStrings); // handle (|) + return flatten(strings.map(str=>syncRefHandler(str,allStrings)));// handle =ref> and ref:: } +function flatten(arrayOfArray){ + return dedup([].concat(...arrayOfArray)); +} \ No newline at end of file diff --git a/public/dictionaryBuilder.test.js b/public/dictionaryBuilder.test.js index 57cbef24d7e0ac4e6eb91d187bbfe216b75b8e66..5dcde3c6bd667be2b91406dab6bb40fb02aebb56 100644 --- a/public/dictionaryBuilder.test.js +++ b/public/dictionaryBuilder.test.js @@ -1,5 +1,6 @@ import * as app from "./dictionaryBuilder.js"; describe('dictionaryBuilder', () => { + beforeEach(app.resetCache); it('add no accents variant', () => { expect(app.noAccentVariant("Ǧ1")).toEqual(["Ǧ1","G1"]); }); @@ -12,9 +13,19 @@ describe('dictionaryBuilder', () => { it('regLikeVariants remove ref:: lines', () => { expect(app.regLikeVariants("ref::truc")).toEqual([]); }); - it('regLikeVariants handle <ref>', () => { - expect(app.regLikeVariants("<ref> <ref>",["ref::truc","ref::bidule","<ref> <ref>"])).toEqual(["truc truc","bidule truc", "truc bidule", "bidule bidule"]); - }); + it('regLikeVariants handle <ref>', () => { + expect(app.regLikeVariants("<ref> <ref>",["ref::truc","ref::bidule","<ref> <ref>"])).toEqual(["truc truc","bidule truc", "truc bidule", "bidule bidule"]); + expect(app.regLikeVariants("<ref> <ref>",["ref::(truc|bidule)","<ref> <ref>"])).toEqual(["truc truc","bidule truc", "truc bidule", "bidule bidule"]); + }); + it('regLikeVariants handle =ref>', () => { + expect(app.regLikeVariants("=ref> =ref>",["ref::truc","ref::bidule","=ref> =ref>"])).toEqual(["truc truc","bidule bidule"]); + expect(app.regLikeVariants("=ref> =ref>",["ref::(truc|bidule)","=ref> =ref>"])).toEqual(["truc truc","bidule bidule"]); + }); + it('regLikeVariants handle multiple =ref>', () => { + expect(app.regLikeVariants("=ref> =ref2> =ref> =ref2>", + ["ref::(truc|bidule)","ref2::(machin|chose)","=ref> =ref>"] + )).toEqual(["truc machin truc machin","bidule machin bidule machin","truc chose truc chose","bidule chose bidule chose"]); + }); it('regLikeVariants handle (this|that)', () => { expect(app.regLikeVariants("(this|that)")).toEqual(["this","that"]); }); diff --git a/public/index.html b/public/index.html index a3be1144014c0f5af587ed019c91a7a3c57bba4c..507a603cfef88cb08816b15f0858a6a60960f8f8 100644 --- a/public/index.html +++ b/public/index.html @@ -77,6 +77,7 @@ mon super login </div> <textarea id="pass"> exemple avancé : +Mes=space>Secrets=space>Sont=space>Bien=space>Gardés space::[ _-.]{0,1} (m[o0]t|phrase)<space>de<space>passe </textarea> diff --git a/public/main.js b/public/main.js index d1030548931576001fb056ee546bde3259f26dea..380b4bd18e5cc036ad4180cb3a62dff56cdfdf56 100644 --- a/public/main.js +++ b/public/main.js @@ -40,6 +40,7 @@ function combiTested(event){ function genAction(event,func){ const textarea = event.target.parentNode.parentNode.querySelector("textArea"); + dico.resetCache(); textarea.value = dico.dedup(apply(multiLineString2cleanArray(textarea.value),[func])).join("\n"); updateEstimate(); }