From b7308a1f70c97cd3634b9ce12d93e7f8360961c0 Mon Sep 17 00:00:00 2001
From: "[1000i100] Millicent Billette" <git@1000i100.fr>
Date: Mon, 2 Jan 2023 07:29:16 +0100
Subject: [PATCH] =?UTF-8?q?v3.0.0-alpha6=20Corrections=20multiples=20bug?=
 =?UTF-8?q?=20am=C3=A9liorations=20d'ergonomie=20Options=20d'interface=20c?=
 =?UTF-8?q?ompl=C3=A8tes=20TODO:=20-=20doc=20/=20guide=20interactif=20-=20?=
 =?UTF-8?q?auto-save/resume=20-=20responsive=20-=20pub=20offline?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 package-lock.json                  | 14 +++++------
 package.json                       |  2 +-
 src/js/logic/dico.mjs              |  3 +++
 src/js/logic/saveResume.mjs        | 29 +++++++++++++++++++++++
 src/js/logic/search.mjs            |  9 +++++---
 src/js/logic/utils.mjs             | 10 ++++++++
 src/js/logic/workerManager.mjs     |  5 ++--
 src/js/ux/1_pubKey.mjs             |  6 +++--
 src/js/ux/2_inputSecrets.mjs       | 37 +++++++++++++++++++++++++++++-
 src/js/ux/3_inProgress.mjs         |  2 +-
 src/js/ux/4_result.mjs             |  9 ++++++--
 src/js/ux/nav.mjs                  | 12 ++++++++--
 src/js/ux/rangeOption.mjs          |  1 +
 src/pages/fragments/browse.ejs     |  7 ++++--
 src/pages/steps/1_pubKey.ejs       |  9 ++++++++
 src/pages/steps/2_inputSecrets.ejs |  7 ++----
 src/pages/steps/4_result.ejs       |  2 +-
 17 files changed, 134 insertions(+), 30 deletions(-)
 create mode 100644 src/js/logic/saveResume.mjs
 create mode 100644 src/js/logic/utils.mjs

diff --git a/package-lock.json b/package-lock.json
index 9822cb6..b27b35d 100755
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,7 +6,7 @@
     "": {
       "hasInstallScript": true,
       "dependencies": {
-        "g1lib": "^3.5.5"
+        "g1lib": "^3.5.7"
       },
       "devDependencies": {
         "@fortawesome/fontawesome-free": "^5.15.2",
@@ -1004,9 +1004,9 @@
       }
     },
     "node_modules/g1lib": {
-      "version": "3.5.5",
-      "resolved": "https://registry.npmjs.org/g1lib/-/g1lib-3.5.5.tgz",
-      "integrity": "sha512-3JnCfMOc2ynTgCv4MsoeEAT9gGkwYMehFULOx6kMNY0tlPu1ytMI40UtcFPjCMUxnAXhBixtUTgbf8u1UxlgMA==",
+      "version": "3.5.7",
+      "resolved": "https://registry.npmjs.org/g1lib/-/g1lib-3.5.7.tgz",
+      "integrity": "sha512-gxCMQvn4wl8huRwUiW2zc2Bvul2ch+7VB2dHcbJkME1TLivpx/azljA3OGnhhfMJG93mGZvHoBxCFnqOqmqb1A==",
       "funding": {
         "url": "https://git.duniter.org/libs/g1lib.js"
       }
@@ -4456,9 +4456,9 @@
       "dev": true
     },
     "g1lib": {
-      "version": "3.5.5",
-      "resolved": "https://registry.npmjs.org/g1lib/-/g1lib-3.5.5.tgz",
-      "integrity": "sha512-3JnCfMOc2ynTgCv4MsoeEAT9gGkwYMehFULOx6kMNY0tlPu1ytMI40UtcFPjCMUxnAXhBixtUTgbf8u1UxlgMA=="
+      "version": "3.5.7",
+      "resolved": "https://registry.npmjs.org/g1lib/-/g1lib-3.5.7.tgz",
+      "integrity": "sha512-gxCMQvn4wl8huRwUiW2zc2Bvul2ch+7VB2dHcbJkME1TLivpx/azljA3OGnhhfMJG93mGZvHoBxCFnqOqmqb1A=="
     },
     "get-caller-file": {
       "version": "2.0.5",
diff --git a/package.json b/package.json
index faa6ae2..cf2ebb5 100755
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
     "build:stylus": "stylus --include-css src/stylus/_all.styl -o generated/public/style.css"
   },
   "dependencies": {
-    "g1lib": "^3.5.5"
+    "g1lib": "^3.5.7"
   },
   "devDependencies": {
     "@fortawesome/fontawesome-free": "^5.15.2",
diff --git a/src/js/logic/dico.mjs b/src/js/logic/dico.mjs
index 90705ca..bce5fce 100755
--- a/src/js/logic/dico.mjs
+++ b/src/js/logic/dico.mjs
@@ -2,6 +2,7 @@ import {subscribe} from "./workerManager.mjs";
 import getCombiPerSec from "./speedBench.mjs";
 import {Dictionary} from "g1lib/browser/dictionary.mjs";
 import staticPreview from "./staticPreview.mjs";
+import {cacheScale} from "./search.mjs";
 
 let currentDico;
 let lastData;
@@ -52,6 +53,8 @@ export function getActualData(withSpeed = true) {
       accent:so.accent.value<0?undefined:so.accent.value,
       lowerCase:so.caps.value<0?undefined:so.caps.value,
       leetSpeak:0,
+      save:so.save.value,
+      cacheMax:cacheScale[so.cacheMax.value],
     },
     previewIndex: (window.browseIndex || 0),
   };
diff --git a/src/js/logic/saveResume.mjs b/src/js/logic/saveResume.mjs
new file mode 100644
index 0000000..b451e58
--- /dev/null
+++ b/src/js/logic/saveResume.mjs
@@ -0,0 +1,29 @@
+import {subscribe} from "./workerManager.mjs";
+import {swapKeyValue,sleep} from "./utils.mjs";
+
+export const saveScale = [];
+saveScale[0]=0;
+saveScale[1]=24*60*60;
+saveScale[2]=2*24*60*60;
+saveScale[3]=7*24*60*60;
+export const revertSaveScale = swapKeyValue(saveScale);
+
+const MIN_COMPUTE_TIME_FOR_SAVE = 3600;
+
+export let saveMode=0
+
+async function init(){
+  await sleep(1);
+  subscribe('smartPreview', updateSaveMode);
+  window.searchOptions.save.listen('saveResume',manualUpdate);
+}
+init();
+function manualUpdate(mode){
+  if(mode>=0) saveMode = saveScale[mode];
+}
+function updateSaveMode(data){
+  const smartDico = JSON.parse(data.dico);
+  if(smartDico.config.save<0) saveMode = smartDico.estimateDuration >= MIN_COMPUTE_TIME_FOR_SAVE ? saveScale[2] : 0;
+  else saveMode = saveScale[smartDico.config.save];
+  window.searchOptions.save.setComputed(revertSaveScale[saveMode]);
+}
diff --git a/src/js/logic/search.mjs b/src/js/logic/search.mjs
index 0ada2e7..d71ea42 100644
--- a/src/js/logic/search.mjs
+++ b/src/js/logic/search.mjs
@@ -10,7 +10,9 @@ export let departTime;
 export let endTime;
 export let found;
 export let cacheActive = true;
-export const CACHE_MAX = 1_000_000;
+export const DEFAULT_CACHE_MAX = 1_000_000;
+export const cacheScale = [];
+
 const globalCache = {};
 
 export function duplicateCount() {
@@ -53,6 +55,7 @@ export function stopSearch() {
 function updateProgress(data) {
   if (data.bench) return;
   tested++;
+  if(found) endSearchTest();
   if (data.publicKey === pubKey) {
     endSearchTest(data);
   } else {
@@ -61,9 +64,9 @@ function updateProgress(data) {
       if (!globalCache[cacheKey]) globalCache[cacheKey] = {};
       globalCache[cacheKey][data.index] = true;
     }
-    if (Object.keys(globalCache).length > CACHE_MAX) cacheActive = false;
+    if (Object.keys(globalCache).length > getDico().config.cacheMax) cacheActive = false;
   }
-  if (tested >= getDico().length) endSearchTest()
+  if (tested >= getDico().length) endSearchTest();
 }
 
 function endSearchTest(somethingFound = undefined) {
diff --git a/src/js/logic/utils.mjs b/src/js/logic/utils.mjs
new file mode 100644
index 0000000..8139f09
--- /dev/null
+++ b/src/js/logic/utils.mjs
@@ -0,0 +1,10 @@
+export function swapKeyValue(object) {
+  const result = {};
+  for (const key in object) {
+    result[object[key]] = key;
+  }
+  return result;
+}
+export async function sleep(ms) {
+  return new Promise((resolve, reject) => setTimeout(resolve, ms));
+}
diff --git a/src/js/logic/workerManager.mjs b/src/js/logic/workerManager.mjs
index 64462bf..2a16ab8 100644
--- a/src/js/logic/workerManager.mjs
+++ b/src/js/logic/workerManager.mjs
@@ -1,3 +1,5 @@
+import {sleep} from "./utils.mjs";
+
 const workers = [];
 const MAX_ALLOWED_IN_QUEUE = 5;
 const subscriptionList = {};
@@ -56,6 +58,3 @@ export function unSubscribe(action,registeredFunc){
   const index = subscriptionList[action].indexOf(registeredFunc);
   subscriptionList[action].splice(index, 1);
 }
-async function sleep(ms) {
-  return new Promise((resolve, reject) => setTimeout(resolve, ms));
-}
diff --git a/src/js/ux/1_pubKey.mjs b/src/js/ux/1_pubKey.mjs
index eec4f61..443e956 100644
--- a/src/js/ux/1_pubKey.mjs
+++ b/src/js/ux/1_pubKey.mjs
@@ -14,8 +14,10 @@ export default function focus_1_pubKey(){
 export let pubKey;
 export function updatePubKey(){
     const pubKeyInputNode = document.getElementById('pubKey');
-    if(location.hash.match(/pubkey=/i) !== null)
-        pubKeyInputNode.value = location.hash.split('=')[1];
+    if(location.hash.match(/pubkey=/i) !== null){
+      pubKeyInputNode.value = location.hash.split('=')[1];
+      return location.hash = location.hash.split('=')[0];
+    }
     pubKey = pubKeyInputNode.value;
     const nextBtn = document.querySelector('.step_1_pubKey a.button');
     const errorArea = document.querySelector('.step_1_pubKey .warning');
diff --git a/src/js/ux/2_inputSecrets.mjs b/src/js/ux/2_inputSecrets.mjs
index 97d63b5..8aba54a 100755
--- a/src/js/ux/2_inputSecrets.mjs
+++ b/src/js/ux/2_inputSecrets.mjs
@@ -4,6 +4,9 @@ import {timeUnitStrings} from "pretty-ms";
 import {sendToWorker, subscribe} from "../logic/workerManager.mjs";
 import {getActualData} from "../logic/dico.mjs";
 import RangeOption from "./rangeOption.mjs";
+import {cacheScale, DEFAULT_CACHE_MAX} from "../logic/search.mjs";
+import {saveScale} from "../logic/saveResume.mjs";
+import {swapKeyValue} from "../logic/utils.mjs";
 
 const regexDesc = {
   '-1': 'Syntaxe : auto-sélection',
@@ -28,6 +31,36 @@ const capsDesc = {
   '1': `Majuscules : classiques`,
   '2': `Majuscules : fluctuantes`,
 }
+
+
+const saveDesc = {
+  '-1': `Reprendre : auto-sélection`,
+  '0': `Reprendre : désactivé par sécurité`,
+  '1': `Reprendre : possible pendant 24h`,
+  '2': `Reprendre : possible pendant 48h`,
+  '3': `Reprendre : possible pendant 7j`,
+}
+cacheScale[-1]=DEFAULT_CACHE_MAX;
+cacheScale[0]=0;
+cacheScale[1]=10_000;
+cacheScale[2]=100_000;
+cacheScale[3]=1_000_000;
+cacheScale[4]=10_000_000;
+cacheScale[5]=100_000_000;
+cacheScale[6]=1_000_000_000;
+const revertCacheScale = swapKeyValue(cacheScale.filter((v,i)=> i>=0 ));
+
+const cacheMaxDesc = {
+  '-1': `Cache : auto-sélection`,
+  '0': `Cache : désactivé`,
+  '1': `Cache : ${cacheScale[1].toLocaleString('fr-FR')}`,
+  '2': `Cache : ${cacheScale[2].toLocaleString('fr-FR')}`,
+  '3': `Cache : ${cacheScale[3].toLocaleString('fr-FR')}`,
+  '4': `Cache : ${cacheScale[4].toLocaleString('fr-FR')}`,
+  '5': `Cache : ${cacheScale[5].toLocaleString('fr-FR')}`,
+  '6': `Cache : ${cacheScale[6].toLocaleString('fr-FR')}`,
+//  '6': `Cache : ${new Intl.NumberFormat().format(cacheScale[6])}`,
+}
 export function init() {
   const secretsInputNode = document.getElementById('secrets');
   secretsInputNode.addEventListener('keyup', askPreview);
@@ -38,7 +71,8 @@ export function init() {
     'mirror': new RangeOption('mirror', -1, mirrorDesc, askPreview),
     'accent': new RangeOption('accent', -1, accentDesc, askPreview),
     'caps': new RangeOption('caps', -1, capsDesc, askPreview),
-    //TODO: AutoSave & resume
+    'save': new RangeOption('save', -1, saveDesc, askPreview),
+    'cacheMax': new RangeOption('cacheMax', -1, cacheMaxDesc, askPreview),
   };
   subscribe('fastPreview',updateFastPreviewDisplay);
   subscribe('smartPreview', updateSmartPreviewDisplay);
@@ -93,6 +127,7 @@ function updateSmartPreviewDisplay(data){
   so.accent.setComputed(sdc.accent || 0);
   so.caps.setComputed(sdc.lowerCase || 0);
   so.accent.setComputed(sdc.accent || 0);
+  so.cacheMax.setComputed(revertCacheScale[sdc.cacheMax]);
 
   try{window.displayBrowsePreview();}catch (e){}
 }
diff --git a/src/js/ux/3_inProgress.mjs b/src/js/ux/3_inProgress.mjs
index e954879..294624d 100644
--- a/src/js/ux/3_inProgress.mjs
+++ b/src/js/ux/3_inProgress.mjs
@@ -8,7 +8,7 @@ export default function init(){
   setInterval(showProgress,200);
 }
 init();
-const progressColor = getComputedStyle(document.getElementById('startSearch')).color;
+const progressColor = getComputedStyle(document.querySelector('.success h3')).color;
 const progressBar = document.getElementById('progress_bar');
 const bgColor = getComputedStyle(progressBar).backgroundColor;
 const timing = [];
diff --git a/src/js/ux/4_result.mjs b/src/js/ux/4_result.mjs
index 97d8157..381f5ab 100644
--- a/src/js/ux/4_result.mjs
+++ b/src/js/ux/4_result.mjs
@@ -21,8 +21,13 @@ export default function showResult(){
   document.getElementById('resSpent').innerHTML = timeFormat(timeSpent);
   document.getElementById('resSpeed').innerHTML = (1000*tested/timeSpent).toFixed(1);
   const duplicates = duplicateCount();
-  document.getElementById('resDuplicate').innerHTML = duplicates;
-  document.getElementById('resDupRatio').innerHTML = (100 * duplicates / tested).toPrecision(3);
+  if (duplicates) {
+    document.getElementById('resDup').classList.remove('hidden');
+    document.getElementById('resDuplicate').innerHTML = duplicates;
+    document.getElementById('resDupRatio').innerHTML = (100 * duplicates / tested).toPrecision(3);
+  } else {
+    document.getElementById('resDup').classList.add('hidden');
+  }
 }
 function timeFormat(milliseconds) {
   return prettyMilliseconds(Math.trunc(milliseconds / 1000)*1000, {
diff --git a/src/js/ux/nav.mjs b/src/js/ux/nav.mjs
index 2be619d..4fbc8fd 100644
--- a/src/js/ux/nav.mjs
+++ b/src/js/ux/nav.mjs
@@ -16,6 +16,14 @@ const setFocus = [
   ()=>0,
   showResult,
 ];
+
+const pages = [
+  'page_0_home',
+  'page_1_pubKey',
+  'page_2_inputSecrets',
+  'page_3_inProgress',
+  'page_4_result',
+];
 init();
 
 // internal navigation
@@ -25,8 +33,8 @@ function showPageFromHash(){
     const pageNumber = parseInt(window.location.hash.split('_')[1]);
     if(isNaN(pageNumber)) return window.location = '#page_0_home';
     if(pageNumber<2 || pubKey){
-      document.body.className = window.location.hash.slice(1)
-    } else window.location = '#page_0_home';
+      document.body.className = pages[pageNumber]
+    } else return window.location = '#page_0_home';
     setFocus[pageNumber]();
     if(pageNumber!==3) stopSearch();
   } catch (e){
diff --git a/src/js/ux/rangeOption.mjs b/src/js/ux/rangeOption.mjs
index fc6e4d1..78839f7 100644
--- a/src/js/ux/rangeOption.mjs
+++ b/src/js/ux/rangeOption.mjs
@@ -78,6 +78,7 @@ export default function RangeOption(fieldId, initialValue, descArray, uiListener
     Object.keys(self.listeners).forEach(k=>self.listeners[k](mode));
   }
   checkBoxNode.addEventListener('change', ()=>uiChanged(hiddenNode.value = checkBoxNode.checked?-1:(rangeNode.value || 0)));
+  rangeNode.addEventListener('input', ()=>descLabel.innerHTML = descArray[rangeNode.value]);
   rangeNode.addEventListener('change', ()=>uiChanged(hiddenNode.value = rangeNode.value));
   if(typeof uiListener !== 'undefined') self.listen('default',uiListener)
   if(initialValue) self.setUserChoice(initialValue);
diff --git a/src/pages/fragments/browse.ejs b/src/pages/fragments/browse.ejs
index 9ef4f7b..d3dc5c6 100644
--- a/src/pages/fragments/browse.ejs
+++ b/src/pages/fragments/browse.ejs
@@ -135,11 +135,14 @@
     return (index + staticDico.length) % staticDico.length;
   }
   function updateBrowseIndex(e){
-    if(e && e.key === "ArrowUp") return browseUp();
-    if(e && e.key === "ArrowDown") return browseDown();
+    if(e && e.key && e.key === "ArrowUp") return browseUp();
+    if(e && e.key && e.key === "ArrowDown") return browseDown();
+    if(e && e.deltaY && e.deltaY >0) return browseDown();
+    if(e && e.deltaY && e.deltaY <0) return browseUp();
     window.browseIndex = parseInt(document.querySelector('.currentIndex input').value);
     displayBrowsePreview();
   }
   document.querySelector('.currentIndex input').addEventListener('keyup',updateBrowseIndex);
+  document.querySelector('.browse tbody').addEventListener('wheel',updateBrowseIndex);
 </script>
 </section>
diff --git a/src/pages/steps/1_pubKey.ejs b/src/pages/steps/1_pubKey.ejs
index 3c8d497..e8b5c76 100644
--- a/src/pages/steps/1_pubKey.ejs
+++ b/src/pages/steps/1_pubKey.ejs
@@ -11,4 +11,13 @@
     <a href="#page_2_inputSecrets" class="button">
         Suivant <i class="fas fa-long-arrow-alt-right"></i>
     </a>
+    <div class="notice">
+        Vous êtes juste là pour tester, sans compte à récupérer&nbsp;?
+        Voici une clef publique valide utilisable comme exemple&nbsp;:
+        <a href="#page_1_pubKey=BCHERdQbh3HhyDSoqHdXTyGQZLZvDA9b6DBABcMAPdDW">BCHERdQbh3HhyDSoqHdXTyGQZLZvDA9b6DBABcMAPdDW</a>
+        <!--
+        idSec : "Ça me serait pas venu à l'idée !"
+        mdp : " MOI NON PLUS "
+        -->
+    </div>
 </section>
diff --git a/src/pages/steps/2_inputSecrets.ejs b/src/pages/steps/2_inputSecrets.ejs
index 1bbd0a2..e330644 100755
--- a/src/pages/steps/2_inputSecrets.ejs
+++ b/src/pages/steps/2_inputSecrets.ejs
@@ -15,11 +15,8 @@
             <a id="startSearch" class="button" href="#page_3_inProgress">Valider</a>
         </div>
         <div class="inputArea">
-        <textarea id="secrets">
-un exemple
-un autre
-le 3ème
-        </textarea>
+        <textarea id="secrets">ça (|ne )me serait pas venu[e]{0,1} à l'idée !
+ moi non plus[ ]</textarea>
         <%- include('../fragments/browse.ejs'); %>
         </div>
         <div class="tabs">
diff --git a/src/pages/steps/4_result.ejs b/src/pages/steps/4_result.ejs
index 341b7f7..3593442 100644
--- a/src/pages/steps/4_result.ejs
+++ b/src/pages/steps/4_result.ejs
@@ -18,7 +18,7 @@
         </div>
         <div class="stats">
             <div><span id="resTested"></span> combinaisons testées en <span id="resSpent"></span> (soit <span id="resSpeed"></span>/s)</div>
-            <div><span id="resDuplicate"></span> doublons évités soit <span id="resDupRatio"></span>% des combinaisons testées</div>
+            <div id="resDup"><span id="resDuplicate"></span> doublons évités soit <span id="resDupRatio"></span>% des combinaisons testées</div>
         </div>
         <div class="success">
             <div class="gift notice">
-- 
GitLab