/* ---------------------------------------------------------------------------- Online Moodle/Elearning/KMOOC test help Greasyfork: GitLab: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ------------------------------------------------------------------------- */ // ==UserScript== // @name Moodle/Elearning/KMOOC test help // @version 1.6.4.8 // @description Online Moodle/Elearning/KMOOC test help // @author MrFry // @match https://elearning.uni-obuda.hu/main/* // @match https://elearning.uni-obuda.hu/kmooc/* // @match https://mooc.unideb.hu/* // @grant GM_getResourceText // @grant GM_info // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_openInTab // @license GNU General Public License v3.0 or later // @supportURL qmining.frylabs.net // @contributionURL qmining.frylabs.net // @namespace https://qmining.frylabs.net // @updateURL https://qmining.frylabs.net/moodle-test-userscript/stable.user.js?up // ==/UserScript== (function() { // eslint-disable-line // GM functions, only to disable ESLINT errors /* eslint-disable */ const a = Main function getVal (name) { return GM_getValue(name) } function setVal (name, val) { return GM_setValue(name, val) } function openInTab (address, options) { GM_openInTab(address, options) } function xmlhttpRequest (opts) { GM_xmlhttpRequest(opts) } function info () { return GM_info } /* eslint-enable */ var data // all data, which is in the resource txt var addEventListener // add event listener function const lastChangeLog = 'Kérdés parsolás bugfixek, old school fálj beolvasás kiszedése, részletesebb hibajelentés és egyéb fixek' 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 forceResultPage = false const forceDefaultPage = false const logElementGetting = false const log = true const motdShowCount = 3 /* Ammount of times to show motd */ var motd = '' var lastestVersion = '' const minResultMatchPercent = 99 /* Minimum ammount to consider that two questions match during saving */ // : question-classes {{{ const commonUselessAnswerParts = [ 'A helyes válasz az ', 'A helyes válasz a ', 'A helyes válaszok: ', 'A helyes válaszok:', 'A helyes válasz: ', 'A helyes válasz:', 'The correct answer is:', '\'' ] 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 assert = (val) => { if (!val) { throw new Error('Assertion failed') } } class StringUtils { RemoveStuff (value, removableStrings, toReplace) { removableStrings.forEach((x) => { var regex = new RegExp(x, 'g') value = value.replace(regex, toReplace || '') }) return value } SimplifyQuery (q) { assert(q) var result = q.replace(/\n/g, ' ').replace(/\s/g, ' ') return this.RemoveUnnecesarySpaces(result) } ShortenString (toShorten, ammount) { assert(toShorten) var result = '' var i = 0 while (i < toShorten.length && i < ammount) { result += toShorten[i] i++ } return result } ReplaceCharsWithSpace (val, char) { assert(val) assert(char) var toremove = this.NormalizeSpaces(val) var regex = new RegExp(char, 'g') toremove = toremove.replace(regex, ' ') return this.RemoveUnnecesarySpaces(toremove) } // removes whitespace from begining and and, and replaces multiple spaces with one space RemoveUnnecesarySpaces (toremove) { assert(toremove) toremove = this.NormalizeSpaces(toremove) while (toremove.includes(' ')) { toremove = toremove.replace(/ {2}/g, ' ') } return toremove.trim() } // simplifies a string for easier comparison SimplifyStringForComparison (value) { assert(value) value = this.RemoveUnnecesarySpaces(value).toLowerCase() return this.RemoveStuff(value, commonUselessStringParts) } RemoveSpecialChars (value) { assert(value) return this.RemoveStuff(value, specialChars, ' ') } // if the value is empty, or whitespace EmptyOrWhiteSpace (value) { // replaces /n-s with "". then replaces spaces with "". if it equals "", then its empty, or only consists of white space if (value === undefined) { return true } return (value.replace(/\n/g, '').replace(/ /g, '').replace(/\s/g, ' ') === '') } // damn nonbreaking space NormalizeSpaces (input) { assert(input) return input.replace(/\s/g, ' ') } CompareString (s1, s2) { if (!s1 || !s2) { return 0 } s1 = this.SimplifyStringForComparison(s1).split(' ') s2 = this.SimplifyStringForComparison(s2).split(' ') var match = 0 for (var i = 0; i < s1.length; i++) { if (s2.includes(s1[i])) { match++ } } var percent = Math.round(((match / s1.length) * 100).toFixed(2)) // matched words percent var lengthDifference = Math.abs(s2.length - s1.length) percent -= lengthDifference * lengthDiffMultiplier if (percent < 0) { percent = 0 } return percent } AnswerPreProcessor (value) { assert(value) return this.RemoveStuff( value, commonUselessAnswerParts) } // 'a. pécsi sör' -> 'pécsi sör' RemoveAnswerLetters (value) { assert(value) let s = value.split('. ') if (s[0].length < 2 && s.length > 1) { s.shift() return s.join(' ') } else { return value } } SimplifyQA (value, mods) { if (!value) { return } const reducer = (res, fn) => { return fn(res) } return mods.reduce(reducer, value) } SimplifyAnswer (value) { return this.SimplifyQA( value, [ this.RemoveSpecialChars.bind(this), this.RemoveUnnecesarySpaces.bind(this), this.AnswerPreProcessor.bind(this), this.RemoveAnswerLetters.bind(this) ]) } SimplifyQuestion (value) { return this.SimplifyQA( value, [ this.RemoveSpecialChars.bind(this), this.RemoveUnnecesarySpaces.bind(this) ]) } SimplifyStack (stack) { return this.SimplifyQuery(stack) } } const SUtils = new StringUtils() class Question { constructor (q, a, i) { this.Q = SUtils.SimplifyQuestion(q) this.A = SUtils.SimplifyAnswer(a) this.I = i } toString () { var r = '?' + this.Q + '\n!' + this.A if (this.I) { r += '\n>' + this.I } return r } HasQuestion () { return this.Q !== undefined } HasAnswer () { return this.A !== undefined } HasImage () { return this.I !== undefined && (typeof this.I === 'string' || Array.isArray(this.I)) } IsComplete () { return this.HasQuestion() && this.HasAnswer() } Compare (q2, i) { assert(q2) if (typeof q2 === 'string') { var qmatchpercent = SUtils.CompareString(this.Q, q2) if (i === undefined || i.length === 0) { return qmatchpercent } else { if (this.HasImage()) { const iString = typeof this.I === 'string' ? this.I : this.I.join(' ') const imatchpercent = this.HasImage() ? SUtils.CompareString(iString, i.join(' ')) : 0 return (qmatchpercent + imatchpercent) / 2 } else { qmatchpercent -= 30 if (qmatchpercent < 0) { return 0 } else { return qmatchpercent } } } } else { const qmatchpercent = SUtils.CompareString(this.Q, q2.Q) const amatchpercent = SUtils.CompareString(this.A, q2.A) if (this.I !== undefined) { const imatchpercent = this.I === undefined ? SUtils.CompareString(this.I.join(' '), q2.I.join( ' ')) : 0 return (qmatchpercent + amatchpercent + imatchpercent) / 3 } else { return (qmatchpercent + amatchpercent) / 2 } } } } class Subject { constructor (n) { assert(n) this.Name = n this.Questions = [] this.active = false } setIndex (i) { this.index = i } getIndex () { return this.index || -1 } get length () { return this.Questions.length } markActive () { this.active = true } 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] || '' } else { return '' } } 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, img) { assert(q) var r = [] for (let i = 0; i < this.length; i++) { let percent = this.Questions[i].Compare(q, img) 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) { this.Subjects = [] this.getVal = getVal this.setVal = setVal } get length () { return this.Subjects.length } get activeIndexes () { var r = [] for (var i = 0; i < this.length; i++) { if (this.getVal('Is' + i + 'Active')) { r.push(i) } } return r } GetIfActive (ind) { return this.getVal('Is' + ind + 'Active') } ChangeActive (i, value) { this.setVal('Is' + i + 'Active', !!value) } 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, img) { assert(q) var r = [] for (let i = 0; i < this.length; i++) { if (this.GetIfActive(i)) { r = r.concat(this.Subjects[i].Search(q, img)) } } 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 {{{ // all dom getting stuff are in this sections, so on // moodle dom change, stuff breaks here class QuestionsPageModell { GetAllQuestionsDropdown () { if (logElementGetting) { Log('getting dropdown question') } let items = document.getElementById('responseform').getElementsByTagName('p')[0].childNodes let r = '' items.forEach((item) => { if (item.tagName === undefined) { r += item.nodeValue } }) return r } GetAllQuestionsQtext () { if (logElementGetting) { Log('getting all questions qtext') } return document.getElementById('responseform').getElementsByClassName('qtext') // getting questions } GetAllQuestionsP () { if (logElementGetting) { Log('getting all questions by tag p') } return document.getElementById('responseform').getElementsByTagName('p') } GetFormulationClearfix () { if (logElementGetting) { Log('getting formulation clearfix lol') } return document.getElementsByClassName('formulation clearfix') } GetAnswerOptions () { if (logElementGetting) { Log('getting all answer options') } return this.GetFormulationClearfix()[0].childNodes[3].innerText } GetQuestionImages () { if (logElementGetting) { Log('getting question images') } return this.GetFormulationClearfix()[0].getElementsByTagName('img') } // this function should return the question, posible answers, and image names GetQuestionFromTest () { var questions // the important questions var allQuestions // all questions try { allQuestions = this.GetAllQuestionsQtext() // getting questions if (allQuestions.length === 0) { var ddq = this.GetAllQuestionsDropdown() if (SUtils.EmptyOrWhiteSpace(ddq)) { var questionData = '' for (var j = 0; j < allQuestions.length; j++) { // TODO: test dis let subAllQuestions = allQuestions[j].childNodes for (let i = 0; i < subAllQuestions.length; i++) { if (subAllQuestions[i].data !== undefined && !SUtils.EmptyOrWhiteSpace(subAllQuestions[i].data)) { questionData += subAllQuestions[i].data + ' ' // adding text to question data } } } questions = [questionData] } else { questions = [ddq] } } else { questions = [] for (let i = 0; i < allQuestions.length; i++) { questions.push(allQuestions[i].innerText) } } } catch (e) { Exception(e, 'script error at getting question:') } var imgNodes = '' // the image nodes for questions try { imgNodes = this.GetQuestionImages() // getting question images, if there is any AddImageNamesToImages(imgNodes) // adding image names to images, so its easier to search for, or even guessing } catch (e) { Log(e) Log('Some error with images') } questions = questions.map((item) => { if (item) { return SUtils.ReplaceCharsWithSpace(item, '\n') } }) return { imgnodes: imgNodes, allQ: allQuestions, q: questions } } } class ResultsPageModell { DetermineQuestionType (nodes) { let qtype = '' let i = 0 while (i < nodes.length && qtype === '') { let inps = nodes[i].getElementsByTagName('input') if (inps.length > 0) { qtype = inps[0].type } i++ } return qtype } GetSelectAnswer () { if (logElementGetting) { Log('getting selected answer') } var t = document.getElementsByTagName('select') if (t.length > 0) { return t[0].options[document.getElementsByTagName('select')[0].selectedIndex].innerText } } GetCurrQuestion (i) { if (logElementGetting) { Log('getting curr questions by index: ' + i) } return document.getElementsByTagName('form')[0].childNodes[0].childNodes[i].childNodes[1].childNodes[0].innerText } GetFormResult () { if (logElementGetting) { Log('getting form result') } var t = document.getElementsByTagName('form')[0].childNodes[0].childNodes if (t.length > 0 && t[0].tagName === undefined) { // debreceni moodle return document.getElementsByTagName('form')[1].childNodes[0].childNodes } else { return t } } GetAnswerNode (i) { if (logElementGetting) { Log('getting answer node') } var results = this.GetFormResult() // getting results element var r = results[i].getElementsByClassName('answer')[0].childNodes var ret = [] for (var j = 0; j < r.length; j++) { if (r[j].tagName !== undefined && r[j].tagName.toLowerCase() === 'div') { ret.push(r[j]) } } let qtype = this.DetermineQuestionType(ret) return { nodes: ret, type: qtype } } GetCurrentAnswer (i) { if (logElementGetting) { Log('getting curr answer by index: ' + i) } var results = this.GetFormResult() // getting results element var t = results[i].getElementsByClassName('formulation clearfix')[0].getElementsByTagName('span') if (t.length > 2) { return t[1].innerHTML.split('
')[1] } } GetQText (i) { if (logElementGetting) { Log('getting qtext by index: ' + i) } var results = this.GetFormResult() // getting results element return results[i].getElementsByClassName('qtext') } GetDropboxes (i) { if (logElementGetting) { Log('getting dropboxes by index: ' + i) } var results = this.GetFormResult() // getting results element return results[i].getElementsByTagName('select') } GetAllAnswer (index) { if (logElementGetting) { Log('getting all answers, ind: ' + index) } return document.getElementsByClassName('answer')[index].childNodes } GetPossibleAnswers (i) { if (logElementGetting) { Log('getting possible answers') } var results = this.GetFormResult() // getting results element var items = results[i].getElementsByTagName('label') var r = [] for (var j = 0; j < items.length; j++) { const TryGetCorrect = (j) => { var cn = items[j].parentNode.className if (cn.includes('correct')) { return cn.includes('correct') && !cn.includes('incorrect') } } r.push({ value: items[j].innerText, iscorrect: TryGetCorrect(j) }) } return r } GetRightAnswerIfCorrectShown (i) { if (logElementGetting) { Log('getting right answer if correct shown') } var results = this.GetFormResult() // getting results element return results[i].getElementsByClassName('rightanswer') } GetWrongAnswerIfCorrectNotShown (i) { if (logElementGetting) { Log('getting wrong answer if correct not shown') } var results = this.GetFormResult() // getting results element var n = results[i].getElementsByTagName('i')[0].parentNode if (n.className.includes('incorrect')) { return results[i].getElementsByTagName('i')[0].parentNode.innerText } else { return '' } } GetRightAnswerIfCorrectNotShown (i) { if (logElementGetting) { Log('Getting right answer if correct not shown') } var results = this.GetFormResult() // getting results element var n = results[i].getElementsByTagName('i')[0].parentNode if (n.className.includes('correct') && !n.className.includes('incorrect')) { return results[i].getElementsByTagName('i')[0].parentNode.innerText } } GetFormCFOfResult (result) { if (logElementGetting) { Log('getting formulation clearfix') } return result.getElementsByClassName('formulation clearfix')[0] } GetResultText (i) { if (logElementGetting) { Log('getting result text') } var results = this.GetFormResult() // getting results element return this.GetFormCFOfResult(results[i]).getElementsByTagName('p') } GetResultImage (i) { if (logElementGetting) { Log('getting result image') } var results = this.GetFormResult() // getting results element return this.GetFormCFOfResult(results[i]).getElementsByTagName('img') } // gets the question from the result page // i is the index of the question GetQuestionFromResult (i) { var temp = this.GetQText(i) var currQuestion = '' if (temp.length > 0) { currQuestion = temp[0].innerText // adding the question to curr question as .q } else { // this is black magic fuckery a bit if (this.GetDropboxes(i).length > 0) { var allNodes = this.GetResultText(i) currQuestion = '' for (var k = 0; k < allNodes.length; k++) { var allQuestions = this.GetResultText(i)[k].childNodes for (var j = 0; j < allQuestions.length; j++) { if (allQuestions[j].data !== undefined && !SUtils.EmptyOrWhiteSpace(allQuestions[j].data)) { currQuestion += allQuestions[j].data + ' ' } } } } else { try { currQuestion = this.GetCurrQuestion(i) } catch (e) { currQuestion = 'REEEEEEEEEEEEEEEEEEEEE' // this shouldnt really happen sry guys Log('Unable to get question in GetQuestionFromResult') } } } return currQuestion } // tries to get right answer from result page // i is the index of the question GetRightAnswerFromResult (i) { var fun = [] // the basic type of getting answers fun.push(function TryGet0 (i) { var temp = RPM.GetRightAnswerIfCorrectShown(i) // getting risht answer if (temp.length > 0) { return temp[0].innerText } // adding the answer to curr question as .a }) // if there is dropdown list in the current question fun.push(function TryGet1 (i) { if (RPM.GetDropboxes(i).length > 0) { return RPM.GetCurrentAnswer(i) } }) // if the correct answers are not shown, and the selected answer // is correct fun.push(function TryGet2 (i) { return RPM.GetRightAnswerIfCorrectNotShown(i) }) // if there is dropbox in the question fun.push(function TryGet3 (i) { return RPM.GetSelectAnswer() }) // if the correct answers are not shown, and the selected answer // is incorrect, and there are only 2 options fun.push(function TryGet4 (i) { var possibleAnswers = RPM.GetPossibleAnswers(i) if (possibleAnswers.length === 2) { for (var k = 0; k < possibleAnswers.length; k++) { if (possibleAnswers[k].iscorrect === undefined) { return possibleAnswers[k].value } } } }) fun.push(function TryGetFinal (i) { return undefined }) var j = 0 var currAnswer while (j < fun.length && SUtils.EmptyOrWhiteSpace(currAnswer)) { try { currAnswer = fun[j](i) } catch (e) { } j++ } return currAnswer } // version 2 of getting right answer from result page // i is the index of the question GetRightAnswerFromResultv2 (i) { try { var answerNodes = this.GetAnswerNode(i) let items = answerNodes.nodes if (answerNodes.type === 'checkbox') { return RPM.GetRightAnswerFromResult(i) } for (let j = 0; j < items.length; j++) { let cn = items[j].className if (cn.includes('correct') && !cn.includes('incorrect')) { return items[j].innerText } } if (items.length === 2) { for (let j = 0; j < items.length; j++) { let cn = items[j].className if (!cn.includes('correct')) { return items[j].innerText } } } } catch (e) { Log('error at new nodegetting, trying the oldschool way') } } } class MiscPageModell { GetCurrentSubjectName () { if (logElementGetting) { Log('getting current subjects name') } return document.getElementById('page-header').innerText.split('\n')[0] } GetVideo () { if (logElementGetting) { Log('getting video stuff') } return document.getElementsByTagName('video')[0] } GetVideoElement () { if (logElementGetting) { Log('getting video element') } return document.getElementById('videoElement').parentNode } GetInputType (answers, i) { if (logElementGetting) { Log('getting input type') } return answers[i].getElementsByTagName('input')[0].type } } var QPM = new QuestionsPageModell() var RPM = new ResultsPageModell() var MPM = new MiscPageModell() // : }}} // : Main function {{{ Main() function Main () { 'use strict' console.time('main') Init(function (count, subjCount) { var url = location.href // eslint-disable-line let skipLoad = getVal('skipLoad') if (count === -2 && subjCount === -2 && skipLoad) { if (url.includes('/quiz/') && url.includes('attempt.php')) { ShowMessage({ m: 'Passzív mód bekapcsolva, válaszok megjelenítéséhez menü gomb alatt kapcsold ki, és frissíts!', isSimple: true }) } } else { 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, count, subjCount) } } catch (e) { ShowMessage({ m: 'Fatál error. Check console (f12). Kattints az üzenetre az összes kérdés/válaszért manuális kereséshez!', isSimple: true }, undefined, () => { OpenErrorPage(e) }) Exception(e, 'script error at main:') } if (url.includes('eduplayer')) { AddVideoHotkeys(url) } // adding video hotkeys Log( 'Itteni hibák 100% a moodle hiba. Kivéve, ha oda van írva hogy script error ;) Ha ilyesmi szerepel itt, akkor olvasd el a segítség szekciót! Nagy esélyel a kérdéseket nem lehetett beolvasni.' ) } }) console.log('Moodle Test Script run time:') console.timeEnd('main') if (forceTestPage || forceResultPage || forceDefaultPage) { if (document.getElementById('scriptMessage')) { document.getElementById('scriptMessage').style.background = 'green' } } } // : }}} // : Main logic stuff {{{ // : Loading {{{ function Init (cwith) { if (false) { // eslint-disable-line setVal('version16', undefined) setVal('version15', undefined) setVal('firstRun', undefined) setVal('showQuestions', undefined) 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 // -------------------------------------------------------------------------------------- try { // adding addeventlistener stuff, for the ability to add more event listeners for the same event addEventListener = (function () { if (document.addEventListener) { return function (element, event, handler) { element.addEventListener(event, handler, false) } } else { return function (element, event, handler) { element.attachEvent('on' + event, handler) } } }()) } catch (e) { Exception(e, 'script error at addEventListener:') } VersionActions() count = Load(cwith) // loads resources if (!url.includes('.pdf')) { ShowMenu() } return count } function VersionActions () { // FOR TESTING ONLY // setVal("version15", true); // setVal("firstRun", true); // setVal("version16", true); // throw "asd"; FreshStart() Version15() Version16() } // : Version action functions {{{ function FreshStart () { var firstRun = getVal('firstRun') // if the current run is the frst if (firstRun === undefined || firstRun === true) { setVal('firstRun', false) ShowHelp() // showing help return true } } function Version15 () { var version15 = getVal('version15') // if the current run is the frst if (version15 === undefined || version15 === true) { setVal('version15', false) document.write( '

Moodle teszt userscript:

1.5.0 verzió: a script mostantól XMLHTTP kéréseket küld szerver fele! Erre a userscript futtató kiegészitőd is figyelmeztetni fog! Ha ez történik, a script rendes működése érdekében engedélyezd (Always allow domain)! Ha nem akarod, hogy ez történjen, akkor ne engedélyezd, vagy a menüben válaszd ki a "helyi fájl használata" opciót!

Elküldött adatok: minden teszt után a kérdés-válasz páros. Fogadott adatok: Az összes eddig ismert kérdés. Érdemes help-et elolvasni!!!

Ez az ablak frissités után eltűnik. Ha nem, akkor a visza gombbal próbálkozz.
' ) document.close() throw new Error('something, so this stuff stops') } } function Version16 () { var version16 = getVal('version16') // if the current run is the frst if (version16 === undefined || version16 === true) { var i = 0 while (getVal('Is' + i + 'Active') !== undefined) { setVal('Is' + i + 'Active', false) i++ } setVal('version16', false) } } // : }}} 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: 'Nem sikerült betölteni az adatokat! Kattints a manualért', isSimple: true }, undefined, ShowHelp) } var r = new QuestionDB(getVal, setVal) var rt = [] var allCount = -1 LoadMOTD(d) LoadVersion(d) for (let i = 0; i < d.Subjects.length; i++) { let s = new Subject(d.Subjects[i].Name) s.setIndex(i) if (getVal('Is' + i + 'Active')) { s.markActive() 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.I)) } rt.push({ name: d.Subjects[i].Name, count: j }) allCount += j subjCount++ } r.AddSubject(s) } data = r count = allCount + 1 // couse starting with -1 to show errors let i = 0 while (i < data.length && !getVal('Is' + i + 'Active')) { i++ } } 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) { var newVersion = false // if the script is newer than last start var loaded = count !== -1 // if script could load stuff try { newVersion = info().script.version !== getVal('lastVerson') } catch (e) { Log('Some weird error trying to set new verison') } var greetMsg = '' // message to show at the end var timeout = null // the timeout. if null, it wont be hidden // no new version, nothing loaded if (!newVersion && !loaded) { // -------------------------------------------------------------------------------------------------------------- greetMsg = 'Hiba a @resource tagnál, vagy a fileval van gond! (Lehet át lett helyezve, vagy üres, vagy nincs tárgy kiválasztva) Vagy válaszd a netes adatok használatát menüben. Ellenőrizd az elérési utat, vagy hogy a Tampermonkey bővítmény eléri-e a fájlokat. Ha netes forrást használsz, akkor nem elérhető a szerver! Segítségért kattints!' } var showSplash = (getVal('showSplash') === undefined) || getVal('showSplash') // getting value, if splash screen should be shown. Its true, if its undefined, or true // no new version, everything loaded, and show splash is enabled. otherwise something happened, so showing it if (!newVersion && loaded && showSplash) { // ------------------------------------------------------------------------------------------------ timeout = 5 greetMsg = 'Moodle/Elearning/KMOOC segéd v. ' + info().script.version + '. ' if (lastestVersion !== undefined && info().script.version !== lastestVersion) { greetMsg += 'Új verzió elérhető: ' + lastestVersion + '\n' 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 += ' Az adatfájlban nem adtál meg nevet. Vagy nem elérhető a szerver. Katt a helpért!' } } // new version, nothing loaded if (newVersion && !loaded) { // -------------------------------------------------------------------------------------------------------------- greetMsg = 'Moodle/Elearning/KMOOC segéd v. ' + info().script.version + '. Új verzió!\n Írd át a @resouce tagnál az elírési utat! Kivéve ha üres a file, akkor töltsd fel :) Nincs kérdés betöltve! Segítséghez kattints. Changelog:\n' + lastChangeLog // showing changelog too } // new version, everything loaded -> set lastVerson to current if (newVersion && loaded) { // -------------------------------------------------------------------------------------------------------------- greetMsg = 'Moodle/Elearning/KMOOC segéd v. ' + info().script.version + '. ' + count + ' kérdés és ' + subjCount + ' tárgy betöltve. Verzió frissítve ' + info().script.version + '-re. Changelog:\n' + lastChangeLog setVal('lastVerson', info().script.version) // setting lastVersion } if (!SUtils.EmptyOrWhiteSpace(motd)) { var prevmotd = getVal('motd') if (prevmotd !== motd) { greetMsg += '\nMOTD:\n' + motd timeout = null setVal('motdcount', motdShowCount) setVal('motd', motd) } else { var motdcount = getVal('motdcount') if (motdcount === undefined) { setVal('motdcount', motdShowCount) motdcount = motdShowCount } motdcount-- if (motdcount > 0) { greetMsg += '\nMOTD:\n' + motd timeout = null setVal('motdcount', motdcount) } } } ShowMessage({ m: greetMsg, isSimple: true }, timeout, ShowHelp) // showing message. If "m" is empty it wont show it, thats how showSplash works. } // : }}} // : Answering stuffs {{{ function HandleQuiz () { var q = QPM.GetQuestionFromTest() var questions = q.q var imgNodes = q.imgnodes // ------------------------------------------------------------------------------------------------------ var answers = [] for (var j = 0; j < questions.length; j++) { var question = SUtils.RemoveUnnecesarySpaces(questions[j]) // simplifying question var result = data.Search(question, SimplifyImages(imgNodes)) 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) } function PrepareAnswers (result, j) { assert(result) if (result.length > 0) { var allMessages = [] // preparing all messages for (var k = 0; k < result.length; k++) { var msg = '' // the current message if ((getVal('showQuestions') === undefined) || getVal('showQuestions')) { 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' + result[k].q.I // if it has image part, adding that too } allMessages.push({ m: msg, p: result[k].match }) } return allMessages } } function ShowAnswers (answers, question) { assert(answers) if (answers.length > 0) { // if there are more than 0 answer ShowMessage(answers) } else { ShowMessage({ m: 'Nincs találat :( Kattints az üzenetre az összes kérdés/válaszért manuális kereséshez! Előfordulhat, hogy a tárgyat nem válsztottad ki a menüben.', isSimple: true }, undefined, function () { OpenErrorPage({ message: 'No result found', stack: JSON.stringify(question) }) }) } } // : }}} // : Quiz saving {{{ function HandleResults (url) { var d = SaveQuiz(GetQuiz(), data) // saves the quiz questions and answers if (d) { ShowSaveQuizDialog(d.addedQ, d.allQ, d.allOutput, d.output, d.sendSuccess, d.sentData) } } function ShowSaveQuizDialog (addedQ, allQ, allOutput, output, sendSuccess, sentData) { var msg = '' if (addedQ > 0) { msg = 'Klikk ide a nyers adatokhoz. ' + addedQ + ' új kérdés!' 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.' } } // showing a message wit the click event, and the generated page ShowMessage({ m: msg, isSimple: true }, null, function () { var towrite = '

' + sentData.subj + '
TXT-ben nem szereplő kérdések: ' + addedQ + '/' + allQ + '


' + output.replace(/\n/g, '
') + '

Összes kérdés/válasz:

' + allOutput.replace( /\n/g, '
') try { towrite += '

Elküldött adatok:

' + JSON.stringify(sentData) } catch (e) { towrite += '

Elküldött adatok:

' + sentData } document.write(towrite) document.close() }) } 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 function GetImageFormResult (i) { var temp = null try { var imgElements = RPM.GetResultImage(i) // trying to get image var imgURL = [] // image urls for (var j = 0; j < imgElements.length; j++) { if (!imgElements[j].src.includes('brokenfile')) { var filePart = imgElements[j].src.split('/') // splits the link by "/" filePart = filePart[filePart.length - 1] // the last one is the image name imgURL.push(decodeURI(SUtils.ShortenString(filePart, 30))) } } if (imgURL.length > 0) { temp = JSON.stringify(imgURL) return temp } } catch (e) { Log("Couldn't get images from result") } } // saves the current quiz. questionData contains the active subjects questions function SaveQuiz (quiz, questionData) { try { if (quiz.length === 0) { throw new Error('quiz length is zero!') } var output = '' // thefinal output var allOutput = '' // thefinal output with all questions var allQ = 0 var addedQ = 0 var newQuestions = [] for (var i = 0; i < quiz.length; i++) { // searching for same questions in questionData 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()) { toAdd += '>' + SUtils.RemoveUnnecesarySpaces(quiz[i].I) + '\n' // adding quiz image if there is any } if (SearchSameQuestion(questionData, quiz, i) === -1) { output += toAdd // adding to output newQuestions.push(quiz[i]) addedQ++ } allOutput += toAdd // adding to all allQ++ } var sendSuccess = false var sentData = {} try { try { sentData.subj = MPM.GetCurrentSubjectName() } catch (e) { sentData.subj = 'NOSUBJ' Log('unable to get subject name :c') } sentData.allData = quiz sentData.data = newQuestions sentData.version = info().script.version sentData.id = GetId() SendXHRMessage('datatoadd=' + JSON.stringify(sentData)) sendSuccess = true } catch (e) { Exception(e, 'error at sending data to server.') } return { addedQ: addedQ, allQ: allQ, allOutput: allOutput, output: output, sendSuccess: sendSuccess, sentData: sentData } } catch (e) { Exception(e, 'script error at saving quiz') } } // getting quiz from finish page function GetQuiz () { try { var quiz = [] // final quiz stuff var results = RPM.GetFormResult() // getting results element for (var i = 0; i < results.length - 2; i++) { var question = {} // the current question // QUESTION -------------------------------------------------------------------------------------------------------------------- var q = RPM.GetQuestionFromResult(i) if (q !== undefined) { question.q = SUtils.SimplifyQuestion(q) } // RIGHTANSWER --------------------------------------------------------------------------------------------------------------------- var a = RPM.GetRightAnswerFromResultv2(i) if (a === undefined) { a = RPM.GetRightAnswerFromResult(i) } if (a !== undefined) { question.a = SUtils.SimplifyAnswer(a) } // IMG --------------------------------------------------------------------------------------------------------------------- var img = GetImageFormResult(i) question.i = img if (question.a !== undefined) { quiz.push(new Question(question.q, question.a, question.i)) // adding current question to quiz } else { Log('error getting queston, no correct answer given, or its incorrect') Log(question) } } return quiz } catch (e) { Exception(e, 'script error at quiz parsing:') } } // : }}} // : Helpers {{{ function SimplifyImages (imgs) { var questionImages = [] // the array for the image names in question for (var i = 0; i < imgs.length; i++) { if (!imgs[i].src.includes('brokenfile')) { var filePart = imgs[i].src.split('/') // splits the link by "/" filePart = filePart[filePart.length - 1] // the last one is the image name questionImages.push(decodeURI(SUtils.RemoveUnnecesarySpaces(SUtils.ShortenString(filePart, 30)))) // decodes uri codes, and removes exess spaces, and shortening it } } return questionImages } // adds image names to image nodes function AddImageNamesToImages (imgs) { for (var i = 0; i < imgs.length; i++) { if (!imgs[i].src.includes('brokenfile')) { var filePart = imgs[i].src.split('/') // splits the link by "/" filePart = filePart[filePart.length - 1] // the last one is the image name var appedtTo = imgs[i].parentNode // it will be appended here var mainDiv = document.createElement('div') var fileName = SUtils.ShortenString(decodeURI(filePart), 15) // shortening name, couse it can be long as fuck var textNode = document.createTextNode('(' + fileName + ')') mainDiv.appendChild(textNode) appedtTo.appendChild(mainDiv) } } } // this function adds basic hotkeys for video controll. function AddVideoHotkeys (url) { var seekTime = 20 document.addEventListener('keydown', function (e) { try { var video = MPM.GetVideo() var keyCode = e.keyCode // getting keycode if (keyCode === 32) { // if the keycode is 32 (space) e.preventDefault() // preventing default action (space scrolles down) if (video.paused && video.buffered.length > 0) { video.play() } else { video.pause() } } if (keyCode === 39) { // rigth : 39 video.currentTime += seekTime } if (keyCode === 37) { // left : 37 video.currentTime -= seekTime } } catch (err) { Log('Hotkey error.') Log(err.message) } }) var toadd = MPM.GetVideoElement() var node = CreateNodeWithText(toadd, 'Miután elindítottad: Play/pause: space. Seek: Bal/jobb nyíl.') node.style.margin = '5px 5px 5px 5px' // fancy margin } // removes stuff like " a. q1" -> "q1" function RemoveLetterMarking (inp) { let dotIndex = inp.indexOf('.') let doubledotIndex = inp.indexOf(':') let maxInd = 4 // inp.length * 0.2; if (dotIndex < maxInd) { return SUtils.RemoveUnnecesarySpaces(inp.substr(inp.indexOf('.') + 1, inp.length)) } else if (doubledotIndex < maxInd) { return SUtils.RemoveUnnecesarySpaces(inp.substr(inp.indexOf(':') + 1, inp.length)) } else { return inp } } // highlights the possible solutions to the current question function HighLightAnswer (results, currQuestionNumber) { try { if (results.length > 0) { var answers = RPM.GetAllAnswer(currQuestionNumber) // getting all answers var toColor = [] // the numberth in the array will be colored, and .length items will be colored var type = '' // type of the question. radio or ticbox or whatitscalled for (let i = 0; i < answers.length; i++) { // going thtough answers if (answers[i].tagName && answers[i].tagName.toLowerCase() === 'div') { // if its not null and is "div" var correct = results[0].q.A.toLowerCase() // getting current correct answer from data var answer = answers[i].innerText.replace(/\n/g, '').toLowerCase() // getting current answer // removing stuff like "a." answer = RemoveLetterMarking(answer) if (SUtils.EmptyOrWhiteSpace(correct) || SUtils.EmptyOrWhiteSpace(answer)) { continue } if (SUtils.NormalizeSpaces(SUtils.RemoveUnnecesarySpaces(correct)).includes(answer)) { // if the correct answer includes the current answer toColor.push(i) // adding the index type = MPM.GetInputType(answers, i) // setting the type } } } if (results[0].match === 100) { // if the result is 100% correct if (type !== 'radio' || toColor.length === 1) { // TODO why not radio for (let i = 0; i < toColor.length; i++) { // going through "toColor" answers[toColor[i]].style.backgroundColor = '#8cff66' } } } // and coloring the correct index } } catch (e) { // catching errors. Sometimes there are random errors, wich i did not test, but they are rare, and does not break the main script. Log('script error at highlightin answer: ' + e.message) } } // : }}} function Log (value) { if (log) { console.log(value) } } function Exception (e, msg) { Log('------------------------------------------') Log(msg) Log(e.message) Log('------------------------------------------') Log(e.stack) Log('------------------------------------------') } // : }}} // : Minor UI stuff {{{ // shows a message with "msg" text, "matchPercent" tip and transp, and "timeout" time function ShowMessage (msgItem, timeout, funct) { // msgItem help: // [ [ {}{}{}{} ] [ {}{}{} ] ] // msgItem[] <- a questions stuff // msgItem[][] <- a questions relevant answers array // msgItem[][].p <- a questions precent // msgItem[][].m <- a questions message try { var defMargin = '0px 5px 0px 5px' var isSimpleMessage = false var simpleMessageText = '' if (msgItem.isSimple) { // parsing msgItem for easier use simpleMessageText = msgItem.m if (simpleMessageText === '') { return } msgItem = [ [{ m: simpleMessageText }] ] isSimpleMessage = true } var appedtTo = document.body // will be appended here var width = window.innerWidth - window.innerWidth / 6 // with of the box var startFromTop = 25 // top distance var mainDiv = document.createElement('div') // the main divider, wich items will be attached to mainDiv.setAttribute('id', 'messageMainDiv') if (funct) { // if there is a function as parameter addEventListener(mainDiv, 'click', funct) // adding it as click } // lotsa crap style mainDiv.style.position = 'fixed' mainDiv.style.zIndex = 999999 mainDiv.style.textAlign = 'center' mainDiv.style.width = width + 'px' // mainDiv.style.height = height + 'px'; mainDiv.style.padding = '0px' mainDiv.style.background = '#222d32' // background color mainDiv.style.color = '#ffffff' // text color mainDiv.style.borderColor = '#035a8f' // border color mainDiv.style.border = 'none' mainDiv.style.top = (startFromTop) + 'px' mainDiv.style.left = (window.innerWidth - width) / 2 + 'px' mainDiv.style.opacity = '0.9' // setting starting opacity mainDiv.setAttribute('id', 'scriptMessage') var matchPercent = msgItem[0][0].p if (isSimpleMessage) { var simpleMessageParagrapg = document.createElement('p') // new paragraph simpleMessageParagrapg.style.margin = defMargin // fancy margin var splitText = simpleMessageText.split('\n') for (var i = 0; i < splitText.length; i++) { var mesageNode = CreateNodeWithText(simpleMessageParagrapg, splitText[i]) mesageNode.style.margin = defMargin // fancy margin } mainDiv.appendChild(simpleMessageParagrapg) // adding text box to main div } else { // if its a fucking complicated message // TABLE SETUP ------------------------------------------------------------------------------------------------------------ var table = document.createElement('table') table.style.width = '100%' // ROWS ----------------------------------------------------------------------------------------------------- var rowOne = table.insertRow() // previous suggestion, question text, and prev question var rowTwo = table.insertRow() // next question button var rowThree = table.insertRow() // next suggetsion button // CELLS ----------------------------------------------------------------------------------------------------- // row one var numberTextCell = rowOne.insertCell() var questionCell = rowOne.insertCell() // QUESTION CELL questionCell.setAttribute('id', 'questionCell') questionCell.rowSpan = 3 questionCell.style.width = '90%' var prevQuestionCell = rowOne.insertCell() // row two var percentTextCell = rowTwo.insertCell() var nextQuestionCell = rowTwo.insertCell() // row three var prevSuggestionCell = rowThree.insertCell() var nextSuggestionCell = rowThree.insertCell() // adding finally mainDiv.appendChild(table) // PERCENT TEXT SETUP ----------------------------------------------------------------------------------------------------- var percentTextBox = CreateNodeWithText(percentTextCell, '') percentTextBox.setAttribute('id', 'percentTextBox') if (matchPercent) { // if match percent param is not null percentTextBox.innerText = matchPercent + '%' } // NUMBER SETUP ----------------------------------------------------------------------------------------------------- var numberTextBox = CreateNodeWithText(numberTextCell, '1.') numberTextBox.setAttribute('id', 'numberTextBox') // ANSWER NODE SETUP ------------------------------------------------------------------------------------------------------------- var questionTextElement = CreateNodeWithText(questionCell, 'ur question goes here, mister OwO') questionTextElement.setAttribute('id', 'questionTextElement') // BUTTON SETUP ----------------------------------------------------------------------------------------------------------- var currItem = 0 var currRelevantQuestion = 0 const GetRelevantQuestion = () => { // returns the currItemth questions currRelevantQuestionth relevant question return msgItem[currItem][currRelevantQuestion] } const ChangeCurrItemIndex = (to) => { currItem += to if (currItem < 0) { currItem = 0 } if (currItem > msgItem.length - 1) { currItem = msgItem.length - 1 } currRelevantQuestion = 0 } const ChangeCurrRelevantQuestionIndex = (to) => { currRelevantQuestion += to if (currRelevantQuestion < 0) { currRelevantQuestion = 0 } if (currRelevantQuestion > msgItem[currItem].length - 1) { currRelevantQuestion = msgItem[currItem].length - 1 } } const SetQuestionText = () => { var relevantQuestion = GetRelevantQuestion() questionTextElement.innerText = relevantQuestion.m if (currItem === 0 && currRelevantQuestion === 0) { numberTextBox.innerText = (currRelevantQuestion + 1) + '.' } else { numberTextBox.innerText = (currItem + 1) + './' + (currRelevantQuestion + 1) + '.' } percentTextBox.innerText = relevantQuestion.p + '%' } var buttonMargin = '2px 2px 2px 2px' // uniform button margin if (msgItem[currItem].length > 1) { // PREV SUGG BUTTON ------------------------------------------------------------------------------------------------------------ var prevSuggButton = CreateNodeWithText(prevSuggestionCell, '<', 'button') prevSuggButton.style.margin = buttonMargin // fancy margin prevSuggButton.addEventListener('click', function () { ChangeCurrRelevantQuestionIndex(-1) SetQuestionText() }) // NEXT SUGG BUTTON ------------------------------------------------------------------------------------------------------------ var nextSuggButton = CreateNodeWithText(nextSuggestionCell, '>', 'button') nextSuggButton.style.margin = buttonMargin // fancy margin nextSuggButton.addEventListener('click', function () { ChangeCurrRelevantQuestionIndex(1) SetQuestionText() }) } // deciding if has multiple questions ------------------------------------------------------------------------------------------------ if (msgItem.length === 1) { SetQuestionText() } else { // if there are multiple items to display // PREV QUESTION BUTTON ------------------------------------------------------------------------------------------------------------ var prevButton = CreateNodeWithText(prevQuestionCell, '^', 'button') prevButton.style.margin = buttonMargin // fancy margin // event listener prevButton.addEventListener('click', function () { ChangeCurrItemIndex(-1) SetQuestionText() }) // NEXT QUESTION BUTTON ------------------------------------------------------------------------------------------------------------ var nextButton = CreateNodeWithText(nextQuestionCell, 'ˇ', 'button') nextButton.style.margin = buttonMargin // fancy margin // event listener nextButton.addEventListener('click', function () { ChangeCurrItemIndex(1) SetQuestionText() }) SetQuestionText() } } appedtTo.appendChild(mainDiv) // THE FINAL APPEND // setting some events // addEventListener(window, 'scroll', function () { // mainDiv.style.top = (pageYOffset + startFromTop) + 'px'; // }) addEventListener(window, 'resize', function () { mainDiv.style.left = (window.innerWidth - width) / 2 + 'px' }) var timeOut if (timeout && timeout > 0) { // setting timeout if not zero or null timeOut = setTimeout(function () { mainDiv.parentNode.removeChild(mainDiv) }, timeout * 1000) } // middle click close event listener addEventListener(mainDiv, 'mousedown', function (e) { if (e.which === 2) { mainDiv.parentNode.removeChild(mainDiv) if (timeOut) { clearTimeout(timeOut) } } }) } catch (e) { Exception(e, 'script error at showing message:') } } // shows a fancy menu function ShowMenu () { try { var buttonWidth = 100 // button size ;) var buttonHeight = 85 var appedtTo = document.body // will be appended here // mainDiv.style.left = (window.innerWidth - width) / 2 + 'px'; var menuButtonDiv = document.createElement('div') menuButtonDiv.style.width = buttonWidth + 'px' menuButtonDiv.style.height = buttonHeight + 'px' menuButtonDiv.style.top = (window.innerHeight - buttonHeight * 1.5) + 'px' menuButtonDiv.style.left = window.innerWidth - buttonWidth * 1.5 + 'px' menuButtonDiv.style.zIndex = 999999 // TO THE MAX menuButtonDiv.style.position = 'fixed' // menuButtonDiv.style.borderStyle = "solid"; // menuButtonDiv.style.borderWidth = "1px"; // design menuButtonDiv.style.textAlign = 'center' menuButtonDiv.style.padding = '0px' menuButtonDiv.style.margin = '0px' menuButtonDiv.style.background = 'transparent' // background color // menu text // var menuTextBox = CreateNodeWithText(menuButtonDiv, "Kérdések\nMenü"); var menuButton = CreateNodeWithText(menuButtonDiv, 'Kérdések Menu', 'button') menuButton.style.width = buttonWidth + 'px' menuButton.style.border = 'none' menuButton.style.height = buttonHeight - 20 + 'px' menuButton.style.background = '#222d32' // background color menuButton.style.color = '#ffffff' // background color menuButton.setAttribute('id', 'HelperMenuButton') menuButton.addEventListener('click', function () { if (document.getElementById('HelperMenu') == null) { ShowMenuList() } else { CloseMenu() } }) // adding click // passive mode stuff var questionsTickBox = document.createElement('input') questionsTickBox.type = 'checkbox' questionsTickBox.checked = getVal('skipLoad') questionsTickBox.style.position = '' questionsTickBox.style.left = 10 + 'px' questionsTickBox.style.margin = '5px 5px 5px 5px' // fancy margin questionsTickBox.style.top = 0 + 'px' menuButtonDiv.appendChild(questionsTickBox) // adding to main div questionsTickBox.addEventListener('click', function () { setVal('skipLoad', questionsTickBox.checked) var msg = '' if (getVal('skipLoad')) { msg = 'Passzív mód bekapcsolva, mostantól kérdések nem lesznek betöltve/lekérve.' } else { msg = 'Passzív mód kikapcsolva, frissíts az érvénybe lépéshez!' } ShowMessage({ m: msg, isSimple: true }, 6) }) var loadDataCheckBoxText = CreateNodeWithText(questionsTickBox, 'Passzív mód', 'span') loadDataCheckBoxText.style.fontSize = '12px' menuButtonDiv.appendChild(loadDataCheckBoxText) addEventListener(window, 'resize', function () { menuButtonDiv.style.left = window.innerWidth - buttonWidth * 2 + 'px' }) appedtTo.appendChild(menuButtonDiv) } catch (e) { Exception(e, 'script error at showing menu:') } } // shows a fancy menu list with the subjects function ShowMenuList () { try { var appedtTo = document.body // will be appended here var menuDiv = document.createElement('div') menuDiv.setAttribute('id', 'HelperMenu') menuDiv.style.width = (window.innerWidth / 2) + 'px' menuDiv.style.top = (window.innerHeight / 10) + 'px' menuDiv.style.left = window.innerWidth / 2 - (window.innerWidth / 2) / 2 + 'px' menuDiv.style.zIndex = 999999 menuDiv.style.position = 'fixed' // design menuDiv.style.textAlign = 'center' menuDiv.style.padding = '0px' menuDiv.style.background = '#222d32' // background color menuDiv.style.color = '#ffffff' // text color menuDiv.style.borderColor = '#035a8f' // border color menuDiv.style.border = 'none' menuDiv.style.opacity = '1' // setting starting opacity var fiveMargin = '5px 5px 5px 5px' var tbl = document.createElement('table') tbl.style.margin = fiveMargin tbl.style.textAlign = 'left' tbl.style.width = '98%' // adding headers --------------------------------------------------------------------------------------------------------------- var subjTable = document.createElement('div') subjTable.style.margin = fiveMargin subjTable.style.textAlign = 'left' subjTable.style.width = '98%' // var tr = subjTable.insertRow() // var header1 = tr.insertCell() // var headerSubjInfoParagraph = CreateNodeWithText(header1, 'Tárgynév [darab kérdés]', 'center') // headerSubjInfoParagraph.style.margin = fiveMargin // fancy margin // var header2 = tr.insertCell() // var headerSubjInfoParagraph2 = CreateNodeWithText(header2, 'Aktív') // headerSubjInfoParagraph2.style.margin = fiveMargin // fancy margin 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: [] }) let collapsibles = [] Object.entries(grouped).forEach(([subjName, subjGroup]) => { let b = CreateNodeWithText(subjTable, subjName, 'button') b.style.backgroundColor = '#222d32' b.style.color = '#ffffff' b.style.cursor = 'pointer' b.style.padding = '5px' b.style.width = '100%' b.style.border = 'none' b.style.textAlign = 'left' b.style.outline = 'none' collapsibles.push(b) let content = document.createElement('div') content.style.padding = '0 18px' content.style.overflow = 'hidden' content.style.backgroundColor = '#222d32' content.style.borderColor = '#212127' content.style.borderStyle = 'solid' content.style.borderWidth = '5px' let ifGroupActive = subjGroup.some((x) => { return x.getIfActive() }) content.style.display = ifGroupActive ? 'block' : 'none' subjTable.appendChild(content) subjGroup.forEach((subj) => { let tbl = document.createElement('table') content.appendChild(tbl) 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 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(i, checked) }) // adding click }) }) collapsibles.forEach((x) => { x.addEventListener('click', function () { this.classList.toggle('active') var content = this.nextElementSibling 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' 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 if (getVal('skipLoad')) { textBox = CreateNodeWithText(noDataRowCell, 'Passszív mód bekapcsolva. Kapcsold ki a kérdések betöltéséhez!' ) } else { textBox = CreateNodeWithText(noDataRowCell, 'A kérdéseket nem lehetett beolvasni. Vagy nem elérhető a szerver, vagy ha offline módot használsz, akkor hibás a fájl elérési útja, vagy a fájl maga. Olvasd el a manualt!' ) } textBox.style.margin = fiveMargin // fancy margin } // show splash tickbox ----------------------------------------------------------------------------------------------------------------------------- var splasTickboxRow = tbl.insertRow() var splashTickboxCell = splasTickboxRow.insertCell() var splashTickBox = document.createElement('input') splashTickBox.type = 'checkbox' splashTickBox.checked = getVal('showSplash') || false splashTickBox.style.position = '' // splashTickBox.style.background = "white"; splashTickBox.style.left = 10 + 'px' splashTickBox.style.margin = '5px 5px 5px 5px' // fancy margin splashTickBox.style.top = menuDiv.offsetHeight + 'px' splashTickboxCell.appendChild(splashTickBox) // adding to main div splashTickBox.addEventListener('click', function () { setVal('showSplash', splashTickBox.checked) }) // adding clicktextNode CreateNodeWithText(splashTickboxCell, 'Üdvözlő üzenet mutatása minden oldalon', 'span') // show questons tickbox ----------------------------------------------------------------------------------------------------------------------------- var questionTickboxRow = tbl.insertRow() var questionTickboxCell = questionTickboxRow.insertCell() var questionsTickBox = document.createElement('input') questionsTickBox.type = 'checkbox' questionsTickBox.checked = getVal('showQuestions') questionsTickBox.style.position = '' // questionsTickBox.style.background = "white"; questionsTickBox.style.left = 10 + 'px' questionsTickBox.style.margin = '5px 5px 5px 5px' // fancy margin questionsTickBox.style.top = menuDiv.offsetHeight + 'px' questionTickboxCell.appendChild(questionsTickBox) // adding to main div questionsTickBox.addEventListener('click', function () { setVal('showQuestions', questionsTickBox.checked) if (!questionsTickBox.checked) { ShowMessage({ m: 'Szinte mindég jó az talált válasz a kérdésre, de attól még könnyen előfordulhat, hogy rosz kérdésre írja ki a választ! Ez a opció nélkül ezt az ellenőrzési lehetőséget nem tudod kihasználni', isSimple: true }, 7) } }) // adding clicktextNode CreateNodeWithText(questionTickboxCell, 'Kérdések mutatása válaszhoz', 'span') // setting up buttons var buttonRow = tbl.insertRow() var buttonCell = buttonRow.insertCell() buttonCell.style.textAlign = 'center' // x button ------------------------------------------------------------------------------------------------------------------------------ var xButton = CreateNodeWithText(buttonCell, 'Bezárás', 'button') xButton.style.position = '' xButton.style.left = 10 + 'px' xButton.style.margin = '5px 5px 5px 5px' // fancy margin xButton.style.top = menuDiv.offsetHeight + 'px' xButton.addEventListener('click', function () { CloseMenu() }) // adding clicktextNode // help button ---------------------------------------------------------------------------------------------------------------- var helpButton = CreateNodeWithText(buttonCell, 'Help', 'button') helpButton.style.position = '' helpButton.style.left = 10 + 'px' helpButton.style.margin = '5px 5px 5px 5px' // fancy margin helpButton.style.top = menuDiv.offsetHeight + 'px' helpButton.addEventListener('click', function () { ShowHelp() }) // adding clicktextNode // site link ---------------------------------------------------------------------------------------------------------------- var siteLink = CreateNodeWithText(buttonCell, 'Help', 'button') siteLink.innerText = 'Weboldal' siteLink.addEventListener('click', function () { location.href = serverAdress + 'menuClick' // eslint-disable-line }) // addEventListener(window, 'scroll', function () { // menuDiv.style.top = (pageYOffset + window.innerHeight / 3) + 'px'; // }) addEventListener(window, 'resize', function () { menuDiv.style.left = window.innerWidth / 2 + 'px' }) menuDiv.appendChild(tbl) appedtTo.appendChild(menuDiv) } catch (e) { Exception(e, 'script error at showing menu list:') } document.addEventListener('keydown', EscClose) } function EscClose (e) { if (e.keyCode === 27) { CloseMenu() } } function CloseMenu () { document.getElementById('HelperMenu').parentNode.removeChild(document.getElementById( 'HelperMenu')) document.removeEventListener('keydown', EscClose) } // : }}} // : Generic utils {{{ function GetId () { let currId = getVal('clientId') if (currId) { return currId } else { currId = new Date() currId = currId.getTime() + Math.floor(Math.random() * 10000000) setVal('clientId', currId.toString()) return currId } } function CreateNodeWithText (to, text, type) { var paragraphElement = document.createElement(type || 'p') // new paragraph var textNode = document.createTextNode(text) paragraphElement.appendChild(textNode) to.appendChild(paragraphElement) return paragraphElement } function SendXHRMessage (message) { var url = serverAdress + 'isAdding' xmlhttpRequest({ method: 'POST', url: url, data: message, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, onerror: function (response) { Log('XMLHTTP request POST error') } }) } function OpenErrorPage (e) { let path = 'lred' try { if (e.message || e.stack) { path += '?' } if (e.message) { path += 'msg:' + SUtils.SimplifyQuery(e.message) } if (e.stack) { path += '___stack:' + SUtils.SimplifyStack(e.stack) } path += '___version:' + info().script.version path = SUtils.RemoveSpecialChars(path) } catch (e) { Exception(e, 'error at setting error stack/msg link') } path = path.replace(/ /g, '_') openInTab(serverAdress + path, { active: true }) } // : }}} // : Help {{{ // shows some neat help function ShowHelp () { openInTab(serverAdress + 'manual', { active: true }) } // : }}} // I am not too proud to cry that He and he // Will never never go out of my mind. // All his bones crying, and poor in all but pain, // Being innocent, he dreaded that he died // Hating his God, but what he was was plain: // An old kind man brave in his burning pride. // The sticks of the house were his; his books he owned. // Even as a baby he had never cried; // Nor did he now, save to his secret wound. // Out of his eyes I saw the last light glide. // Here among the liught of the lording sky // An old man is with me where I go // Walking in the meadows of his son's eye // Too proud to cry, too frail to check the tears, // And caught between two nights, blindness and death. // O deepest wound of all that he should die // On that darkest day. })(); // eslint-disable-line