mirror of
https://gitlab.com/MrFry/moodle-test-userscript
synced 2025-04-01 20:22:48 +02:00
2278 lines
70 KiB
JavaScript
2278 lines
70 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 = ''; // TODO
|
|
const serverAdress = "https://qmining.tk/";
|
|
|
|
// forcing pages for testing. unless you test, do not set these to true!
|
|
// only one of these should be true for testing
|
|
const forceTestPage = true;
|
|
const forceResultPage = false;
|
|
const forceDefaultPage = false;
|
|
const logElementGetting = false;
|
|
const log = true;
|
|
|
|
const motdShowCount = 3; /* Ammount of times to show motd */
|
|
var motd = "";
|
|
var lastestVersion = "";
|
|
|
|
const minMatchAmmount = 55; /* 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 = this.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 (this.GetDropboxes(i).length > 0)
|
|
return this.GetCurrentAnswer(i);
|
|
});
|
|
|
|
// if the correct answers are not shown, and the selected answer
|
|
// is correct
|
|
fun.push(function TryGet2(i) {
|
|
return this.GetRightAnswerIfCorrectNotShown(i);
|
|
});
|
|
|
|
// if there is dropbox in the question
|
|
fun.push(function TryGet3(i) {
|
|
return this.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 = this.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();
|
|
|
|
//: 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");
|
|
}
|
|
//: }}}
|
|
|
|
//: 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];
|
|
}
|
|
}
|
|
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++;
|
|
}
|
|
|
|
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 + '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 = 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;
|
|
|
|
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 = 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
|
|
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.
|