moodle-test-userscript/main.js
YourFriendlyNeighborhoodDealer 73f3631f75 Merge branch 'devel'
2019-03-09 14:40:55 +01:00

2246 lines
69 KiB
JavaScript

// vim:foldmethod=marker
/* ----------------------------------------------------------------------------
Online Moodle/Elearning/KMOOC test help
Greasyfork: <https://greasyfork.org/en/scripts/38999-moodle-elearning-kmooc-test-help>
GitLab: <https://gitlab.com/YourFriendlyNeighborhoodDealer/moodle-test-userscript>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
------------------------------------------------------------------------- */
var data; // all data, which is in the resource txt
var addEventListener; // add event listener function
const lastChangeLog =
'- Apró, de lényeges fixek\n - Ha találkoztok bugokkal, akkor pls report! thanx';
const serverAdress = "https://qmining.tk/";
// forcing pages for testing. unless you test, do not set these to true!
// only one of these should be true for testing
const forceTestPage = false;
const forceResultPage = false;
const forceDefaultPage = false;
const logElementGetting = false;
const log = true;
const motdShowCount = 3;
var motd = "";
var lastestVersion = "";
const minMatchAmmount = 55;
const minResultMatchPercent = 99;
const lengthDiffMultiplier = 10;
//: Class descriptions {{{
class Question {
constructor(q, a, i) {
this.Q = q;
this.A = a;
this.I = i;
}
toString() {
var r = "?" + this.Q + "\n!" + this.A;
if (this.I)
r += "\n>" + this.I;
return r;
}
HasQuestion() {
return this.Q != undefined;
}
HasAnswer() {
return this.A != undefined;
}
HasImage() {
return this.I != undefined;
}
IsComplete() {
return this.HasQuestion() && this.HasAnswer();
}
Compare(q2, i) {
if (typeof q2 == 'string') {
var qmatchpercent = Question.CompareString(this.Q, q2);
if (i == undefined || i.length == 0)
return qmatchpercent;
else {
if (this.HasImage()) {
const imatchpercent = this.HasImage() ? Question.CompareString(this.I.join(" "), i.join(" ")) :
0;
return (qmatchpercent + imatchpercent) / 2;
} else {
qmatchpercent -= 30;
if (qmatchpercent < 0)
return 0;
else
return qmatchpercent;
}
}
} else {
const qmatchpercent = Question.CompareString(this.Q, q2.Q);
const amatchpercent = Question.CompareString(this.A, q2.A);
if (this.I != undefined) {
const imatchpercent = this.I == undefined ? Question.CompareString(this.I.join(" "), q2.I.join(
" ")) : 0;
return (qmatchpercent + amatchpercent + imatchpercent) / 3;
} else {
return (qmatchpercent + amatchpercent) / 2;
}
}
}
static CompareString(s1, s2) {
s1 = SimplifyStringForComparison(s1).split(" ");
s2 = SimplifyStringForComparison(s2).split(" ");
var match = 0;
for (var i = 0; i < s1.length; i++)
if (s2.includes(s1[i]))
match++;
var percent = Math.round(((match / s1.length) * 100).toFixed(2)); // matched words percent
var lengthDifference = Math.abs(s2.length - s1.length);
percent -= lengthDifference * lengthDiffMultiplier;
if (percent < 0)
percent = 0;
return percent;
}
}
class Subject {
constructor(n) {
this.Name = n;
this.Questions = [];
}
get length() {
return this.Questions.length;
}
AddQuestion(q) {
this.Questions.push(q);
}
Search(q, img) {
var r = [];
for (var i = 0; i < this.length; i++) {
let percent = this.Questions[i].Compare(q, img);
if (percent > minMatchAmmount)
r.push({
q: this.Questions[i],
match: percent
});
}
for (var i = 0; i < r.length; i++)
for (var j = i; j < r.length; j++)
if (r[i].match < r[j].match) {
var tmp = r[i];
r[i] = r[j];
r[j] = tmp;
}
return r;
}
toString() {
var r = [];
for (var i = 0; i < this.Questions.length; i++)
r.push(this.Questions[i].toString());
return "+" + this.Name + "\n" + r.join("\n");
}
}
class QuestionDB {
constructor() {
this.Subjects = [];
}
get length() {
return this.Subjects.length;
}
get activeIndexes() {
var r = [];
for (var i = 0; i < this.length; i++) {
if (GM_getValue("Is" + i + "Active")) {
r.push(i);
}
}
return r;
}
GetIfActive(ind) {
return GM_getValue("Is" + ind + "Active");
}
ChangeActive(i, value) {
GM_setValue("Is" + i + "Active", !!value);
}
AddQuestion(subj, q) {
var i = 0;
while (i < this.Subjects.length && this.Subjects[i].Name != subj)
i++;
if (i < this.Subjects.length)
this.Subjects[i].AddQuestion(q);
else {
const n = new Subject(subj);
n.AddQuestion(q);
this.Subjects.push(n);
}
}
Search(q, img) {
var r = [];
for (var i = 0; i < this.length; i++)
if (this.GetIfActive(i))
r = r.concat(this.Subjects[i].Search(q, img));
for (var i = 0; i < r.length; i++)
for (var j = i; j < r.length; j++)
if (r[i].match < r[j].match) {
var tmp = r[i];
r[i] = r[j];
r[j] = tmp;
}
return r;
}
AddSubject(subj) {
var i = 0;
while (i < this.length && subj.Name != this.Subjects[i].Name)
i++;
if (i < this.length) {
this.Subjects.concat(subj.Questions);
} else {
this.Subjects.push(subj);
}
}
toString() {
var r = [];
for (var i = 0; i < this.Subjects.length; i++)
r.push(this.Subjects[i].toString());
return r.join("\n\n");
}
}
//: }}}
//: Main 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 + 'legacy', {
active: true
});
});
Exception(e, "script error at main:");
}
if (url.includes("eduplayer")) // if the current site is a video site
AddVideoHotkeys(url); // adding video hotkeys
Log(
"Itteni hibák 100% a moodle hiba. Kivéve, ha oda van írva hogy script error ;) Ha ilyesmi szerepel itt, akkor olvasd el a segítség szekciót! Nagy esélyel a kérdéseket nem lehetett beolvasni."
);
}
});
console.log("Moodle Test Script run time:");
console.timeEnd('main');
if (forceTestPage || forceResultPage || forceDefaultPage)
alert("TEST MODE");
}
//: }}}
//: DOM getting stuff {{{
// all dom getting stuff are in this sections, so on
// moodle dom change, stuff breaks here
function GetAllQuestionsDropdown() {
if (logElementGetting)
Log("getting dropdown question");
let items = document.getElementById("responseform").getElementsByTagName("p")[0].childNodes;
let r = "";
items.forEach((item) => {
if (item.tagName == undefined)
r += item.nodeValue;
});
return r;
}
function GetAllQuestionsQtext() {
if (logElementGetting)
Log("getting all questions qtext");
return document.getElementById("responseform").getElementsByClassName("qtext"); // getting questions
}
function GetAllQuestionsP() {
if (logElementGetting)
Log("getting all questions by tag p");
return document.getElementById("responseform").getElementsByTagName("p");
}
function GetFormulationClearfix() {
if (logElementGetting)
Log("getting formulation clearfix lol");
return document.getElementsByClassName("formulation clearfix");
}
function GetAnswerOptions() {
if (logElementGetting)
Log("getting all answer options");
return GetFormulationClearfix()[0].childNodes[3].innerText;
}
function GetQuestionImages() {
if (logElementGetting)
Log("getting question images");
return GetFormulationClearfix()[0].getElementsByTagName("img");
}
function GetCurrentSubjectName() {
if (logElementGetting)
Log("getting current subjects name");
return document.getElementById("page-header").innerText.split("\n")[0];
}
function GetVideo() {
if (logElementGetting)
Log("getting video stuff");
return document.getElementsByTagName("video")[0];
}
function GetVideoElement() {
if (logElementGetting)
Log("getting video element");
return document.getElementById("videoElement").parentNode;
}
function GetAllAnswer(index) {
if (logElementGetting)
Log("getting all answers, ind: " + index);
return document.getElementsByClassName("answer")[index].childNodes;
}
function GetInputType(answers, i) {
if (logElementGetting)
Log("getting input type");
return answers[i].getElementsByTagName("input")[0].type;
}
function GetFormResult() {
if (logElementGetting)
Log("getting form result");
var t = document.getElementsByTagName("form")[0].childNodes[0].childNodes;
if (t.length > 0 && t[0].tagName == undefined) // debreceni moodle
return document.getElementsByTagName("form")[1].childNodes[0].childNodes;
else
return t;
}
function GetQText(i) {
if (logElementGetting)
Log("getting qtext by index: " + i);
var results = GetFormResult(); // getting results element
return results[i].getElementsByClassName("qtext");
}
function GetDropboxes(i) {
if (logElementGetting)
Log("getting dropboxes by index: " + i);
var results = GetFormResult(); // getting results element
return results[i].getElementsByTagName("select");
}
function GetCurrQuestion(i) {
if (logElementGetting)
Log("getting curr questions by index: " + i);
return document.getElementsByTagName("form")[0].childNodes[0].childNodes[i].childNodes[1].childNodes[
0].innerText;
}
function GetCurrentAnswer(i) {
if (logElementGetting)
Log("getting curr answer by index: " + i);
var results = GetFormResult(); // getting results element
var t = results[i].getElementsByClassName("formulation clearfix")[0].getElementsByTagName("span");
if (t.length > 2)
return t[1].innerHTML.split("<br>")[1];
}
function GetSelectAnswer() {
if (logElementGetting)
Log("getting selected answer");
var t = document.getElementsByTagName("select");
if (t.length > 0)
return t[0].options[document.getElementsByTagName("select")[
0].selectedIndex].innerText;
}
function GetAnswerNode(i) {
if (logElementGetting)
Log("getting answer node");
var results = GetFormResult(); // getting results element
var r = results[i].getElementsByClassName("answer")[0].childNodes;
var ret = [];
for (var j = 0; j < r.length; j++)
if (r[j].tagName != undefined && r[j].tagName.toLowerCase() == "div")
ret.push(r[j]);
let qtype = DetermineQuestionType(ret);
return {
nodes: ret,
type: qtype
};
}
function DetermineQuestionType(nodes) {
let qtype = "";
let i = 0;
while (i < nodes.length && qtype == "") {
let inps = nodes[i].getElementsByTagName("input");
if (inps.length > 0) {
qtype = inps[0].type;
}
i++;
}
return qtype;
}
function GetPossibleAnswers(i) {
if (logElementGetting)
Log("getting possible answers");
var results = GetFormResult(); // getting results element
var items = GetFormResult()[i].getElementsByTagName("label");
var r = [];
for (var j = 0; j < items.length; j++) {
function TryGetCorrect(j) {
var cn = items[j].parentNode.className;
if (cn.includes("correct"))
return cn.includes("correct") && !cn.includes("incorrect");
}
r.push({
value: items[j].innerText,
iscorrect: TryGetCorrect(j)
});
}
return r;
}
function GetRightAnswerIfCorrectShown(i) {
if (logElementGetting)
Log("getting right answer if correct shown");
var results = GetFormResult(); // getting results element
return results[i].getElementsByClassName("rightanswer");
}
function GetWrongAnswerIfCorrectNotShown(i) {
if (logElementGetting)
Log("getting wrong answer if correct not shown");
var results = GetFormResult(); // getting results element
var n = results[i].getElementsByTagName("i")[0].parentNode;
if (n.className.includes("incorrect"))
return results[i].getElementsByTagName("i")[0].parentNode.innerText;
else
return "";
}
function GetRightAnswerIfCorrectNotShown(i) {
if (logElementGetting)
Log("Getting right answer if correct not shown");
var results = GetFormResult(); // getting results element
var n = results[i].getElementsByTagName("i")[0].parentNode;
if (n.className.includes("correct") && !n.className.includes("incorrect"))
return results[i].getElementsByTagName("i")[0].parentNode.innerText;
else
return "";
}
function GetFormCFOfResult(result) {
if (logElementGetting)
Log("getting formulation clearfix");
return result.getElementsByClassName("formulation clearfix")[0];
}
function GetResultText(i) {
if (logElementGetting)
Log("getting result text");
var results = GetFormResult(); // getting results element
return GetFormCFOfResult(results[i]).getElementsByTagName("p");
}
function GetResultImage(i) {
if (logElementGetting)
Log("getting result image");
var results = GetFormResult(); // getting results element
return GetFormCFOfResult(results[i]).getElementsByTagName("img");
}
// this function should return the question, posible answers, and image names
function GetQuestionFromTest() {
var questions; // the important questions
var allQuestions; // all questions
try // trying to get tha current questions
{
allQuestions = GetAllQuestionsQtext(); // getting questions
if (allQuestions.length == 0) // if there are no found questions
{
var ddq = GetAllQuestionsDropdown();
if (EmptyOrWhiteSpace(ddq)) {
var questionData = "";
for (var j = 0; j < allQuestions.length; j++) {
allQuestions = GetAllQuestionsQtext()[j].childNodes;
for (var i = 0; i < allQuestions.length; i++) {
if (allQuestions[i].data != undefined && !EmptyOrWhiteSpace(allQuestions[i].data)) // if the current element has some text data to add
{
questionData += allQuestions[i].data + " "; // adding text to question data
}
}
}
questions = [questionData];
} else
questions = [ddq];
} else // if there is
{
questions = [];
for (var i = 0; i < allQuestions.length; i++) {
questions.push(allQuestions[i].innerText);
}
}
} catch (e) {
Exception(e, "script error at getting question:");
}
var imgNodes = ""; // the image nodes for questions
try {
imgNodes = GetQuestionImages(); // getting question images, if there is any
AddImageNamesToImages(imgNodes); // adding image names to images, so its easier to search for, or even guessing
} catch (e) {
Log(e);
Log("Some error with images");
}
questions = questions.map((item, ind) => {
return ReplaceCharsWithSpace(item, "\n");
});
return {
imgnodes: imgNodes,
allQ: allQuestions,
q: questions
};
}
// gets the question from the result page
// i is the index of the question
function GetQuestionFromResult(i) {
var temp = GetQText(i);
var currQuestion = "";
if (temp.length > 0) // if the qtext element is not 0 length
{
currQuestion = temp[0].innerText; // adding the question to curr question as .q
} else {
// this is black magic fuckery a bit
if (GetDropboxes(i).length > 0) // if there is dropdown list in the current question
{
var allNodes = GetResultText(i);
currQuestion = "";
for (var k = 0; k < allNodes.length; k++) {
var allQuestions = GetResultText(i)[k].childNodes;
for (var j = 0; j < allQuestions.length; j++) {
if (allQuestions[j].data != undefined && !EmptyOrWhiteSpace(allQuestions[j].data)) {
currQuestion += allQuestions[j].data + " ";
}
}
}
} else {
try {
currQuestion = GetCurrQuestion(i);
} catch (e) {
currQuestion = "REEEEEEEEEEEEEEEEEEEEE"; // this shouldnt really happen sry guys
}
}
}
return currQuestion;
}
// tries to get right answer from result page
// i is the index of the question
function GetRightAnswerFromResult(i) {
var fun = [];
// the basic type of getting answers
fun.push(function TryGet0(i) {
var temp = GetRightAnswerIfCorrectShown(i); // getting risht answer
if (temp.length > 0) // if the rightanswer element is not 0 length
return temp[0].innerText; // adding the answer to curr question as .a
});
// if there is dropdown list in the current question
fun.push(function TryGet1(i) {
if (GetDropboxes(i).length > 0)
return GetCurrentAnswer(i);
});
// if the correct answers are not shown, and the selected answer
// is correct
fun.push(function TryGet2(i) {
return GetRightAnswerIfCorrectNotShown(i);
});
// if there is dropbox in the question
fun.push(function TryGet3(i) {
return GetSelectAnswer();
});
// if the correct answers are not shown, and the selected answer
// is incorrect, and there are only 2 options
fun.push(function TryGet4(i) {
var possibleAnswers = GetPossibleAnswers(i);
if (possibleAnswers.length == 2) {
for (var k = 0; k < possibleAnswers.length; k++)
if (possibleAnswers[k].iscorrect == undefined)
return possibleAnswers[k].value;
}
});
fun.push(function TryGetFinal(i) {
return undefined;
});
var j = 0;
var currAnswer;
while (j < fun.length && EmptyOrWhiteSpace(currAnswer)) {
currAnswer = fun[j](i);
j++;
}
return currAnswer;
}
// version 2 of getting right answer from result page
// i is the index of the question
function GetRightAnswerFromResultv2(i) {
try {
var answerNodes = GetAnswerNode(i);
let items = answerNodes.nodes;
if (answerNodes.type == "checkbox")
return GetRightAnswerFromResult(i);
for (var j = 0; j < items.length; j++) {
var cn = items[j].className;
if (cn.includes("correct") && !cn.includes("incorrect"))
return items[j].innerText;
}
if (items.length == 2) {
for (var j = 0; j < items.length; j++) {
var cn = items[j].className;
if (!cn.includes("correct"))
return items[j].innerText;
}
}
} catch (e) {
Log("error at new nodegetting, trying the oldschool way");
}
}
//: }}}
//: Main logic stuff {{{
//: Loading {{{
function Init(cwith) {
if (false) // reset, only for testing!
{
GM_setValue("version16", undefined);
GM_setValue("version15", undefined);
GM_setValue("firstRun", undefined); // GM_getValue("lastVerson") == undefined => firstrun
GM_setValue("showQuestions", undefined);
GM_setValue("showSplash", undefined);
}
var url = location.href; // window location
var count = -1; // loaded question count. stays -1 if the load failed.
// --------------------------------------------------------------------------------------
// event listener fuckery
// --------------------------------------------------------------------------------------
try {
// adding addeventlistener stuff, for the ability to add more event listeners for the same event
addEventListener = (function() {
if (document.addEventListener) {
return function(element, event, handler) {
element.addEventListener(event, handler, false);
};
} else {
return function(element, event, handler) {
element.attachEvent('on' + event, handler);
};
}
}());
} catch (e) {
Exception(e, "script error at addEventListener:");
}
VersionActions();
count = Load(cwith); // loads resources
if (!url.includes(".pdf"))
ShowMenu();
return count;
}
function VersionActions() {
// FOR TESTING ONLY
// GM_setValue("version15", true);
// GM_setValue("firstRun", true);
// GM_setValue("version16", true);
// GM_setValue("version161", true);
// throw "asd";
let r = FreshStart();
if (r != true)
GM_setValue("version161", false);
Version15();
Version16();
Version161();
}
//: Version action functions {{{
function FreshStart() {
var firstRun = GM_getValue("firstRun"); // if the current run is the frst
if (firstRun == undefined || firstRun == true) // if its undefined, or true
{
GM_setValue("firstRun", false);
ShowHelp(); // showing help
return true;
}
}
function Version15() {
var version15 = GM_getValue("version15"); // if the current run is the frst
if (version15 == undefined || version15 == true) // if its undefined, or true
{
GM_setValue("useNetDB", "0");
GM_setValue("version15", false);
document.write(
'<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";
}
}
//: }}}
function ReadFile(cwith) {
var resource = "";
try {
resource = GM_getResourceText("data"); // getting data from txt
if (resource == undefined) {
ShowMessage({
m: "Nem lehetett beolvasni a fájlt :c Ellenőrizd az elérési utat, vagy a fájl jogosultságokat",
isSimple: true
});
return;
}
if (EmptyOrWhiteSpace(resource)) {
throw {
message: "data file empty"
};
}
} catch (e) {
Exception(e, "script error at reading file:");
}
NLoad(resource, cwith);
}
function ReadNetDB(cwith, useNetDB) {
function NewXMLHttpRequest() {
const url = serverAdress + "data.json";
GM_xmlhttpRequest({
method: "GET",
synchronous: true,
url: url,
onload: function(response) {
NLoad(response.responseText, cwith);
},
onerror: function() {
NLoad(undefined, cwith); // server down
}
});
}
try {
Log("Sending XMLHTTP Request...");
return NewXMLHttpRequest();
} catch (e) {
Exception(e, "script error at reading online database:");
}
}
/*
* Returns a question database from the given data.
* Parameter should be raw read file in string with "\n"-s
* */
function ParseRawData(data) {
const d = data.split("\n");
const r = new QuestionDB();
var logs = [];
var currSubj = ""; // the current subjects name
var ExpectedIdentifier = ['+', '?'];
let currQuestion = new Question();
var i = -1;
while (i < d.length) {
let currIdentifier;
let skipped = 0;
do {
if (skipped >= 1)
logs.push(i + ": " + d[i]);
i++;
if (i >= d.length) {
if (currQuestion.IsComplete())
r.AddQuestion(currSubj, currQuestion);
return {
result: r,
logs: logs
};
}
currIdentifier = d[i][0];
skipped++;
} while (!ExpectedIdentifier.includes(currIdentifier) && i < d.length);
let currData = d[i].substring(1).trim();
if (currIdentifier == '+') {
if (currQuestion.IsComplete())
r.AddQuestion(currSubj, currQuestion);
currQuestion = new Question();
currSubj = currData;
ExpectedIdentifier = ['?'];
continue;
}
if (currIdentifier == '?') {
if (currQuestion.IsComplete()) {
r.AddQuestion(currSubj, currQuestion);
currQuestion = new Question();
}
// overwriting is allowed here, bcus:
// ?????!>
currQuestion.Q = currData;
ExpectedIdentifier = ['!', '?'];
continue;
}
if (currIdentifier == '!') {
// if dont have question continue
if (!currQuestion.HasQuestion())
throw "No question! (A)";
// dont allow overwriting
// ?!!!!
if (!currQuestion.HasAnswer()) {
currData = currData.replace("A helyes válaszok: ", "");
currData = currData.replace("A helyes válasz: ", "");
currQuestion.A = currData;
}
ExpectedIdentifier = ['?', '>', '+'];
continue;
}
if (currIdentifier == '>') {
// if dont have question or answer continue
if (!currQuestion.HasQuestion())
throw "No question! (I)";
if (!currQuestion.HasAnswer())
throw "No asnwer! (I)";
// dont allow overwriting
// ?!>>>
if (!currQuestion.HasImage()) {
try {
currQuestion.I = JSON.parse(currData);
} catch (e) {
currQuestion.I = [currData];
}
}
ExpectedIdentifier = ['?', '+'];
continue;
}
}
return {
result: r,
logs: logs
};
}
function Load(cwith) {
var useNetDB = GM_getValue("useNetDB");
let skipLoad = GM_getValue("skipLoad");
if (skipLoad) {
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++;
}
if (i >= data.length)
document.getElementById("HelperMenuButton").style.background = "yellow";
} catch (e) {
Exception(e, "script error at loading:");
count = -1; // returns -1 if error
}
cwith(count, subjCount);
}
//: }}}
//: UI handling {{{
function HandleUI(url, count, subjCount) {
var newVersion = false; // if the script is newer than last start
var loaded = count != -1; // if script could load stuff
try // try, cus im suspicious
{
newVersion = GM_info.script.version !== GM_getValue("lastVerson");
} catch (e) {}
var greetMsg = ""; // message to show at the end
var timeout = null; // the timeout. if null, it wont be hidden
// no new version, nothing loaded
if (!newVersion && !loaded) // --------------------------------------------------------------------------------------------------------------
{
greetMsg =
"Hiba a @resource tagnál, vagy a fileval van gond! (Lehet át lett helyezve, vagy üres, vagy 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 + " ";
timeout = undefined;
}
greetMsg += count + " kérdés és " + subjCount + " tárgy betöltve. (click for help).";
if (data.length > 0) {
var toAdd = [];
for (var i = 0; i < data.length; i++) {
if (data.GetIfActive(i)) {
toAdd.push(data.Subjects[i].Name + " (" + data.Subjects[i].length + ")");
}
}
if (toAdd.length != 0) {
greetMsg += "\nAktív tárgyak: " + toAdd.join(", ") + ".";
} else {
greetMsg += "\nNincs aktív tárgyad. Menüből válassz ki eggyet!";
timeout = undefined;
}
} else {
greetMsg += " Az adatfájlban nem adtál meg nevet. Vagy nem elérhető a szerver. Katt a helpért!";
}
}
// new version, nothing loaded
if (newVersion && !loaded) // --------------------------------------------------------------------------------------------------------------
{
greetMsg = "Moodle/Elearning/KMOOC segéd v. " + GM_info.script.version +
". Új verzió!\n Írd át a @resouce tagnál az elírési utat! Kivéve ha üres a file, akkor töltsd fel :) Nincs kérdés betöltve! Segítséghez kattints. Changelog:\n" +
lastChangeLog; // showing changelog too
}
// new version, everything loaded -> set lastVerson to current
if (newVersion && loaded) // --------------------------------------------------------------------------------------------------------------
{
greetMsg = "Moodle/Elearning/KMOOC segéd v. " + GM_info.script.version + ". " + count +
" kérdés és " + subjCount + " tárgy betöltve. Verzió frissítve " + GM_info.script.version +
"-re. Changelog:\n" + lastChangeLog;
GM_setValue("lastVerson", GM_info.script.version); // setting lastVersion
}
if (!EmptyOrWhiteSpace(motd)) {
var prevmotd = GM_getValue("motd");
if (prevmotd != motd) {
greetMsg += "\nMOTD:\n" + motd;
timeout = null;
GM_setValue("motdcount", motdShowCount);
GM_setValue("motd", motd);
} else {
var motdcount = GM_getValue("motdcount");
if (motdcount == undefined) {
GM_setValue("motdcount", motdShowCount);
motdcount = motdShowCount;
}
motdcount--;
if (motdcount > 0) {
greetMsg += "\nMOTD:\n" + motd;
timeout = null;
GM_setValue("motdcount", motdcount);
}
}
}
ShowMessage({
m: greetMsg,
isSimple: true
}, timeout, ShowHelp); // showing message. If "m" is empty it wont show it, thats how showSplash works.
}
//: }}}
//: Answering stuffs {{{
function HandleQuiz() {
var q = GetQuestionFromTest();
var questions = q.q;
var allQuestions = q.allQ;
var imgNodes = q.imgnodes;
// ------------------------------------------------------------------------------------------------------
var answers = [];
for (var j = 0; j < questions.length; j++) // going thru all answers
{
var question = RemoveUnnecesarySpaces(questions[j]); // simplifying question
var result = data.Search(question, SimplifyImages(imgNodes));
var r = PrepareAnswers(result, j);
if (r != undefined)
answers.push(r);
HighLightAnswer(result, j); // highlights the answer for the current result
}
ShowAnswers(answers);
}
function PrepareAnswers(result, j) {
if (result.length > 0) // if there are more than zero results
{
var allMessages = []; // preparing all messages
for (var k = 0; k < result.length; k++) // going throuh all results
{
var msg = ""; // the current message
if ((GM_getValue("showQuestions") == undefined) || GM_getValue("showQuestions")) // if the question should be shown
{
msg += result[k].q.Q + "\n"; // adding the question if yes
}
msg += result[k].q.A.replace(/, /g, "\n"); // adding answer
if (result[k].q.HasImage()) // and adding image, if it exists
{
msg += "\n" + result[k].q.I; // if it has image part, adding that too
}
allMessages.push({
m: msg,
p: result[k].match
});
}
return allMessages;
}
}
function ShowAnswers(answers) {
if (answers.length > 0) { // if there are more than 0 answer
ShowMessage(answers);
} else {
ShowMessage({
m: "Nincs találat :( Kattints az üzenetre az összes kérdés/válaszért manuális kereséshez! Előfordulhat, hogy a tárgyat nem válsztottad ki a menüben.",
isSimple: true
}, undefined, function() {
GM_openInTab(serverAdress + 'legacy', {
active: true
});
});
}
}
//: }}}
//: Quiz saving {{{
function HandleResults(url) {
var allResults = new QuestionDB();
SaveQuiz(GetQuiz(), data); // saves the quiz questions and answers
}
function ShowSaveQuizDialog(addedQ, allQ, allOutput, output, sendSuccess, sentData) {
var msg = "";
if (addedQ > 0) // if there are more than 0 new question
{
msg = "Klikk ide a nyers adatokhoz. " + addedQ +
" új kérdés!";
var useNetDB = GM_getValue("useNetDB");
if (useNetDB != undefined && useNetDB == 1) {
if (!sendSuccess)
msg += " Nem sikerült kérdéseket elküldeni szervernek. Ha gondolod utánanézhetsz.";
else
msg += "Az új kérdések elküldve.";
} else
msg += "Ne felejtsd el bemásolni a fő txt-be!";
} else // if there is 0 or less new question
{
msg = "A kérdőívben nincsen új kérdés. Ha mégis le akarod menteni klikk ide.";
if (!data)
msg += " Lehet azért, mert nincs kérdés betöltve.";
}
// showing a message wit the click event, and the generated page
ShowMessage({
m: msg,
isSimple: true
}, null, function() {
var towrite = '<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 = GetResultImage(i); // trying to get image
var imgURL = []; // image urls
for (var j = 0; j < imgElements.length; j++) {
if (!imgElements[j].src.includes("brokenfile")) // idk why brokenfile is in some urls, which are broken, so why tf are they there damn moodle
{
var filePart = imgElements[j].src.split("/"); // splits the link by "/"
filePart = filePart[filePart.length - 1]; // the last one is the image name
imgURL.push(decodeURI(ShortenString(filePart, 30)));
}
}
if (imgURL.length > 0) {
temp = JSON.stringify(imgURL);
return temp;
}
} catch (e) {}
}
// saves the current quiz. questionData contains the active subjects questions
function SaveQuiz(quiz, questionData) {
try {
if (quiz.length == 0)
throw {
message: "quiz length is zero!",
stack: "no stack."
};
var output = ""; // thefinal output
var allOutput = ""; // thefinal output with all questions
var allQ = 0;
var addedQ = 0;
var newQuestions = [];
for (var i = 0; i < quiz.length; i++) // going though quiz
{
// searching for same questions in questionData
var toAdd = ""; // this will be added to some variable depending on if its already in the database
toAdd += "?" + RemoveUnnecesarySpaces(quiz[i].Q) + "\n"; // adding quiz question
toAdd += "!" + RemoveUnnecesarySpaces(quiz[i].A) + "\n"; // adding quiz answer
if (quiz[i].HasImage()) {
toAdd += ">" + RemoveUnnecesarySpaces(quiz[i].I) + "\n"; // adding quiz image if there is any
}
if (SearchSameQuestion(questionData, quiz, i) == -1) // if there is no such item in the database (w/ same q and a)
{
output += toAdd; // adding to output
newQuestions.push(quiz[i]);
addedQ++;
}
allOutput += toAdd; // adding to all
allQ++;
}
var sendSuccess = false;
var sentData = {};
try {
try {
sentData.subj = GetCurrentSubjectName();
} catch (e) {
sentData.subj = "NOSUBJ";
Log("unable to get subject name :c");
}
var useNetDB = GM_getValue("useNetDB");
if (useNetDB != undefined && useNetDB == 1) {
sentData.allData = quiz;
sentData.data = newQuestions;
sentData.version = GM_info.script.version;
SendXHRMessage("datatoadd=" + JSON.stringify(sentData));
sendSuccess = true;
}
} catch (e) {
Exception(e, "error at sending data to server.");
}
ShowSaveQuizDialog(addedQ, allQ, allOutput, output, sendSuccess, sentData);
} catch (e) {
Exception(e, "script error at saving quiz");
}
}
// getting quiz from finish page
function GetQuiz() {
try {
var quiz = []; // final quiz stuff
var results = GetFormResult(); // getting results element
for (var i = 0; i < results.length - 2; i++) // going through results, except last two, idk why, dont remember, go check it man
{
var question = {}; // the current question
// QUESTION --------------------------------------------------------------------------------------------------------------------
var q = GetQuestionFromResult(i);
if (q != undefined)
question.q = SimplifyQuery(q);
// RIGHTANSWER ---------------------------------------------------------------------------------------------------------------------
var a = GetRightAnswerFromResultv2(i);
if (a == undefined)
a = GetRightAnswerFromResult(i);
if (a != undefined)
question.a = SimplifyQuery(a);
// IMG ---------------------------------------------------------------------------------------------------------------------
var img = GetImageFormResult(i);
question.i = img;
q = ReplaceCharsWithSpace(q, "\n");
a = ReplaceCharsWithSpace(a, "\n");
if (question.a != undefined) // adding only if has question
{
quiz.push(new Question(question.q, question.a, question.i)); // adding current question to quiz
} else {
Log("error getting queston, or its incorrect");
Log(question);
}
}
return quiz;
} catch (e) {
Exception(e, "script error at quiz parsing:");
}
}
//: }}}
//: Helpers {{{
function SimplifyImages(imgs) {
var questionImages = []; // the array for the image names in question
for (var i = 0; i < imgs.length; i++) // going through all image
{
if (!imgs[i].src.includes("brokenfile")) // if its includes borken file its broken. Its another moodle crap. So i just wont check those
{
var filePart = imgs[i].src.split("/"); // splits the link by "/"
filePart = filePart[filePart.length - 1]; // the last one is the image name
questionImages.push(decodeURI(RemoveUnnecesarySpaces(ShortenString(filePart, 30)))); // decodes uri codes, and removes exess spaces, and shortening it
}
}
return questionImages;
}
// adds image names to image nodes
function AddImageNamesToImages(imgs) {
for (var i = 0; i < imgs.length; i++) // going through all image
{
if (!imgs[i].src.includes("brokenfile")) // if its includes borken file its broken. Its another moodle crap. So i just wont check those
{
var filePart = imgs[i].src.split("/"); // splits the link by "/"
filePart = filePart[filePart.length - 1]; // the last one is the image name
var appedtTo = imgs[i].parentNode; // it will be appended here
var mainDiv = document.createElement("div");
var fileName = ShortenString(decodeURI(filePart), 15); // shortening name, couse it can be long as fuck
var textNode = document.createTextNode("(" + fileName + ")");
mainDiv.appendChild(textNode);
appedtTo.appendChild(mainDiv);
}
}
}
// this function adds basic hotkeys for video controll.
function AddVideoHotkeys(url) {
var seekTime = 20;
document.addEventListener("keydown", function(e) // keydown event listener
{
try {
var video = GetVideo();
var keyCode = e.keyCode; // getting keycode
if (keyCode == 32) // if the keycode is 32 (space)
{
e.preventDefault(); // preventing default action (space scrolles down)
if (video.paused && video.buffered.length > 0) {
video.play();
} else {
video.pause();
}
}
if (keyCode == 39) // rigth : 39
{
video.currentTime += seekTime;
}
if (keyCode == 37) // left : 37
{
video.currentTime -= seekTime;
}
} catch (err) {
Log("Hotkey error.");
Log(err.message);
}
});
var toadd = GetVideoElement();
var node = CreateNodeWithText(toadd,
"Miután elindítottad: Play/pause: space. Seek: Bal/jobb nyíl.");
node.style.margin = "5px 5px 5px 5px"; // fancy margin
}
function GetMatchPercent(currData, questionParts) {
var currQuestion = SimplifyQuery(currData.q); // current question simplified
var match = 0; // how many times the current question matches the current question in the database
for (var i = 0; i < questionParts.length; i++) // going through the question parts
{
if (currQuestion.includes(questionParts[i])) // if the current question from questionData includes one of the question parts
{
match++;
}
}
var percent = Math.round(((match / questionParts.length) * 100).toFixed(2)); // matched words percent
var lengthDifference = RemoveMultipleItems(SimplifyQuery(currQuestion).split(" ")).length -
questionParts.length;
percent -= Math.abs(lengthDifference) * 2;
return percent;
}
// simple sort.
function SortByPercent(results) {
for (var i = 0; i < results.length; i++) {
for (var j = results.length - 1; j > i; j--) {
if (results[i].p < results[j].p) {
var temp = results[i];
results[i] = results[j];
results[j] = temp;
}
}
}
return results;
}
// removes stuff like " a. q1" -> "q1"
function RemoveLetterMarking(inp) {
let dotIndex = inp.indexOf('.');
let doubledotIndex = inp.indexOf(':');
let maxInd = 4; // inp.length * 0.2;
if (dotIndex < maxInd)
return RemoveUnnecesarySpaces(inp.substr(inp.indexOf(".") + 1, inp.length));
else if (doubledotIndex < maxInd)
return RemoveUnnecesarySpaces(inp.substr(inp.indexOf(":") + 1, inp.length));
else
return inp;
}
// highlights the possible solutions to the current question
function HighLightAnswer(results, currQuestionNumber) {
try {
if (results.length > 0) // if there are items in the result
{
var answers = GetAllAnswer(currQuestionNumber); // getting all answers
var toColor = []; // the numberth in the array will be colored, and .length items will be colored
var type = ""; // type of the question. radio or ticbox or whatitscalled
for (var i = 0; i < answers.length; i++) // going thtough answers
{
if (answers[i].tagName && answers[i].tagName.toLowerCase() == "div") // if its not null and is "div"
{
var correct = results[0].q.A.toLowerCase(); // getting current correct answer from data
var answer = answers[i].innerText.replace(/\n/g, "").toLowerCase(); // getting current answer
// removing stuff like "a."
answer = RemoveLetterMarking(answer);
if (EmptyOrWhiteSpace(correct) || EmptyOrWhiteSpace(answer))
continue;
if (NormalizeSpaces(RemoveUnnecesarySpaces(correct)).includes(answer)) // if the correct answer includes the current answer
{
toColor.push(i); // adding the index
type = GetInputType(answers, i); // setting the type
}
}
}
if (results[0].match == 100) // if the result is 100% correct
if (type !== "radio" || toColor.length == 1) // if the type is not radio or there is exactly one correct solution
for (var i = 0; i < toColor.length; i++) // going through "toColor"
answers[toColor[i]].style.backgroundColor = "#8cff66"; // and coloring the correct index
}
} catch (e) // catching errors. Sometimes there are random errors, wich i did not test, but they are rare, and does not break the main script.
{
Log("script error at highlightin answer: " + e.message);
}
}
//: }}}
function Log(value) {
if (log)
console.log(value);
}
function Exception(e, msg) {
Log("------------------------------------------");
Log(msg);
Log(e.message);
Log("------------------------------------------");
Log(e.stack);
Log("------------------------------------------");
}
//: }}}
//: Minor UI stuff {{{
// shows a message with "msg" text, "matchPercent" tip and transp, and "timeout" time
function ShowMessage(msgItem, timeout, funct) {
// msgItem help:
// [ [ {}{}{}{} ] [ {}{}{} ] ]
// msgItem[] <- a questions stuff
// msgItem[][] <- a questions relevant answers array
// msgItem[][].p <- a questions precent
// msgItem[][].m <- a questions message
try {
var defMargin = "0px 5px 0px 5px";
var isSimpleMessage = false;
var simpleMessageText = "";
if (msgItem.isSimple) // parsing msgItem for easier use
{
simpleMessageText = msgItem.m;
if (simpleMessageText == "") {
return;
}
msgItem = [
[{
m: simpleMessageText
}]
];
isSimpleMessage = true;
}
var appedtTo = document.body; // will be appended here
var width = window.innerWidth - window.innerWidth / 6; // with of the box
var startFromLeft = window.innerWidth / 2 - width / 2; // dont change this
var startFromTop = 25; // top distance
var mainDiv = document.createElement("div"); // the main divider, wich items will be attached to
mainDiv.setAttribute("id", "messageMainDiv");
if (funct) // if there is a function as parameter
{
addEventListener(mainDiv, 'click', funct); // adding it as click
}
// lotsa crap style
mainDiv.style.position = "fixed";
mainDiv.style.zIndex = 999999;
mainDiv.style.textAlign = "center";
mainDiv.style.width = width + 'px';
//mainDiv.style.height = height + 'px';
mainDiv.style.padding = "0px";
mainDiv.style.background = "#222d32"; // background color
mainDiv.style.color = "#ffffff"; // text color
mainDiv.style.borderColor = "#035a8f"; // border color
mainDiv.style.border = "none";
mainDiv.style.top = (startFromTop) + 'px';
mainDiv.style.left = (window.innerWidth - width) / 2 + 'px';
mainDiv.style.opacity = "0.9"; // setting starting opacity
var matchPercent = msgItem[0][0].p;
if (isSimpleMessage) {
var simpleMessageParagrapg = document.createElement("p"); // new paragraph
simpleMessageParagrapg.style.margin = defMargin; // fancy margin
var splitText = simpleMessageText.split("\n");
for (var i = 0; i < splitText.length; i++) {
var mesageNode = CreateNodeWithText(simpleMessageParagrapg, splitText[i]);
mesageNode.style.margin = defMargin; // fancy margin
}
mainDiv.appendChild(simpleMessageParagrapg); // adding text box to main div
} else // if its a fucking complicated message
{
// TABLE SETUP ------------------------------------------------------------------------------------------------------------
var table = document.createElement('table');
table.style.width = "100%";
// ROWS -----------------------------------------------------------------------------------------------------
var rowOne = table.insertRow(); // previous suggestion, question text, and prev question
var rowTwo = table.insertRow(); // next question button
var rowThree = table.insertRow(); // next suggetsion button
// CELLS -----------------------------------------------------------------------------------------------------
// row one
var numberTextCell = rowOne.insertCell();
var questionCell = rowOne.insertCell(); // QUESTION CELL
questionCell.setAttribute("id", "questionCell");
questionCell.rowSpan = 3;
questionCell.style.width = "90%";
var prevQuestionCell = rowOne.insertCell();
// row two
var percentTextCell = rowTwo.insertCell();
var nextQuestionCell = rowTwo.insertCell();
// row three
var prevSuggestionCell = rowThree.insertCell();
var nextSuggestionCell = rowThree.insertCell();
// adding finally
mainDiv.appendChild(table);
// PERCENT TEXT SETUP -----------------------------------------------------------------------------------------------------
var percentTextBox = CreateNodeWithText(percentTextCell, "");
percentTextBox.setAttribute("id", "percentTextBox");
if (matchPercent) // if match percent param is not null
{
percentTextBox.innerText = matchPercent + "%";
}
// NUMBER SETUP -----------------------------------------------------------------------------------------------------
var numberTextBox = CreateNodeWithText(numberTextCell, "1.");
numberTextBox.setAttribute("id", "numberTextBox");
// ANSWER NODE SETUP -------------------------------------------------------------------------------------------------------------
var questionTextElement = CreateNodeWithText(questionCell, "ur question goes here, mister OwO");
questionTextElement.setAttribute("id", "questionTextElement");
// BUTTON SETUP -----------------------------------------------------------------------------------------------------------
var currItem = 0;
var currRelevantQuestion = 0;
function GetRelevantQuestion() // returns the currItemth questions currRelevantQuestionth relevant question
{
return msgItem[currItem][currRelevantQuestion];
}
function ChangeCurrItemIndex(to) {
currItem += to;
if (currItem < 0) {
currItem = 0;
}
if (currItem > msgItem.length - 1) {
currItem = msgItem.length - 1;
}
currRelevantQuestion = 0;
}
function ChangeCurrRelevantQuestionIndex(to) {
currRelevantQuestion += to;
if (currRelevantQuestion < 0) {
currRelevantQuestion = 0;
}
if (currRelevantQuestion > msgItem[currItem].length - 1) {
currRelevantQuestion = msgItem[currItem].length - 1;
}
}
function SetQuestionText() {
var relevantQuestion = GetRelevantQuestion();
questionTextElement.innerText = relevantQuestion.m;
if (currItem == 0 && currRelevantQuestion == 0) {
numberTextBox.innerText = (currRelevantQuestion + 1) + ".";
} else {
numberTextBox.innerText = (currItem + 1) + "./" + (currRelevantQuestion + 1) + ".";
}
percentTextBox.innerText = relevantQuestion.p + "%";
}
var buttonMargin = "2px 2px 2px 2px"; // uniform button margin
if (msgItem[currItem].length > 1) {
// PREV SUGG BUTTON ------------------------------------------------------------------------------------------------------------
var prevSuggButton = CreateNodeWithText(prevSuggestionCell, "<", "button");
prevSuggButton.style.margin = buttonMargin; // fancy margin
prevSuggButton.addEventListener("click", function() {
ChangeCurrRelevantQuestionIndex(-1);
SetQuestionText();
});
// NEXT SUGG BUTTON ------------------------------------------------------------------------------------------------------------
var nextSuggButton = CreateNodeWithText(nextSuggestionCell, ">", "button");
nextSuggButton.style.margin = buttonMargin; // fancy margin
nextSuggButton.addEventListener("click", function() {
ChangeCurrRelevantQuestionIndex(1);
SetQuestionText();
});
}
// deciding if has multiple questions ------------------------------------------------------------------------------------------------
if (msgItem.length == 1) {
SetQuestionText();
} else // if there are multiple items to display
{
// PREV QUESTION BUTTON ------------------------------------------------------------------------------------------------------------
var prevButton = CreateNodeWithText(prevQuestionCell, "^", "button");
prevButton.style.margin = buttonMargin; // fancy margin
//event listener
prevButton.addEventListener("click", function() {
ChangeCurrItemIndex(-1);
SetQuestionText();
});
//NEXT QUESTION BUTTON ------------------------------------------------------------------------------------------------------------
var nextButton = CreateNodeWithText(nextQuestionCell, "ˇ", "button");
nextButton.style.margin = buttonMargin; // fancy margin
//event listener
nextButton.addEventListener("click", function() {
ChangeCurrItemIndex(1);
SetQuestionText();
});
SetQuestionText();
}
}
appedtTo.appendChild(mainDiv); // THE FINAL APPEND
//setting some events
//addEventListener(window, 'scroll', function () {
// mainDiv.style.top = (pageYOffset + startFromTop) + 'px';
//})
addEventListener(window, 'resize', function() {
mainDiv.style.left = (window.innerWidth - width) / 2 + 'px';
});
var timeOut;
if (timeout && timeout > 0) // setting timeout if not zero or null
{
timeOut = setTimeout(function() {
mainDiv.parentNode.removeChild(mainDiv);
}, timeout * 1000);
}
// middle click close event listener
addEventListener(mainDiv, 'mousedown', function(e) {
if (e.which == 2) {
mainDiv.parentNode.removeChild(mainDiv);
if (timeOut) {
clearTimeout(timeOut);
}
}
});
} catch (e) {
Exception(e, "script error at showing message:");
}
}
// shows a fancy menu
function ShowMenu() {
try {
var buttonWidth = 100; // button size ;)
var buttonHeight = 85;
var appedtTo = document.body; // will be appended here
// mainDiv.style.left = (window.innerWidth - width) / 2 + 'px';
var menuButtonDiv = document.createElement("div");
menuButtonDiv.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) {
toremove = NormalizeSpaces(val);
var regex = new RegExp(char, "g");
toremove.replace(regex, " ");
return RemoveUnnecesarySpaces(toremove);
}
// removes whitespace from begining and and, and replaces multiple spaces with one space
function RemoveUnnecesarySpaces(toremove) {
toremove = NormalizeSpaces(toremove);
while (toremove.includes(" ")) // while the text includes double spaces replaces all of them with a single one
{
toremove = toremove.replace(/ /g, " ");
}
return toremove.trim();
}
// simplifies a string for easier comparison
function SimplifyStringForComparison(value) {
value = RemoveUnnecesarySpaces(value).toLowerCase();
var removableChars = [",", ".", ":", "!"];
for (var i = 0; i < removableChars.length; i++) {
var regex = new RegExp(removableChars[i], "g");
value.replace(regex, "");
}
return value;
}
// if the value is empty, or whitespace
function EmptyOrWhiteSpace(value) {
// replaces /n-s with "". then replaces spaces with "". if it equals "", then its empty, or only consists of white space
if (value === undefined)
return true;
return (value.replace(/\n/g, "").replace(/ /g, "").replace(/\s/g, ' ') === "");
}
// damn nonbreaking space
function NormalizeSpaces(input) {
return input.replace(/\s/g, ' ');
}
function SendXHRMessage(message) {
var url = serverAdress + "isAdding";
GM_xmlhttpRequest({
method: "POST",
url: url,
data: message,
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
onerror: function(response) {
Log("XMLHTTP request POST error");
}
});
}
//: }}}
//: Help {{{
// shows some neat help
function ShowHelp() {
GM_openInTab(serverAdress + 'manual', {
active: true
});
}
//: }}}
// I am not too proud to cry that He and he
// Will never never go out of my mind.
// All his bones crying, and poor in all but pain,
// Being innocent, he dreaded that he died
// Hating his God, but what he was was plain:
// An old kind man brave in his burning pride.
// The sticks of the house were his; his books he owned.
// Even as a baby he had never cried;
// Nor did he now, save to his secret wound.
// Out of his eyes I saw the last light glide.
// Here among the liught of the lording sky
// An old man is with me where I go
// Walking in the meadows of his son's eye
// Too proud to cry, too frail to check the tears,
// And caught between two nights, blindness and death.
// O deepest wound of all that he should die
// On that darkest day.