From 4789da434221b48e93695abe577fde2dd5b7c1a9 Mon Sep 17 00:00:00 2001
From: YourFriendlyNeighborhoodDealer <qminer@2girls1dj.com>
Date: Wed, 14 Aug 2019 15:48:51 +0200
Subject: [PATCH] Stable update

---
 stable.js | 4496 ++++++++++++++++++++++++++---------------------------
 1 file changed, 2218 insertions(+), 2278 deletions(-)

diff --git a/stable.js b/stable.js
index 6a007a3..3df0d3a 100644
--- a/stable.js
+++ b/stable.js
@@ -21,7 +21,7 @@
 
 // ==UserScript==
 // @name         Moodle/Elearning/KMOOC test help
-// @version      1.6.3.0
+// @version      1.6.3.1
 // @description  Online Moodle/Elearning/KMOOC test help
 // @author       YourFriendlyNeighborhoodDealer
 // @match        https://elearning.uni-obuda.hu/main/*
@@ -34,2287 +34,2227 @@
 // @grant        GM_xmlhttpRequest
 // @grant        GM_openInTab
 // @license      GNU General Public License v3.0 or later
-// @supportURL	 qmining.tk
-// @contributionURL qmining.tk
+// @supportURL	 qmining.frylabs.net
+// @contributionURL qmining.frylabs.net
 // @resource     data file:///<file path space is %20, and use "/"-s plz not "\" ty (and add .txt)// UTF-8 PLZ>
 // @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 =
-		'Néhány szerkezeti átalakítás, és bugfix. Ha valami elromlott 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 = false;
-	const forceResultPage = false;
-	const forceDefaultPage = false;
-	const logElementGetting = false;
-	const log = false;
-
-	const motdShowCount = 3; /* Ammount of times to show motd */
-	var motd = "";
-	var lastestVersion = "";
-
-	const minMatchAmmount = 60; /* Minimum ammount to consider that two questions match during answering */
-	const minResultMatchPercent = 99; /* Minimum ammount to consider that two questions match during saving */
-	const lengthDiffMultiplier = 10; /* Percent minus for length difference */
-
-	//: 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");
-		}
-	}
-	//: }}}
-
-	//: 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 // trying to get tha current questions
-			{
-				allQuestions = this.GetAllQuestionsQtext(); // getting questions
-				if (allQuestions.length == 0) // if there are no found questions
-				{
-					var ddq = this.GetAllQuestionsDropdown();
-					if (EmptyOrWhiteSpace(ddq)) {
-						var questionData = "";
-						for (var j = 0; j < allQuestions.length; j++) {
-							// TODO: test dis
-							let subAllQuestions = allQuestions[j].childNodes;
-							for (var i = 0; i < subAllQuestions.length; i++) {
-								if (subAllQuestions[i].data != undefined && !EmptyOrWhiteSpace(subAllQuestions[i].data)) // if the current element has some text data to add
-								{
-									questionData += subAllQuestions[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 = 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, ind) => {
-				return 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("<br>")[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++) {
-
-				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;
-		}
-
-		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;
-			else
-				return "";
-		}
-
-		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) // 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 (this.GetDropboxes(i).length > 0) // if there is dropdown list in the current question
-				{
-					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 && !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) // 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 (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 && 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
-		GetRightAnswerFromResultv2(i) {
-			try {
-				var answerNodes = this.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");
-			}
-		}
-	}
-
-	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();
-
-	//: }}}
+  /* TODO
+   * remove stuff like "b. answer", "c. answer" ....
+   * */
+
+  var data // all data, which is in the resource txt
+  var addEventListener // add event listener function
+  const lastChangeLog = 'Néhány szerkezeti átalakítás, és bugfix. Ha valami elromlott akkor pls report, thanx'
+  const serverAdress = 'https://qmining.frylabs.net/'
+
+  // 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 minMatchAmmount = 60 /* Minimum ammount to consider that two questions match during answering */
+  const minResultMatchPercent = 99 /* Minimum ammount to consider that two questions match during saving */
+  const lengthDiffMultiplier = 10 /* Percent minus for length difference */
+
+  // : Class descriptions {{{
+
+  class StringUtils {
+    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.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()
+      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
+    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) {
+      assert(s1)
+      assert(s2)
+
+      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
+    }
+  }
+
+  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) {
+      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 imatchpercent = this.HasImage() ? SUtils.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 = 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 = []
+    }
+
+    get length () {
+      return this.Questions.length
+    }
+
+    AddQuestion (q) {
+      assert(q)
+
+      this.Questions.push(q)
+    }
+
+    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 () {
+      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) {
+      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')
+    }
+  }
+
+  var SUtils = new StringUtils()
+
+  // : }}}
+
+  // : 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, ind) => {
+        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('<br>')[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++) {
+        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
+    }
+
+    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 } else { return '' }
+    }
+
+    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)) {
+        currAnswer = fun[j](i)
+        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()
+
+  // : Main function {{{
+  function Main () {
+    'use strict'
+    console.time('main')
+
+    Init(function (count, subjCount) {
+      var url = location.href
+
+      let skipLoad = GM_getValue('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, function () {
+            GM_openInTab(serverAdress + 'lred', {
+              active: true
+            })
+          })
+          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) {
+      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) {
+      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) {
+      GM_setValue('useNetDB', '1')
+      GM_setValue('version15', false)
+      document.write(
+        '<h1>Moodle teszt userscript:<h1><h3>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!</h3> <h3>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!!!</h3><h5>Ez az ablak frissités után eltűnik. Ha nem, akkor a visza gombbal próbálkozz.</h5>'
+      )
+      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', '1')
+      GM_setValue('version161', false)
+      document.write(
+        '<h1>Moodle teszt userscript:<h1><h3>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</h3>'
+      )
+      document.close()
+      throw 'something, so this stuff stops'
+    }
+  }
+
+  // : }}}
+
+  var GetFileData = () => {
+    return GM_getResourceText('data')
+  }
+
+  function ReadFile (cwith) {
+    var resource = ''
+    try {
+      resource = GetFileData() // 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 (SUtils.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.split(',')
+          }
+        }
+        ExpectedIdentifier = ['?', '+']
+        continue
+      }
+    }
+
+    return {
+      result: r,
+      logs: logs
+    }
+  }
+
+  function Load (cwith) {
+    var useNetDB = GM_getValue('useNetDB')
+    let skipLoad = GM_getValue('skipLoad')
+
+    if (skipLoad) {
+      cwith(-2, -2)
+      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) {
+    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....')
+        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++
+      }
+    } 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 // try, cus im suspicious
+    {
+      newVersion = GM_info.script.version !== GM_getValue('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 = (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 + '\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. ' + 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 (!SUtils.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 = QPM.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 = 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)
+  }
+
+  function PrepareAnswers (result, j) {
+    assert(result)
+
+    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) {
+    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 () {
+        GM_openInTab(serverAdress + 'lred', {
+          active: true
+        })
+      })
+    }
+  }
+
+  // : }}}
+
+  // : Quiz saving {{{
+
+  function HandleResults (url) {
+    var allResults = new QuestionDB()
+    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) // 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 = '<h3>' + sentData.subj + '<br>TXT-ben nem szereplő kérdések: ' + addedQ + '/' +
+              allQ + '</h3><br>' +
+              output.replace(/\n/g, '<br>') + '<br><h3>Összes kérdés/válasz:</h3>' + allOutput.replace(
+        /\n/g, '<br>')
+
+      var useNetDB = GM_getValue('useNetDB')
+      if (useNetDB != undefined && useNetDB == 1) {
+        try {
+          towrite += '</p>Elküldött adatok:</p> ' + JSON.stringify(sentData)
+        } catch (e) {
+          towrite += '</p>Elküldött adatok:</p> ' + 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')) // 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(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 {
+          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 += '?' + 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) // 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 = MPM.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.')
+      }
+      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++) // going through results, except last two, idk why, dont remember, go check it man
+      {
+        var question = {} // the current question
+        // QUESTION --------------------------------------------------------------------------------------------------------------------
+        var q = RPM.GetQuestionFromResult(i)
+        if (q != undefined) { question.q = SUtils.SimplifyQuery(q) }
+
+        // RIGHTANSWER ---------------------------------------------------------------------------------------------------------------------
+        var a = RPM.GetRightAnswerFromResultv2(i)
+        if (a == undefined) { a = RPM.GetRightAnswerFromResult(i) }
+        if (a != undefined) { question.a = SUtils.SimplifyQuery(a) }
+        // IMG ---------------------------------------------------------------------------------------------------------------------
+        var img = GetImageFormResult(i)
+        question.i = img
+
+        if (q != undefined) { q = SUtils.ReplaceCharsWithSpace(q, '\n') }
+        if (a != undefined) { a = SUtils.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, 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++) // 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(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++) // 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 = 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) // keydown event listener
+    {
+      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
+  }
+
+  // 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 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) // if there are items in the result
+      {
+        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 (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 (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 (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 = '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
+
+        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.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 = 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)
+        var msg = ''
+        if (GM_getValue('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('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
+  }
+
+  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'
+    GM_xmlhttpRequest({
+      method: 'POST',
+      url: url,
+      data: message,
+      headers: {
+        'Content-Type': 'application/x-www-form-urlencoded'
+      },
+      onerror: function (response) {
+        Log('XMLHTTP request POST error')
+      }
+    })
+  }
+
+  var assert = (val) => {
+    if (!val) { throw new Error('Assertion failed') }
+  }
+
+  // : }}}
+
+  // : 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.
 	
-	Main();
-
-	//: Main function {{{
-	function Main() {
-		'use strict';
-		console.time('main');
-
-		Init(function(count, subjCount) {
-			var url = location.href;
-
-			let skipLoad = GM_getValue("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, function() {
-						GM_openInTab(serverAdress + 'lred', {
-							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."
-				);
-			}
-		});
-
-		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) // 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(
-				'<h1>Moodle teszt userscript:<h1><h3>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!</h3> <h3>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!!!</h3><h5>Ez az ablak frissités után eltűnik. Ha nem, akkor a visza gombbal próbálkozz.</h5>'
-			);
-			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(
-				'<h1>Moodle teszt userscript:<h1><h3>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</h3>'
-			);
-			document.close();
-			throw "something, so this stuff stops";
-		}
-	}
-
-	//: }}}
-
-	var GetFileData = () => {
-		return GM_getResourceText("data");
-	};
-
-	function ReadFile(cwith) {
-		var resource = "";
-		try {
-			resource = GetFileData(); // 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.split(',');
-					}
-				}
-				ExpectedIdentifier = ['?', '+'];
-				continue;
-			}
-		}
-
-		return {
-			result: r,
-			logs: logs
-		};
-	}
-
-	function Load(cwith) {
-		var useNetDB = GM_getValue("useNetDB");
-		let skipLoad = GM_getValue("skipLoad");
-
-		if (skipLoad) {
-			cwith(-2, -2);
-			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++;
-			}
-
-			// TODO: move this
-			AlertOnNoQuestion(i);
-
-		} catch (e) {
-			Exception(e, "script error at loading:");
-			count = -1; // returns -1 if error
-		}
-		cwith(count, subjCount);
-	}
-
-	var AlertOnNoQuestion = (i) => {
-		try {
-			if (i >= data.length)
-				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 // try, cus im suspicious
-		{
-			newVersion = GM_info.script.version !== GM_getValue("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 = (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 + "\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 {
-					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 = QPM.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 + 'lred', {
-					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 = '<h3>TXT-ben nem szereplő kérdések: ' + addedQ + '/' + allQ + '</h3><br>' +
-				output.replace(/\n/g, '<br>') + '<br><h3>Összes kérdés/válasz:</h3>' + allOutput.replace(
-					/\n/g, '<br>');
-
-			var useNetDB = GM_getValue("useNetDB");
-			if (useNetDB != undefined && useNetDB == 1)
-				towrite += "</p>Elküldött adatok:</p> " + sentData;
-			document.write(towrite);
-			document.close();
-		});
-	}
-
-	function SearchSameQuestion(questionData, quiz, i) {
-		var r = questionData.Search(quiz[i].Q);
-
-		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")) // 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) {
-			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 {
-					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 = MPM.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 = RPM.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 = RPM.GetQuestionFromResult(i);
-				if (q != undefined)
-					question.q = SimplifyQuery(q);
-
-				// RIGHTANSWER ---------------------------------------------------------------------------------------------------------------------
-				var a = RPM.GetRightAnswerFromResultv2(i);
-				if (a == undefined)
-					a = RPM.GetRightAnswerFromResult(i);
-				if (a != undefined)
-					question.a = SimplifyQuery(a);
-				// IMG ---------------------------------------------------------------------------------------------------------------------
-				var img = GetImageFormResult(i);
-				question.i = img;
-
-				if (q != undefined)
-					q = ReplaceCharsWithSpace(q, "\n");
-				if (a != undefined)
-					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, 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++) // 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 = 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
-	}
-
-	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 = 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 (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 = 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 (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 = "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;
-
-				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.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 = 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);
-				var msg = "";
-				if (GM_getValue("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('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) {
-
-		assert(val);
-		assert(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");
-			}
-		});
-	}
-
-	var assert = (val) => {
-		if (!val)
-			throw new Error("Assertion failed");
-	};
-
-	//: }}}
-
-	//: 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.
-
 })();