diff --git a/stable.user.js b/stable.user.js index 5142780..2136547 100755 --- a/stable.user.js +++ b/stable.user.js @@ -46,7 +46,7 @@ // : Script header {{{ // ==UserScript== // @name Moodle/Elearning/KMOOC test help -// @version 2.1.2.2 +// @version 2.1.3.0 // @description Online Moodle/Elearning/KMOOC test help // @author MrFry // @match https://elearning.uni-obuda.hu/* @@ -58,6 +58,8 @@ // @match https://moodle.pte.hu/* // @match https://szelearning.sze.hu/* // @match https://moodle.kre.hu/* +// @match https://moodle.pte.hu/* +// @match https://portal.kgk.uni-obuda.hu/ // @noframes // @match https://qmining.frylabs.net/* // @run-at document-start @@ -115,6 +117,7 @@ // forcing pages for testing. unless you test, do not set these to true! const isDevel = false setVal('ISDEVEL', isDevel) + const forcedMatchString = isDevel ? '' : '' // only one of these should be true for testing const forceTestPage = isDevel && false const forceResultPage = isDevel && false @@ -128,12 +131,11 @@ var addEventListener // add event listener function let serverAdress = 'https://qmining.frylabs.net/' let apiAdress = 'https://api.frylabs.net/' - // const ircAddress = 'https://kiwiirc.com/nextclient/irc.sub.fm/#qmining' const motdShowCount = 3 /* Ammount of times to show motd */ const messageOpacityDelta = 0.1 const minMessageOpacity = 0.2 - let infoExpireTime = 60 // Every n seconds basic info should be loaded from server + let infoExpireTime = 60 * 5 // Every n seconds basic info should be loaded from server var motd = '' var userSpecificMotd = undefined var lastestVersion = '' @@ -213,6 +215,8 @@ // : HTML parsers {{{ + // : Moodle {{{ + // : Basic processing helpers {{{ function getTextPromisesFromNode(node) { @@ -323,7 +327,7 @@ // : Test page processing functions {{{ - function handleQuiz() { + function handleMoodleQuiz() { const { removeMessage: removeLoadingMessage } = ShowMessage( texts.loadingAnswer ) @@ -364,6 +368,7 @@ log(sentData) + log('Sent data', sentData) post('ask', sentData).then((results) => { removeLoadingMessage() ShowAnswers( @@ -378,7 +383,7 @@ }) .catch((err) => { warn(err) - warn('Error in handleQuiz()') + warn('Error in handleMoodleQuiz()') }) } @@ -466,7 +471,7 @@ resolve(errorsRemoved) }) .catch((err) => { - warn('Error in handleQuiz()') + warn('Error in handleMoodleQuiz()') warn(err) }) }) @@ -834,10 +839,12 @@ hashedImages: images.map((x) => { return x.val }), + source: 'script', } } else { return { type: 'simple', + source: 'script', } } } @@ -1014,6 +1021,121 @@ // : }}} + // : }}} + + // : KGK {{{ + + function getKGKAnswerNodesFromQuiz() { + let i = 1 + let currElem = null + const elems = [] + do { + currElem = document.getElementsByClassName(`kvalasz${i}`)[0] + if (currElem) { + elems.push(currElem) + } + i++ + } while (currElem !== undefined) + return elems + } + + function getKGKQuestionNodeFromQuiz() { + return document.getElementsByClassName('kkerdes')[0] + } + + function getKGKSubjName() { + return document.getElementsByTagName('header')[0].innerText + } + + function HandleKGKResults(url) { + const tableChilds = document.getElementsByTagName('table')[0].childNodes[0] + .childNodes + const question = removeUnnecesarySpaces( + tableChilds[0].innerText.split(':')[1] + ) + const answer = removeUnnecesarySpaces( + tableChilds[1].innerText.split(':')[1] + ) + const correct = removeUnnecesarySpaces( + tableChilds[2].innerText.split(':')[1] + ) + if (correct.toLowerCase() === 'helyes') { + const sentData = { + subj: getKGKSubjName(), + version: info().script.version, + id: getCid(), + location: url, + quiz: [ + { + Q: question, + A: answer, + data: { + type: 'simple', + date: new Date().getTime(), + source: 'script', + }, + }, + ], + } + + post('isAdding', sentData).then((res) => { + ShowSaveQuizDialog(res.success, sentData, res.totalNewQuestions) + }) + } else { + ShowMessage('Nem eldönthető a helyes válasz') + } + } + + function simplifyKGKQuestionString(val) { + // FIXME: this is ugly + let x = val.split('\n') + x.shift() + x = x.join('\n').split(' ') + x.pop() + return x.join(' ') + } + + function handleKGKQuiz(url) { + try { + const { removeMessage: removeLoadingMessage } = ShowMessage( + texts.loadingAnswer + ) + const answerNodes = getKGKAnswerNodesFromQuiz() + const questionNode = getKGKQuestionNodeFromQuiz() + + const sentData = { + questions: [ + { + Q: simplifyKGKQuestionString(questionNode.innerText), + subj: 'asd', + data: { type: 'simple' }, + possibleAnswers: answerNodes.map((node) => { + return node.innerText + }), + }, + ], + testUrl: url, + } + + log('Sent data', sentData) + post('ask', sentData).then((results) => { + removeLoadingMessage() + ShowAnswers( + results.map((res, i) => { + return { + answers: res.answers, + question: sentData.questions[i], + } + }) + ) + }) + } catch (e) { + console.warn(e) + } + } + + // : }}} + // : Misc {{{ function getVideo() { @@ -1195,31 +1317,101 @@ } } + const pageMatchers = [ + { + matchString: 'portal.kgk', + testPage: { + match: (url) => { + return url.includes('vizsga') + }, + action: (url) => { + handleKGKQuiz(url) + }, + }, + resultPage: { + match: (url) => { + return false // TODO :insert real url + }, + action: (url) => { + HandleKGKResults(url) + }, + }, + default: { + match: (url) => { + return true // TODO :insert real url + }, + action: (url) => { + HandleUI(url) + }, + }, + }, + { + matchString: 'default', // moodle, elearning, mooc + testPage: { + match: (url) => { + return ( + (url.includes('/quiz/') && url.includes('attempt.php')) || + forceTestPage + ) + }, + action: () => { + handleMoodleQuiz() + }, + }, + resultPage: { + match: (url) => { + return ( + (url.includes('/quiz/') && url.includes('review.php')) || + forceResultPage + ) + }, + action: (url) => { + HandleMoodleResults(url) + }, + }, + default: { + match: (url) => { + return ( + (!url.includes('/quiz/') && + !url.includes('review.php') && + !url.includes('.pdf')) || + forceDefaultPage + ) + }, + action: (url) => { + HandleUI(url) + }, + }, + }, + ] + function AfterLoad() { const url = currUrl try { - if ( - (url.includes('/quiz/') && url.includes('attempt.php')) || - forceTestPage - ) { - // if the current page is a test - handleQuiz() - } else if ( - (url.includes('/quiz/') && url.includes('review.php')) || - forceResultPage - ) { - // if the current window is a test-s result - HandleResults(url) - } else if ( - (!url.includes('/quiz/') && - !url.includes('review.php') && - !url.includes('.pdf')) || - forceDefaultPage - ) { - // if the current window is any other window than a quiz or pdf. - HandleUI(url) - } + pageMatchers.some((matcher) => { + if ( + url.includes(matcher.matchString) || + matcher.matchString === 'default' || + matcher.matchString === forcedMatchString + ) { + if (matcher.testPage && matcher.testPage.match(url)) { + matcher.testPage.action(url) + return true + } else if (matcher.resultPage && matcher.resultPage.match(url)) { + matcher.resultPage.action(url) + return true + } else if (matcher.default && matcher.default.match(url)) { + matcher.default.action(url) + return true + } else { + console.warn( + 'Matcher did not have matched handler implemented, or there was no match!', + matcher + ) + } + } + }) } catch (e) { ShowMessage( texts.fatalError, @@ -1515,6 +1707,10 @@ } function addImageIdsToImageNodes(imgs) { + if (!imgs || !Array.isArray(imgs.images)) { + return + } + imgs.images.forEach((img, i) => { const text = document.createElement('div') text.innerText = `[${i}]` @@ -1529,33 +1725,78 @@ }) } + // results = Array<{ + // answers: Array<{ + // detailedMatch: { + // qMatch: Number, + // aMatch: Number, + // dMatch: Number, + // matchedSubjName: String, + // avg: Number + // }, + // match: Number, + // q: { + // Q: String, + // A: String, + // cache: { + // Q: Array, + // A: Array, + // }, + // data: { + // type: String, + // date: Number + // images?: Array + // } + // } + // }>, + // question: { + // question: String, + // success: Boolean, + // images: Array<{ + // val: String, + // node: HtmlNode + // }>, + // data: { + // type: String, + // date: Number + // hashedImages?: Array, + // images?: Array + // }, + // possibleAnswers: Array + // } + // }> function ShowAnswers(results) { log(results) - const answers = results.reduce((acc, res) => { - const prepared = PrepareAnswers(res) - addImageIdsToImageNodes(res.question) - if (prepared) { - acc.push(prepared) - } - return acc - }, []) - - if (answers.length > 0) { - ShowMessage(answers) - } else { - ShowMessage( - texts.noResult, - - undefined, - function () { - OpenErrorPage({ - message: 'No result found', - question: Array.isArray(answers[0]) - ? answers[0][0].replace(/"/g, '').replace(/:/g, '') - : answers[0], - }) + try { + const answers = results.reduce((acc, res) => { + const prepared = PrepareAnswers(res) + addImageIdsToImageNodes(res.question) + if (prepared) { + acc.push(prepared) } - ) + return acc + }, []) + + if (answers.length > 0) { + ShowMessage(answers) + } else { + ShowMessage( + texts.noResult, + + undefined, + function () { + OpenErrorPage({ + message: 'No result found', + question: Array.isArray(answers[0]) + ? answers[0][0].replace(/"/g, '').replace(/:/g, '') + : answers[0], + }) + } + ) + } + } catch (e) { + console.warn('Error showing answers') + console.warn(e) } } @@ -1563,9 +1804,9 @@ // : Quiz saving {{{ - function HandleResults() { + function HandleMoodleResults() { getQuiz().then((res) => { - SaveQuiz(res, ShowSaveQuizDialog) // saves the quiz questions and answers + SaveQuiz(res) // saves the quiz questions and answers }) } @@ -1601,7 +1842,7 @@ } // saves the current quiz. questionData contains the active subjects questions - function SaveQuiz(quiz, next) { + function SaveQuiz(quiz) { try { let sentData = {} if (quiz.length === 0) { @@ -1623,7 +1864,7 @@ } log('SENT DATA', sentData) post('isAdding', sentData).then((res) => { - next(res.success, sentData, res.totalNewQuestions) + ShowSaveQuizDialog(res.success, sentData, res.totalNewQuestions) }) } catch (e) { Exception(e, 'error at sending data to server.')