From beabb2d1e379cd61ffd099e22e3c0820096f4fdf Mon Sep 17 00:00:00 2001 From: 1280px Date: Mon, 21 Aug 2023 17:40:50 +0700 Subject: [PATCH] Separate UI bindings from actual function --- index.html | 20 ++- js/UI.js | 130 ------------------ js/UIUpdater.js | 57 -------- js/UI_canvas.js | 12 ++ js/UI_fileman.js | 14 ++ js/UI_header.js | 36 +++++ js/UI_resman.js | 15 ++ js/UI_typeman.js | 13 ++ js/canvas.js | 4 - js/classifier.js | 20 --- js/{setsLoadSave.js => classifierLoadSave.js} | 13 +- js/configs.js | 64 +++++++-- js/configsLoadSave.js | 15 ++ js/resman.js | 59 ++++++++ js/{setsTypeman.js => typeman.js} | 1 - 15 files changed, 235 insertions(+), 238 deletions(-) delete mode 100644 js/UI.js delete mode 100644 js/UIUpdater.js create mode 100644 js/UI_canvas.js create mode 100644 js/UI_fileman.js create mode 100644 js/UI_header.js create mode 100644 js/UI_resman.js create mode 100644 js/UI_typeman.js rename js/{setsLoadSave.js => classifierLoadSave.js} (99%) create mode 100644 js/configsLoadSave.js create mode 100644 js/resman.js rename js/{setsTypeman.js => typeman.js} (99%) diff --git a/index.html b/index.html index 6d76aa1..31d4a2c 100644 --- a/index.html +++ b/index.html @@ -81,13 +81,19 @@ See debug logging in DevTools for more information. - - - - - - - + + + + + + + + + + + + + diff --git a/js/UI.js b/js/UI.js deleted file mode 100644 index 730b37c..0000000 --- a/js/UI.js +++ /dev/null @@ -1,130 +0,0 @@ -/* ### ### RECOGNITION MODE UI ELM ### ### */ -/* result btn when left-clicked */ -function resultBtnOnSelect(btn) { - if (document.getElementById('clipboardField').value.trim().length > 0) { - document.getElementById('clipboardField').value += (' ' + btn.className); - } else { - document.getElementById('clipboardField').value += (btn.className); - } -} - -/* result btn when right-clicked */ -function quickCopy(btn) { - navigator.clipboard.writeText(btn.className); - document.getElementById('clipboardField').value = btn.className; - - btn.disabled = true; - btn.innerHTML = (`${btn.innerHTML.split('')[1]}`); - setTimeout( function() { - btn.disabled = false; - btn.innerHTML = (`${btn.className}${btn.innerHTML.split('')[1]}`); - }, 500); - - /* autotrain */ - if (Configs.useAutotrain && isInteracted) addExample(btn); - - return false; // don't show context menu -} - -/* presiction used by the "Predict" button (if autoprediction is disabled) */ -document.getElementById('predictBtn').onclick = async function() { - predict(); - resetCanvas(); - document.getElementById('predictBtn').disabled = true; -} - -/* clipboard field copy all button */ -document.getElementById('clipboardCopyBtn').onclick = function() { - navigator.clipboard.writeText(document.getElementById('clipboardField').value); - document.getElementById('clipboardField').value = ''; - - document.getElementById('clipboardCopyBtn').disabled = true; - document.getElementById('clipboardCopyBtn').innerHTML = ''; - setTimeout( function() { - document.getElementById('clipboardCopyBtn').disabled = false; - document.getElementById('clipboardCopyBtn').innerHTML = ''; - }, 500); -} - -/* autoprediction toggler */ -document.getElementById('autopreditionToggler').onclick = () => { - autopreditionToggler.checked ? Configs.useAutoprediction = 1 : Configs.useAutoprediction = 0; - saveConfigs(); - updateUI(); -}; - -/* autotrain toggler */ -document.getElementById('autotrainToggler').onclick = () => { - autotrainToggler.checked ? Configs.useAutotrain = 1 : Configs.useAutotrain = 0; - saveConfigs(); - updateUI(); -}; - - - -/* ### ### TRAINING MODE UI ELMS ### ### */ -/* typeman input validation */ -document.getElementById('addTypeInput').onchange = () => addTypeInputValidate(); -document.getElementById('addTypeInput').onpaste = () => addTypeInputValidate(); -document.getElementById('addTypeInput').oninput = () => addTypeInputValidate(); - -/* add class button using input name */ -document.getElementById('addTypeBtn').onclick = () => createClassBtn( - document.getElementById('addTypeInput').value.trim().replace(/ /g, '-') ); - -/* class removal mode button */ -document.getElementById('removalModeTogglerBtn').onclick = () => toggleClassRemover(); - - - -/* ### ### ### HEADER ELEMENTS ### ### ### */ -/* show/hide UI blocks ("AppMode") */ -document.getElementById('appModeSwitcher').onclick = () => { - appModeSwitcher.checked ? Configs.currentAppMode = 1 : Configs.currentAppMode = 0; - saveConfigs(); - updateUI(); -}; -document.getElementById('appModeSwitcherHandler').oncontextmenu = () => { - appModeSwitcher.indeterminate ? - (appModeSwitcher.checked ? Configs.currentAppMode = 1 : Configs.currentAppMode = 0) - : Configs.currentAppMode = 2; - saveConfigs(); - updateUI(); - - return false; // don't show context menu -}; - -/* show/hide menu popup button */ -document.getElementById('filemanTogglerBtn').onclick = function() { - document.getElementById('fileman').style.transform != 'translateY(-115%)' - ? document.getElementById('fileman').style.transform = 'translateY(-115%)' - : document.getElementById('fileman').style.transform = 'translateY(0%)'; -} - -/* view mode buttons */ -document.getElementById('viewAsGridBtn').onclick = () => { - Configs.resultsViewMode = 1; - saveConfigs(); - updateUI(); -}; -document.getElementById('viewAsListBtn').onclick = () => { - Configs.resultsViewMode = 0; - saveConfigs(); - updateUI(); -}; - - - -/* ### ### MAIN MENU POP-UP ELMS ### ### */ -/* wipe all data button */ -document.getElementById('filemanWipe').onclick = () => { - localStorage.removeItem('dataset'); - init(); -} - -/* show/hide debug */ -document.getElementById('debugTogglerBtn').onclick = () => { - document.getElementById('debug').style.display == "none" ? Configs.showDebug = 1 : Configs.showDebug = 0; - saveConfigs(); - updateUI(); -}; \ No newline at end of file diff --git a/js/UIUpdater.js b/js/UIUpdater.js deleted file mode 100644 index 28a3180..0000000 --- a/js/UIUpdater.js +++ /dev/null @@ -1,57 +0,0 @@ -/* update UI correspondingly to current Configs data */ -function updateUI() { - if (Configs.currentAppMode == 0) { - appModeSwitcher.checked = false; - appModeSwitcher.indeterminate = false; - document.getElementById('trainingMode').style.display = 'unset'; - document.getElementById('recognitionMode').style.display = 'none'; - } - if (Configs.currentAppMode == 1) { - appModeSwitcher.checked = true; - appModeSwitcher.indeterminate = false; - document.getElementById('trainingMode').style.display = 'none'; - document.getElementById('recognitionMode').style.display = 'unset'; - } - if (Configs.currentAppMode == 2) { - appModeSwitcher.indeterminate = true; - document.getElementById('trainingMode').style.display = 'unset'; - document.getElementById('recognitionMode').style.display = 'unset'; - } - - if (Configs.resultsViewMode) { - document.getElementById('viewAsGridBtn').style.display = 'none'; - document.getElementById('viewAsListBtn').style.display = 'block'; - document.getElementById('resman').classList.replace('viewAsGrid', 'viewAsList'); - document.getElementById('typeman').classList.replace('viewAsGrid', 'viewAsList'); - } else { - document.getElementById('viewAsGridBtn').style.display = 'block'; - document.getElementById('viewAsListBtn').style.display = 'none'; - document.getElementById('resman').classList.replace('viewAsList', 'viewAsGrid'); - document.getElementById('typeman').classList.replace('viewAsList', 'viewAsGrid'); - } - - if (Configs.showDebug) { - document.getElementById('debug').style.display = 'block'; - document.getElementById('debugTogglerBtn').classList.add('selected'); - } else { - document.getElementById('debug').style.display = 'none'; - document.getElementById('debugTogglerBtn').classList.remove('selected'); - } - - if (Configs.useAutoprediction) { - document.getElementById('autopreditionToggler').checked = true; - document.getElementById('predictBtn').style.display = 'none'; - if (isInteracted) predict(); - } else { - document.getElementById('autopreditionToggler').checked = false; - document.getElementById('predictBtn').style.display = 'unset'; - } - - if (Configs.useAutotrain) { - document.getElementById('autotrainToggler').checked = true; - } else { - document.getElementById('autotrainToggler').checked = false; - } - - if (Configs.showDebug) console.log('[UIUpdater] UI updated successfully.') -} \ No newline at end of file diff --git a/js/UI_canvas.js b/js/UI_canvas.js new file mode 100644 index 0000000..305ff38 --- /dev/null +++ b/js/UI_canvas.js @@ -0,0 +1,12 @@ +// UI_* files contain JS code for GUI elements, isolated from any actual functions + +document.getElementById('clearDrawingPadBtn').onclick = function() { + resetCanvas(); +} + +/* presiction used by the "Predict" button (if autoprediction is disabled) */ +document.getElementById('predictBtn').onclick = async function() { + predict(); + resetCanvas(); + document.getElementById('predictBtn').disabled = true; +} \ No newline at end of file diff --git a/js/UI_fileman.js b/js/UI_fileman.js new file mode 100644 index 0000000..184049c --- /dev/null +++ b/js/UI_fileman.js @@ -0,0 +1,14 @@ +// UI_* files contain JS code for GUI elements, isolated from any actual functions + +/* wipe all data button */ +document.getElementById('filemanWipe').onclick = () => { + localStorage.removeItem('dataset'); + init(); +} + +/* show/hide debug */ +document.getElementById('debugTogglerBtn').onclick = () => { + document.getElementById('debug').style.display == "none" ? Configs.showDebug = 1 : Configs.showDebug = 0; + saveConfigs(); + updateUI(); +}; \ No newline at end of file diff --git a/js/UI_header.js b/js/UI_header.js new file mode 100644 index 0000000..987ec0e --- /dev/null +++ b/js/UI_header.js @@ -0,0 +1,36 @@ +// UI_* files contain JS code for GUI elements, isolated from any actual functions + +/* show/hide UI blocks ("AppMode") */ +document.getElementById('appModeSwitcher').onclick = () => { + appModeSwitcher.checked ? Configs.currentAppMode = 1 : Configs.currentAppMode = 0; + saveConfigs(); + updateUI(); +}; +document.getElementById('appModeSwitcherHandler').oncontextmenu = () => { + appModeSwitcher.indeterminate ? + (appModeSwitcher.checked ? Configs.currentAppMode = 1 : Configs.currentAppMode = 0) + : Configs.currentAppMode = 2; + saveConfigs(); + updateUI(); + + return false; // don't show context menu +}; + +/* show/hide menu popup button */ +document.getElementById('filemanTogglerBtn').onclick = function() { + document.getElementById('fileman').style.transform != 'translateY(-115%)' + ? document.getElementById('fileman').style.transform = 'translateY(-115%)' + : document.getElementById('fileman').style.transform = 'translateY(0%)'; +} + +/* view mode buttons */ +document.getElementById('viewAsGridBtn').onclick = () => { + Configs.resultsViewMode = 1; + saveConfigs(); + updateUI(); +}; +document.getElementById('viewAsListBtn').onclick = () => { + Configs.resultsViewMode = 0; + saveConfigs(); + updateUI(); +}; \ No newline at end of file diff --git a/js/UI_resman.js b/js/UI_resman.js new file mode 100644 index 0000000..4bbeacd --- /dev/null +++ b/js/UI_resman.js @@ -0,0 +1,15 @@ +// UI_* files contain JS code for GUI elements, isolated from any actual functions + +/* autoprediction toggler */ +document.getElementById('autopreditionToggler').onclick = () => { + autopreditionToggler.checked ? Configs.useAutoprediction = 1 : Configs.useAutoprediction = 0; + saveConfigs(); + updateUI(); +}; + +/* autotrain toggler */ +document.getElementById('autotrainToggler').onclick = () => { + autotrainToggler.checked ? Configs.useAutotrain = 1 : Configs.useAutotrain = 0; + saveConfigs(); + updateUI(); +}; \ No newline at end of file diff --git a/js/UI_typeman.js b/js/UI_typeman.js new file mode 100644 index 0000000..29add1a --- /dev/null +++ b/js/UI_typeman.js @@ -0,0 +1,13 @@ +// UI_* files contain JS code for GUI elements, isolated from any actual functions + +/* typeman input validation */ +document.getElementById('addTypeInput').onchange = () => addTypeInputValidate(); +document.getElementById('addTypeInput').onpaste = () => addTypeInputValidate(); +document.getElementById('addTypeInput').oninput = () => addTypeInputValidate(); + +/* add class button using input name */ +document.getElementById('addTypeBtn').onclick = () => createClassBtn( + document.getElementById('addTypeInput').value.trim().replace(/ /g, '-') ); + +/* class removal mode button */ +document.getElementById('removalModeTogglerBtn').onclick = () => toggleClassRemover(); \ No newline at end of file diff --git a/js/canvas.js b/js/canvas.js index b732fce..be9527c 100644 --- a/js/canvas.js +++ b/js/canvas.js @@ -82,8 +82,4 @@ function resetCanvas() { isInteracted = false; document.getElementById('predictBtn').disabled = true; } -} - -document.getElementById('clearDrawingPadBtn').onclick = function() { - resetCanvas(); } \ No newline at end of file diff --git a/js/classifier.js b/js/classifier.js index b63bb9f..878be25 100644 --- a/js/classifier.js +++ b/js/classifier.js @@ -39,8 +39,6 @@ async function predict() { } } - - /* results data formatter (remove zero-probs and sort by most probable) */ function formatResultData(raw) { rawKeys = Object.keys(raw.confidences); @@ -86,24 +84,6 @@ function addExample(cls) { -/* create buttons for all the result options */ -function outputResult(data) { - document.getElementById('resman').innerHTML = ''; - for (let i in data) { - let newResultButton = document.createElement("button"); - newResultButton.classList.add( data[i][0] ); - newResultButton.title = data[i][0]; - newResultButton.innerHTML = (`${data[i][0]}${(data[i][1]*100) + '%'}`); - - newResultButton.setAttribute('onclick', 'resultBtnOnSelect(this);'); - newResultButton.setAttribute('oncontextmenu', 'quickCopy(this);'); - - document.getElementById('resman').appendChild(newResultButton); - } -} - - - /* update KNN neighboors quantity coefficient */ function updateKNNCoefficient() { let cValues = Object.values(classifier.getClassExampleCount()); diff --git a/js/setsLoadSave.js b/js/classifierLoadSave.js similarity index 99% rename from js/setsLoadSave.js rename to js/classifierLoadSave.js index 4709057..9335808 100644 --- a/js/setsLoadSave.js +++ b/js/classifierLoadSave.js @@ -1,8 +1,3 @@ -function saveSets() { - let datasetJSON = JSON.stringify( Object.entries(classifier.getClassifierDataset()).map(([label, data])=>[label, Array.from(data.dataSync()), data.shape]) ); - localStorage.setItem('dataset', datasetJSON); -} - function loadSets() { if (localStorage.getItem('dataset') != null) { let datasetJSON = localStorage.getItem('dataset'); @@ -34,9 +29,13 @@ function loadSets() { typeman.innerHTML = '

Nothing to train! Please add one or more examples you want to begin with.

'; } } - function setsImporter(dataset) { for (i in dataset) { createClassBtn(i); } -} \ No newline at end of file +} + +function saveSets() { + let datasetJSON = JSON.stringify( Object.entries(classifier.getClassifierDataset()).map(([label, data])=>[label, Array.from(data.dataSync()), data.shape]) ); + localStorage.setItem('dataset', datasetJSON); +} diff --git a/js/configs.js b/js/configs.js index 7e57c70..a532b89 100644 --- a/js/configs.js +++ b/js/configs.js @@ -11,20 +11,60 @@ const defaultConfigs = { -/* configs LS loader */ -function loadConfigs() { - /* add default settings to LS 'configs' if it doesn't exist */ - if (localStorage.getItem('configs') == null) { - localStorage.setItem('configs', JSON.stringify(defaultConfigs)); - console.log('[configs] Warning - configs not found or corrupted; loading from default configs...'); +/* update UI correspondingly to current Configs data */ +function updateUI() { + if (Configs.currentAppMode == 0) { + appModeSwitcher.checked = false; + appModeSwitcher.indeterminate = false; + document.getElementById('trainingMode').style.display = 'unset'; + document.getElementById('recognitionMode').style.display = 'none'; + } + if (Configs.currentAppMode == 1) { + appModeSwitcher.checked = true; + appModeSwitcher.indeterminate = false; + document.getElementById('trainingMode').style.display = 'none'; + document.getElementById('recognitionMode').style.display = 'unset'; + } + if (Configs.currentAppMode == 2) { + appModeSwitcher.indeterminate = true; + document.getElementById('trainingMode').style.display = 'unset'; + document.getElementById('recognitionMode').style.display = 'unset'; } - Configs = Object.assign({}, JSON.parse(localStorage.getItem('configs'))); -} + if (Configs.resultsViewMode) { + document.getElementById('viewAsGridBtn').style.display = 'none'; + document.getElementById('viewAsListBtn').style.display = 'block'; + document.getElementById('resman').classList.replace('viewAsGrid', 'viewAsList'); + document.getElementById('typeman').classList.replace('viewAsGrid', 'viewAsList'); + } else { + document.getElementById('viewAsGridBtn').style.display = 'block'; + document.getElementById('viewAsListBtn').style.display = 'none'; + document.getElementById('resman').classList.replace('viewAsList', 'viewAsGrid'); + document.getElementById('typeman').classList.replace('viewAsList', 'viewAsGrid'); + } + if (Configs.showDebug) { + document.getElementById('debug').style.display = 'block'; + document.getElementById('debugTogglerBtn').classList.add('selected'); + } else { + document.getElementById('debug').style.display = 'none'; + document.getElementById('debugTogglerBtn').classList.remove('selected'); + } + + if (Configs.useAutoprediction) { + document.getElementById('autopreditionToggler').checked = true; + document.getElementById('predictBtn').style.display = 'none'; + if (isInteracted) predict(); + } else { + document.getElementById('autopreditionToggler').checked = false; + document.getElementById('predictBtn').style.display = 'unset'; + } + + if (Configs.useAutotrain) { + document.getElementById('autotrainToggler').checked = true; + } else { + document.getElementById('autotrainToggler').checked = false; + } -/* configs LS saver */ -function saveConfigs() { - localStorage.setItem('configs', JSON.stringify(Configs)); - if (Configs.showDebug) console.log('[configs] Saved current configs:', JSON.parse(localStorage.getItem('configs'))); + if (Configs.showDebug) console.log('[UIUpdater] UI updated successfully.') } \ No newline at end of file diff --git a/js/configsLoadSave.js b/js/configsLoadSave.js new file mode 100644 index 0000000..36585d3 --- /dev/null +++ b/js/configsLoadSave.js @@ -0,0 +1,15 @@ +/* configs LS loader */ +function loadConfigs() { + /* add default settings to LS 'configs' if it doesn't exist */ + if (localStorage.getItem('configs') == null) { + localStorage.setItem('configs', JSON.stringify(defaultConfigs)); + console.log('[configs] Warning - configs not found or corrupted; loading from default configs...'); + } + Configs = Object.assign({}, JSON.parse(localStorage.getItem('configs'))); +} + +/* configs LS saver */ +function saveConfigs() { + localStorage.setItem('configs', JSON.stringify(Configs)); + if (Configs.showDebug) console.log('[configs] Saved current configs:', JSON.parse(localStorage.getItem('configs'))); +} \ No newline at end of file diff --git a/js/resman.js b/js/resman.js new file mode 100644 index 0000000..dcd7f56 --- /dev/null +++ b/js/resman.js @@ -0,0 +1,59 @@ +/* create buttons for all the result options */ +function outputResult(data) { + document.getElementById('resman').innerHTML = ''; + for (let i in data) { + let newResultButton = document.createElement("button"); + newResultButton.classList.add( data[i][0] ); + newResultButton.title = data[i][0]; + newResultButton.innerHTML = (`${data[i][0]}${(data[i][1]*100) + '%'}`); + + newResultButton.setAttribute('onclick', 'resultBtnOnSelect(this);'); + newResultButton.setAttribute('oncontextmenu', 'quickCopy(this);'); + + document.getElementById('resman').appendChild(newResultButton); + } +} + + + +/* result btn when left-clicked */ +function resultBtnOnSelect(btn) { + if (document.getElementById('clipboardField').value.trim().length > 0) { + document.getElementById('clipboardField').value += (' ' + btn.className); + } else { + document.getElementById('clipboardField').value += (btn.className); + } +} + +/* result btn when right-clicked */ +function quickCopy(btn) { + navigator.clipboard.writeText(btn.className); + document.getElementById('clipboardField').value = btn.className; + + btn.disabled = true; + btn.innerHTML = (`${btn.innerHTML.split('')[1]}`); + setTimeout( function() { + btn.disabled = false; + btn.innerHTML = (`${btn.className}${btn.innerHTML.split('')[1]}`); + }, 500); + + /* autotrain */ + if (Configs.useAutotrain && isInteracted) addExample(btn); + + return false; // don't show context menu +} + + + +/* clipboard field copy all button */ +document.getElementById('clipboardCopyBtn').onclick = function() { + navigator.clipboard.writeText(document.getElementById('clipboardField').value); + document.getElementById('clipboardField').value = ''; + + document.getElementById('clipboardCopyBtn').disabled = true; + document.getElementById('clipboardCopyBtn').innerHTML = ''; + setTimeout( function() { + document.getElementById('clipboardCopyBtn').disabled = false; + document.getElementById('clipboardCopyBtn').innerHTML = ''; + }, 500); +} \ No newline at end of file diff --git a/js/setsTypeman.js b/js/typeman.js similarity index 99% rename from js/setsTypeman.js rename to js/typeman.js index 7b37e5d..dfac3f8 100644 --- a/js/setsTypeman.js +++ b/js/typeman.js @@ -73,7 +73,6 @@ function toggleClassRemover() { - /* lazy typeman statistics updater */ function updateTypemanStats(setName) { let setNameValue = (classifier.getClassExampleCount())[setName];