Major code clean, XMLHTTP question answer getting

This commit is contained in:
MrFry 2020-01-24 19:44:51 +01:00
parent 5773a950f5
commit 4e9525c167

View file

@ -56,14 +56,13 @@
function info () { return GM_info }
/* eslint-enable */
var data // all data, which is in the resource txt
var addEventListener // add event listener function
const serverAdress = 'https://qmining.frylabs.net/'
// const serverAdress = 'http://localhost:8080/'
// const serverAdress = 'https://qmining.frylabs.net/'
const serverAdress = 'http://localhost:8080/'
// forcing pages for testing. unless you test, do not set these to true!
// only one of these should be true for testing
const forceTestPage = false
const forceTestPage = true
const forceResultPage = false
const forceDefaultPage = false
const logElementGetting = false
@ -98,8 +97,6 @@
var texts = huTexts
const minResultMatchPercent = 99 /* Minimum ammount to consider that two questions match during saving */
// : question-classes {{{
const commonUselessAnswerParts = [
'A helyes válasz az ',
@ -114,7 +111,6 @@
const commonUselessStringParts = [',', '\\.', ':', '!', '\\+']
const specialChars = [ '&', '\\+' ]
const lengthDiffMultiplier = 10 /* Percent minus for length difference */
const minMatchAmmount = 60 /* Minimum ammount to consider that two questions match during answering */
const notSameDataTypePenalty = 30 // substracted from match percent if 2 questions are not same type
const assert = (val) => {
@ -354,190 +350,6 @@
}
}
class Subject {
constructor (n) {
assert(n)
this.Name = n
this.Questions = []
this.active = false
}
setIndex (i) {
this.index = i
}
getIndex () {
return this.index
}
get length () {
return this.Questions.length
}
setActive (val) {
this.active = !!val
}
getIfActive () {
return this.active
}
AddQuestion (q) {
assert(q)
this.Questions.push(q)
}
getSubjNameWithoutYear () {
let t = this.Name.split(' - ')
if (t[0].match(/^[0-9]{4}\/[0-9]{2}\/[0-9]{1}$/i)) {
return t[1] || this.Name
} else {
return this.Name
}
}
getYear () {
let t = this.Name.split(' - ')[0]
if (t.match(/^[0-9]{4}\/[0-9]{2}\/[0-9]{1}$/i)) {
return t
} else {
return ''
}
}
Search (q, data) {
assert(q)
var r = []
for (let i = 0; i < this.length; i++) {
let percent = this.Questions[i].Compare(q, data)
if (percent > minMatchAmmount) {
r.push({
q: this.Questions[i],
match: percent
})
}
}
for (let i = 0; i < r.length; i++) {
for (var j = i; j < r.length; j++) {
if (r[i].match < r[j].match) {
var tmp = r[i]
r[i] = r[j]
r[j] = tmp
}
}
}
return r
}
toString () {
var r = []
for (var i = 0; i < this.Questions.length; i++) { r.push(this.Questions[i].toString()) }
return '+' + this.Name + '\n' + r.join('\n')
}
}
class QuestionDB {
constructor (getVal, setVal, delVal) {
this.Subjects = []
this.getVal = getVal
this.setVal = setVal
this.delVal = delVal
}
get length () {
return this.Subjects.length
}
get activeIndexes () {
return this.Subjects.reduce((acc, item, i) => {
if (item.getIfActive()) {
acc.push(i)
}
return acc
}, [])
}
GetIfActive (ind) {
return this.Subjects[ind].getIfActive()
}
ChangeActive (subjName, value) {
this.Subjects.find((x) => {
return x.Name === subjName
}).setActive(value)
let actives = JSON.parse(getVal('actives'))
if (value) {
actives.push(subjName)
} else {
actives = actives.reduce((acc, item) => {
if (item !== subjName) {
acc.push(item)
}
return acc
}, [])
}
setVal('actives', JSON.stringify(actives))
}
AddQuestion (subj, q) {
assert(subj)
var i = 0
while (i < this.Subjects.length && this.Subjects[i].Name !== subj) { i++ }
if (i < this.Subjects.length) { this.Subjects[i].AddQuestion(q) } else {
const n = new Subject(subj)
n.AddQuestion(q)
this.Subjects.push(n)
}
}
Search (q, data) {
assert(q)
var r = []
for (let i = 0; i < this.length; i++) {
if (this.GetIfActive(i)) { r = r.concat(this.Subjects[i].Search(q, data)) }
}
for (let i = 0; i < r.length; i++) {
for (var j = i; j < r.length; j++) {
if (r[i].match < r[j].match) {
var tmp = r[i]
r[i] = r[j]
r[j] = tmp
}
}
}
return r
}
AddSubject (subj) {
assert(subj)
var i = 0
while (i < this.length && subj.Name !== this.Subjects[i].Name) { i++ }
if (i < this.length) {
this.Subjects.concat(subj.Questions)
} else {
this.Subjects.push(subj)
}
}
toString () {
var r = []
for (var i = 0; i < this.Subjects.length; i++) { r.push(this.Subjects[i].toString()) }
return r.join('\n\n')
}
}
// : }}}
// : DOM getting stuff {{{
@ -919,7 +731,7 @@
class MiscPageModell {
GetCurrentSubjectName () {
if (logElementGetting) { Log('getting current subjects name') }
return document.getElementById('page-header').innerText.split('\n')[0]
return document.getElementById('page-header').innerText.split('\n')[0] || ''
}
GetVideo () {
@ -986,7 +798,6 @@
}
console.log('Moodle Test Script run time:')
console.timeEnd('main')
SetActivesAsJSON()
})
if (forceTestPage || forceResultPage || forceDefaultPage) {
@ -1008,7 +819,6 @@
setVal('showSplash', undefined)
}
var url = location.href // eslint-disable-line
var count = -1 // loaded question count. stays -1 if the load failed.
// --------------------------------------------------------------------------------------
// event listener fuckery
// --------------------------------------------------------------------------------------
@ -1029,9 +839,8 @@
Exception(e, 'script error at addEventListener:')
}
VersionActions()
count = Load(cwith) // loads resources
if (!url.includes('.pdf')) { ShowMenu() }
return count
cwith()
}
function VersionActions () {
@ -1046,21 +855,6 @@
// : Version action functions {{{
function SetActivesAsJSON () {
if (!getVal('actives')) {
let res = []
for (let i = 0; i < 100; i++) {
let a = getVal('Is' + i + 'Active')
if (a && data.Subjects[i]) {
res.push(data.Subjects[i].Name)
}
delVal('Is' + i + 'Active')
}
delVal('Is-1Active')
setVal('actives', JSON.stringify(res))
}
}
function FreshStart () {
var firstRun = getVal('firstRun') // if the current run is the frst
if (firstRun === undefined || firstRun === true) {
@ -1075,134 +869,6 @@
// : }}}
function ReadNetDB (cwith) {
function NewXMLHttpRequest () {
const url = serverAdress + 'data.json'
xmlhttpRequest({
method: 'GET',
synchronous: true,
url: url,
onload: function (response) {
NLoad(response.responseText, cwith)
},
onerror: function () {
NLoad(undefined, cwith) // server down
}
})
}
try {
Log('Sending XMLHTTP Request...')
return NewXMLHttpRequest()
} catch (e) {
Exception(e, 'script error at reading online database:')
}
}
function Load (cwith) {
let skipLoad = getVal('skipLoad')
if (skipLoad) {
cwith(-2, -2)
return -1
}
ReadNetDB(cwith)
}
function LoadMOTD (resource) {
try {
motd = resource.motd
} catch (e) {
Log('Error loading motd :c')
Log(e)
}
}
function LoadVersion (resource) {
try {
lastestVersion = resource.version
} catch (e) {
Log('Error loading version :c')
Log(e)
}
}
// loading stuff
function NLoad (resource, cwith) {
assert(resource)
var count = -1
var subjCount = 0
try {
var d = {}
try {
d = JSON.parse(resource)
} catch (e) {
Log('Old data, trying with old methods....')
Log('Couldt parse data!')
Log(e)
ShowMessage({
m: texts.couldntLoadData,
isSimple: true
}, undefined, ShowHelp)
}
data = new QuestionDB(getVal, setVal, delVal)
var rt = []
var allCount = -1
LoadMOTD(d)
LoadVersion(d)
let actives = []
try {
actives = JSON.parse(getVal('actives'))
if (!Array.isArray(actives)) {
throw new Error('not an array')
}
} catch (e) {
Log('Unable to parse active subjects!')
setVal('actives', '[]')
actives = []
}
for (let i = 0; i < d.Subjects.length; i++) {
let s = new Subject(d.Subjects[i].Name)
s.setIndex(i)
let isActive = actives.includes(d.Subjects[i].Name)
if (isActive) {
s.setActive(true)
var j = 0
for (j = 0; j < d.Subjects[i].Questions.length; j++) {
var currQ = d.Subjects[i].Questions[j]
s.AddQuestion(new Question(currQ.Q, currQ.A, currQ.data))
}
rt.push({
name: d.Subjects[i].Name,
count: j
})
allCount += j
subjCount++
}
data.AddSubject(s)
}
count = allCount + 1 // couse starting with -1 to show errors
} catch (e) {
Exception(e, 'script error at loading:')
count = -1 // returns -1 if error
}
cwith(count, subjCount)
}
function AlertOnNoQuestion () {
try {
document.getElementById('HelperMenuButton').style.background = 'yellow'
} catch (e) {
Log('Unable to get helper menu button')
}
}
// : }}}
// : UI handling {{{
function HandleUI (url, count, subjCount) {
// FIXME: normal string building with localisation :/
@ -1231,23 +897,24 @@
timeout = undefined
}
greetMsg += count + ' kérdés és ' + subjCount + ' tárgy betöltve. (click for help).'
if (data.length > 0) {
var toAdd = []
for (var i = 0; i < data.length; i++) {
if (data.GetIfActive(i)) {
toAdd.push(data.Subjects[i].Name + ' (' + data.Subjects[i].length + ')')
}
}
if (toAdd.length !== 0) {
greetMsg += '\nAktív tárgyak: ' + toAdd.join(', ') + '.'
} else {
AlertOnNoQuestion()
greetMsg += '\nNincs aktív tárgyad. Menüből válassz ki eggyet!'
timeout = undefined
}
} else {
greetMsg += ' nem elérhető a szerver. Katt a helpért!'
}
// TODO
// if (data.length > 0) {
// var toAdd = []
// for (var i = 0; i < data.length; i++) {
// if (data.GetIfActive(i)) {
// toAdd.push(data.Subjects[i].Name + ' (' + data.Subjects[i].length + ')')
// }
// }
// if (toAdd.length !== 0) {
// greetMsg += '\nAktív tárgyak: ' + toAdd.join(', ') + '.'
// } else {
// AlertOnNoQuestion()
// greetMsg += '\nNincs aktív tárgyad. Menüből válassz ki eggyet!'
// timeout = undefined
// }
// } else {
// greetMsg += ' nem elérhető a szerver. Katt a helpért!'
// }
}
// new version, nothing loaded
if (newVersion && !loaded) { // --------------------------------------------------------------------------------------------------------------
@ -1295,15 +962,30 @@
var questions = q.q
var imgNodes = q.imgnodes
// ------------------------------------------------------------------------------------------------------
var answers = []
questions.forEach((x, j) => {
let promises = []
// TODO: test multiple promises
questions.forEach((x) => {
let question = SUtils.EmptyOrWhiteSpace(x) ? '' : SUtils.RemoveUnnecesarySpaces(x) // simplifying question
var result = data.Search(question, GetImageDataFromImgNodes(imgNodes))
var r = PrepareAnswers(result, j)
if (r !== undefined) { answers.push(r) }
HighLightAnswer(result, j) // highlights the answer for the current result
promises.push(GetXHRQuestionAnswer({
q: question,
data: GetImageDataFromImgNodes(imgNodes),
subj: '' // MPM.GetCurrentSubjectName() // TODO: set subj to '' if no result as backup plan
}))
})
// TODO: check answer order!
Promise.all(promises).then((res) => {
console.log('All data recieved', res) // TODO: delete
let answers = []
res.forEach((result, j) => {
var r = PrepareAnswers(result, j)
if (r !== undefined) { answers.push(r) }
HighLightAnswer(result, j) // highlights the answer for the current result
})
ShowAnswers(answers, q.q)
})
ShowAnswers(answers, q.q)
}
function PrepareAnswers (result, j) {
@ -1317,9 +999,10 @@
msg += result[k].q.Q + '\n' // adding the question if yes
}
msg += result[k].q.A.replace(/, /g, '\n') // adding answer
if (result[k].q.HasImage()) {
msg += '\n\nKépek fenti válaszok sorrendjében: ' + result[k].q.data.images.join(', ') // if it has image part, adding that too
}
// TODO
// if (result[k].q.HasImage()) {
// msg += '\n\nKépek fenti válaszok sorrendjében: ' + result[k].q.data.images.join(', ') // if it has image part, adding that too
// }
allMessages.push({
m: msg,
p: result[k].match
@ -1352,7 +1035,7 @@
// : Quiz saving {{{
function HandleResults (url) {
var d = SaveQuiz(GetQuiz(), data) // saves the quiz questions and answers
var d = SaveQuiz(GetQuiz()) // saves the quiz questions and answers
if (d) { ShowSaveQuizDialog(d.addedQ, d.allQ, d.allOutput, d.output, d.sendSuccess, d.sentData) }
}
@ -1366,7 +1049,7 @@
if (!sendSuccess) { msg += ' Nem sikerült kérdéseket elküldeni szervernek. Ha gondolod utánanézhetsz.' } else { msg += 'Az új kérdések elküldve.' }
} else {
msg = 'A kérdőívben nincsen új kérdés. Ha mégis le akarod menteni klikk ide.'
if (!data) { msg += ' Lehet azért, mert nincs kérdés betöltve.' }
// TODO if (!data) { msg += ' Lehet azért, mert nincs kérdés betöltve.' }
}
// showing a message wit the click event, and the generated page
ShowMessage({
@ -1386,19 +1069,6 @@
})
}
function SearchSameQuestion (questionData, quiz, i) {
var r = questionData.Search(quiz[i])
let count = 0
r.forEach((item) => {
if (item.match > minResultMatchPercent) {
count++
}
})
return count === 0 ? -1 : count
}
// this should get the image url from a result page
// i is the index of the question
// FIXME: move this to RPM class ??? and refactor this
@ -1444,7 +1114,7 @@
}
// saves the current quiz. questionData contains the active subjects questions
function SaveQuiz (quiz, questionData) {
function SaveQuiz (quiz) {
try {
if (quiz.length === 0) {
throw new Error('quiz length is zero!')
@ -1459,15 +1129,12 @@
var toAdd = '' // this will be added to some variable depending on if its already in the database
toAdd += '?' + SUtils.RemoveUnnecesarySpaces(quiz[i].Q) + '\n' // adding quiz question
toAdd += '!' + SUtils.RemoveUnnecesarySpaces(quiz[i].A) + '\n' // adding quiz answer
if (quiz[i].HasImage()) {
let imgString = quiz[i].data.images.join(', ')
toAdd += '>' + imgString + '\n' // adding quiz image if there is any
}
if (SearchSameQuestion(questionData, quiz, i) === -1) {
output += toAdd // adding to output
newQuestions.push(quiz[i])
addedQ++
}
// TODO: hasimage
// if (quiz[i].HasImage()) {
// let imgString = quiz[i].data.images.join(', ')
// toAdd += '>' + imgString + '\n' // adding quiz image if there is any
// }
// TODO: search same question removed
allOutput += toAdd // adding to all
allQ++
}
@ -2005,146 +1672,146 @@
width: '98%'
})
if (data && data.length > 0) {
let grouped = data.Subjects.reduce((res, s) => {
let sName = s.getSubjNameWithoutYear()
if (sName) {
if (!res[sName]) {
res[sName] = []
}
res[sName].push(s)
} else {
res.others.push(s)
}
return res
}, {
others: []
})
// if (data && data.length > 0) {
// let grouped = data.Subjects.reduce((res, s) => {
// let sName = s.getSubjNameWithoutYear()
// if (sName) {
// if (!res[sName]) {
// res[sName] = []
// }
// res[sName].push(s)
// } else {
// res.others.push(s)
// }
// return res
// }, {
// others: []
// })
const ordered = {}
Object.keys(grouped).sort().forEach((key) => {
ordered[key] = grouped[key]
})
// const ordered = {}
// Object.keys(grouped).sort().forEach((key) => {
// ordered[key] = grouped[key]
// })
grouped = ordered
// grouped = ordered
let collapsibles = []
// let collapsibles = []
// --------------------------------------------------------------------------------
let searchBar = CreateNodeWithText(subjTable, '', 'input')
SetStyle(searchBar, {
backgroundColor: '#222d32',
color: '#ffffff',
width: '100%',
border: 'none'
})
searchBar.placeholder = texts.search
searchBar.addEventListener('keyup', function (e) {
collapsibles.forEach((x) => {
if (x.innerText.toLowerCase().includes(this.value.toLowerCase())) {
x.style.display = ''
} else {
x.style.display = 'none'
}
})
}) // adding click
// // --------------------------------------------------------------------------------
// let searchBar = CreateNodeWithText(subjTable, '', 'input')
// SetStyle(searchBar, {
// backgroundColor: '#222d32',
// color: '#ffffff',
// width: '100%',
// border: 'none'
// })
// searchBar.placeholder = texts.search
// searchBar.addEventListener('keyup', function (e) {
// collapsibles.forEach((x) => {
// if (x.innerText.toLowerCase().includes(this.value.toLowerCase())) {
// x.style.display = ''
// } else {
// x.style.display = 'none'
// }
// })
// }) // adding click
Object.entries(grouped).forEach(([subjName, subjGroup], i) => {
let b = CreateNodeWithText(subjTable, subjName, 'div')
SetStyle(b, {
backgroundColor: '#222d32',
color: '#ffffff',
cursor: 'pointer',
padding: '5px',
width: '100%',
border: 'none',
textAlign: 'left',
outline: 'none'
})
b.setAttribute('id', 'subjectGroup' + i)
collapsibles.push(b)
// Object.entries(grouped).forEach(([subjName, subjGroup], i) => {
// let b = CreateNodeWithText(subjTable, subjName, 'div')
// SetStyle(b, {
// backgroundColor: '#222d32',
// color: '#ffffff',
// cursor: 'pointer',
// padding: '5px',
// width: '100%',
// border: 'none',
// textAlign: 'left',
// outline: 'none'
// })
// b.setAttribute('id', 'subjectGroup' + i)
// collapsibles.push(b)
let content = document.createElement('div')
SetStyle(content, {
padding: '0 18px',
overflow: 'hidden',
backgroundColor: '#222d32',
borderColor: '#212127',
borderStyle: 'solid',
borderWidth: '2px'
})
content.addEventListener('click', function (e) {
e.stopPropagation()
})
// let content = document.createElement('div')
// SetStyle(content, {
// padding: '0 18px',
// overflow: 'hidden',
// backgroundColor: '#222d32',
// borderColor: '#212127',
// borderStyle: 'solid',
// borderWidth: '2px'
// })
// content.addEventListener('click', function (e) {
// e.stopPropagation()
// })
let ifGroupActive = subjGroup.some((x) => {
return x.getIfActive()
})
content.style.display = ifGroupActive ? 'block' : 'none'
// let ifGroupActive = subjGroup.some((x) => {
// return x.getIfActive()
// })
// content.style.display = ifGroupActive ? 'block' : 'none'
b.appendChild(content)
// b.appendChild(content)
let tbl = document.createElement('table')
content.appendChild(tbl)
subjGroup.forEach((subj) => {
var row = tbl.insertRow()
let td = row.insertCell()
let text = subj.getYear() || subj.Name
if (subj.length !== 0) { text += ' [ ' + subj.length + 'db ]' }
CreateNodeWithText(td, text)
// let tbl = document.createElement('table')
// content.appendChild(tbl)
// subjGroup.forEach((subj) => {
// var row = tbl.insertRow()
// let td = row.insertCell()
// let text = subj.getYear() || subj.Name
// if (subj.length !== 0) { text += ' [ ' + subj.length + 'db ]' }
// CreateNodeWithText(td, text)
td = row.insertCell()
let checkbox = document.createElement('input') // new paragraph
checkbox.type = 'checkbox'
checkbox.style.background = 'white'
checkbox.style.margin = '5px 5px 5px 5px' // fancy margin
td.appendChild(checkbox) // adding text box to main td
// td = row.insertCell()
// let checkbox = document.createElement('input') // new paragraph
// checkbox.type = 'checkbox'
// checkbox.style.background = 'white'
// checkbox.style.margin = '5px 5px 5px 5px' // fancy margin
// td.appendChild(checkbox) // adding text box to main td
checkbox.checked = subj.active
let i = subj.getIndex()
checkbox.setAttribute('id', 'HelperTextNode' + i)
checkbox.addEventListener('click', function () {
var checked = document.getElementById('HelperTextNode' + i).checked
data.ChangeActive(subj.Name, checked)
}) // adding click
})
// checkbox.checked = subj.active
// let i = subj.getIndex()
// checkbox.setAttribute('id', 'HelperTextNode' + i)
// checkbox.addEventListener('click', function () {
// var checked = document.getElementById('HelperTextNode' + i).checked
// data.ChangeActive(subj.Name, checked)
// }) // adding click
// })
b.addEventListener('click', function (e) {
this.classList.toggle('active')
if (content.style.display === 'block') {
content.style.display = 'none'
} else {
content.style.display = 'block'
}
})
})
// b.addEventListener('click', function (e) {
// this.classList.toggle('active')
// if (content.style.display === 'block') {
// content.style.display = 'none'
// } else {
// content.style.display = 'block'
// }
// })
// })
var scrollDiv = document.createElement('div')
scrollDiv.style.width = '100%'
scrollDiv.style.height = window.innerHeight - (window.innerHeight * 0.4) + 'px'
scrollDiv.style.overflow = 'auto'
// var scrollDiv = document.createElement('div')
// scrollDiv.style.width = '100%'
// scrollDiv.style.height = window.innerHeight - (window.innerHeight * 0.4) + 'px'
// scrollDiv.style.overflow = 'auto'
scrollDiv.appendChild(subjTable)
// scrollDiv.appendChild(subjTable)
var subjtblrow = tbl.insertRow()
var subjtbltd = subjtblrow.insertCell()
subjtbltd.appendChild(scrollDiv)
} else { // if no data
var noDataRow = tbl.insertRow()
var noDataRowCell = noDataRow.insertCell()
let textBox
// var subjtblrow = tbl.insertRow()
// var subjtbltd = subjtblrow.insertCell()
// subjtbltd.appendChild(scrollDiv)
// } else { // if no data
// var noDataRow = tbl.insertRow()
// var noDataRowCell = noDataRow.insertCell()
// let textBox
if (getVal('skipLoad')) {
textBox = CreateNodeWithText(noDataRowCell,
texts.passiveModeActivePopupMenuText
)
} else {
textBox = CreateNodeWithText(noDataRowCell,
texts.couldntLoadDataPopupMenuText
)
}
textBox.style.margin = fiveMargin // fancy margin
}
// if (getVal('skipLoad')) {
// textBox = CreateNodeWithText(noDataRowCell,
// texts.passiveModeActivePopupMenuText
// )
// } else {
// textBox = CreateNodeWithText(noDataRowCell,
// texts.couldntLoadDataPopupMenuText
// )
// }
// textBox.style.margin = fiveMargin // fancy margin
// }
// show splash tickbox -----------------------------------------------------------------------------------------------------------------------------
var splasTickboxRow = tbl.insertRow()
@ -2271,6 +1938,37 @@
return paragraphElement
}
function GetXHRQuestionAnswer (question) {
return new Promise((resolve, reject) => {
let url = serverAdress + 'q?'
let params = []
Object.keys(question).forEach((key) => {
let val = question[key]
if (typeof val !== 'string') {
val = JSON.stringify(val)
}
params.push(key + '=' + encodeURIComponent(val))
})
url += params.join('&')
xmlhttpRequest({
method: 'GET',
url: url,
onload: function (response) {
try {
resolve(JSON.parse(response.responseText))
} catch (e) {
reject(new Error('json parse error'))
}
},
onerror: (e) => {
console.log('GET ERROR', e)
reject(new Error('get error'))
}
})
})
}
function SendXHRMessage (message) {
var url = serverAdress + 'isAdding'
xmlhttpRequest({