alessandro trinca tornidor
commited on
Commit
·
2ee4481
1
Parent(s):
6fad7aa
feat: add output table filtering, sorting and ordering
Browse files- static/index.css +11 -2
- static/index.html +34 -4
- static/index.js +74 -15
static/index.css
CHANGED
|
@@ -74,6 +74,9 @@ td {
|
|
| 74 |
.margin5px-top {
|
| 75 |
margin-top: 5px;
|
| 76 |
}
|
|
|
|
|
|
|
|
|
|
| 77 |
.background-color-lightgray {
|
| 78 |
background-color: lightgray;
|
| 79 |
}
|
|
@@ -83,8 +86,8 @@ td {
|
|
| 83 |
.max-height-90vh {
|
| 84 |
max-height: 90vh;
|
| 85 |
}
|
| 86 |
-
.max-height-
|
| 87 |
-
max-height:
|
| 88 |
}
|
| 89 |
.overflow-hidden {
|
| 90 |
/* needed for scroll bar*/
|
|
@@ -93,4 +96,10 @@ td {
|
|
| 93 |
.overflow-auto {
|
| 94 |
/* needed for scroll bar*/
|
| 95 |
overflow: auto;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
}
|
|
|
|
| 74 |
.margin5px-top {
|
| 75 |
margin-top: 5px;
|
| 76 |
}
|
| 77 |
+
.margin2px-bottom {
|
| 78 |
+
margin-bottom: 2px;
|
| 79 |
+
}
|
| 80 |
.background-color-lightgray {
|
| 81 |
background-color: lightgray;
|
| 82 |
}
|
|
|
|
| 86 |
.max-height-90vh {
|
| 87 |
max-height: 90vh;
|
| 88 |
}
|
| 89 |
+
.max-height-80vh {
|
| 90 |
+
max-height: 80vh;
|
| 91 |
}
|
| 92 |
.overflow-hidden {
|
| 93 |
/* needed for scroll bar*/
|
|
|
|
| 96 |
.overflow-auto {
|
| 97 |
/* needed for scroll bar*/
|
| 98 |
overflow: auto;
|
| 99 |
+
}
|
| 100 |
+
.width-50perc {
|
| 101 |
+
width: 50%;
|
| 102 |
+
}
|
| 103 |
+
.width-80perc {
|
| 104 |
+
width: 80%;
|
| 105 |
}
|
static/index.html
CHANGED
|
@@ -17,16 +17,46 @@
|
|
| 17 |
<div id="id-container-row-global-editor-frequency" class="display-flex">
|
| 18 |
<div id="id-col1-editor" class="col-flex50 border-black padding10px margin10px overflow-hidden background-color-lightgray">
|
| 19 |
<h4>Text Editor</h4>
|
| 20 |
-
<div id="editor" contenteditable="true" class="max-height-
|
| 21 |
Hi there, how are you? There are some pasties for you. Can you give me also... Take a pasty from the table there!
|
| 22 |
</div>
|
| 23 |
</div>
|
| 24 |
-
<div id="id-col2-
|
| 25 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
<span id="id-n-total-rows" aria-label="id-n-total-rows" class="display-none"></span>
|
| 27 |
<span id="waiting-for-be" class="display-none">waiting for backend response...</span>
|
| 28 |
<span id="waiting-for-be-error" class="display-none">Error!</span>
|
| 29 |
-
<div id="words-frequency" class="max-height-
|
| 30 |
</div>
|
| 31 |
</div>
|
| 32 |
</body>
|
|
|
|
| 17 |
<div id="id-container-row-global-editor-frequency" class="display-flex">
|
| 18 |
<div id="id-col1-editor" class="col-flex50 border-black padding10px margin10px overflow-hidden background-color-lightgray">
|
| 19 |
<h4>Text Editor</h4>
|
| 20 |
+
<div id="editor" contenteditable="true" class="max-height-80vh overflow-auto background-color-whitesmoke" aria-label="editor">
|
| 21 |
Hi there, how are you? There are some pasties for you. Can you give me also... Take a pasty from the table there!
|
| 22 |
</div>
|
| 23 |
</div>
|
| 24 |
+
<div id="id-col2-words-frequency" class="col-flex50 border-blue padding10px margin10px overflow-hidden background-color-lightgray">
|
| 25 |
+
<div class="display-flex">
|
| 26 |
+
<div class="width-50perc">
|
| 27 |
+
<h4 id="id-words-frequency-table-title">Word Frequency Table</h4>
|
| 28 |
+
<input type="text" id="filter-words-frequency" aria-label="filter-words-frequency" placeholder="Filter the output table..." class="width-80perc" />
|
| 29 |
+
<button type="submit" id="btn-filter-words-frequency" aria-label="btn-filter-words-frequency" onclick="updateWordFrequencyTables()">Submit</button>
|
| 30 |
+
</div>
|
| 31 |
+
<div class="display-flex width-50perc margin2px-bottom">
|
| 32 |
+
<form id="form2" aria-label="form2">
|
| 33 |
+
<fieldset>
|
| 34 |
+
<legend>Sorting method:</legend>
|
| 35 |
+
<div>
|
| 36 |
+
<input type="radio" id="sort-by-count" aria-label="sort-by-count" name="sort" value="count"/>
|
| 37 |
+
<label for="sort-by-count" id="sort-by-count-label" aria-label="sort-by-count-label">Count</label>
|
| 38 |
+
<input type="radio" id="sort-by-name" aria-label="sort-by-name" name="sort" value="word_prefix" />
|
| 39 |
+
<label for="sort-by-name" id="sort-by-name-label" aria-label="sort-by-name-label">Name</label>
|
| 40 |
+
</div>
|
| 41 |
+
</fieldset>
|
| 42 |
+
</form>
|
| 43 |
+
<form id="form1" aria-label="form1">
|
| 44 |
+
<fieldset>
|
| 45 |
+
<legend>Order:</legend>
|
| 46 |
+
<div>
|
| 47 |
+
<input type="radio" id="order-by-count" aria-label="order-by-count" name="order" value="asc" />
|
| 48 |
+
<label for="order-by-count" id="order-by-count-label" aria-label="order-by-count-label">Ascending</label>
|
| 49 |
+
<input type="radio" id="order-by-name" aria-label="order-by-name" name="order" value="desc" checked/>
|
| 50 |
+
<label for="order-by-name" id="order-by-name-label" aria-label="order-by-name-label">Descending</label>
|
| 51 |
+
</div>
|
| 52 |
+
</fieldset>
|
| 53 |
+
</form>
|
| 54 |
+
</div>
|
| 55 |
+
</div>
|
| 56 |
<span id="id-n-total-rows" aria-label="id-n-total-rows" class="display-none"></span>
|
| 57 |
<span id="waiting-for-be" class="display-none">waiting for backend response...</span>
|
| 58 |
<span id="waiting-for-be-error" class="display-none">Error!</span>
|
| 59 |
+
<div id="words-frequency" class="max-height-80vh overflow-auto background-color-whitesmoke" aria-label="words-frequency"></div>
|
| 60 |
</div>
|
| 61 |
</div>
|
| 62 |
</body>
|
static/index.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
| 1 |
const wordsFrequencyTableTitleText = "Word Frequency Table"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
/**
|
| 4 |
* Read and preview a selected text file.
|
|
@@ -33,7 +44,6 @@ const previewFile = () => {
|
|
| 33 |
const scrollToGivenPoint = (editorElement, line, nTotalRows, negativeOffsetPerc=0.12) => {
|
| 34 |
// try to scroll div to row... font-size on div is 12px
|
| 35 |
let scrollHeight = parseFloat(editorElement.scrollHeight, 10)
|
| 36 |
-
console.log(`setCaret::updatedOffsetPosition:scroll to line/height position/int height position:", ${line}, ${editorElement.scrollHeight}, ${scrollHeight}`)
|
| 37 |
let offsetToScrollPerc = line / nTotalRows
|
| 38 |
let offsetToScroll = scrollHeight * offsetToScrollPerc
|
| 39 |
// if already at the end of the page, don't scroll anymore to avoid missing words in the upper side of the viewport
|
|
@@ -41,7 +51,6 @@ const scrollToGivenPoint = (editorElement, line, nTotalRows, negativeOffsetPerc=
|
|
| 41 |
offsetToScroll -= offsetToScroll * negativeOffsetPerc
|
| 42 |
}
|
| 43 |
let offsetToScrollInt = parseInt(offsetToScroll, 10)
|
| 44 |
-
console.log(`setCaret::offsetToScroll: '${offsetToScrollInt}', '${offsetToScroll}'`)
|
| 45 |
editorElement.scrollTo(0, offsetToScrollInt)
|
| 46 |
}
|
| 47 |
|
|
@@ -99,7 +108,7 @@ const getWordFrequency = async () => {
|
|
| 99 |
let bodyRequest = {"text": text.innerText}
|
| 100 |
setElementCssClass("waiting-for-be-error", "display-none")
|
| 101 |
setElementCssClass("waiting-for-be", "display-block")
|
| 102 |
-
let wordsFrequencyTableTitleEl = document.getElementById("id-
|
| 103 |
wordsFrequencyTableTitleEl.innerText = wordsFrequencyTableTitleText
|
| 104 |
let wordsFrequency = document.getElementById("words-frequency")
|
| 105 |
wordsFrequency.innerHTML = ""
|
|
@@ -108,14 +117,12 @@ const getWordFrequency = async () => {
|
|
| 108 |
method: "POST",
|
| 109 |
body: JSON.stringify(bodyRequest)
|
| 110 |
})
|
| 111 |
-
console.log(`getWordFreq::response.status: '${response.status}'`)
|
| 112 |
console.assert(response.status, 200)
|
| 113 |
let bodyResponseJson = await response.json()
|
| 114 |
setElementCssClass("waiting-for-be", "display-none")
|
| 115 |
-
console.log(`getWordFreq::body keys: '${Object.keys(bodyResponseJson)}'`)
|
| 116 |
let freq = bodyResponseJson["words_frequency"]
|
| 117 |
let nTotalRows = bodyResponseJson["n_total_rows"]
|
| 118 |
-
console.log(`getWordFreq::
|
| 119 |
populateWordFrequencyTables(freq, bodyResponseJson["n_total_rows"])
|
| 120 |
} catch (err) {
|
| 121 |
console.error("getWordFrequency::err:", err, "#")
|
|
@@ -124,6 +131,64 @@ const getWordFrequency = async () => {
|
|
| 124 |
}
|
| 125 |
}
|
| 126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
/**
|
| 128 |
* Populate the word frequency tables in the UI with data from the provided JSON object.
|
| 129 |
*
|
|
@@ -131,15 +196,9 @@ const getWordFrequency = async () => {
|
|
| 131 |
* @param {number} nTotalRows - The total number of lines/rows to display for each word group.
|
| 132 |
*/
|
| 133 |
const populateWordFrequencyTables = (wordsFrequencyObj, nTotalRows) => {
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
wordsFrequency.innerHTML = ""
|
| 138 |
-
let wordsFrequencyTableTitleEl = document.getElementById("id-word-frequency-table-title")
|
| 139 |
-
wordsFrequencyTableTitleEl.innerText = `${wordsFrequencyTableTitleText} (${reduced.length} word groups, ${nTotalRows} rows)`
|
| 140 |
-
for (let i=0; i<reduced.length; i++ ) {
|
| 141 |
-
insertCurrentTable(i, reduced[i], nTotalRows, wordsFrequency);
|
| 142 |
-
}
|
| 143 |
}
|
| 144 |
|
| 145 |
/**
|
|
|
|
| 1 |
const wordsFrequencyTableTitleText = "Word Frequency Table"
|
| 2 |
+
let wfo = {
|
| 3 |
+
"word_frequency": {},
|
| 4 |
+
"nTotalRows": null
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
const getFormDataByKey = (formId, key) => {
|
| 8 |
+
let formElement = document.getElementById(formId)
|
| 9 |
+
const data = new FormData(formElement);
|
| 10 |
+
let dataValue = data.get(key)
|
| 11 |
+
return dataValue
|
| 12 |
+
}
|
| 13 |
|
| 14 |
/**
|
| 15 |
* Read and preview a selected text file.
|
|
|
|
| 44 |
const scrollToGivenPoint = (editorElement, line, nTotalRows, negativeOffsetPerc=0.12) => {
|
| 45 |
// try to scroll div to row... font-size on div is 12px
|
| 46 |
let scrollHeight = parseFloat(editorElement.scrollHeight, 10)
|
|
|
|
| 47 |
let offsetToScrollPerc = line / nTotalRows
|
| 48 |
let offsetToScroll = scrollHeight * offsetToScrollPerc
|
| 49 |
// if already at the end of the page, don't scroll anymore to avoid missing words in the upper side of the viewport
|
|
|
|
| 51 |
offsetToScroll -= offsetToScroll * negativeOffsetPerc
|
| 52 |
}
|
| 53 |
let offsetToScrollInt = parseInt(offsetToScroll, 10)
|
|
|
|
| 54 |
editorElement.scrollTo(0, offsetToScrollInt)
|
| 55 |
}
|
| 56 |
|
|
|
|
| 108 |
let bodyRequest = {"text": text.innerText}
|
| 109 |
setElementCssClass("waiting-for-be-error", "display-none")
|
| 110 |
setElementCssClass("waiting-for-be", "display-block")
|
| 111 |
+
let wordsFrequencyTableTitleEl = document.getElementById("id-words-frequency-table-title")
|
| 112 |
wordsFrequencyTableTitleEl.innerText = wordsFrequencyTableTitleText
|
| 113 |
let wordsFrequency = document.getElementById("words-frequency")
|
| 114 |
wordsFrequency.innerHTML = ""
|
|
|
|
| 117 |
method: "POST",
|
| 118 |
body: JSON.stringify(bodyRequest)
|
| 119 |
})
|
|
|
|
| 120 |
console.assert(response.status, 200)
|
| 121 |
let bodyResponseJson = await response.json()
|
| 122 |
setElementCssClass("waiting-for-be", "display-none")
|
|
|
|
| 123 |
let freq = bodyResponseJson["words_frequency"]
|
| 124 |
let nTotalRows = bodyResponseJson["n_total_rows"]
|
| 125 |
+
console.log(`getWordFreq::nTotalRows: '${nTotalRows}'`)
|
| 126 |
populateWordFrequencyTables(freq, bodyResponseJson["n_total_rows"])
|
| 127 |
} catch (err) {
|
| 128 |
console.error("getWordFrequency::err:", err, "#")
|
|
|
|
| 131 |
}
|
| 132 |
}
|
| 133 |
|
| 134 |
+
function dynamicsort(property, order) {
|
| 135 |
+
let sort_order = 1;
|
| 136 |
+
if(order === "desc"){
|
| 137 |
+
sort_order = -1;
|
| 138 |
+
}
|
| 139 |
+
return function (a, b){
|
| 140 |
+
// a should come before b in the sorted order
|
| 141 |
+
if(a[property] < b[property]){
|
| 142 |
+
return -1 * sort_order;
|
| 143 |
+
// a should come after b in the sorted order
|
| 144 |
+
}else if(a[property] > b[property]){
|
| 145 |
+
return 1 * sort_order;
|
| 146 |
+
// a and b are the same
|
| 147 |
+
}else{
|
| 148 |
+
return 0 * sort_order;
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
function filteredArray(arr, key, value) {
|
| 154 |
+
const newArray = [];
|
| 155 |
+
for(let i=0, l=arr.length; i<l; i++) {
|
| 156 |
+
let currentElement = arr[i]
|
| 157 |
+
let currentValue = currentElement[key]
|
| 158 |
+
if(currentValue.toLowerCase().includes(value)) {
|
| 159 |
+
newArray.push(arr[i]);
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
return newArray;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
const updateWordFrequencyTables = () => {
|
| 166 |
+
let nTotalRows = wfo["nTotalRows"]
|
| 167 |
+
if (nTotalRows === null || nTotalRows < 1) {
|
| 168 |
+
alert("let's get some data before updating the result table...")
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
let _wfo = wfo["word_frequency"]
|
| 172 |
+
let reduced = Object.values(_wfo)
|
| 173 |
+
let order = getFormDataByKey("form1", "order")
|
| 174 |
+
let sort = getFormDataByKey("form2", "sort")
|
| 175 |
+
reduced.sort(dynamicsort(sort, order))
|
| 176 |
+
|
| 177 |
+
let inputFilter = document.getElementById("filter-words-frequency")
|
| 178 |
+
let inputFilterValue = inputFilter.value
|
| 179 |
+
if (inputFilterValue != undefined && inputFilter.value !== "") {
|
| 180 |
+
reduced = filteredArray(reduced, "word_prefix", inputFilterValue)
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
let wordsFrequency = document.getElementById("words-frequency")
|
| 184 |
+
wordsFrequency.innerHTML = ""
|
| 185 |
+
let wordsFrequencyTableTitleEl = document.getElementById("id-words-frequency-table-title")
|
| 186 |
+
wordsFrequencyTableTitleEl.innerText = `${wordsFrequencyTableTitleText} (${reduced.length} word groups, ${nTotalRows} rows)`
|
| 187 |
+
for (let i=0; i<reduced.length; i++ ) {
|
| 188 |
+
insertCurrentTable(i, reduced[i], nTotalRows, wordsFrequency);
|
| 189 |
+
}
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
/**
|
| 193 |
* Populate the word frequency tables in the UI with data from the provided JSON object.
|
| 194 |
*
|
|
|
|
| 196 |
* @param {number} nTotalRows - The total number of lines/rows to display for each word group.
|
| 197 |
*/
|
| 198 |
const populateWordFrequencyTables = (wordsFrequencyObj, nTotalRows) => {
|
| 199 |
+
wfo["word_frequency"] = JSON.parse(wordsFrequencyObj)
|
| 200 |
+
wfo["nTotalRows"] = nTotalRows
|
| 201 |
+
updateWordFrequencyTables()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
}
|
| 203 |
|
| 204 |
/**
|