From a3809aa0f24f5a23d69f1406e4d364f4dd94cb74 Mon Sep 17 00:00:00 2001 From: YourFriendlyNeighborhoodDealer Date: Tue, 5 Mar 2019 08:28:51 +0100 Subject: [PATCH] Updated frame, and added stable.js --- frame.js | 2 +- stable.js | 2241 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2242 insertions(+), 1 deletion(-) create mode 100644 stable.js diff --git a/frame.js b/frame.js index 7c2957c..4980ee5 100644 --- a/frame.js +++ b/frame.js @@ -21,7 +21,7 @@ // ==UserScript== // @name Moodle/Elearning/KMOOC test help -// @version 1.6.2.3 +// @version 1.6.2.4 // @description Online Moodle/Elearning/KMOOC test help // @author YourFriendlyNeighborhoodDealer // @match https://elearning.uni-obuda.hu/main/* diff --git a/stable.js b/stable.js new file mode 100644 index 0000000..6fab799 --- /dev/null +++ b/stable.js @@ -0,0 +1,2241 @@ +/* ---------------------------------------------------------------------------- + + 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.2.4 +// @description Online Moodle/Elearning/KMOOC test help +// @author YourFriendlyNeighborhoodDealer +// @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.tk +// @contributionURL qmining.tk +// @resource data file:/// +// @namespace https://greasyfork.org/users/153067 +// ==/UserScript== + +(function() { + var data; // all data, which is in the resource txt + var addEventListener; // add event listener function + const lastChangeLog = + '- Passzív mód: ha bepipálod a menü gomb alatt, akkor nem tölti be minden alkalommal a kérdéseket (csak csendben vár).\n - Pár lényeges bugfix\n - Ha találkoztok bugokkal, akkor pls report! thanx'; + const serverAdress = "https://qmining.tk/"; + + // forcing pages for testing. unless you test, do not set these to true! + // only one of these should be true for testing + const forceTestPage = true; + const forceResultPage = false; + const forceDefaultPage = false; + const logElementGetting = false; + const log = true; + + const motdShowCount = 3; + var motd = ""; + var lastestVersion = ""; + + const minMatchAmmount = 55; + const lengthDiffMultiplier = 10; + + //: Class descriptions {{{ + class Question { + constructor(q, a, i) { + this.Q = q; + this.A = 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; + } + IsComplete() { + return this.HasQuestion() && this.HasAnswer(); + } + Compare(q2, i) { + if (typeof q2 == 'string') { + var qmatchpercent = Question.CompareString(this.Q, q2); + + if (i == undefined || i.length == 0) + return qmatchpercent; + else { + if (this.HasImage()) { + const imatchpercent = this.HasImage() ? Question.CompareString(this.I.join(" "), i.join(" ")) : + 0; + return (qmatchpercent + imatchpercent) / 2; + } else { + qmatchpercent -= 30; + if (qmatchpercent < 0) + return 0; + else + return qmatchpercent; + } + } + } else { + const qmatchpercent = Question.CompareString(this.Q, q2.Q); + const amatchpercent = Question.CompareString(this.A, q2.A); + if (this.I != undefined) { + const imatchpercent = this.I == undefined ? Question.CompareString(this.I.join(" "), q2.I.join( + " ")) : 0; + return (qmatchpercent + amatchpercent + imatchpercent) / 3; + } else { + return (qmatchpercent + amatchpercent) / 2; + } + } + } + static CompareString(s1, s2) { + s1 = SimplifyStringForComparison(s1).split(" "); + s2 = 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; + } + } + + class Subject { + constructor(n) { + this.Name = n; + this.Questions = []; + } + get length() { + return this.Questions.length; + } + AddQuestion(q) { + this.Questions.push(q); + } + Search(q, img) { + var r = []; + for (var 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 (var 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() { + this.Subjects = []; + } + get length() { + return this.Subjects.length; + } + get activeIndexes() { + var r = []; + for (var i = 0; i < this.length; i++) { + if (GM_getValue("Is" + i + "Active")) { + r.push(i); + } + } + return r; + } + GetIfActive(ind) { + return GM_getValue("Is" + ind + "Active"); + } + ChangeActive(i, value) { + GM_setValue("Is" + i + "Active", !!value); + } + AddQuestion(subj, q) { + 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) { + var r = []; + for (var i = 0; i < this.length; i++) + if (this.GetIfActive(i)) + r = r.concat(this.Subjects[i].Search(q, img)); + + for (var 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) { + 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"); + } + } + //: }}} + + Main(); + + //: Main function {{{ + function Main() { + 'use strict'; + Init(function(count, subjCount) { + var url = location.href; + 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, function() { + GM_openInTab(serverAdress + 'legacy', { + active: true + }); + }); + Exception(e, "script error at main:"); + } + if (url.includes("eduplayer")) // if the current site is a video site + 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." + ); + }); + } + //: }}} + + //: DOM getting stuff {{{ + // all dom getting stuff are in this sections, so on + // moodle dom change, stuff breaks here + + function 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; + } + + function GetAllQuestionsQtext() { + if (logElementGetting) + Log("getting all questions qtext"); + return document.getElementById("responseform").getElementsByClassName("qtext"); // getting questions + } + + function GetAllQuestionsP() { + if (logElementGetting) + Log("getting all questions by tag p"); + return document.getElementById("responseform").getElementsByTagName("p"); + } + + function GetFormulationClearfix() { + if (logElementGetting) + Log("getting formulation clearfix lol"); + return document.getElementsByClassName("formulation clearfix"); + } + + function GetAnswerOptions() { + if (logElementGetting) + Log("getting all answer options"); + return GetFormulationClearfix()[0].childNodes[3].innerText; + } + + function GetQuestionImages() { + if (logElementGetting) + Log("getting question images"); + return GetFormulationClearfix()[0].getElementsByTagName("img"); + } + + function GetCurrentSubjectName() { + if (logElementGetting) + Log("getting current subjects name"); + return document.getElementById("page-header").innerText.split("\n")[0]; + } + + function GetVideo() { + if (logElementGetting) + Log("getting video stuff"); + return document.getElementsByTagName("video")[0]; + } + + function GetVideoElement() { + if (logElementGetting) + Log("getting video element"); + return document.getElementById("videoElement").parentNode; + } + + function GetAllAnswer(index) { + if (logElementGetting) + Log("getting all answers, ind: " + index); + return document.getElementsByClassName("answer")[index].childNodes; + } + + function GetInputType(answers, i) { + if (logElementGetting) + Log("getting input type"); + return answers[i].getElementsByTagName("input")[0].type; + } + + function 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; + } + + function GetQText(i) { + if (logElementGetting) + Log("getting qtext by index: " + i); + var results = GetFormResult(); // getting results element + return results[i].getElementsByClassName("qtext"); + } + + function GetDropboxes(i) { + if (logElementGetting) + Log("getting dropboxes by index: " + i); + var results = GetFormResult(); // getting results element + return results[i].getElementsByTagName("select"); + } + + function 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; + } + + function GetCurrentAnswer(i) { + if (logElementGetting) + Log("getting curr answer by index: " + i); + var results = GetFormResult(); // getting results element + var t = results[i].getElementsByClassName("formulation clearfix")[0].getElementsByTagName("span"); + if (t.length > 2) + return t[1].innerHTML.split("
")[1]; + } + + function 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; + } + + function GetAnswerNode(i) { + if (logElementGetting) + Log("getting answer node"); + + var results = 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 = DetermineQuestionType(ret); + + return { + nodes: ret, + type: qtype + }; + } + + function 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; + + } + + function GetPossibleAnswers(i) { + if (logElementGetting) + Log("getting possible answers"); + var results = GetFormResult(); // getting results element + var items = GetFormResult()[i].getElementsByTagName("label"); + var r = []; + for (var j = 0; j < items.length; j++) { + + function 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; + } + + function GetRightAnswerIfCorrectShown(i) { + if (logElementGetting) + Log("getting right answer if correct shown"); + var results = GetFormResult(); // getting results element + return results[i].getElementsByClassName("rightanswer"); + } + + function GetWrongAnswerIfCorrectNotShown(i) { + if (logElementGetting) + Log("getting wrong answer if correct not shown"); + var results = 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 ""; + } + + function GetRightAnswerIfCorrectNotShown(i) { + if (logElementGetting) + Log("Getting right answer if correct not shown"); + var results = 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; + else + return ""; + } + + function GetFormCFOfResult(result) { + if (logElementGetting) + Log("getting formulation clearfix"); + return result.getElementsByClassName("formulation clearfix")[0]; + } + + function GetResultText(i) { + if (logElementGetting) + Log("getting result text"); + var results = GetFormResult(); // getting results element + return GetFormCFOfResult(results[i]).getElementsByTagName("p"); + } + + function GetResultImage(i) { + if (logElementGetting) + Log("getting result image"); + var results = GetFormResult(); // getting results element + return GetFormCFOfResult(results[i]).getElementsByTagName("img"); + } + + // this function should return the question, posible answers, and image names + function GetQuestionFromTest() { + var questions; // the important questions + var allQuestions; // all questions + try // trying to get tha current questions + { + allQuestions = GetAllQuestionsQtext(); // getting questions + if (allQuestions.length == 0) // if there are no found questions + { + var ddq = GetAllQuestionsDropdown(); + if (EmptyOrWhiteSpace(ddq)) { + var questionData = ""; + for (var j = 0; j < allQuestions.length; j++) { + allQuestions = GetAllQuestionsQtext()[j].childNodes; + for (var i = 0; i < allQuestions.length; i++) { + if (allQuestions[i].data != undefined && !EmptyOrWhiteSpace(allQuestions[i].data)) // if the current element has some text data to add + { + questionData += allQuestions[i].data + " "; // adding text to question data + } + } + } + questions = [questionData]; + } else + questions = [ddq]; + } else // if there is + { + questions = []; + for (var 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 = 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, ind) => { + return ReplaceCharsWithSpace(item, "\n"); + }); + + return { + imgnodes: imgNodes, + allQ: allQuestions, + q: questions + }; + } + + // gets the question from the result page + // i is the index of the question + function GetQuestionFromResult(i) { + var temp = GetQText(i); + var currQuestion = ""; + if (temp.length > 0) // if the qtext element is not 0 length + { + currQuestion = temp[0].innerText; // adding the question to curr question as .q + } else { + // this is black magic fuckery a bit + if (GetDropboxes(i).length > 0) // if there is dropdown list in the current question + { + var allNodes = GetResultText(i); + currQuestion = ""; + for (var k = 0; k < allNodes.length; k++) { + var allQuestions = GetResultText(i)[k].childNodes; + for (var j = 0; j < allQuestions.length; j++) { + if (allQuestions[j].data != undefined && !EmptyOrWhiteSpace(allQuestions[j].data)) { + currQuestion += allQuestions[j].data + " "; + } + } + } + } else { + try { + currQuestion = GetCurrQuestion(i); + } catch (e) { + currQuestion = "REEEEEEEEEEEEEEEEEEEEE"; // this shouldnt really happen sry guys + } + } + } + return currQuestion; + } + + // tries to get right answer from result page + // i is the index of the question + function GetRightAnswerFromResult(i) { + var fun = []; + + // the basic type of getting answers + fun.push(function TryGet0(i) { + var temp = GetRightAnswerIfCorrectShown(i); // getting risht answer + if (temp.length > 0) // if the rightanswer element is not 0 length + 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 (GetDropboxes(i).length > 0) + return GetCurrentAnswer(i); + }); + + // if the correct answers are not shown, and the selected answer + // is correct + fun.push(function TryGet2(i) { + return GetRightAnswerIfCorrectNotShown(i); + }); + + // if there is dropbox in the question + fun.push(function TryGet3(i) { + return 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 = 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 && EmptyOrWhiteSpace(currAnswer)) { + currAnswer = fun[j](i); + j++; + } + + return currAnswer; + } + + // version 2 of getting right answer from result page + // i is the index of the question + function GetRightAnswerFromResultv2(i) { + try { + var answerNodes = GetAnswerNode(i); + let items = answerNodes.nodes; + + if (answerNodes.type == "checkbox") + return GetRightAnswerFromResult(i); + + for (var j = 0; j < items.length; j++) { + var cn = items[j].className; + if (cn.includes("correct") && !cn.includes("incorrect")) + return items[j].innerText; + } + if (items.length == 2) { + for (var j = 0; j < items.length; j++) { + var cn = items[j].className; + if (!cn.includes("correct")) + return items[j].innerText; + } + } + } catch (e) { + Log("error at new nodegetting, trying the oldschool way"); + } + } + + //: }}} + + //: Main logic stuff {{{ + + //: Loading {{{ + + function Init(cwith) { + if (false) // reset, only for testing! + { + GM_setValue("version16", undefined); + GM_setValue("version15", undefined); + GM_setValue("firstRun", undefined); // GM_getValue("lastVerson") == undefined => firstrun + GM_setValue("showQuestions", undefined); + GM_setValue("showSplash", undefined); + } + var url = location.href; // window location + 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 + // GM_setValue("version15", true); + // GM_setValue("firstRun", true); + // GM_setValue("version16", true); + // GM_setValue("version161", true); + // throw "asd"; + + let r = FreshStart(); + if (r != true) + GM_setValue("version161", false); + + Version15(); + Version16(); + Version161(); + } + + //: Version action functions {{{ + + function FreshStart() { + var firstRun = GM_getValue("firstRun"); // if the current run is the frst + if (firstRun == undefined || firstRun == true) // if its undefined, or true + { + GM_setValue("firstRun", false); + ShowHelp(); // showing help + return true; + } + } + + function Version15() { + var version15 = GM_getValue("version15"); // if the current run is the frst + if (version15 == undefined || version15 == true) // if its undefined, or true + { + GM_setValue("useNetDB", "0"); + GM_setValue("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 "something, so this stuff stops"; + } + } + + function Version16() { + var version16 = GM_getValue("version16"); // if the current run is the frst + if (version16 == undefined || version16 == true) // if its undefined, or true + { + var i = 0; + while (GM_getValue("Is" + i + "Active") != undefined) { + GM_setValue("Is" + i + "Active", false); + i++; + } + GM_setValue("version16", false); + } + } + + function Version161() { + var version161 = GM_getValue("version161"); // if the current run is the frst + if (version161 == undefined || version161 == true) // if its undefined, or true + { + GM_setValue("useNetDB", "0"); + GM_setValue("version161", false); + document.write( + '

Moodle teszt userscript:

1.6.1.0 verzió: Új domain név: qmining.tk. Ha frissíted az oldalt, akkor tampremonkey rá fog kérdezni, hpgy engedélyezed-e a kérdések külését erre az új domain-re. A rendes működés érdekében kattints a "Allow always domain"-gombra

' + ); + document.close(); + throw "something, so this stuff stops"; + } + } + + //: }}} + + function ReadFile(cwith) { + var resource = ""; + try { + resource = GM_getResourceText("data"); // getting data from txt + if (resource == undefined) { + + ShowMessage({ + m: "Nem lehetett beolvasni a fájlt :c Ellenőrizd az elérési utat, vagy a fájl jogosultságokat", + isSimple: true + }); + return; + } + if (EmptyOrWhiteSpace(resource)) { + throw { + message: "data file empty" + }; + } + } catch (e) { + Exception(e, "script error at reading file:"); + } + NLoad(resource, cwith); + } + + function ReadNetDB(cwith, useNetDB) { + function NewXMLHttpRequest() { + const url = serverAdress + "data.json"; + GM_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:"); + } + } + + /* + * Returns a question database from the given data. + * Parameter should be raw read file in string with "\n"-s + * */ + function ParseRawData(data) { + + const d = data.split("\n"); + const r = new QuestionDB(); + var logs = []; + var currSubj = ""; // the current subjects name + var ExpectedIdentifier = ['+', '?']; + let currQuestion = new Question(); + + var i = -1; + while (i < d.length) { + let currIdentifier; + let skipped = 0; + do { + if (skipped >= 1) + logs.push(i + ": " + d[i]); + i++; + if (i >= d.length) { + if (currQuestion.IsComplete()) + r.AddQuestion(currSubj, currQuestion); + return { + result: r, + logs: logs + }; + } + currIdentifier = d[i][0]; + skipped++; + } while (!ExpectedIdentifier.includes(currIdentifier) && i < d.length); + + let currData = d[i].substring(1).trim(); + + if (currIdentifier == '+') { + if (currQuestion.IsComplete()) + r.AddQuestion(currSubj, currQuestion); + currQuestion = new Question(); + currSubj = currData; + ExpectedIdentifier = ['?']; + continue; + } + + if (currIdentifier == '?') { + if (currQuestion.IsComplete()) { + r.AddQuestion(currSubj, currQuestion); + currQuestion = new Question(); + } + // overwriting is allowed here, bcus: + // ?????!> + currQuestion.Q = currData; + ExpectedIdentifier = ['!', '?']; + continue; + } + + if (currIdentifier == '!') { + // if dont have question continue + if (!currQuestion.HasQuestion()) + throw "No question! (A)"; + // dont allow overwriting + // ?!!!! + if (!currQuestion.HasAnswer()) { + currData = currData.replace("A helyes válaszok: ", ""); + currData = currData.replace("A helyes válasz: ", ""); + + currQuestion.A = currData; + } + ExpectedIdentifier = ['?', '>', '+']; + continue; + } + + if (currIdentifier == '>') { + // if dont have question or answer continue + if (!currQuestion.HasQuestion()) + throw "No question! (I)"; + if (!currQuestion.HasAnswer()) + throw "No asnwer! (I)"; + // dont allow overwriting + // ?!>>> + if (!currQuestion.HasImage()) { + try { + currQuestion.I = JSON.parse(currData); + } catch (e) { + currQuestion.I = [currData]; + } + } + ExpectedIdentifier = ['?', '+']; + continue; + } + } + + return { + result: r, + logs: logs + }; + } + + function Load(cwith) { + var useNetDB = GM_getValue("useNetDB"); + let skipLoad = GM_getValue("skipLoad"); + + if (skipLoad) + return -1; + + if (useNetDB != undefined && useNetDB == 1) + return ReadNetDB(cwith, useNetDB); + else + return ReadFile(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) { + var count = -1; + var subjCount = 0; + try { + var d = {}; + try { + d = JSON.parse(resource); + } catch (e) { + Log("Old data, trying with old methods...."); + try { + d = ParseRawData(resource).result; + } catch (e2) { + Log("Couldt parse data!"); + ShowMessage({ + m: "Nem sikerült betölteni az adatokat! Ellenőriz a megadott fájlt, vagy az internetelérésed!", + isSimple: true + }); + return; + } + } + var r = new QuestionDB(); + var rt = []; + var allCount = -1; + LoadMOTD(d); + LoadVersion(d); + + for (var i = 0; i < d.Subjects.length; i++) { + let s = new Subject(d.Subjects[i].Name); + if (GM_getValue("Is" + i + "Active")) { + 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 + + var i = 0; + while (i < data.length && !GM_getValue("Is" + i + "Active")) { + i++; + } + if (i >= data.length) + document.getElementById("HelperMenuButton").style.background = "yellow"; + + } catch (e) { + Exception(e, "script error at loading:"); + count = -1; // returns -1 if error + } + cwith(count, subjCount); + } + + //: }}} + + //: 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 // try, cus im suspicious + { + newVersion = GM_info.script.version !== GM_getValue("lastVerson"); + } catch (e) {} + 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 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 = (GM_getValue("showSplash") == undefined) || GM_getValue("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. " + GM_info.script.version + ". "; + + if (lastestVersion != undefined && GM_info.script.version != lastestVersion) { + greetMsg += "Új verzió elérhető: " + lastestVersion + " "; + 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 { + 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. " + GM_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. " + GM_info.script.version + ". " + count + + " kérdés és " + subjCount + " tárgy betöltve. Verzió frissítve " + GM_info.script.version + + "-re. Changelog:\n" + lastChangeLog; + GM_setValue("lastVerson", GM_info.script.version); // setting lastVersion + } + if (!EmptyOrWhiteSpace(motd)) { + + var prevmotd = GM_getValue("motd"); + if (prevmotd != motd) { + greetMsg += "\nMOTD:\n" + motd; + timeout = null; + GM_setValue("motdcount", motdShowCount); + GM_setValue("motd", motd); + } else { + var motdcount = GM_getValue("motdcount"); + if (motdcount == undefined) { + GM_setValue("motdcount", motdShowCount); + motdcount = motdShowCount; + } + + motdcount--; + if (motdcount > 0) { + greetMsg += "\nMOTD:\n" + motd; + timeout = null; + GM_setValue("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 = GetQuestionFromTest(); + var questions = q.q; + var allQuestions = q.allQ; + var imgNodes = q.imgnodes; + // ------------------------------------------------------------------------------------------------------ + var answers = []; + for (var j = 0; j < questions.length; j++) // going thru all answers + { + var question = 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); + } + + function PrepareAnswers(result, j) { + if (result.length > 0) // if there are more than zero results + { + var allMessages = []; // preparing all messages + for (var k = 0; k < result.length; k++) // going throuh all results + { + var msg = ""; // the current message + if ((GM_getValue("showQuestions") == undefined) || GM_getValue("showQuestions")) // if the question should be shown + { + 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()) // and adding image, if it exists + { + 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) { + 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() { + GM_openInTab(serverAdress + 'legacy', { + active: true + }); + }); + } + } + + //: }}} + + //: Quiz saving {{{ + + function HandleResults(url) { + var allResults = new QuestionDB(); + SaveQuiz(GetQuiz(), data); // saves the quiz questions and answers + } + + function ShowSaveQuizDialog(addedQ, allQ, allOutput, output, sendSuccess, sentData) { + var msg = ""; + if (addedQ > 0) // if there are more than 0 new question + { + msg = "Klikk ide a nyers adatokhoz. " + addedQ + + " új kérdés!"; + + var useNetDB = GM_getValue("useNetDB"); + if (useNetDB != undefined && useNetDB == 1) { + 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 += "Ne felejtsd el bemásolni a fő txt-be!"; + + } else // if there is 0 or less new question + { + 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 = '

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


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

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

' + allOutput.replace( + /\n/g, '
'); + + var useNetDB = GM_getValue("useNetDB"); + if (useNetDB != undefined && useNetDB == 1) + towrite += "

Elküldött adatok:

" + sentData; + document.write(towrite); + document.close(); + }); + } + + function SearchSameQuestion(questionData, quiz, i) { + var r = questionData.Search(quiz[i].Q); + return r.length == 0 ? -1 : r.length; + } + + // 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 = GetResultImage(i); // trying to get image + var imgURL = []; // image urls + for (var j = 0; j < imgElements.length; j++) { + if (!imgElements[j].src.includes("brokenfile")) // idk why brokenfile is in some urls, which are broken, so why tf are they there damn moodle + { + 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(ShortenString(filePart, 30))); + } + } + if (imgURL.length > 0) { + temp = JSON.stringify(imgURL); + return temp; + } + } catch (e) {} + } + + // saves the current quiz. questionData contains the active subjects questions + function SaveQuiz(quiz, questionData) { + try { + if (quiz.length == 0) + throw { + message: "quiz length is zero!", + stack: "no stack." + }; + 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++) // going though quiz + { + // searching for same questions in questionData + var toAdd = ""; // this will be added to some variable depending on if its already in the database + toAdd += "?" + RemoveUnnecesarySpaces(quiz[i].Q) + "\n"; // adding quiz question + toAdd += "!" + RemoveUnnecesarySpaces(quiz[i].A) + "\n"; // adding quiz answer + if (quiz[i].HasImage()) { + toAdd += ">" + RemoveUnnecesarySpaces(quiz[i].I) + "\n"; // adding quiz image if there is any + } + if (SearchSameQuestion(questionData, quiz, i) == -1) // if there is no such item in the database (w/ same q and a) + { + 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 = GetCurrentSubjectName(); + } catch (e) { + sentData.subj = "NOSUBJ"; + Log("unable to get subject name :c"); + } + var useNetDB = GM_getValue("useNetDB"); + if (useNetDB != undefined && useNetDB == 1) { + sentData.allData = quiz; + sentData.data = newQuestions; + sentData.version = GM_info.script.version; + SendXHRMessage("datatoadd=" + JSON.stringify(sentData)); + sendSuccess = true; + } + } catch (e) { + Exception(e, "error at sending data to server."); + } + ShowSaveQuizDialog(addedQ, allQ, allOutput, output, sendSuccess, 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 = GetFormResult(); // getting results element + for (var i = 0; i < results.length - 2; i++) // going through results, except last two, idk why, dont remember, go check it man + { + var question = {}; // the current question + // QUESTION -------------------------------------------------------------------------------------------------------------------- + var q = GetQuestionFromResult(i); + if (q != undefined) + question.q = SimplifyQuery(q); + + // RIGHTANSWER --------------------------------------------------------------------------------------------------------------------- + var a = GetRightAnswerFromResultv2(i); + if (a == undefined) + a = GetRightAnswerFromResult(i); + if (a != undefined) + question.a = SimplifyQuery(a); + // IMG --------------------------------------------------------------------------------------------------------------------- + var img = GetImageFormResult(i); + question.i = img; + + q = ReplaceCharsWithSpace(q, "\n"); + a = ReplaceCharsWithSpace(a, "\n"); + + if (question.a != undefined) // adding only if has question + { + quiz.push(new Question(question.q, question.a, question.i)); // adding current question to quiz + } else { + Log("error getting queston, 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++) // going through all image + { + if (!imgs[i].src.includes("brokenfile")) // if its includes borken file its broken. Its another moodle crap. So i just wont check those + { + 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(RemoveUnnecesarySpaces(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++) // going through all image + { + if (!imgs[i].src.includes("brokenfile")) // if its includes borken file its broken. Its another moodle crap. So i just wont check those + { + 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 = 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) // keydown event listener + { + try { + var video = 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 = 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 + } + + function GetMatchPercent(currData, questionParts) { + var currQuestion = SimplifyQuery(currData.q); // current question simplified + var match = 0; // how many times the current question matches the current question in the database + for (var i = 0; i < questionParts.length; i++) // going through the question parts + { + if (currQuestion.includes(questionParts[i])) // if the current question from questionData includes one of the question parts + { + match++; + } + } + var percent = Math.round(((match / questionParts.length) * 100).toFixed(2)); // matched words percent + var lengthDifference = RemoveMultipleItems(SimplifyQuery(currQuestion).split(" ")).length - + questionParts.length; + percent -= Math.abs(lengthDifference) * 2; + return percent; + } + + // simple sort. + function SortByPercent(results) { + for (var i = 0; i < results.length; i++) { + for (var j = results.length - 1; j > i; j--) { + if (results[i].p < results[j].p) { + var temp = results[i]; + results[i] = results[j]; + results[j] = temp; + } + } + } + return results; + } + + // 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 RemoveUnnecesarySpaces(inp.substr(inp.indexOf(".") + 1, inp.length)); + else if (doubledotIndex < maxInd) + return 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) // if there are items in the result + { + var answers = 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 (var 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 (EmptyOrWhiteSpace(correct) || EmptyOrWhiteSpace(answer)) + continue; + + if (NormalizeSpaces(RemoveUnnecesarySpaces(correct)).includes(answer)) // if the correct answer includes the current answer + { + toColor.push(i); // adding the index + type = GetInputType(answers, i); // setting the type + } + } + } + if (results[0].match == 100) // if the result is 100% correct + if (type !== "radio" || toColor.length == 1) // if the type is not radio or there is exactly one correct solution + for (var 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 startFromLeft = window.innerWidth / 2 - width / 2; // dont change this + 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 = "solid"; + mainDiv.style.top = (startFromTop) + 'px'; + mainDiv.style.left = (window.innerWidth - width) / 2 + 'px'; + mainDiv.style.opacity = "0.9"; // setting starting opacity + 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; + + function GetRelevantQuestion() // returns the currItemth questions currRelevantQuestionth relevant question + { + return msgItem[currItem][currRelevantQuestion]; + } + + function ChangeCurrItemIndex(to) { + currItem += to; + if (currItem < 0) { + currItem = 0; + } + if (currItem > msgItem.length - 1) { + currItem = msgItem.length - 1; + } + currRelevantQuestion = 0; + } + + function ChangeCurrRelevantQuestionIndex(to) { + currRelevantQuestion += to; + if (currRelevantQuestion < 0) { + currRelevantQuestion = 0; + } + if (currRelevantQuestion > msgItem[currItem].length - 1) { + currRelevantQuestion = msgItem[currItem].length - 1; + } + } + + function 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.setAttribute("id", "HelperMenuButton"); + 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.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 = GM_getValue("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() { + GM_setValue("skipLoad", questionsTickBox.checked); + if (GM_getValue("skipLoad")) { + ShowMessage({ + m: "Passzív mód bekapcsolva, mostantól kérdések nem lesznek betöltve/lekérve.", + isSimple: true + }, 10); + } + + }); + 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 = "solid"; + 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('table'); + 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 headerSubjInfoParagraph = CreateNodeWithText(header2, "Aktív"); + headerSubjInfoParagraph.style.margin = fiveMargin; // fancy margin + + if (data && data.length > 0) { + + for (let i = 0; i < data.length; i++) { + var subjRow = subjTable.insertRow(); + subjRow.style.border = "1px solid #131319"; + + var td = subjRow.insertCell(); + var text = data.Subjects[i].Name; + if (data.Subjects[i].length != 0) + text += " [ " + data.Subjects[i].length + "db ]"; + + var textBox = CreateNodeWithText(td, text); + + textBox.style.margin = fiveMargin; // fancy margin + + td = subjRow.insertCell(); + var 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 + + var active = data.GetIfActive(i); + checkbox.checked = active; + + checkbox.setAttribute("id", "HelperTextNode" + i); + + checkbox.addEventListener("click", function() { + var checked = document.getElementById("HelperTextNode" + i).checked; + data.ChangeActive(i, checked); + }); // adding click + } + + 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(); + var textBox; + + if (GM_getValue("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 = GM_getValue("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() { + GM_setValue("showSplash", splashTickBox.checked); + }); // adding clicktextNode + + var splashTickBoxTextSpan = 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 = GM_getValue("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() { + GM_setValue("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 + var questionsTickBoxTextSpan = CreateNodeWithText(questionTickboxCell, + "Kérdések mutatása válaszhoz", "span"); + + // database mode listbox ----------------------------------------------------------------------------------------------------------------------------- + var databasemodeListboxRow = tbl.insertRow(); + var databasemodeListboxCell = databasemodeListboxRow.insertCell(); + + var databasemodeListbox = document.createElement("select"); + databasemodeListbox.type = "checkbox"; + //databasemodeListbox.checked = GM_getValue("showSplash") || false; + databasemodeListbox.style.position = ""; + //databasemodeListbox.style.background = "white"; + databasemodeListbox.style.left = 10 + 'px'; + databasemodeListbox.style.margin = "5px 5px 5px 5px"; // fancy margin + databasemodeListbox.style.top = menuDiv.offsetHeight + 'px'; + + var databasemodeListboxText = CreateNodeWithText(questionTickboxCell, + "Kérdések beszerzése:", "span"); + databasemodeListboxCell.appendChild(databasemodeListboxText); + + databasemodeListboxCell.appendChild(databasemodeListbox); // adding to main div + + databasemodeListbox.addEventListener("change", function(e) { + // sorry for using selectedindex :c + GM_setValue("useNetDB", databasemodeListbox.selectedIndex); + }); + + var uselocal = document.createElement('option'); + uselocal.text = "Helyi fájlból (old school)"; + uselocal.value = 2; + databasemodeListbox.add(uselocal, 0); + + var usenetsafe = document.createElement('option'); + usenetsafe.text = "Netről"; + usenetsafe.value = 0; + databasemodeListbox.add(usenetsafe, 1); + + var selected = GM_getValue("useNetDB"); + if (selected != undefined) + databasemodeListbox.selectedIndex = selected; + + var databasemodeListboxElement = document.createElement("span"); // new paragraph + databasemodeListboxCell.appendChild(databasemodeListboxElement); + + // 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"; + }); + + //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 RemoveMultipleItems(array) { + var newArray = []; + for (var i = 0; i < array.length; i++) { + var j = 0; + while (j < newArray.length && newArray[j] !== array[i]) { + j++; + } + if (j >= newArray.length) { + newArray.push(array[i]); + } + } + return newArray; + } + + // removes some crap from "q" + function SimplifyQuery(q) { + var result = q.replace(/\n/g, "").replace(/\s/g, ' '); // WHY TF ARE THERE TWO KINDA SPACES??? (charcode 160 n 32) + return RemoveUnnecesarySpaces(result); + } + + function ShortenString(toShorten, ammount) { + var result = ""; + var i = 0; + while (i < toShorten.length && i < ammount) { + result += toShorten[i]; + i++; + } + return result; + } + + 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 ReplaceCharsWithSpace(val, char) { + toremove = NormalizeSpaces(val); + + var regex = new RegExp(char, "g"); + toremove.replace(regex, " "); + + return RemoveUnnecesarySpaces(toremove); + } + + // removes whitespace from begining and and, and replaces multiple spaces with one space + function RemoveUnnecesarySpaces(toremove) { + toremove = NormalizeSpaces(toremove); + while (toremove.includes(" ")) // while the text includes double spaces replaces all of them with a single one + { + toremove = toremove.replace(/ /g, " "); + } + return toremove.trim(); + } + + // simplifies a string for easier comparison + function SimplifyStringForComparison(value) { + value = RemoveUnnecesarySpaces(value).toLowerCase(); + var removableChars = [",", ".", ":", "!"]; + for (var i = 0; i < removableChars.length; i++) { + var regex = new RegExp(removableChars[i], "g"); + value.replace(regex, ""); + } + return value; + } + + // if the value is empty, or whitespace + function 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 + function NormalizeSpaces(input) { + return input.replace(/\s/g, ' '); + } + + function SendXHRMessage(message) { + var url = serverAdress + "isAdding"; + GM_xmlhttpRequest({ + method: "POST", + url: url, + data: message, + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + onerror: function(response) { + Log("XMLHTTP request POST error"); + } + }); + } + + //: }}} + + //: Help {{{ + + // shows some neat help + function ShowHelp() { + GM_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. + + +})();