Error handling, showing more messages to user, localisation improvements

This commit is contained in:
mrfry 2020-11-22 09:47:13 +01:00
parent 771b37d59f
commit 84cd7ffc58

View file

@ -81,12 +81,12 @@
// : Constants and global variables {{{
const logElementGetting = false
const log = true
const logEnabled = true
const showErrors = true
// forcing pages for testing. unless you test, do not set these to true!
setVal('ISDEVEL', true)
// only one of these should be true for testing
const forceTestPage = false
const forceTestPage = true
const forceResultPage = false
const forceDefaultPage = false
@ -149,12 +149,24 @@
search: 'Keresés ...',
loading: 'Betöltés ...',
login: 'Belépés',
requestPWInsteadOfLogin: 'Jelszó igénylés',
requestPWInsteadOflogin: 'Jelszó igénylés',
newPWTitle: 'Új jelszó új felhasználónak',
pwRequest: 'Jelszó új felhasználónak',
noServer: 'Nem elérhető a szerver!',
noUser: 'Nem vagy bejelentkezve!',
noServerConsoleMessage: `Nem elérhető a szerver, vagy kis eséllyel kezeletlen hiba történt! Ha elérhető a weboldal, akkor ott meg bírod nézni a kérdéseket itt: ${serverAdress}legacy`,
noParseableQuestionResult:
'A tesztben nem találhatók kérdések, amit fel lehet dolgozni, vagy hiba történt feldolgozásuk közben',
unableToParseTestQuestion:
'Hiba történt a kérdések beolvasása közben :/ Kattints az üzenetre a manuális kereséshez (új böngésző tab-ban)',
loadingAnswer: 'Válaszok betöltése ...',
reinstallFromCorrectSource:
'Scriptet nem a weboldalról raktad fel. PLS reinstall <a href="https://qmining.frylabs.net/manual.html#reinstallfromqmining">aaaaaaaaa</a>', // TODO: new text
versionUpdated: 'Verzió frissítve ',
newVersionAvaible: 'Új verzió elérhető: ',
scriptName: 'Moodle/Elearning/KMOOC segéd ',
userMOTD: 'Felhasználó MOTD (ezt csak te látod):\n',
motd: 'MOTD:\n',
}
var texts = huTexts
@ -189,17 +201,17 @@
return promises
}
function makeTextFromElements(item) {
// TODO!
// if (emptyOrWhiteSpace(item)) {
// return ''
// }
function makeTextFromElements(acc, item) {
if (emptyOrWhiteSpace(item.val)) {
return acc
}
if (item.type === 'img') {
return '[' + item.val + ']'
acc.push('[' + item.val + ']')
} else {
return item.val
acc.push(item.val)
}
return acc
}
function getImagesFromElements(elements) {
@ -213,7 +225,7 @@
function getCurrentSubjectName() {
if (logElementGetting) {
Log('getting current subjects name')
log('getting current subjects name')
}
return document.getElementById('page-header').innerText.split('\n')[0] || ''
}
@ -227,28 +239,53 @@
// : Test page processing functions {{{
function handleQuiz() {
getQuizData().then(res => {
const promises = []
const { removeMessage: removeLoadingMessage } = ShowMessage({
m: texts.loadingAnswer,
isSimple: true,
})
res.forEach(question => {
promises.push(
GetXHRQuestionAnswer({
q: question.question,
data: question.data,
subj: getCurrentSubjectName(),
})
)
})
getQuizData()
.then(res => {
if (res.length === 0) {
ShowMessage(
{
m: texts.unableToParseTestQuestion,
isSimple: true,
},
undefined,
() => {
OpenErrorPage({
message: 'No result found',
})
}
)
return
}
Promise.all(promises).then(res => {
const answers = res.map(result => {
return PrepareAnswers(result)
const promises = []
res.forEach(question => {
promises.push(
GetXHRQuestionAnswer({
q: question.question,
data: question.data,
subj: getCurrentSubjectName(),
})
)
})
// TODO: new lines in answers
ShowAnswers(answers, res[0].question)
Promise.all(promises).then(res => {
const answers = res.map(result => {
return PrepareAnswers(result)
})
removeLoadingMessage()
ShowAnswers(answers, res[0].question)
})
})
.catch(err => {
warn(err)
warn('Error in handleQuiz()')
})
})
}
function getQuizData() {
@ -271,8 +308,14 @@
}
Promise.all(promises)
.then(res => {
resolve(res)
.then(result => {
const errorsRemoved = result.reduce((acc, res) => {
if (res.success) {
acc.push(res)
}
return acc
}, [])
resolve(errorsRemoved)
})
.catch(err => {
console.warn('Error in handleQuiz()')
@ -314,46 +357,58 @@
function getQuestionPromiseForSingleQuestion(node) {
return new Promise(resolve => {
const qtextNode = node.getElementsByClassName('qtext')[0]
try {
const qtextNode = node.getElementsByClassName('qtext')[0]
const questionPromises = getTextPromisesFromNode(qtextNode)
const possibleAnswerPromises = getPossibleAnswersFromTest(node)
const questionPromises = getTextPromisesFromNode(qtextNode)
const possibleAnswerPromises = getPossibleAnswersFromTest(node)
const unflattenedPossibleAnswerPromises = possibleAnswerPromises
? possibleAnswerPromises.map(x => {
return Promise.all(x)
const unflattenedPossibleAnswerPromises = possibleAnswerPromises
? possibleAnswerPromises.map(x => {
return Promise.all(x)
})
: []
Promise.all([
Promise.all(questionPromises),
Promise.all(unflattenedPossibleAnswerPromises),
])
.then(([question, possibleAnswerArray]) => {
const questionText = removeUnnecesarySpaces(
question.reduce(makeTextFromElements, []).join(' ')
)
const possibleAnswers = possibleAnswerArray.map(x => {
return removeUnnecesarySpaces(
x.reduce(makeTextFromElements, []).join(' ')
)
})
let images = getImagesFromElements([
...question,
...possibleAnswerArray.reduce((acc, x) => {
return [...acc, ...x]
}, []),
])
images = uniq(images)
const data = getDataFromTest(images, possibleAnswerArray)
resolve({
question: questionText,
possibleAnswers,
images,
data,
success: true,
})
})
: []
Promise.all([
Promise.all(questionPromises),
Promise.all(unflattenedPossibleAnswerPromises),
])
.then(([question, possibleAnswerArray]) => {
const questionText = question.map(makeTextFromElements).join(' ')
const possibleAnswers = possibleAnswerArray.map(x => {
return removeUnnecesarySpaces(x.map(makeTextFromElements).join(' '))
.catch(err => {
warn('Error in getQuestionPromiseForSingleQuestion()')
warn(err)
resolve({ success: false })
})
let images = getImagesFromElements([
...question,
...possibleAnswerArray.reduce((acc, x) => {
return [...acc, ...x]
}, []),
])
images = uniq(images)
const data = getDataFromTest(images, possibleAnswerArray)
resolve({
question: questionText,
possibleAnswers,
images,
data,
})
})
.catch(err => {
console.warn('Error in getQuestionPromiseForSingleQuestion()')
console.warn(err)
})
} catch (err) {
warn('Error in getQuestionPromiseForSingleQuestion()')
warn(err)
resolve({ success: false })
}
})
}
@ -389,11 +444,17 @@
Promise.all(promises)
.then(result => {
resolve(result)
const errorsRemoved = result.reduce((acc, res) => {
if (res.success) {
acc.push(res)
}
return acc
}, [])
resolve(errorsRemoved)
})
.catch(err => {
console.warn('Error in getQuiz()')
console.warn(err)
warn('Error in getQuiz()')
warn(err)
})
})
}
@ -407,10 +468,10 @@
res = getter.getterFunction(node)
return true
} catch (e) {
Log(`${key} failed`)
log(`${key} failed`)
}
} else {
Log(`${key} did not pass`)
log(`${key} did not pass`)
}
})
@ -429,14 +490,16 @@
)
if (!answerPromises || !questionPromises) {
Log('Answer or question array is empty, skipping question')
log('Answer or question array is empty, skipping question')
resolve({ success: false })
}
Promise.all([Promise.all(questionPromises), Promise.all(answerPromises)])
.then(([question, answer]) => {
const questionText = question.map(makeTextFromElements).join(' ')
const answerText = answer.map(makeTextFromElements).join(' ')
const questionText = question
.reduce(makeTextFromElements, [])
.join(' ')
const answerText = answer.reduce(makeTextFromElements, []).join(' ')
let images = getImagesFromElements([...question, ...answer])
// images = uniq(images)
@ -448,8 +511,9 @@
})
})
.catch(err => {
console.warn('Error in getQuizFromNode()')
console.warn(err)
warn('Error in getQuizFromNode()')
warn(err)
resolve({ success: false })
})
})
}
@ -545,6 +609,16 @@
return 'asd'
},
},
getDragBoxAnswer: {
description: 'Get complex answer',
requirement: node => {
return false
},
getterFunction: node => {
// TODO dragboxes
return 'asd'
},
},
}
function getIfSolutionIsCorrect(node) {
@ -606,14 +680,14 @@
function getVideo() {
if (logElementGetting) {
Log('getting video stuff')
log('getting video stuff')
}
return document.getElementsByTagName('video')[0]
}
function getVideoElement() {
if (logElementGetting) {
Log('getting video element')
log('getting video element')
}
return document.getElementById('videoElement').parentNode
}
@ -767,18 +841,13 @@
// : Main function {{{
let timerStarted = false
// window.addEventListener("load", () => {})
Main()
function Main() {
'use strict'
// https://qmining.frylabs.net/moodle-test-userscript/stable.user.js?up
console.log('Moodle / E-Learning script')
console.time('main')
timerStarted = true
log('Moodle / E-Learning script')
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', Init)
@ -829,19 +898,7 @@
if (url.includes('eduplayer')) {
AddVideoHotkeys(url)
} // adding video hotkeys
Log(texts.consoleErrorInfo)
if (timerStarted) {
console.log('Moodle Test Script run time:')
console.timeEnd('main')
timerStarted = false
}
if (forceTestPage || forceResultPage || forceDefaultPage) {
if (overlay.querySelector('#scriptMessage')) {
overlay.querySelector('#scriptMessage').style.background = 'green'
}
}
log(texts.consoleErrorInfo)
}
// : }}}
@ -963,7 +1020,7 @@
NoUserAction()
return
}
lastestVersion = inf.version
lastestVersion = inf.version.replace(/\n/g, '')
motd = inf.motd
userSpecificMotd = inf.userSpecificMotd
subjInfo = inf.subjinfo
@ -1008,7 +1065,7 @@
SafeGetElementById('retryButton', elem => {
elem.style.display = ''
})
Log(texts.noServerConsoleMessage)
log(texts.noServerConsoleMessage)
}
function VersionActions() {
@ -1018,23 +1075,14 @@
// : }}}
// : UI handling {{{
function HandleUI(url) {
// FIXME: normal string building with localisation :/
var newVersion = false // if the script is newer than last start
try {
newVersion = info().script.version !== getVal('lastVerson')
} catch (e) {
Log('Some weird error trying to set new verison')
}
let showMOTD = false
function shouldShowMotd() {
if (!emptyOrWhiteSpace(motd)) {
var prevmotd = getVal('motd')
if (prevmotd !== motd) {
showMOTD = true
setVal('motdcount', motdShowCount)
setVal('motd', motd)
return true
} else {
var motdcount = getVal('motdcount')
if (motdcount === undefined) {
@ -1044,51 +1092,61 @@
motdcount--
if (motdcount > 0) {
showMOTD = true
setVal('motdcount', motdcount)
return true
}
}
}
}
function HandleUI(url) {
var newVersion = false // if the script is newer than last start
try {
newVersion = info().script.version !== getVal('lastVerson')
} catch (e) {
log('Some weird error trying to set new verison')
}
const showMOTD = shouldShowMotd()
const showUserSpecificMOTD = !!userSpecificMotd
let isNewVersionAvaible =
lastestVersion !== undefined && info().script.version !== lastestVersion
var greetMsg = '' // message to show at the end
var timeout = null // the timeout. if null, it wont be hidden
let timeout = null
const greetMsg = []
if (isNewVersionAvaible || newVersion || showMOTD || showUserSpecificMOTD) {
greetMsg =
'Moodle/Elearning/KMOOC segéd v. ' + info().script.version + '. '
greetMsg.push(texts.scriptName + info().script.version)
}
if (isNewVersionAvaible) {
timeout = 5
greetMsg += 'Új verzió elérhető: ' + lastestVersion
greetMsg.push(texts.newVersionAvaible + lastestVersion)
timeout = undefined
}
if (newVersion) {
greetMsg += 'Verzió frissítve ' + info().script.version + '-re.'
greetMsg.push(texts.versionUpdated + info().script.version)
setVal('lastVerson', info().script.version) // setting lastVersion
}
if (!installedFromCorrectSource(correctSource)) {
greetMsg +=
'\nScriptet nem a weboldalról raktad fel. PLS reinstall (a href = ...)'
greetMsg.push(texts.reinstallFromCorrectSource)
}
if (showMOTD) {
greetMsg += '\nMOTD:\n' + motd
greetMsg.push(texts.motd + motd)
timeout = null
}
if (showUserSpecificMOTD) {
greetMsg += '\nFelhasználó MOTD (ezt csak te látod):\n' + userSpecificMotd
greetMsg.push(texts.userMOTD + userSpecificMotd)
timeout = null
}
ShowMessage(
{
m: greetMsg,
m: greetMsg.join('\n'),
isSimple: true,
},
timeout
) // showing message. If "m" is empty it wont show it, thats how showSplash works.
)
}
// : }}}
@ -1103,7 +1161,8 @@
let msg = res.q.Q + '\n'
// TODO: remove this maybe?
msg += res.q.A.replace(/, /g, '\n') // adding answer
// msg += res.q.A.replace(/, /g, '\n')
msg += res.q.A
if (res.q.data.type === 'image') {
msg +=
@ -1192,7 +1251,11 @@
try {
let sentData = {}
if (quiz.length === 0) {
throw new Error('quiz length is zero!')
ShowMessage({
m: texts.noParseableQuestionResult,
isSimple: true,
})
return
}
try {
sentData = {
@ -1204,7 +1267,7 @@
sentData.subj = getCurrentSubjectName()
} catch (e) {
sentData.subj = 'NOSUBJ'
Log('unable to get subject name :c')
log('unable to get subject name :c')
}
console.log('SENT DATA', sentData)
SendXHRMessage('isAdding', sentData).then(res => {
@ -1225,8 +1288,9 @@
// : Install source checker {{{
function installedFromCorrectSource(source) {
// https://qmining.frylabs.net/moodle-test-userscript/stable.user.js?up
// TODO: test this
return info().script.updateURL === correctSource
return info().script.updateURL === correctSource && false
}
// : }}}
@ -1274,8 +1338,8 @@
video.currentTime -= seekTime
}
} catch (err) {
Log('Hotkey error.')
Log(err.message)
log('Hotkey error.')
log(err.message)
}
})
var toadd = getVideoElement()
@ -1295,9 +1359,37 @@
overlay.querySelectorAll('#scriptMessage').forEach(x => x.remove())
}
function getConvertedMessageNode(message) {
const messageNode = document.createElement('p')
const resultNode = document.createElement('p')
messageNode.innerHTML = message.replace(/\n/g, '</br>')
Array.from(messageNode.childNodes).forEach(node => {
if (node.tagName === 'A') {
let linkNode = document.createElement('span')
SetStyle(linkNode, {
color: 'lightblue',
textDecoration: 'underline',
cursor: 'pointer',
})
linkNode.innerText = node.innerText
linkNode.addEventListener('mousedown', e => {
e.stopPropagation()
openInTab(node.href, {
active: true,
})
})
resultNode.appendChild(linkNode)
} else {
resultNode.appendChild(node)
}
})
return resultNode
}
// shows a message with "msg" text, "matchPercent" tip and transp, and "timeout" time
function ShowMessage(msgItem, timeout, funct) {
// TODO: simple message not selectable
// msgItem help:
// [ [ {}{}{}{} ] [ {}{}{} ] ]
// msgItem[] <- a questions stuff
@ -1349,32 +1441,38 @@
top: startFromTop + 'px',
left: (window.innerWidth - width) / 2 + 'px',
opacity: '1',
cursor: 'move',
cursor: funct ? 'pointer' : 'move',
})
mainDiv.setAttribute('id', 'scriptMessage')
// ------------------------------------------------------------------
// moving msg
// ------------------------------------------------------------------
const movingEnabled = !funct
let isMouseDown = false
let offset = [0, 0]
let mousePosition
mainDiv.addEventListener('mousedown', e => {
isMouseDown = true
offset = [mainDiv.offsetLeft - e.clientX, mainDiv.offsetTop - e.clientY]
})
mainDiv.addEventListener('mouseup', e => {
isMouseDown = false
})
mainDiv.addEventListener('mousemove', e => {
if (isMouseDown) {
mousePosition = {
x: e.clientX,
y: e.clientY,
if (movingEnabled) {
mainDiv.addEventListener('mousedown', e => {
isMouseDown = true
offset = [
mainDiv.offsetLeft - e.clientX,
mainDiv.offsetTop - e.clientY,
]
})
mainDiv.addEventListener('mouseup', e => {
isMouseDown = false
})
mainDiv.addEventListener('mousemove', e => {
if (isMouseDown) {
mousePosition = {
x: e.clientX,
y: e.clientY,
}
mainDiv.style.left = mousePosition.x + offset[0] + 'px'
mainDiv.style.top = mousePosition.y + offset[1] + 'px'
}
mainDiv.style.left = mousePosition.x + offset[0] + 'px'
mainDiv.style.top = mousePosition.y + offset[1] + 'px'
}
})
})
}
const xButton = CreateNodeWithText(null, '❌', 'div')
SetStyle(xButton, {
@ -1382,6 +1480,7 @@
position: 'absolute',
right: '0px',
display: 'inline',
margin: '3px',
})
xButton.addEventListener('mousedown', e => {
e.stopPropagation()
@ -1395,16 +1494,15 @@
var simpleMessageParagrapg = document.createElement('p') // new paragraph
simpleMessageParagrapg.style.margin = defMargin // fancy margin
var mesageNode = document.createElement('p') // new paragraph
mesageNode.innerHTML = simpleMessageText.replace(/\n/g, '</br>')
const mesageNode = getConvertedMessageNode(simpleMessageText)
simpleMessageParagrapg.appendChild(mesageNode)
mesageNode.addEventListener('mousedown', e => {
e.stopPropagation()
})
// TODO: edit this margin to make simple message draggable
SetStyle(mesageNode, {
margin: '10px 0px',
cursor: 'auto',
margin: '10px 100px',
cursor: funct ? 'pointer' : 'auto',
})
Array.from(mesageNode.getElementsByTagName('a')).forEach(anchorElem => {
@ -1491,7 +1589,7 @@
})
SetStyle(questionTextElement, {
cursor: 'auto',
cursor: funct ? 'pointer' : 'auto',
})
questionTextElement.setAttribute('id', 'questionTextElement')
@ -1636,6 +1734,13 @@
} catch (e) {
Exception(e, 'script error at showing message:')
}
return {
messageElement: mainDiv,
removeMessage: () => {
mainDiv.parentNode.removeChild(mainDiv)
},
}
}
// shows a fancy menu
@ -1668,6 +1773,7 @@
position: 'absolute',
right: '0px',
display: 'inline',
margin: '5px',
})
xButton.addEventListener('mousedown', e => {
e.stopPropagation()
@ -1836,7 +1942,7 @@
const clientId = getVal('clientId')
if (clientId && clientId.toString()[0] !== '0') {
loginInput.value = clientId || ''
loginButton.innerText = texts.requestPWInsteadOfLogin
loginButton.innerText = texts.requestPWInsteadOflogin
}
loginDiv.appendChild(loginInput)
loginDiv.appendChild(loginButton)
@ -1845,7 +1951,7 @@
loginInput.addEventListener('keyup', e => {
if (e.target.value === clientId) {
loginButton.innerText = texts.requestPWInsteadOfLogin
loginButton.innerText = texts.requestPWInsteadOflogin
} else if (e.target.value !== '') {
loginButton.innerText = texts.login
}
@ -1914,7 +2020,6 @@
// : String utils 2 {{{
function removeUnnecesarySpaces(toremove) {
// TODO: check if this doesnt kill if question / answer is empty
if (!toremove) {
return ''
}
@ -1959,19 +2064,31 @@
}
}
function Log(value) {
if (log) {
console.log(value)
function logHelper(logMethod, value) {
if (logEnabled) {
if (typeof value === 'string') {
logMethod('[Moodle Script]: ' + value)
} else {
logMethod(value)
}
}
}
function warn(value) {
logHelper(console.warn, value)
}
function log(value) {
logHelper(console.log, value)
}
function Exception(e, msg) {
Log('------------------------------------------')
Log(msg)
Log(e.message)
Log('------------------------------------------')
Log(e.stack)
Log('------------------------------------------')
log('------------------------------------------')
log(msg)
log(e.message)
log('------------------------------------------')
log(e.stack)
log('------------------------------------------')
}
function GetId() {
@ -1994,7 +2111,7 @@
if (element) {
next(element)
} else {
Log(`Unable to safe get element by id: ${id}`)
log(`Unable to safe get element by id: ${id}`)
}
}
@ -2058,14 +2175,14 @@
setVal('lastInfo', response.responseText)
resolve(res)
} catch (e) {
Log('Errro paring JSON in GetXHRInfos')
Log(response.responseText)
Log(e)
log('Errro paring JSON in GetXHRInfos')
log(response.responseText)
log(e)
reject(e)
}
},
onerror: e => {
Log('Info get Error', e)
log('Info get Error', e)
reject(e)
},
})
@ -2075,8 +2192,8 @@
try {
resolve(lastInfo)
} catch (e) {
Log('Errro paring JSON in GetXHRInfos, when using old data!')
Log(e)
log('Errro paring JSON in GetXHRInfos, when using old data!')
log(e)
reject(e)
}
})
@ -2107,26 +2224,14 @@
onload: function(response) {
try {
let res = JSON.parse(response.responseText)
// FIXME: check if res is a valid answer array
// res.json({
// result: r,
// success: true
// })
// ERROR:
// res.json({
// message: `Invalid question :(`,
// result: [],
// recievedData: JSON.stringify(req.query),
// success: false
// })
resolve(res.result)
} catch (e) {
reject(e)
}
},
onerror: e => {
Log('Errro paring JSON in GetXHRQuestionAnswer')
Log(e)
log('Errro paring JSON in GetXHRQuestionAnswer')
log(e)
reject(e)
reject(e)
},
@ -2135,7 +2240,6 @@
}
function SendXHRMessage(path, message) {
// message = SUtils.RemoveSpecialChars(message) // TODO: check this
if (typeof message === 'object') {
message = JSON.stringify(message)
}
@ -2151,7 +2255,7 @@
'Content-Type': 'application/json',
},
onerror: function(e) {
Log('Data send error', e)
log('Data send error', e)
reject(e)
},
onload: resp => {
@ -2159,9 +2263,9 @@
const res = JSON.parse(resp.responseText)
resolve(res)
} catch (e) {
Log('Error paring JSON in SendXHRMessage')
Log(resp.responseText)
Log(e)
log('Error paring JSON in SendXHRMessage')
log(resp.responseText)
log(e)
reject(e)
}
},