alessandro trinca tornidor
commited on
Commit
·
125ee1c
1
Parent(s):
3dc190b
feat: handle mobile portrait mode for current words table, improve top menù layout
Browse files- my_ghost_writer/text_parsers.py +1 -2
- static/index.css +28 -2
- static/index.html +29 -21
- static/index.js +42 -3
my_ghost_writer/text_parsers.py
CHANGED
|
@@ -19,7 +19,7 @@ def text_stemming(text: str | RequestTextRowsParentList) -> ResponseTextRowsDict
|
|
| 19 |
text (str): Input string containing the text to be stemmed.
|
| 20 |
|
| 21 |
Returns:
|
| 22 |
-
tuple[int, dict]: a tuple with the number of processed total rows within the initial text and the
|
| 23 |
"""
|
| 24 |
import json
|
| 25 |
from nltk.tokenize import wordpunct_tokenize, WordPunctTokenizer
|
|
@@ -68,7 +68,6 @@ def get_words_tokens_and_indexes(
|
|
| 68 |
Args:
|
| 69 |
words_tokens_list (list): List of words tokens.
|
| 70 |
offsets_tokens_list (list): List of offsets for each token.
|
| 71 |
-
ps (PorterStemmer): The stemmer to use.
|
| 72 |
idx_rows_list (list[int]): List of row indices corresponding to the tokens.
|
| 73 |
idx_rows_child (list[int]): List of child row indices corresponding to the tokens.
|
| 74 |
idx_rows_parent (list[int]): List of parent row indices corresponding to the tokens.
|
|
|
|
| 19 |
text (str): Input string containing the text to be stemmed.
|
| 20 |
|
| 21 |
Returns:
|
| 22 |
+
tuple[int, dict]: a tuple with the number of processed total rows within the initial text and the word frequency dict
|
| 23 |
"""
|
| 24 |
import json
|
| 25 |
from nltk.tokenize import wordpunct_tokenize, WordPunctTokenizer
|
|
|
|
| 68 |
Args:
|
| 69 |
words_tokens_list (list): List of words tokens.
|
| 70 |
offsets_tokens_list (list): List of offsets for each token.
|
|
|
|
| 71 |
idx_rows_list (list[int]): List of row indices corresponding to the tokens.
|
| 72 |
idx_rows_child (list[int]): List of child row indices corresponding to the tokens.
|
| 73 |
idx_rows_parent (list[int]): List of parent row indices corresponding to the tokens.
|
static/index.css
CHANGED
|
@@ -75,10 +75,10 @@
|
|
| 75 |
margin: 2px;
|
| 76 |
}
|
| 77 |
.margin10px-left {
|
| 78 |
-
margin-left:
|
| 79 |
}
|
| 80 |
.margin5px-right {
|
| 81 |
-
margin-right:
|
| 82 |
}
|
| 83 |
.margin5px-top {
|
| 84 |
margin-top: 5px;
|
|
@@ -125,6 +125,10 @@
|
|
| 125 |
max-height: calc(100vh - 200px);
|
| 126 |
overflow: auto;
|
| 127 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
/* Classic UI Main Text, lite.koboldai.net */
|
| 130 |
#gamescreen {
|
|
@@ -155,6 +159,17 @@
|
|
| 155 |
height: calc(100vh - 200px);
|
| 156 |
}
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
@media (prefers-color-scheme: dark) {
|
| 159 |
.underlinedBlue {
|
| 160 |
color: yellow;
|
|
@@ -196,3 +211,14 @@
|
|
| 196 |
content: "(Mobile users: use this in landscape mode!)"
|
| 197 |
}
|
| 198 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
margin: 2px;
|
| 76 |
}
|
| 77 |
.margin10px-left {
|
| 78 |
+
margin-left: 5px;
|
| 79 |
}
|
| 80 |
.margin5px-right {
|
| 81 |
+
margin-right: 5px;
|
| 82 |
}
|
| 83 |
.margin5px-top {
|
| 84 |
margin-top: 5px;
|
|
|
|
| 125 |
max-height: calc(100vh - 200px);
|
| 126 |
overflow: auto;
|
| 127 |
}
|
| 128 |
+
#id-input-webserver-wordfreq-checkbox-container {
|
| 129 |
+
display: flex;
|
| 130 |
+
justify-content: right;
|
| 131 |
+
}
|
| 132 |
|
| 133 |
/* Classic UI Main Text, lite.koboldai.net */
|
| 134 |
#gamescreen {
|
|
|
|
| 159 |
height: calc(100vh - 200px);
|
| 160 |
}
|
| 161 |
|
| 162 |
+
.grid-container-elements-menu-top {
|
| 163 |
+
margin: 0 auto;
|
| 164 |
+
display: grid;
|
| 165 |
+
gap: 2px;
|
| 166 |
+
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
| 167 |
+
}
|
| 168 |
+
.grid-element-menu-top {
|
| 169 |
+
padding: 2px;
|
| 170 |
+
height: auto;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
@media (prefers-color-scheme: dark) {
|
| 174 |
.underlinedBlue {
|
| 175 |
color: yellow;
|
|
|
|
| 211 |
content: "(Mobile users: use this in landscape mode!)"
|
| 212 |
}
|
| 213 |
}
|
| 214 |
+
|
| 215 |
+
@media only screen
|
| 216 |
+
and (min-device-width: 320px)
|
| 217 |
+
and (max-device-width: 480px)
|
| 218 |
+
and (-webkit-min-device-pixel-ratio: 2)
|
| 219 |
+
and (orientation: portrait) {
|
| 220 |
+
#id-input-webserver-wordfreq-checkbox-container {
|
| 221 |
+
display: flex;
|
| 222 |
+
justify-content: left !important;
|
| 223 |
+
}
|
| 224 |
+
}
|
static/index.html
CHANGED
|
@@ -11,28 +11,33 @@
|
|
| 11 |
<div id="id-orientation-message" class="id-orientation-message" aria-hidden="true"><!-- ::before (Mobile users: use this project in landscape mode!) --></div>
|
| 12 |
<div id="id-container-menu">
|
| 13 |
<div id="id-container-mobile-menu" class="collapse">
|
| 14 |
-
<button id="id-menu-mobile-top" onclick="toggleElementWithClassById('id-container-desktop-menu')">Global
|
| 15 |
-
<button id="id-menu-mobile-filter-sort-order" onclick="toggleElementWithClassById('id-container-filter-sort-order')">
|
| 16 |
</div>
|
| 17 |
<div id="id-container-desktop-menu">
|
| 18 |
<h1 id="id-title" class="h1">Word Frequency Statistics in text</h1>
|
| 19 |
-
<div class="margin4px">
|
| 20 |
-
<button id="btn4-get-words-frequency" aria-label="btn4-get-words-frequency" onclick="getWordsFrequency()" type="submit">get words frequency</button>
|
| 21 |
-
<
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
</div>
|
| 37 |
</div>
|
| 38 |
</div>
|
|
@@ -109,8 +114,11 @@ Another row.<br>More one.<br>A last row, I hope.
|
|
| 109 |
<span id="waiting-for-be-error" class="display-none">Error!</span>
|
| 110 |
<div id="words-frequency" class="calc100vh_less_200px overflow-hidden background-color-whitesmoke display-flex" aria-label="words-frequency">
|
| 111 |
<div id="id-list-of-words" class="margin4px overflow-auto" aria-label="id-list-of-words"></div>
|
| 112 |
-
<div id="id-current-table-of-words-container" class="">
|
| 113 |
-
<
|
|
|
|
|
|
|
|
|
|
| 114 |
<div id="id-current-table-of-words" class="overflow-auto" aria-label="id-current-table-of-words"></div>
|
| 115 |
</div>
|
| 116 |
</div>
|
|
|
|
| 11 |
<div id="id-orientation-message" class="id-orientation-message" aria-hidden="true"><!-- ::before (Mobile users: use this project in landscape mode!) --></div>
|
| 12 |
<div id="id-container-menu">
|
| 13 |
<div id="id-container-mobile-menu" class="collapse">
|
| 14 |
+
<button id="id-menu-mobile-top" onclick="toggleElementWithClassById('id-container-desktop-menu')">Global menù word freq. stats</button>
|
| 15 |
+
<button id="id-menu-mobile-filter-sort-order" onclick="toggleElementWithClassById('id-container-filter-sort-order')">Menù filter word freq.</button>
|
| 16 |
</div>
|
| 17 |
<div id="id-container-desktop-menu">
|
| 18 |
<h1 id="id-title" class="h1">Word Frequency Statistics in text</h1>
|
| 19 |
+
<div class="margin4px grid-container-elements-menu-top">
|
| 20 |
+
<button id="btn4-get-words-frequency" aria-label="btn4-get-words-frequency" onclick="getWordsFrequency()" type="submit" class="grid-element-menu-top">get words frequency</button>
|
| 21 |
+
<span class="grid-element-menu-top">
|
| 22 |
+
<label for="id-input-file-selector" class="">Text file selector</label>
|
| 23 |
+
<input type="file" onchange="previewFile()" id="id-input-file-selector" aria-label="id-input-file-selector" placeholder="text file selector"/>
|
| 24 |
+
</span>
|
| 25 |
+
<span id="id-input-webserver-wordfreq-checkbox-container" class="grid-element-menu-top">
|
| 26 |
+
<label for="id-input-webserver-wordfreq-checkbox" class="margin5px-left grid-element-menu-top">Use a custom webserver endpoint</label>
|
| 27 |
+
<input
|
| 28 |
+
type="checkbox"
|
| 29 |
+
id="id-input-webserver-wordfreq-checkbox"
|
| 30 |
+
aria-label="id-input-webserver-wordfreq-checkbox"
|
| 31 |
+
name="id-input-webserver-wordfreq-checkbox"
|
| 32 |
+
class="grid-element-menu-top"
|
| 33 |
+
onchange="toggleWebserverCheckbox()"
|
| 34 |
+
checked
|
| 35 |
+
/>
|
| 36 |
+
</span>
|
| 37 |
+
<span class="grid-element-menu-top">
|
| 38 |
+
<input type="URL" id="id-input-webserver-wordfreq" aria-label="id-input-webserver-wordfreq" value="http://localhost:7860"/>
|
| 39 |
+
<span>Now using: <span id="id-wordfreq-show-analyzer" aria-label="id-wordfreq-show-analyzer">webserver</span></span>
|
| 40 |
+
</span>
|
| 41 |
</div>
|
| 42 |
</div>
|
| 43 |
</div>
|
|
|
|
| 114 |
<span id="waiting-for-be-error" class="display-none">Error!</span>
|
| 115 |
<div id="words-frequency" class="calc100vh_less_200px overflow-hidden background-color-whitesmoke display-flex" aria-label="words-frequency">
|
| 116 |
<div id="id-list-of-words" class="margin4px overflow-auto" aria-label="id-list-of-words"></div>
|
| 117 |
+
<div id="id-current-table-of-words-container" class="display-block">
|
| 118 |
+
<div class="display-flex">
|
| 119 |
+
<span id="id-current-table-of-words-btn-back" onclick="backToListFromCurrentTable()" class="collapse">↩️</span>
|
| 120 |
+
<span id="id-current-table-of-words-title"></span>
|
| 121 |
+
</div>
|
| 122 |
<div id="id-current-table-of-words" class="overflow-auto" aria-label="id-current-table-of-words"></div>
|
| 123 |
</div>
|
| 124 |
</div>
|
static/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const objectChildNodeNamesToParse = {
|
|
| 16 |
"SPAN": "textContent"
|
| 17 |
}
|
| 18 |
const mobileInnerSize = 767
|
|
|
|
| 19 |
|
| 20 |
/**
|
| 21 |
* Object containing functions for word frequency analysis.
|
|
@@ -663,6 +664,10 @@ function populateWordsFrequencyTables(wordsFrequencyObj, nTotalRows, rowArray) {
|
|
| 663 |
updateWordsFrequencyTables()
|
| 664 |
}
|
| 665 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 666 |
/**
|
| 667 |
* Inserts a table into the DOM displaying the frequency of word prefixes and their corresponding row nths and offsets.
|
| 668 |
*
|
|
@@ -679,7 +684,7 @@ function insertCurrentTable(i, iReduced, currentTableOfWords) {
|
|
| 679 |
// let currentCaption = currentTableWordsFreq.createCaption()
|
| 680 |
// currentCaption.setAttribute("aria-label", `id-table-${i}-caption`
|
| 681 |
const titleCurrentTable = document.getElementById("id-current-table-of-words-title")
|
| 682 |
-
titleCurrentTable.innerText =
|
| 683 |
let currentTBody = document.createElement("tbody")
|
| 684 |
let offsetsArray = iReduced.offsets_array
|
| 685 |
for (let ii = 0; ii < offsetsArray.length; ii++) {
|
|
@@ -704,13 +709,17 @@ function insertCurrentTable(i, iReduced, currentTableOfWords) {
|
|
| 704 |
function insertListOfWords(i, iReduced, wordListElement, currentTableOfWords) {
|
| 705 |
const li = document.createElement("li");
|
| 706 |
const a = document.createElement("a")
|
| 707 |
-
a.innerText =
|
| 708 |
a.addEventListener("click", function() {
|
| 709 |
currentTableOfWords.innerHTML = ""
|
| 710 |
console.log(`insertListOfWords::'a', ${iReduced["word_prefix"]}: ${iReduced["count"]} repetitions`)
|
| 711 |
insertCurrentTable(i, iReduced, currentTableOfWords)
|
| 712 |
setElementCssClassByOldClass(underlinedClicked, underlinedPrimary)
|
| 713 |
a.className = underlinedClicked
|
|
|
|
|
|
|
|
|
|
|
|
|
| 714 |
});
|
| 715 |
a.className = underlinedPrimary
|
| 716 |
li.appendChild(a);
|
|
@@ -746,7 +755,10 @@ function insertCellIntoTRow(currentTBody, i, ii, nthOffset) {
|
|
| 746 |
const wfoContainerWidth = getStylePropertyById("id-col2-words-frequency", "width", "int")
|
| 747 |
const listOfWordsWidth = getStylePropertyById("id-list-of-words", "width", "int")
|
| 748 |
const sentencesContainerWidth = wfoContainerWidth - listOfWordsWidth
|
| 749 |
-
|
|
|
|
|
|
|
|
|
|
| 750 |
console.log(`insertCellIntoTRow::sentencesContainerWidth: ${sentencesContainerWidth}px, nCharsMore: ${nCharsMore}.`)
|
| 751 |
const {substring0, substringWord, substring2} = getSubstringForTextWithGivenOffset(rowArray, nthRowIdx, nthOffset, nCharsMore)
|
| 752 |
|
|
@@ -999,12 +1011,39 @@ async function updateWordsFreqIfPressEnter() {
|
|
| 999 |
}
|
| 1000 |
}
|
| 1001 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1002 |
function toggleWebserverCheckbox() {
|
| 1003 |
const checked = document.getElementById("id-input-webserver-wordfreq-checkbox").checked
|
| 1004 |
document.getElementById('id-input-webserver-wordfreq').disabled=!checked;
|
| 1005 |
document.getElementById('id-wordfreq-show-analyzer').innerText=checked?'webserver':'embedded';
|
| 1006 |
}
|
| 1007 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1008 |
function handleMobileWindow() {
|
| 1009 |
if (isMobile()) {
|
| 1010 |
closeWordsFreqTopNav("id-container-desktop-menu")
|
|
|
|
| 16 |
"SPAN": "textContent"
|
| 17 |
}
|
| 18 |
const mobileInnerSize = 767
|
| 19 |
+
const minNCharsMore = 10
|
| 20 |
|
| 21 |
/**
|
| 22 |
* Object containing functions for word frequency analysis.
|
|
|
|
| 664 |
updateWordsFrequencyTables()
|
| 665 |
}
|
| 666 |
|
| 667 |
+
function getRepetitionsText(iReduced) {
|
| 668 |
+
return isMobile() || isMobilePortrait() ? `${iReduced["word_prefix"]}: ${iReduced["count"]} reps.` : `${iReduced["word_prefix"]}: ${iReduced["count"]} repetitions`
|
| 669 |
+
}
|
| 670 |
+
|
| 671 |
/**
|
| 672 |
* Inserts a table into the DOM displaying the frequency of word prefixes and their corresponding row nths and offsets.
|
| 673 |
*
|
|
|
|
| 684 |
// let currentCaption = currentTableWordsFreq.createCaption()
|
| 685 |
// currentCaption.setAttribute("aria-label", `id-table-${i}-caption`
|
| 686 |
const titleCurrentTable = document.getElementById("id-current-table-of-words-title")
|
| 687 |
+
titleCurrentTable.innerText = getRepetitionsText(iReduced)
|
| 688 |
let currentTBody = document.createElement("tbody")
|
| 689 |
let offsetsArray = iReduced.offsets_array
|
| 690 |
for (let ii = 0; ii < offsetsArray.length; ii++) {
|
|
|
|
| 709 |
function insertListOfWords(i, iReduced, wordListElement, currentTableOfWords) {
|
| 710 |
const li = document.createElement("li");
|
| 711 |
const a = document.createElement("a")
|
| 712 |
+
a.innerText = getRepetitionsText(iReduced)
|
| 713 |
a.addEventListener("click", function() {
|
| 714 |
currentTableOfWords.innerHTML = ""
|
| 715 |
console.log(`insertListOfWords::'a', ${iReduced["word_prefix"]}: ${iReduced["count"]} repetitions`)
|
| 716 |
insertCurrentTable(i, iReduced, currentTableOfWords)
|
| 717 |
setElementCssClassByOldClass(underlinedClicked, underlinedPrimary)
|
| 718 |
a.className = underlinedClicked
|
| 719 |
+
console.log("insertListOfWords::click event:", isMobilePortrait(), "#")
|
| 720 |
+
if(isMobilePortrait()) {
|
| 721 |
+
gotoCurrentTableOfWords()
|
| 722 |
+
}
|
| 723 |
});
|
| 724 |
a.className = underlinedPrimary
|
| 725 |
li.appendChild(a);
|
|
|
|
| 755 |
const wfoContainerWidth = getStylePropertyById("id-col2-words-frequency", "width", "int")
|
| 756 |
const listOfWordsWidth = getStylePropertyById("id-list-of-words", "width", "int")
|
| 757 |
const sentencesContainerWidth = wfoContainerWidth - listOfWordsWidth
|
| 758 |
+
let nCharsMore = Math.floor(sentencesContainerWidth / 20)
|
| 759 |
+
if (nCharsMore < minNCharsMore) {
|
| 760 |
+
nCharsMore = minNCharsMore
|
| 761 |
+
}
|
| 762 |
console.log(`insertCellIntoTRow::sentencesContainerWidth: ${sentencesContainerWidth}px, nCharsMore: ${nCharsMore}.`)
|
| 763 |
const {substring0, substringWord, substring2} = getSubstringForTextWithGivenOffset(rowArray, nthRowIdx, nthOffset, nCharsMore)
|
| 764 |
|
|
|
|
| 1011 |
}
|
| 1012 |
}
|
| 1013 |
|
| 1014 |
+
function gotoCurrentTableOfWords() {
|
| 1015 |
+
if (isMobilePortrait()) {
|
| 1016 |
+
console.log("gotoCurrentTableOfWords::isMobilePortrait()...")
|
| 1017 |
+
addClassById("id-current-table-of-words-btn-back", "display-block")
|
| 1018 |
+
removeClassById("id-current-table-of-words-btn-back", "collapse")
|
| 1019 |
+
addClassById("id-current-table-of-words-container", "display-block")
|
| 1020 |
+
removeClassById("id-current-table-of-words-container", "collapse")
|
| 1021 |
+
addClassById("id-list-of-words", "collapse")
|
| 1022 |
+
removeClassById("id-list-of-words", "display-block")
|
| 1023 |
+
}
|
| 1024 |
+
}
|
| 1025 |
+
|
| 1026 |
function toggleWebserverCheckbox() {
|
| 1027 |
const checked = document.getElementById("id-input-webserver-wordfreq-checkbox").checked
|
| 1028 |
document.getElementById('id-input-webserver-wordfreq').disabled=!checked;
|
| 1029 |
document.getElementById('id-wordfreq-show-analyzer').innerText=checked?'webserver':'embedded';
|
| 1030 |
}
|
| 1031 |
|
| 1032 |
+
function backToListFromCurrentTable() {
|
| 1033 |
+
if (isMobilePortrait()) {
|
| 1034 |
+
removeClassById("id-current-table-of-words-container", "display-block")
|
| 1035 |
+
addClassById("id-current-table-of-words-container", "collapse")
|
| 1036 |
+
removeClassById("id-list-of-words", "collapse")
|
| 1037 |
+
addClassById("id-list-of-words", "display-block")
|
| 1038 |
+
}
|
| 1039 |
+
}
|
| 1040 |
+
|
| 1041 |
+
function isMobilePortrait() {
|
| 1042 |
+
console.log("isMobilePortrait::window.innerWidth:", window.innerWidth, window.screen.orientation, "#")
|
| 1043 |
+
const orientation = window.screen.orientation
|
| 1044 |
+
return window.innerWidth <= mobileInnerSize && (orientation.type === "portrait-primary" || orientation.type === "portrait-secondary")
|
| 1045 |
+
}
|
| 1046 |
+
|
| 1047 |
function handleMobileWindow() {
|
| 1048 |
if (isMobile()) {
|
| 1049 |
closeWordsFreqTopNav("id-container-desktop-menu")
|