diff --git a/.prettierrc.js b/.prettierrc.js
index b0a616d..6cfb93a 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -1,6 +1,6 @@
module.exports = {
trailingComma: "es5",
- tabWidth: 4,
+ tabWidth: 2,
semi: false,
- singleQuote: false,
+ singleQuote: true,
}
diff --git a/stable.user.js b/stable.user.js
index b4cad23..c3fdceb 100755
--- a/stable.user.js
+++ b/stable.user.js
@@ -52,2461 +52,2378 @@
// TODO: test if this ; does not fuck up things
;(function() {
- // eslint-disable-line
- // GM functions, only to disable ESLINT errors
- /* eslint-disable */
- const a = Main
- const usf = unsafeWindow
- function getVal(name) {
- return GM_getValue(name)
- }
- function setVal(name, val) {
- return GM_setValue(name, val)
- }
- function delVal(name) {
- return GM_deleteValue(name)
- }
- function openInTab(address, options) {
- GM_openInTab(address, options)
- }
- function xmlhttpRequest(opts) {
- GM_xmlhttpRequest(opts)
- }
- function info() {
- return GM_info
- }
- /* eslint-enable */
+ // eslint-disable-line
+ // GM functions, only to disable ESLINT errors
+ /* eslint-disable */
+ const a = Main
+ const usf = unsafeWindow
+ function getVal(name) {
+ return GM_getValue(name)
+ }
+ function setVal(name, val) {
+ return GM_setValue(name, val)
+ }
+ function delVal(name) {
+ return GM_deleteValue(name)
+ }
+ function openInTab(address, options) {
+ GM_openInTab(address, options)
+ }
+ function xmlhttpRequest(opts) {
+ GM_xmlhttpRequest(opts)
+ }
+ function info() {
+ return GM_info
+ }
+ /* eslint-enable */
- var addEventListener // add event listener function
- let serverAdress = "https://qmining.frylabs.net/"
- let apiAdress = "https://api.frylabs.net/"
- const ircAddress = "https://kiwiirc.com/nextclient/irc.sub.fm/#qmining"
+ var addEventListener // add event listener function
+ let serverAdress = 'https://qmining.frylabs.net/'
+ let apiAdress = 'https://api.frylabs.net/'
+ const ircAddress = 'https://kiwiirc.com/nextclient/irc.sub.fm/#qmining'
- // 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 // TODO: remove
- const forceResultPage = false
- const forceDefaultPage = false
- const logElementGetting = false
- const log = true
+ // 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 // TODO: remove
+ const forceResultPage = false
+ const forceDefaultPage = false
+ const logElementGetting = false
+ const log = true
- const motdShowCount = 3 /* Ammount of times to show motd */
- let infoExpireTime = 60 // Every n seconds basic info should be loaded from server
- var motd = ""
- var lastestVersion = ""
- var subjInfo
+ const motdShowCount = 3 /* Ammount of times to show motd */
+ let infoExpireTime = 60 // Every n seconds basic info should be loaded from server
+ var motd = ''
+ var lastestVersion = ''
+ var subjInfo
- setVal("ISDEVEL", true) // TODO: remove
+ setVal('ISDEVEL', true) // TODO: remove
- if (getVal("ISDEVEL")) {
- console.log("Moodle script running in developement mode!")
- infoExpireTime = 1
- serverAdress = "http://localhost:8080/"
- apiAdress = "http://localhost:8080/"
+ if (getVal('ISDEVEL')) {
+ console.log('Moodle script running in developement mode!')
+ infoExpireTime = 1
+ serverAdress = 'http://localhost:8080/'
+ apiAdress = 'http://localhost:8080/'
+ }
+
+ const huTexts = {
+ lastChangeLog: '',
+ fatalError:
+ 'Fatál error. Check console (f12). Kattints az üzenetre az összes kérdés/válaszért manuális kereséshez!',
+ consoleErrorInfo:
+ '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!',
+ freshStartWarning:
+ '
Moodle teszt userscript:1.5.0 verzió: a script mostantól XMLHTTP kéréseket küld szerver fele! Erre a userscript futtató kiegészítőd is figyelmeztetni fog! Ha ez történik, a script rendes működése érdekében engedélyezd (Always allow domain)! Ha nem akarod, hogy ez történjen, akkor ne engedélyezd, vagy a menüben válaszd ki a "helyi fájl használata" opciót!
Elküldött adatok: minden teszt után a kérdés-válasz páros. Fogadott adatok: Az összes eddig ismert kérdés. Érdemes help-et elolvasni!!!
Ez az ablak frissités után eltűnik. Ha nem, akkor a visza gombbal próbálkozz.
',
+ noResult:
+ 'Nincs találat :( Kattints az üzenetre az összes kérdés/válaszért manuális kereséshez!',
+ videoHelp: 'Miután elindítottad: Play/pause: space. Seek: Bal/jobb nyíl.',
+ menuButtonText: 'Kérdések Menu',
+ couldntLoadDataPopupMenuText:
+ 'A kérdéseket nem lehetett beolvasni, ellenőrizd hogy elérhető-e a szerver',
+ showGreetingOnEveryPage: 'Üdvözlő üzenet mutatása minden oldalon',
+ close: 'Bezárás',
+ help: 'Help',
+ websiteBugreport: 'Weboldal / Bug report',
+ contribute: 'Contribute',
+ donate: 'Donate',
+ retry: 'Újrapróbálás',
+ ircButton: 'IRC',
+ invalidPW: 'Hibás jelszó: ',
+ search: 'Keresés ...',
+ loading: 'Betöltés ...',
+ login: 'Belépés',
+ requestPWInsteadOfLogin: 'Jelszó igénylés',
+ contributeTitle: 'Hozzájárulás a script és weboldal fejleszétéshez',
+ newPWTitle: 'Új jelszó új felhasználónak',
+ pwRequest: 'Új jelszó',
+ noServer: 'Nem elérhető a szerver!',
+ noUser: 'Nem vagy bejelentkezve!',
+ noServerConsoleMessage: `Nem elérhető a szerver, vagy kis eséllyel kezeletlen hiba történt! Ha elérhető a weboldal, akkor ott meg bírod nézni a kérdéseket itt: ${serverAdress}legacy`,
+ }
+
+ var texts = huTexts
+
+ // : question-classes {{{
+ const specialChars = ['&', '\\+']
+
+ const assert = val => {
+ if (!val) {
+ throw new Error('Assertion failed')
+ }
+ }
+
+ class StringUtils {
+ RemoveStuff(value, removableStrings, toReplace) {
+ removableStrings.forEach(x => {
+ var regex = new RegExp(x, 'g')
+ value = value.replace(regex, toReplace || '')
+ })
+ return value
}
- const huTexts = {
- lastChangeLog: "",
- fatalError:
- "Fatál error. Check console (f12). Kattints az üzenetre az összes kérdés/válaszért manuális kereséshez!",
- consoleErrorInfo:
- "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!",
- freshStartWarning:
- 'Moodle teszt userscript:1.5.0 verzió: a script mostantól XMLHTTP kéréseket küld szerver fele! Erre a userscript futtató kiegészítőd is figyelmeztetni fog! Ha ez történik, a script rendes működése érdekében engedélyezd (Always allow domain)! Ha nem akarod, hogy ez történjen, akkor ne engedélyezd, vagy a menüben válaszd ki a "helyi fájl használata" opciót!
Elküldött adatok: minden teszt után a kérdés-válasz páros. Fogadott adatok: Az összes eddig ismert kérdés. Érdemes help-et elolvasni!!!
Ez az ablak frissités után eltűnik. Ha nem, akkor a visza gombbal próbálkozz.
',
- noResult:
- "Nincs találat :( Kattints az üzenetre az összes kérdés/válaszért manuális kereséshez!",
- videoHelp:
- "Miután elindítottad: Play/pause: space. Seek: Bal/jobb nyíl.",
- menuButtonText: "Kérdések Menu",
- couldntLoadDataPopupMenuText:
- "A kérdéseket nem lehetett beolvasni, ellenőrizd hogy elérhető-e a szerver",
- showGreetingOnEveryPage: "Üdvözlő üzenet mutatása minden oldalon",
- close: "Bezárás",
- help: "Help",
- websiteBugreport: "Weboldal / Bug report",
- contribute: "Contribute",
- donate: "Donate",
- retry: "Újrapróbálás",
- ircButton: "IRC",
- invalidPW: "Hibás jelszó: ",
- search: "Keresés ...",
- loading: "Betöltés ...",
- login: "Belépés",
- requestPWInsteadOfLogin: "Jelszó igénylés",
- contributeTitle: "Hozzájárulás a script és weboldal fejleszétéshez",
- newPWTitle: "Új jelszó új felhasználónak",
- pwRequest: "Új jelszó",
- noServer: "Nem elérhető a szerver!",
- noUser: "Nem vagy bejelentkezve!",
- noServerConsoleMessage: `Nem elérhető a szerver, vagy kis eséllyel kezeletlen hiba történt! Ha elérhető a weboldal, akkor ott meg bírod nézni a kérdéseket itt: ${serverAdress}legacy`,
+ SimplifyQuery(q) {
+ assert(q)
+
+ var result = q.replace(/\n/g, ' ').replace(/\s/g, ' ')
+ return this.RemoveUnnecesarySpaces(result)
}
- var texts = huTexts
+ ShortenString(toShorten, ammount) {
+ assert(toShorten)
- // : question-classes {{{
- const specialChars = ["&", "\\+"]
-
- const assert = val => {
- if (!val) {
- throw new Error("Assertion failed")
- }
+ var result = ''
+ var i = 0
+ while (i < toShorten.length && i < ammount) {
+ result += toShorten[i]
+ i++
+ }
+ return result
}
- class StringUtils {
- RemoveStuff(value, removableStrings, toReplace) {
- removableStrings.forEach(x => {
- var regex = new RegExp(x, "g")
- value = value.replace(regex, toReplace || "")
- })
- return value
- }
+ ReplaceCharsWithSpace(val, char) {
+ assert(val)
+ assert(char)
- SimplifyQuery(q) {
- assert(q)
+ var toremove = this.NormalizeSpaces(val)
- var result = q.replace(/\n/g, " ").replace(/\s/g, " ")
- return this.RemoveUnnecesarySpaces(result)
- }
+ var regex = new RegExp(char, 'g')
+ toremove = toremove.replace(regex, ' ')
- ShortenString(toShorten, ammount) {
- assert(toShorten)
-
- var result = ""
- var i = 0
- while (i < toShorten.length && i < ammount) {
- result += toShorten[i]
- i++
- }
- return result
- }
-
- ReplaceCharsWithSpace(val, char) {
- assert(val)
- assert(char)
-
- var toremove = this.NormalizeSpaces(val)
-
- var regex = new RegExp(char, "g")
- toremove = toremove.replace(regex, " ")
-
- return this.RemoveUnnecesarySpaces(toremove)
- }
-
- // removes whitespace from begining and and, and replaces multiple spaces with one space
- RemoveUnnecesarySpaces(toremove) {
- assert(toremove)
-
- toremove = this.NormalizeSpaces(toremove)
- while (toremove.includes(" ")) {
- toremove = toremove.replace(/ {2}/g, " ")
- }
- return toremove.trim()
- }
-
- RemoveSpecialChars(value) {
- assert(value)
-
- return this.RemoveStuff(value, specialChars, " ")
- }
-
- // if the value is empty, or whitespace
- EmptyOrWhiteSpace(value) {
- // replaces /n-s with "". then replaces spaces with "". if it equals "", then its empty, or only consists of white space
- if (value === undefined) {
- return true
- }
- return (
- value
- .replace(/\n/g, "")
- .replace(/ /g, "")
- .replace(/\s/g, " ") === ""
- )
- }
-
- // damn nonbreaking space
- NormalizeSpaces(input) {
- assert(input)
-
- return input.replace(/\s/g, " ")
- }
-
- SimplifyStack(stack) {
- return this.SimplifyQuery(stack)
- }
+ return this.RemoveUnnecesarySpaces(toremove)
}
- const SUtils = new StringUtils()
+ // removes whitespace from begining and and, and replaces multiple spaces with one space
+ RemoveUnnecesarySpaces(toremove) {
+ assert(toremove)
- // : }}}
-
- // : DOM getting stuff {{{
- // all dom getting stuff are in this sections, so on
- // moodle dom change, stuff breaks here
-
- //Stealth by An0 with love
- function StealthOverlay() {
- //call this before the document scripts
- const document = window.document
-
- const neverEqualPlaceholder = Symbol(`never equal`) //block probing for undefined values in the hooks
- let shadowRootHost = neverEqualPlaceholder
- let shadowRootNewHost = neverEqualPlaceholder
-
- const apply = Reflect.apply //save some things in case they get hooked (only for unsafe contexts)
-
- if (usf.Error.hasOwnProperty("stackTraceLimit")) {
- Reflect.defineProperty(usf.Error, "stackTraceLimit", {
- value: undefined,
- writable: false,
- enumerable: false,
- configurable: false,
- })
- }
-
- const shadowGetHandler = {
- apply: (target, thisArg, argumentsList) =>
- apply(
- target,
- thisArg === shadowRootHost ? shadowRootNewHost : thisArg,
- argumentsList
- ),
- }
-
- const original_attachShadow = usf.Element.prototype.attachShadow
- const attachShadowProxy = new Proxy(
- original_attachShadow,
- shadowGetHandler
- )
- usf.Element.prototype.attachShadow = attachShadowProxy
-
- const getShadowRootProxy = new Proxy(
- Object.getOwnPropertyDescriptor(
- usf.Element.prototype,
- "shadowRoot"
- ).get,
- shadowGetHandler
- )
- Object.defineProperty(usf.Element.prototype, "shadowRoot", {
- get: getShadowRootProxy,
- })
-
- const getHostHandler = {
- apply: function() {
- let result = apply(...arguments)
- return result === shadowRootNewHost ? shadowRootHost : result
- },
- }
- const getHostProxy = new Proxy(
- Object.getOwnPropertyDescriptor(
- usf.ShadowRoot.prototype,
- "host"
- ).get,
- getHostHandler
- )
- Object.defineProperty(usf.ShadowRoot.prototype, "host", {
- get: getHostProxy,
- })
-
- const shadowRootSetInnerHtml = Object.getOwnPropertyDescriptor(
- ShadowRoot.prototype,
- "innerHTML"
- ).set
- const documentFragmentGetChildren = Object.getOwnPropertyDescriptor(
- DocumentFragment.prototype,
- "children"
- ).get
- const documentGetBody = Object.getOwnPropertyDescriptor(
- Document.prototype,
- "body"
- ).get
- const nodeAppendChild = Node.prototype.appendChild
-
- const overlay = document.createElement("div")
- overlay.style.cssText = "position:absolute;left:0;top:0"
-
- const addOverlay = () => {
- shadowRootHost = apply(documentGetBody, document, [])
- const shadowRoot = apply(original_attachShadow, shadowRootHost, [
- { mode: "closed" },
- ])
- apply(shadowRootSetInnerHtml, shadowRoot, [
- `
`,
- ])
- shadowRootNewHost = apply(
- documentFragmentGetChildren,
- shadowRoot,
- []
- )[0]
- apply(nodeAppendChild, shadowRoot, [overlay])
- }
-
- if (!document.body) {
- document.addEventListener("DOMContentLoaded", addOverlay)
- } else {
- addOverlay()
- }
- return overlay
+ toremove = this.NormalizeSpaces(toremove)
+ while (toremove.includes(' ')) {
+ toremove = toremove.replace(/ {2}/g, ' ')
+ }
+ return toremove.trim()
}
- const overlay = StealthOverlay()
+ RemoveSpecialChars(value) {
+ assert(value)
- function appendBelowElement(el, toAppend) {
- const rect = el.getBoundingClientRect()
- const left = rect.left + window.scrollX
- const top = rect.top + window.scrollY
-
- SetStyle(toAppend, {
- position: "absolute",
- zIndex: 999999,
- top: top + "px",
- left: left + "px",
- })
-
- overlay.appendChild(toAppend)
+ return this.RemoveStuff(value, specialChars, ' ')
}
- function createHoverOver(target) {
- const overlayElement = document.createElement("div")
- overlayElement.style.cssText =
- "position:fixed; pointer-events: none; user-select: none; z-index:10000"
- overlay.append(overlayElement)
- let currX, currY, currWidth, currHeight
- const copyBoundingRect = () => {
- let { x, y, width, height } = target.getBoundingClientRect()
- if (x !== currX) {
- overlayElement.style.left = x + "px"
- currX = x
- }
- if (y !== currY) {
- overlayElement.style.top = y + "px"
- currY = y
- }
- if (width !== currWidth) {
- overlayElement.style.width = width + "px"
- currWidth = width
- }
- if (height !== currHeight) {
- overlayElement.style.height = height + "px"
- currHeight = height
- }
- }
- copyBoundingRect()
- const interval = setInterval(copyBoundingRect, 30)
- overlayElement.destroy = () => {
- clearInterval(interval)
- overlayElement.remove()
- }
- return overlayElement
+ // if the value is empty, or whitespace
+ EmptyOrWhiteSpace(value) {
+ // replaces /n-s with "". then replaces spaces with "". if it equals "", then its empty, or only consists of white space
+ if (value === undefined) {
+ return true
+ }
+ return (
+ value
+ .replace(/\n/g, '')
+ .replace(/ /g, '')
+ .replace(/\s/g, ' ') === ''
+ )
}
- 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
- }
+ // damn nonbreaking space
+ NormalizeSpaces(input) {
+ assert(input)
- GetAllQuestionsQtext() {
- if (logElementGetting) {
- Log("getting all questions qtext")
- }
- return document
- .getElementById("responseform")
- .getElementsByClassName("qtext") // getting questions
- }
-
- GetAllQuestionsP() {
- if (logElementGetting) {
- Log("getting all questions by tag p")
- }
- return document
- .getElementById("responseform")
- .getElementsByTagName("p")
- }
-
- GetFormulationClearfix() {
- if (logElementGetting) {
- Log("getting formulation clearfix lol")
- }
- return document.getElementsByClassName("formulation clearfix")
- }
-
- GetAnswerOptions() {
- if (logElementGetting) {
- Log("getting all answer options")
- }
- return this.GetFormulationClearfix()[0].childNodes[3].innerText
- }
-
- GetQuestionImages() {
- if (logElementGetting) {
- Log("getting question images")
- }
- return this.GetFormulationClearfix()[0].getElementsByTagName("img")
- }
-
- // this function should return the question, posible answers, and image names
- GetQuestionFromTest() {
- var questions // the important questions
- var allQuestions // all questions
- try {
- allQuestions = this.GetAllQuestionsQtext() // getting questions
- if (allQuestions.length === 0) {
- var ddq = this.GetAllQuestionsDropdown()
- if (SUtils.EmptyOrWhiteSpace(ddq)) {
- var questionData = ""
- for (var j = 0; j < allQuestions.length; j++) {
- let subAllQuestions = allQuestions[j].childNodes
- for (let i = 0; i < subAllQuestions.length; i++) {
- if (
- subAllQuestions[i].data !== undefined &&
- !SUtils.EmptyOrWhiteSpace(
- subAllQuestions[i].data
- )
- ) {
- questionData +=
- subAllQuestions[i].data + " " // adding text to question data
- }
- }
- }
- questions = [questionData]
- } else {
- questions = [ddq]
- }
- } else {
- questions = []
- for (let i = 0; i < allQuestions.length; i++) {
- questions.push(allQuestions[i].innerText)
- }
- }
- } catch (e) {
- Exception(e, "script error at getting question:")
- }
- var imgNodes = "" // the image nodes for questions
- try {
- imgNodes = this.GetQuestionImages() // getting question images, if there is any
- AddImageNamesToImages(imgNodes) // adding image names to images, so its easier to search for, or even guessing
- } catch (e) {
- Log(e)
- Log("Some error with images")
- }
-
- questions = questions.map(item => {
- if (item) {
- return SUtils.ReplaceCharsWithSpace(item, "\n")
- }
- })
-
- return {
- imgnodes: imgNodes,
- allQ: allQuestions,
- q: questions,
- }
- }
+ return input.replace(/\s/g, ' ')
}
- class ResultsPageModell {
- GetFormulationClearfix() {
- if (logElementGetting) {
- Log("getting formulation clearfix lol")
- }
- return document.getElementsByClassName("formulation clearfix")
+ SimplifyStack(stack) {
+ return this.SimplifyQuery(stack)
+ }
+ }
+
+ const SUtils = new StringUtils()
+
+ // : }}}
+
+ // : DOM getting stuff {{{
+ // all dom getting stuff are in this sections, so on
+ // moodle dom change, stuff breaks here
+
+ //Stealth by An0 with love
+ function StealthOverlay() {
+ //call this before the document scripts
+ const document = window.document
+
+ const neverEqualPlaceholder = Symbol(`never equal`) //block probing for undefined values in the hooks
+ let shadowRootHost = neverEqualPlaceholder
+ let shadowRootNewHost = neverEqualPlaceholder
+
+ const apply = Reflect.apply //save some things in case they get hooked (only for unsafe contexts)
+
+ if (usf.Error.hasOwnProperty('stackTraceLimit')) {
+ Reflect.defineProperty(usf.Error, 'stackTraceLimit', {
+ value: undefined,
+ writable: false,
+ enumerable: false,
+ configurable: false,
+ })
+ }
+
+ const shadowGetHandler = {
+ apply: (target, thisArg, argumentsList) =>
+ apply(
+ target,
+ thisArg === shadowRootHost ? shadowRootNewHost : thisArg,
+ argumentsList
+ ),
+ }
+
+ const original_attachShadow = usf.Element.prototype.attachShadow
+ const attachShadowProxy = new Proxy(original_attachShadow, shadowGetHandler)
+ usf.Element.prototype.attachShadow = attachShadowProxy
+
+ const getShadowRootProxy = new Proxy(
+ Object.getOwnPropertyDescriptor(usf.Element.prototype, 'shadowRoot').get,
+ shadowGetHandler
+ )
+ Object.defineProperty(usf.Element.prototype, 'shadowRoot', {
+ get: getShadowRootProxy,
+ })
+
+ const getHostHandler = {
+ apply: function() {
+ let result = apply(...arguments)
+ return result === shadowRootNewHost ? shadowRootHost : result
+ },
+ }
+ const getHostProxy = new Proxy(
+ Object.getOwnPropertyDescriptor(usf.ShadowRoot.prototype, 'host').get,
+ getHostHandler
+ )
+ Object.defineProperty(usf.ShadowRoot.prototype, 'host', {
+ get: getHostProxy,
+ })
+
+ const shadowRootSetInnerHtml = Object.getOwnPropertyDescriptor(
+ ShadowRoot.prototype,
+ 'innerHTML'
+ ).set
+ const documentFragmentGetChildren = Object.getOwnPropertyDescriptor(
+ DocumentFragment.prototype,
+ 'children'
+ ).get
+ const documentGetBody = Object.getOwnPropertyDescriptor(
+ Document.prototype,
+ 'body'
+ ).get
+ const nodeAppendChild = Node.prototype.appendChild
+
+ const overlay = document.createElement('div')
+ overlay.style.cssText = 'position:absolute;left:0;top:0'
+
+ const addOverlay = () => {
+ shadowRootHost = apply(documentGetBody, document, [])
+ const shadowRoot = apply(original_attachShadow, shadowRootHost, [
+ { mode: 'closed' },
+ ])
+ apply(shadowRootSetInnerHtml, shadowRoot, [`
`])
+ shadowRootNewHost = apply(documentFragmentGetChildren, shadowRoot, [])[0]
+ apply(nodeAppendChild, shadowRoot, [overlay])
+ }
+
+ if (!document.body) {
+ document.addEventListener('DOMContentLoaded', addOverlay)
+ } else {
+ addOverlay()
+ }
+ return overlay
+ }
+
+ const overlay = StealthOverlay()
+
+ function appendBelowElement(el, toAppend) {
+ const rect = el.getBoundingClientRect()
+ const left = rect.left + window.scrollX
+ const top = rect.top + window.scrollY
+
+ SetStyle(toAppend, {
+ position: 'absolute',
+ zIndex: 999999,
+ top: top + 'px',
+ left: left + 'px',
+ })
+
+ overlay.appendChild(toAppend)
+ }
+
+ function createHoverOver(target) {
+ const overlayElement = document.createElement('div')
+ overlayElement.style.cssText =
+ 'position:fixed; pointer-events: none; user-select: none; z-index:10000'
+ overlay.append(overlayElement)
+ let currX, currY, currWidth, currHeight
+ const copyBoundingRect = () => {
+ let { x, y, width, height } = target.getBoundingClientRect()
+ if (x !== currX) {
+ overlayElement.style.left = x + 'px'
+ currX = x
+ }
+ if (y !== currY) {
+ overlayElement.style.top = y + 'px'
+ currY = y
+ }
+ if (width !== currWidth) {
+ overlayElement.style.width = width + 'px'
+ currWidth = width
+ }
+ if (height !== currHeight) {
+ overlayElement.style.height = height + 'px'
+ currHeight = height
+ }
+ }
+ copyBoundingRect()
+ const interval = setInterval(copyBoundingRect, 30)
+ overlayElement.destroy = () => {
+ clearInterval(interval)
+ overlayElement.remove()
+ }
+ return overlayElement
+ }
+
+ 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
+ }
- GetGrade(i) {
- if (logElementGetting) {
- Log("getting grade")
- }
- const fcf = QPM.GetFormulationClearfix()[i]
- return fcf.parentNode.parentNode.childNodes[0].childNodes[2]
- .innerText
- }
+ GetAllQuestionsQtext() {
+ if (logElementGetting) {
+ Log('getting all questions qtext')
+ }
+ return document
+ .getElementById('responseform')
+ .getElementsByClassName('qtext') // getting questions
+ }
- DetermineQuestionType(nodes) {
- let qtype = ""
- let i = 0
+ GetAllQuestionsP() {
+ if (logElementGetting) {
+ Log('getting all questions by tag p')
+ }
+ return document.getElementById('responseform').getElementsByTagName('p')
+ }
- while (i < nodes.length && qtype === "") {
- let inps = nodes[i].getElementsByTagName("input")
+ GetFormulationClearfix() {
+ if (logElementGetting) {
+ Log('getting formulation clearfix lol')
+ }
+ return document.getElementsByClassName('formulation clearfix')
+ }
- if (inps.length > 0) {
- qtype = inps[0].type
- }
+ GetAnswerOptions() {
+ if (logElementGetting) {
+ Log('getting all answer options')
+ }
+ return this.GetFormulationClearfix()[0].childNodes[3].innerText
+ }
- i++
- }
+ GetQuestionImages() {
+ if (logElementGetting) {
+ Log('getting question images')
+ }
+ return this.GetFormulationClearfix()[0].getElementsByTagName('img')
+ }
- return qtype
- }
-
- GetSelectAnswer(i) {
- if (logElementGetting) {
- Log("getting selected answer")
- }
- var t = document.getElementsByTagName("select")
- if (t.length > 0) {
- return t[i].options[t[i].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++) {
+ // this function should return the question, posible answers, and image names
+ GetQuestionFromTest() {
+ var questions // the important questions
+ var allQuestions // all questions
+ try {
+ allQuestions = this.GetAllQuestionsQtext() // getting questions
+ if (allQuestions.length === 0) {
+ var ddq = this.GetAllQuestionsDropdown()
+ if (SUtils.EmptyOrWhiteSpace(ddq)) {
+ var questionData = ''
+ for (var j = 0; j < allQuestions.length; j++) {
+ let subAllQuestions = allQuestions[j].childNodes
+ for (let i = 0; i < subAllQuestions.length; i++) {
if (
- r[j].tagName !== undefined &&
- r[j].tagName.toLowerCase() === "div"
+ subAllQuestions[i].data !== undefined &&
+ !SUtils.EmptyOrWhiteSpace(subAllQuestions[i].data)
) {
- ret.push(r[j])
+ questionData += subAllQuestions[i].data + ' ' // adding text to question data
}
+ }
}
-
- let qtype = this.DetermineQuestionType(ret)
-
- return {
- nodes: ret,
- type: qtype,
- }
- }
-
- GetCurrentAnswer(i) {
- if (logElementGetting) {
- Log("getting curr answer by index: " + i)
- }
- var results = this.GetFormResult() // getting results element
- var t = results[i]
- .getElementsByClassName("formulation clearfix")[0]
- .getElementsByTagName("span")
- if (t.length > 2) {
- return t[1].innerHTML.split("
")[1]
- }
- }
-
- GetQText(i) {
- if (logElementGetting) {
- Log("getting qtext by index: " + i)
- }
- var results = this.GetFormResult() // getting results element
- return results[i].getElementsByClassName("qtext")
- }
-
- GetDropboxes(i) {
- if (logElementGetting) {
- Log("getting dropboxes by index: " + i)
- }
- var results = this.GetFormResult() // getting results element
- return results[i].getElementsByTagName("select")
- }
-
- GetAllAnswer(index) {
- if (logElementGetting) {
- Log("getting all answers, ind: " + index)
- }
- return document.getElementsByClassName("answer")[index].childNodes
- }
-
- GetPossibleAnswers(i) {
- if (logElementGetting) {
- Log("getting possible answers")
- }
- var results = this.GetFormResult() // getting results element
- var items = results[i].getElementsByTagName("label")
- var r = []
- for (var j = 0; j < items.length; j++) {
- const TryGetCorrect = j => {
- var cn = items[j].parentNode.className
- if (cn.includes("correct")) {
- return (
- cn.includes("correct") && !cn.includes("incorrect")
- )
- }
- }
- r.push({
- value: items[j].innerText,
- iscorrect: TryGetCorrect(j),
- })
- }
- return r
- }
-
- GetAnswersFromGrabBox(i) {
- try {
- if (logElementGetting) {
- Log("testing if question is grab-box")
- }
- let results = this.GetFormResult() // getting results element
- let t = results[i].getElementsByClassName("dragitems")[0]
- .childNodes
- if (t.length !== 1) {
- Log("grab box drag items group length is not 1!")
- Log(results[i].getElementsByClassName("dragitems")[0])
- }
- let placedItems = t[0].getElementsByClassName("placed")
- let res = []
- for (let i = 0; i < placedItems.length; i++) {
- let item = placedItems[i]
- res.push({
- text: item.innerText,
- left: item.style.left,
- top: item.style.top,
- })
- }
- return res
- } catch (e) {
- console.info(e)
- }
- }
-
- GetRightAnswerIfCorrectShown(i) {
- if (logElementGetting) {
- Log("getting right answer if correct shown")
- }
- var results = this.GetFormResult() // getting results element
- return results[i].getElementsByClassName("rightanswer")
- }
-
- GetWrongAnswerIfCorrectNotShown(i) {
- if (logElementGetting) {
- Log("getting wrong answer if correct not shown")
- }
- var results = this.GetFormResult() // getting results element
- var n = results[i].getElementsByTagName("i")[0].parentNode
- if (n.className.includes("incorrect")) {
- return results[i].getElementsByTagName("i")[0].parentNode
- .innerText
- } else {
- return ""
- }
- }
-
- GetRightAnswerIfCorrectNotShown(i) {
- if (logElementGetting) {
- Log("Getting right answer if correct not shown")
- }
- var results = this.GetFormResult() // getting results element
- var n = results[i].getElementsByTagName("i")[0].parentNode
- if (
- n.className.includes("correct") &&
- !n.className.includes("incorrect")
- ) {
- return results[i].getElementsByTagName("i")[0].parentNode
- .innerText
- }
- }
-
- GetFormCFOfResult(result) {
- if (logElementGetting) {
- Log("getting formulation clearfix")
- }
- return result.getElementsByClassName("formulation clearfix")[0]
- }
-
- GetResultText(i) {
- if (logElementGetting) {
- Log("getting result text")
- }
- var results = this.GetFormResult() // getting results element
- return this.GetFormCFOfResult(results[i]).getElementsByTagName("p")
- }
-
- GetResultImage(i) {
- if (logElementGetting) {
- Log("getting result image")
- }
- var results = this.GetFormResult() // getting results element
- return this.GetFormCFOfResult(results[i]).getElementsByTagName(
- "img"
- )
- }
-
- // gets the question from the result page
- // i is the index of the question
- GetQuestionFromResult(i) {
- var temp = this.GetQText(i)
- var currQuestion = ""
- if (temp.length > 0) {
- currQuestion = temp[0].innerText // adding the question to curr question as .q
- } else {
- // this is black magic fuckery a bit
- if (this.GetDropboxes(i).length > 0) {
- var allNodes = this.GetResultText(i)
- currQuestion = ""
- for (var k = 0; k < allNodes.length; k++) {
- var allQuestions = this.GetResultText(i)[k].childNodes
- for (var j = 0; j < allQuestions.length; j++) {
- if (
- allQuestions[j].data !== undefined &&
- !SUtils.EmptyOrWhiteSpace(allQuestions[j].data)
- ) {
- currQuestion += allQuestions[j].data + " "
- }
- }
- }
- } else {
- try {
- currQuestion = this.GetCurrQuestion(i)
- } catch (e) {
- currQuestion = "REEEEEEEEEEEEEEEEEEEEE" // this shouldnt really happen sry guys
- Log("Unable to get question in GetQuestionFromResult")
- }
- }
- }
- return currQuestion
- }
-
- // tries to get right answer from result page
- // i is the index of the question
- GetRightAnswerFromResult(i) {
- var fun = []
-
- // "húzza oda ..." skip
- fun.push(i => {
- let temp = RPM.GetAnswersFromGrabBox(i)
- return temp
- .map(x => {
- return x.text
- })
- .join(", ")
- })
-
- // the basic type of getting answers
- fun.push(i => {
- var temp = RPM.GetRightAnswerIfCorrectShown(i) // getting risht answer
- if (temp.length > 0) {
- return temp[0].innerText
- } // adding the answer to curr question as .a
- })
-
- // if there is dropdown list in the current question
- fun.push(i => {
- if (RPM.GetDropboxes(i).length > 0) {
- return RPM.GetCurrentAnswer(i)
- }
- })
-
- // if the correct answers are not shown, and the selected answer
- // is correct
- fun.push(i => {
- return RPM.GetRightAnswerIfCorrectNotShown(i)
- })
-
- // if there is dropbox in the question
- fun.push(i => {
- return RPM.GetSelectAnswer(i)
- })
-
- // if the correct answers are not shown, and the selected answer
- // is incorrect, and there are only 2 options
- fun.push(i => {
- var possibleAnswers = RPM.GetPossibleAnswers(i)
- if (possibleAnswers.length === 2) {
- for (var k = 0; k < possibleAnswers.length; k++) {
- if (possibleAnswers[k].iscorrect === undefined) {
- return possibleAnswers[k].value
- }
- }
- }
- })
-
- // if everything fails
- fun.push(i => {
- return undefined
- })
-
- var j = 0
- var currAnswer
- while (j < fun.length && SUtils.EmptyOrWhiteSpace(currAnswer)) {
- try {
- currAnswer = fun[j](i)
- } catch (e) {
- console.info(e)
- }
- j++
- }
-
- return currAnswer
- }
-
- GuessCorrectIn2LengthAnswersByIncorrect(items) {
- const first = items[0]
- const second = items[1]
- if (first.className.includes("incorrect")) {
- return second.innerText
- }
- if (second.className.includes("incorrect")) {
- return first.innerText
- }
- }
-
- GuessCorrectIn2LengthAnswersByPoints(i, items) {
- const first = {
- elem: items[0],
- val: items[0].childNodes[0].checked,
- text: items[0].innerText,
- }
- const second = {
- elem: items[1],
- val: items[1].childNodes[0].checked,
- text: items[1].innerText,
- }
-
- const grade = RPM.GetGrade(i) // 1,00 közül 1,00 leosztályozva
- const grades = grade.split(" ").reduce((acc, text) => {
- if (text.includes(",")) {
- // FIXME: fancy regexp
- acc.push(parseInt(text))
- } else if (text.includes(".")) {
- // FIXME: fancy regexp
- acc.push(parseInt(text))
- }
- return acc
- }, [])
-
- if (grades[0] === 1) {
- if (first.val) {
- return first.text
- } else {
- return second.text
- }
- } else {
- if (!first.val) {
- return first.text
- } else {
- return second.text
- }
- }
- }
-
- // version 2 of getting right answer from result page
- // i is the index of the question
- GetRightAnswerFromResultv2(i) {
- try {
- var answerNodes = this.GetAnswerNode(i)
- let items = answerNodes.nodes
-
- if (answerNodes.type === "checkbox") {
- return RPM.GetRightAnswerFromResult(i)
- }
-
- for (let j = 0; j < items.length; j++) {
- let cn = items[j].className
- if (cn.includes("correct") && !cn.includes("incorrect")) {
- return items[j].getElementsByTagName("label")[0]
- .innerText
- }
- }
- if (items.length === 2) {
- const resByIncorrect = this.GuessCorrectIn2LengthAnswersByIncorrect(
- items
- )
- if (!resByIncorrect) {
- const resPoints = this.GuessCorrectIn2LengthAnswersByPoints(
- i,
- items
- )
- return resPoints
- }
- return resByIncorrect
- }
- } 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 {{{
- let timerStarted = false
-
- // window.addEventListener("load", () => {})
- Main()
-
- function Main() {
- "use strict"
- console.log("Moodle / E-Learning script")
- console.time("main")
- timerStarted = true
-
- if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", Init)
+ questions = [questionData]
+ } else {
+ questions = [ddq]
+ }
} else {
- Init()
+ questions = []
+ for (let i = 0; i < allQuestions.length; i++) {
+ questions.push(allQuestions[i].innerText)
+ }
}
+ } catch (e) {
+ Exception(e, 'script error at getting question:')
+ }
+ var imgNodes = '' // the image nodes for questions
+ try {
+ imgNodes = this.GetQuestionImages() // getting question images, if there is any
+ AddImageNamesToImages(imgNodes) // adding image names to images, so its easier to search for, or even guessing
+ } catch (e) {
+ Log(e)
+ Log('Some error with images')
+ }
+
+ questions = questions.map(item => {
+ if (item) {
+ return SUtils.ReplaceCharsWithSpace(item, '\n')
+ }
+ })
+
+ return {
+ imgnodes: imgNodes,
+ allQ: allQuestions,
+ q: questions,
+ }
+ }
+ }
+
+ class ResultsPageModell {
+ GetFormulationClearfix() {
+ if (logElementGetting) {
+ Log('getting formulation clearfix lol')
+ }
+ return document.getElementsByClassName('formulation clearfix')
}
- function AfterLoad() {
- const url = location.href // eslint-disable-line
-
- 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)
- }
- } catch (e) {
- ShowMessage(
- {
- m: texts.fatalError,
- isSimple: true,
- },
- undefined,
- () => {
- OpenErrorPage(e)
- }
- )
-
- Exception(e, "script error at main:")
- }
- if (url.includes("eduplayer")) {
- AddVideoHotkeys(url)
- } // adding video hotkeys
- Log(texts.consoleErrorInfo)
-
- if (timerStarted) {
- console.log("Moodle Test Script run time:")
- console.timeEnd("main")
- timerStarted = false
- }
-
- if (forceTestPage || forceResultPage || forceDefaultPage) {
- if (overlay.querySelector("#scriptMessage")) {
- overlay.querySelector("#scriptMessage").style.background =
- "green"
- }
- }
- }
- // : }}}
-
- // : Main logic stuff {{{
-
- // : Loading {{{
- function HandleQminingSite(url) {
- try {
- const idInput = document.getElementById("cid")
- if (idInput) {
- idInput.value = getVal("clientId")
- }
- } catch (e) {
- console.info("Error filling client ID input", e)
- }
- try {
- const sideLinks = document.getElementById("sideBarLinks")
- if (!sideLinks) {
- return
- }
- Array.from(sideLinks.childNodes).forEach(link => {
- link.addEventListener("mousedown", () => {
- FillFeedbackCID(url, link)
- })
- })
-
- FillFeedbackCID(
- url,
- document
- .getElementById("sideBarLinks")
- .getElementsByClassName("active")[0]
- )
- } catch (e) {
- console.info("Error filling client ID input", e)
- }
+ GetGrade(i) {
+ if (logElementGetting) {
+ Log('getting grade')
+ }
+ const fcf = QPM.GetFormulationClearfix()[i]
+ return fcf.parentNode.parentNode.childNodes[0].childNodes[2].innerText
}
- function FillFeedbackCID(url, link) {
- try {
- if (link.id === "feedback") {
- const cidSetInterval = setInterval(() => {
- const cid = document.getElementById("cid")
- if (cid) {
- cid.value = GetId() + "|" + info().script.version
- window.clearInterval(cidSetInterval)
- }
- }, 100)
- }
- } catch (e) {
- console.info("Error filling client ID input", e)
+ 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 Init() {
- const url = location.href // eslint-disable-line
-
- if (url.includes(serverAdress.split("/")[2])) {
- HandleQminingSite(url)
- return
- }
-
- // if (false) {
- // // eslint-disable-line
- // setVal("version16", undefined);
- // setVal("version15", undefined);
- // setVal("firstRun", undefined);
- // setVal("showQuestions", undefined);
- // setVal("showSplash", undefined);
- // }
- // --------------------------------------------------------------------------------------
- // 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()
- if (!url.includes(".pdf")) {
- ShowMenu()
- }
- ConnectToServer(AfterLoad)
+ GetSelectAnswer(i) {
+ if (logElementGetting) {
+ Log('getting selected answer')
+ }
+ var t = document.getElementsByTagName('select')
+ if (t.length > 0) {
+ return t[i].options[t[i].selectedIndex].innerText
+ }
}
- function Auth(pw) {
- SendXHRMessage("login", { pw: pw, script: true }).then(res => {
- if (res.result === "success") {
- ConnectToServer(AfterLoad)
- ClearAllMessages()
- resetMenu()
- } else {
- SafeGetElementById("infoMainDiv", elem => {
- elem.innerText = texts.invalidPW + pw
- })
- }
+ GetCurrQuestion(i) {
+ if (logElementGetting) {
+ Log('getting curr questions by index: ' + i)
+ }
+ return document.getElementsByTagName('form')[0].childNodes[0].childNodes[
+ i
+ ].childNodes[1].childNodes[0].innerText
+ }
+
+ GetFormResult() {
+ if (logElementGetting) {
+ Log('getting form result')
+ }
+ var t = document.getElementsByTagName('form')[0].childNodes[0].childNodes
+ if (t.length > 0 && t[0].tagName === undefined) {
+ // debreceni moodle
+ return document.getElementsByTagName('form')[1].childNodes[0].childNodes
+ } else {
+ return t
+ }
+ }
+
+ GetAnswerNode(i) {
+ if (logElementGetting) {
+ Log('getting answer node')
+ }
+
+ var results = this.GetFormResult() // getting results element
+
+ var r = results[i].getElementsByClassName('answer')[0].childNodes
+ var ret = []
+ for (var j = 0; j < r.length; j++) {
+ if (
+ r[j].tagName !== undefined &&
+ r[j].tagName.toLowerCase() === 'div'
+ ) {
+ ret.push(r[j])
+ }
+ }
+
+ let qtype = this.DetermineQuestionType(ret)
+
+ return {
+ nodes: ret,
+ type: qtype,
+ }
+ }
+
+ GetCurrentAnswer(i) {
+ if (logElementGetting) {
+ Log('getting curr answer by index: ' + i)
+ }
+ var results = this.GetFormResult() // getting results element
+ var t = results[i]
+ .getElementsByClassName('formulation clearfix')[0]
+ .getElementsByTagName('span')
+ if (t.length > 2) {
+ return t[1].innerHTML.split('
')[1]
+ }
+ }
+
+ GetQText(i) {
+ if (logElementGetting) {
+ Log('getting qtext by index: ' + i)
+ }
+ var results = this.GetFormResult() // getting results element
+ return results[i].getElementsByClassName('qtext')
+ }
+
+ GetDropboxes(i) {
+ if (logElementGetting) {
+ Log('getting dropboxes by index: ' + i)
+ }
+ var results = this.GetFormResult() // getting results element
+ return results[i].getElementsByTagName('select')
+ }
+
+ GetAllAnswer(index) {
+ if (logElementGetting) {
+ Log('getting all answers, ind: ' + index)
+ }
+ return document.getElementsByClassName('answer')[index].childNodes
+ }
+
+ GetPossibleAnswers(i) {
+ if (logElementGetting) {
+ Log('getting possible answers')
+ }
+ var results = this.GetFormResult() // getting results element
+ var items = results[i].getElementsByTagName('label')
+ var r = []
+ for (var j = 0; j < items.length; j++) {
+ const TryGetCorrect = j => {
+ var cn = items[j].parentNode.className
+ if (cn.includes('correct')) {
+ return cn.includes('correct') && !cn.includes('incorrect')
+ }
+ }
+ r.push({
+ value: items[j].innerText,
+ iscorrect: TryGetCorrect(j),
})
+ }
+ return r
}
- function resetMenu() {
- SafeGetElementById("menuButtonDiv", elem => {
- elem.style.backgroundColor = "#262626"
- })
- SafeGetElementById("ircButton", elem => {
- elem.style.display = "none"
- })
- SafeGetElementById("retryButton", elem => {
- elem.style.display = "none"
- })
- SafeGetElementById("loginDiv", elem => {
- elem.style.display = "none"
- })
- SafeGetElementById("infoMainDiv", elem => {
- elem.innerText = texts.loading
- })
- }
-
- function ConnectToServer(cwith) {
- ClearAllMessages()
- GetXHRInfos()
- .then(inf => {
- if (inf.result === "nouser") {
- NoUserAction()
- return
- }
- lastestVersion = inf.version
- motd = inf.motd
- subjInfo = inf.subjinfo
- overlay.querySelector(
- "#infoMainDiv"
- ).innerText = `${subjInfo.subjects} tárgy, ${subjInfo.questions} kérdés. Felh #${inf.uid}`
- // FIXME: if cwith() throws an unhandled error it sais server is not avaible
- cwith()
- })
- .catch(() => {
- NoServerAction()
- })
- }
-
- function NoUserAction() {
- SafeGetElementById("menuButtonDiv", elem => {
- elem.style.backgroundColor = "#44cc00"
- })
- SafeGetElementById("infoMainDiv", elem => {
- elem.innerText = texts.noUser
- if (getVal("clientId")) {
- elem.innerText += ` (${getVal("clientId")})`
- }
- })
- SafeGetElementById("loginDiv", elem => {
- elem.style.display = ""
- })
- }
-
- function NoServerAction() {
- SafeGetElementById("menuButtonDiv", elem => {
- elem.style.backgroundColor = "red"
- })
- SafeGetElementById("infoMainDiv", elem => {
- elem.innerText = texts.noServer
- })
- SafeGetElementById("ircButton", elem => {
- elem.style.display = ""
- })
- SafeGetElementById("retryButton", elem => {
- elem.style.display = ""
- })
- Log(texts.noServerConsoleMessage)
- }
-
- function VersionActions() {
- // FOR TESTING ONLY
- // setVal("version15", true);
- // setVal("firstRun", true);
- // setVal("version16", true);
- // throw "asd";
-
- FreshStart()
- }
-
- // : Version action functions {{{
-
- function FreshStart() {
- var firstRun = getVal("firstRun") // if the current run is the frst
- if (firstRun === undefined || firstRun === true) {
- setVal("firstRun", false)
- ShowHelp() // showing help
-
- document.write(texts.freshStartWarning)
- document.close()
- throw new Error("something, so this stuff stops")
+ GetAnswersFromGrabBox(i) {
+ try {
+ if (logElementGetting) {
+ Log('testing if question is grab-box')
}
+ let results = this.GetFormResult() // getting results element
+ let t = results[i].getElementsByClassName('dragitems')[0].childNodes
+ if (t.length !== 1) {
+ Log('grab box drag items group length is not 1!')
+ Log(results[i].getElementsByClassName('dragitems')[0])
+ }
+ let placedItems = t[0].getElementsByClassName('placed')
+ let res = []
+ for (let i = 0; i < placedItems.length; i++) {
+ let item = placedItems[i]
+ res.push({
+ text: item.innerText,
+ left: item.style.left,
+ top: item.style.top,
+ })
+ }
+ return res
+ } catch (e) {
+ console.info(e)
+ }
}
- // : }}}
-
- // : UI handling {{{
- function HandleUI(url) {
- // FIXME: normal string building with localisation :/
- var newVersion = false // if the script is newer than last start
-
- try {
- newVersion = info().script.version !== getVal("lastVerson")
- } catch (e) {
- Log("Some weird error trying to set new verison")
- }
-
- let showMOTD = false
- if (!SUtils.EmptyOrWhiteSpace(motd)) {
- var prevmotd = getVal("motd")
- if (prevmotd !== motd) {
- showMOTD = true
- setVal("motdcount", motdShowCount)
- setVal("motd", motd)
- } else {
- var motdcount = getVal("motdcount")
- if (motdcount === undefined) {
- setVal("motdcount", motdShowCount)
- motdcount = motdShowCount
- }
-
- motdcount--
- if (motdcount > 0) {
- showMOTD = true
- setVal("motdcount", motdcount)
- }
- }
- }
- let isNewVersionAvaible =
- lastestVersion !== undefined &&
- info().script.version !== lastestVersion
- var greetMsg = "" // message to show at the end
- var timeout = null // the timeout. if null, it wont be hidden
-
- if (isNewVersionAvaible || newVersion || showMOTD) {
- greetMsg =
- "Moodle/Elearning/KMOOC segéd v. " +
- info().script.version +
- ". "
- }
- if (isNewVersionAvaible) {
- timeout = 5
- greetMsg += "Új verzió elérhető: " + lastestVersion
- timeout = undefined
- }
- if (newVersion) {
- // --------------------------------------------------------------------------------------------------------------
- greetMsg +=
- "Verzió frissítve " +
- info().script.version +
- "-re. Changelog:\n" +
- texts.lastChangeLog
- setVal("lastVerson", info().script.version) // setting lastVersion
- }
- if (showMOTD) {
- greetMsg += "\nMOTD:\n" + motd
- timeout = null
- }
-
- ShowMessage(
- {
- m: greetMsg,
- isSimple: true,
- },
- timeout
- ) // showing message. If "m" is empty it wont show it, thats how showSplash works.
+ GetRightAnswerIfCorrectShown(i) {
+ if (logElementGetting) {
+ Log('getting right answer if correct shown')
+ }
+ var results = this.GetFormResult() // getting results element
+ return results[i].getElementsByClassName('rightanswer')
}
- // : }}}
-
- // : Answering stuffs {{{
-
- function HandleQuiz() {
- var q = QPM.GetQuestionFromTest()
- var questions = q.q
- var imgNodes = q.imgnodes
- // ------------------------------------------------------------------------------------------------------
- let promises = []
- questions.forEach(x => {
- let question = SUtils.EmptyOrWhiteSpace(x)
- ? ""
- : SUtils.RemoveUnnecesarySpaces(x) // simplifying question
- promises.push(
- GetXHRQuestionAnswer({
- q: question,
- data: GetImageDataFromImgNodes(imgNodes),
- subj: MPM.GetCurrentSubjectName(),
- })
- )
- })
-
- // FIXME: promise.all promise resolve order same as original?
- Promise.all(promises).then(res => {
- let answers = []
-
- res.forEach((result, j) => {
- var r = PrepareAnswers(result, j)
- if (r !== undefined) {
- answers.push(r)
- }
- HighLightAnswer(result, j) // highlights the answer for the current result
- })
-
- ShowAnswers(answers, q.q)
- })
+ 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 ''
+ }
}
- function PrepareAnswers(result, j) {
- assert(result)
-
- if (result.length > 0) {
- var allMessages = [] // preparing all messages
- for (var k = 0; k < result.length; k++) {
- var msg = "" // the current message
- if (
- getVal("showQuestions") === undefined ||
- getVal("showQuestions")
- ) {
- msg += result[k].q.Q + "\n" // adding the question if yes
- }
- msg += result[k].q.A.replace(/, /g, "\n") // adding answer
- if (result[k].q.data.type === "image") {
- msg +=
- "\n\nKépek fenti válaszok sorrendjében: " +
- result[k].q.data.images.join(", ") // if it has image part, adding that too
- }
- if (
- result[k].detailedMatch &&
- result[k].detailedMatch.matchedSubjName
- ) {
- msg +=
- "\n(Tárgy: " +
- result[k].detailedMatch.matchedSubjName +
- ")"
- }
- allMessages.push({
- m: msg,
- p: result[k].match,
- })
- }
- return allMessages
- }
+ 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
+ }
}
- function ShowAnswers(answers, question) {
- assert(answers)
-
- if (answers.length > 0) {
- // if there are more than 0 answer
- ShowMessage(answers)
- } else {
- ShowMessage(
- {
- m: texts.noResult,
- isSimple: true,
- },
- undefined,
- function() {
- OpenErrorPage({
- message: "No result found",
- stack: JSON.stringify(question),
- })
- }
- )
- }
+ GetFormCFOfResult(result) {
+ if (logElementGetting) {
+ Log('getting formulation clearfix')
+ }
+ return result.getElementsByClassName('formulation clearfix')[0]
}
- // : }}}
-
- // : Quiz saving {{{
-
- function HandleResults(url) {
- SaveQuiz(GetQuiz(), ShowSaveQuizDialog) // saves the quiz questions and answers
+ GetResultText(i) {
+ if (logElementGetting) {
+ Log('getting result text')
+ }
+ var results = this.GetFormResult() // getting results element
+ return this.GetFormCFOfResult(results[i]).getElementsByTagName('p')
}
- function ShowSaveQuizDialog(sendResult, sentData, newQuestions) {
- // FIXME: normal string building with localisation :/
- var msg = ""
- if (sendResult) {
- msg = "Kérdések elküldve, katt az elküldött adatokért."
- if (newQuestions > 0) {
- msg += " " + newQuestions + " új kérdés"
- } else {
- msg += " Nincs új kérdés"
- }
- } else {
- msg =
- "Szerver nem elérhető, vagy egyéb hiba kérdések elküldésénél! (F12 -> Console)"
- }
- // showing a message wit the click event, and the generated page
- ShowMessage(
- {
- m: msg,
- isSimple: true,
- },
- null,
- function() {
- let towrite = ""
- try {
- towrite +=
- "Elküldött adatok: " + JSON.stringify(sentData)
- } catch (e) {
- towrite += "Elküldött adatok: " + sentData
- }
- document.write(towrite)
- document.close()
- }
- )
+ GetResultImage(i) {
+ if (logElementGetting) {
+ Log('getting result image')
+ }
+ var results = this.GetFormResult() // getting results element
+ return this.GetFormCFOfResult(results[i]).getElementsByTagName('img')
}
- // this should get the image url from a result page
+ // gets the question from the result page
// i is the index of the question
- // FIXME: move this to RPM class ??? and refactor this
- function GetImageFormResult(i) {
- try {
- var imgElements = RPM.GetResultImage(i) // trying to get image
- var imgURL = [] // image urls
- for (var j = 0; j < imgElements.length; j++) {
- if (!imgElements[j].src.includes("brokenfile")) {
- var filePart = imgElements[j].src.split("/") // splits the link by "/"
- filePart = filePart[filePart.length - 1] // the last one is the image name
- imgURL.push(decodeURI(SUtils.ShortenString(filePart, 30)))
- }
- }
- if (imgURL.length > 0) {
- return imgURL
- }
- } catch (e) {
- Log("Couldn't get images from result")
- }
- }
-
- function GetDataFormResult(i) {
- let data = { type: "simple" }
-
- let img = GetImageFormResult(i)
- let grabbox = RPM.GetAnswersFromGrabBox(i)
- if (img) {
- data = {
- type: "image",
- images: img,
- }
- }
- if (grabbox) {
- data = {
- type: "grabbox",
- images: img,
- grabbox: grabbox,
- }
- }
-
- return data
- }
-
- // saves the current quiz. questionData contains the active subjects questions
- function SaveQuiz(quiz, next) {
- try {
- let sentData = {}
- if (quiz.length === 0) {
- throw new Error("quiz length is zero!")
- }
- try {
- try {
- sentData.subj = MPM.GetCurrentSubjectName()
- } catch (e) {
- sentData.subj = "NOSUBJ"
- Log("unable to get subject name :c")
- }
- sentData.version = info().script.version
- sentData.id = GetId()
- sentData.quiz = quiz
- console.log("SENT DATA", sentData)
- SendXHRMessage("isAdding", sentData).then(res => {
- next(res.success, sentData, res.newQuestions)
- })
- } catch (e) {
- Exception(e, "error at sending data to server.")
- }
- } catch (e) {
- Exception(e, "script error at saving quiz")
- }
- }
-
- // getting quiz from finish page
- function GetQuiz() {
- try {
- var quiz = [] // final quiz stuff
- var results = RPM.GetFormResult() // getting results element
- for (var i = 0; i < results.length - 2; i++) {
- var question = {} // the current question
- // QUESTION --------------------------------------------------------------------------------------------------------------------
- question.Q = RPM.GetQuestionFromResult(i)
-
- // RIGHTANSWER ---------------------------------------------------------------------------------------------------------------------
- question.A = RPM.GetRightAnswerFromResultv2(i)
- if (question.A === undefined) {
- question.A = RPM.GetRightAnswerFromResult(i)
- }
- // DATA ---------------------------------------------------------------------------------------------------------------------
- question.data = GetDataFormResult(i)
-
- if (question.A !== undefined) {
- quiz.push(question) // adding current question to quiz
- } else {
- Log(
- "error getting queston, no correct answer given, or its incorrect"
- )
- Log(question)
- }
- }
- return quiz
- } catch (e) {
- Exception(e, "script error at quiz parsing:")
- }
- }
-
- // : }}}
-
- // : Helpers {{{
-
- function GetImageDataFromImgNodes(imgs) {
- var questionImages = [] // the array for the image names in question
- for (var i = 0; i < imgs.length; i++) {
- if (!imgs[i].src.includes("brokenfile")) {
- var filePart = imgs[i].src.split("/") // splits the link by "/"
- filePart = filePart[filePart.length - 1] // the last one is the image name
- questionImages.push(
- decodeURI(
- SUtils.RemoveUnnecesarySpaces(
- SUtils.ShortenString(filePart, 30)
- )
- )
- ) // decodes uri codes, and removes exess spaces, and shortening it
- }
- }
- if (questionImages.length > 0) {
- return {
- type: "image",
- images: questionImages,
+ GetQuestionFromResult(i) {
+ var temp = this.GetQText(i)
+ var currQuestion = ''
+ if (temp.length > 0) {
+ currQuestion = temp[0].innerText // adding the question to curr question as .q
+ } else {
+ // this is black magic fuckery a bit
+ if (this.GetDropboxes(i).length > 0) {
+ var allNodes = this.GetResultText(i)
+ currQuestion = ''
+ for (var k = 0; k < allNodes.length; k++) {
+ var allQuestions = this.GetResultText(i)[k].childNodes
+ for (var j = 0; j < allQuestions.length; j++) {
+ if (
+ allQuestions[j].data !== undefined &&
+ !SUtils.EmptyOrWhiteSpace(allQuestions[j].data)
+ ) {
+ currQuestion += allQuestions[j].data + ' '
+ }
}
+ }
} else {
- return {
- type: "simple",
- }
+ try {
+ currQuestion = this.GetCurrQuestion(i)
+ } catch (e) {
+ currQuestion = 'REEEEEEEEEEEEEEEEEEEEE' // this shouldnt really happen sry guys
+ Log('Unable to get question in GetQuestionFromResult')
+ }
}
+ }
+ return currQuestion
}
- // adds image names to image nodes
- function AddImageNamesToImages(imgs) {
- for (var i = 0; i < imgs.length; i++) {
- if (!imgs[i].src.includes("brokenfile")) {
- // TODO: add this to shadowroot
- var filePart = imgs[i].src.split("/") // splits the link by "/"
- // console.log(imgs[i].src.split("base64,")[1])
- // TODO: base64
- filePart = filePart[filePart.length - 1] // the last one is the image name
- var appendTo = imgs[i].parentNode // it will be appended here
- var mainDiv = document.createElement("div")
- var fileName = SUtils.ShortenString(decodeURI(filePart), 15) // shortening name, couse it can be long as fuck
- var textNode = document.createTextNode("(" + fileName + ")")
- mainDiv.appendChild(textNode)
- appendBelowElement(appendTo, mainDiv)
- }
+ // tries to get right answer from result page
+ // i is the index of the question
+ GetRightAnswerFromResult(i) {
+ var fun = []
+
+ // "húzza oda ..." skip
+ fun.push(i => {
+ let temp = RPM.GetAnswersFromGrabBox(i)
+ return temp
+ .map(x => {
+ return x.text
+ })
+ .join(', ')
+ })
+
+ // the basic type of getting answers
+ fun.push(i => {
+ var temp = RPM.GetRightAnswerIfCorrectShown(i) // getting risht answer
+ if (temp.length > 0) {
+ return temp[0].innerText
+ } // adding the answer to curr question as .a
+ })
+
+ // if there is dropdown list in the current question
+ fun.push(i => {
+ if (RPM.GetDropboxes(i).length > 0) {
+ return RPM.GetCurrentAnswer(i)
}
+ })
+
+ // if the correct answers are not shown, and the selected answer
+ // is correct
+ fun.push(i => {
+ return RPM.GetRightAnswerIfCorrectNotShown(i)
+ })
+
+ // if there is dropbox in the question
+ fun.push(i => {
+ return RPM.GetSelectAnswer(i)
+ })
+
+ // if the correct answers are not shown, and the selected answer
+ // is incorrect, and there are only 2 options
+ fun.push(i => {
+ var possibleAnswers = RPM.GetPossibleAnswers(i)
+ if (possibleAnswers.length === 2) {
+ for (var k = 0; k < possibleAnswers.length; k++) {
+ if (possibleAnswers[k].iscorrect === undefined) {
+ return possibleAnswers[k].value
+ }
+ }
+ }
+ })
+
+ // if everything fails
+ fun.push(i => {
+ return undefined
+ })
+
+ var j = 0
+ var currAnswer
+ while (j < fun.length && SUtils.EmptyOrWhiteSpace(currAnswer)) {
+ try {
+ currAnswer = fun[j](i)
+ } catch (e) {
+ console.info(e)
+ }
+ j++
+ }
+
+ return currAnswer
}
- // this function adds basic hotkeys for video controll.
- function AddVideoHotkeys(url) {
- var seekTime = 20
- document.addEventListener("keydown", function(e) {
- try {
- var video = MPM.GetVideo()
- var keyCode = e.keyCode // getting keycode
- if (keyCode === 32) {
- // if the keycode is 32 (space)
- e.preventDefault() // preventing default action (space scrolles down)
- if (video.paused && video.buffered.length > 0) {
- video.play()
- } else {
- video.pause()
- }
- }
- if (keyCode === 39) {
- // rigth : 39
- video.currentTime += seekTime
- }
- if (keyCode === 37) {
- // left : 37
- video.currentTime -= seekTime
- }
- } catch (err) {
- Log("Hotkey error.")
- Log(err.message)
- }
+ GuessCorrectIn2LengthAnswersByIncorrect(items) {
+ const first = items[0]
+ const second = items[1]
+ if (first.className.includes('incorrect')) {
+ return second.innerText
+ }
+ if (second.className.includes('incorrect')) {
+ return first.innerText
+ }
+ }
+
+ GuessCorrectIn2LengthAnswersByPoints(i, items) {
+ const first = {
+ elem: items[0],
+ val: items[0].childNodes[0].checked,
+ text: items[0].innerText,
+ }
+ const second = {
+ elem: items[1],
+ val: items[1].childNodes[0].checked,
+ text: items[1].innerText,
+ }
+
+ const grade = RPM.GetGrade(i) // 1,00 közül 1,00 leosztályozva
+ const grades = grade.split(' ').reduce((acc, text) => {
+ if (text.includes(',')) {
+ // FIXME: fancy regexp
+ acc.push(parseInt(text))
+ } else if (text.includes('.')) {
+ // FIXME: fancy regexp
+ acc.push(parseInt(text))
+ }
+ return acc
+ }, [])
+
+ if (grades[0] === 1) {
+ if (first.val) {
+ return first.text
+ } else {
+ return second.text
+ }
+ } else {
+ if (!first.val) {
+ return first.text
+ } else {
+ return second.text
+ }
+ }
+ }
+
+ // version 2 of getting right answer from result page
+ // i is the index of the question
+ GetRightAnswerFromResultv2(i) {
+ try {
+ var answerNodes = this.GetAnswerNode(i)
+ let items = answerNodes.nodes
+
+ if (answerNodes.type === 'checkbox') {
+ return RPM.GetRightAnswerFromResult(i)
+ }
+
+ for (let j = 0; j < items.length; j++) {
+ let cn = items[j].className
+ if (cn.includes('correct') && !cn.includes('incorrect')) {
+ return items[j].getElementsByTagName('label')[0].innerText
+ }
+ }
+ if (items.length === 2) {
+ const resByIncorrect = this.GuessCorrectIn2LengthAnswersByIncorrect(
+ items
+ )
+ if (!resByIncorrect) {
+ const resPoints = this.GuessCorrectIn2LengthAnswersByPoints(
+ i,
+ items
+ )
+ return resPoints
+ }
+ return resByIncorrect
+ }
+ } 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 {{{
+ let timerStarted = false
+
+ // window.addEventListener("load", () => {})
+ Main()
+
+ function Main() {
+ 'use strict'
+ console.log('Moodle / E-Learning script')
+ console.time('main')
+ timerStarted = true
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', Init)
+ } else {
+ Init()
+ }
+ }
+
+ function AfterLoad() {
+ const url = location.href // eslint-disable-line
+
+ 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)
+ }
+ } catch (e) {
+ ShowMessage(
+ {
+ m: texts.fatalError,
+ isSimple: true,
+ },
+ undefined,
+ () => {
+ OpenErrorPage(e)
+ }
+ )
+
+ Exception(e, 'script error at main:')
+ }
+ if (url.includes('eduplayer')) {
+ AddVideoHotkeys(url)
+ } // adding video hotkeys
+ Log(texts.consoleErrorInfo)
+
+ if (timerStarted) {
+ console.log('Moodle Test Script run time:')
+ console.timeEnd('main')
+ timerStarted = false
+ }
+
+ if (forceTestPage || forceResultPage || forceDefaultPage) {
+ if (overlay.querySelector('#scriptMessage')) {
+ overlay.querySelector('#scriptMessage').style.background = 'green'
+ }
+ }
+ }
+ // : }}}
+
+ // : Main logic stuff {{{
+
+ // : Loading {{{
+ function HandleQminingSite(url) {
+ try {
+ const idInput = document.getElementById('cid')
+ if (idInput) {
+ idInput.value = getVal('clientId')
+ }
+ } catch (e) {
+ console.info('Error filling client ID input', e)
+ }
+ try {
+ const sideLinks = document.getElementById('sideBarLinks')
+ if (!sideLinks) {
+ return
+ }
+ Array.from(sideLinks.childNodes).forEach(link => {
+ link.addEventListener('mousedown', () => {
+ FillFeedbackCID(url, link)
})
- var toadd = MPM.GetVideoElement()
- var node = CreateNodeWithText(toadd, texts.videoHelp)
- node.style.margin = "5px 5px 5px 5px" // fancy margin
+ })
+
+ FillFeedbackCID(
+ url,
+ document
+ .getElementById('sideBarLinks')
+ .getElementsByClassName('active')[0]
+ )
+ } catch (e) {
+ console.info('Error filling client ID input', e)
+ }
+ }
+
+ function FillFeedbackCID(url, link) {
+ try {
+ if (link.id === 'feedback') {
+ const cidSetInterval = setInterval(() => {
+ const cid = document.getElementById('cid')
+ if (cid) {
+ cid.value = GetId() + '|' + info().script.version
+ window.clearInterval(cidSetInterval)
+ }
+ }, 100)
+ }
+ } catch (e) {
+ console.info('Error filling client ID input', e)
+ }
+ }
+
+ function Init() {
+ const url = location.href // eslint-disable-line
+
+ if (url.includes(serverAdress.split('/')[2])) {
+ HandleQminingSite(url)
+ return
}
- // removes stuff like " a. q1" -> "q1"
- function RemoveLetterMarking(inp) {
- let dotIndex = inp.indexOf(".")
- let doubledotIndex = inp.indexOf(":")
- let maxInd = 4 // inp.length * 0.2;
-
- if (dotIndex < maxInd) {
- return SUtils.RemoveUnnecesarySpaces(
- inp.substr(inp.indexOf(".") + 1, inp.length)
- )
- } else if (doubledotIndex < maxInd) {
- return SUtils.RemoveUnnecesarySpaces(
- inp.substr(inp.indexOf(":") + 1, inp.length)
- )
+ // if (false) {
+ // // eslint-disable-line
+ // setVal("version16", undefined);
+ // setVal("version15", undefined);
+ // setVal("firstRun", undefined);
+ // setVal("showQuestions", undefined);
+ // setVal("showSplash", undefined);
+ // }
+ // --------------------------------------------------------------------------------------
+ // 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 inp
+ return function(element, event, handler) {
+ element.attachEvent('on' + event, handler)
+ }
}
+ })()
+ } catch (e) {
+ Exception(e, 'script error at addEventListener:')
}
+ VersionActions()
+ if (!url.includes('.pdf')) {
+ ShowMenu()
+ }
+ ConnectToServer(AfterLoad)
+ }
- // highlights the possible solutions to the current question
- function HighLightAnswer(results, currQuestionNumber) {
- try {
- if (results.length > 0) {
- var answers = RPM.GetAllAnswer(currQuestionNumber) // getting all answers
- var toColor = [] // the numberth in the array will be colored, and .length items will be colored
- var type = "" // type of the question. radio or ticbox or whatitscalled
- for (let i = 0; i < answers.length; i++) {
- // going thtough answers
- if (
- answers[i].tagName &&
- answers[i].tagName.toLowerCase() === "div"
- ) {
- // if its not null and is "div"
- var correct = results[0].q.A.toLowerCase() // getting current correct answer from data
- var answer = answers[i].innerText
- .replace(/\n/g, "")
- .toLowerCase() // getting current answer
+ function Auth(pw) {
+ SendXHRMessage('login', { pw: pw, script: true }).then(res => {
+ if (res.result === 'success') {
+ ConnectToServer(AfterLoad)
+ ClearAllMessages()
+ resetMenu()
+ } else {
+ SafeGetElementById('infoMainDiv', elem => {
+ elem.innerText = texts.invalidPW + pw
+ })
+ }
+ })
+ }
- // removing stuff like "a."
- answer = RemoveLetterMarking(answer)
+ function resetMenu() {
+ SafeGetElementById('menuButtonDiv', elem => {
+ elem.style.backgroundColor = '#262626'
+ })
+ SafeGetElementById('ircButton', elem => {
+ elem.style.display = 'none'
+ })
+ SafeGetElementById('retryButton', elem => {
+ elem.style.display = 'none'
+ })
+ SafeGetElementById('loginDiv', elem => {
+ elem.style.display = 'none'
+ })
+ SafeGetElementById('infoMainDiv', elem => {
+ elem.innerText = texts.loading
+ })
+ }
- if (
- SUtils.EmptyOrWhiteSpace(correct) ||
- SUtils.EmptyOrWhiteSpace(answer)
- ) {
- continue
- }
-
- if (
- SUtils.NormalizeSpaces(
- SUtils.RemoveUnnecesarySpaces(correct)
- ).includes(answer)
- ) {
- // if the correct answer includes the current answer
- toColor.push(i) // adding the index
- type = MPM.GetInputType(answers, i) // setting the type
- }
- }
- }
- if (results[0].match === 100) {
- // if the result is 100% correct
- if (type !== "radio" || toColor.length === 1) {
- // FIXME why not radio
- for (let i = 0; i < toColor.length; i++) {
- // going through "toColor"
- let highlight = createHoverOver(answers[toColor[i]])
- Object.assign(highlight.style, {
- border: "7px solid rgba(100, 240, 100, 0.8)",
- borderRadius: "10px",
- margin: "-13px 0 0 -8px",
- })
- }
- }
- } // 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 ConnectToServer(cwith) {
+ ClearAllMessages()
+ GetXHRInfos()
+ .then(inf => {
+ if (inf.result === 'nouser') {
+ NoUserAction()
+ return
}
+ lastestVersion = inf.version
+ motd = inf.motd
+ subjInfo = inf.subjinfo
+ overlay.querySelector(
+ '#infoMainDiv'
+ ).innerText = `${subjInfo.subjects} tárgy, ${subjInfo.questions} kérdés. Felh #${inf.uid}`
+ // FIXME: if cwith() throws an unhandled error it sais server is not avaible
+ cwith()
+ })
+ .catch(() => {
+ NoServerAction()
+ })
+ }
+
+ function NoUserAction() {
+ SafeGetElementById('menuButtonDiv', elem => {
+ elem.style.backgroundColor = '#44cc00'
+ })
+ SafeGetElementById('infoMainDiv', elem => {
+ elem.innerText = texts.noUser
+ if (getVal('clientId')) {
+ elem.innerText += ` (${getVal('clientId')})`
+ }
+ })
+ SafeGetElementById('loginDiv', elem => {
+ elem.style.display = ''
+ })
+ }
+
+ function NoServerAction() {
+ SafeGetElementById('menuButtonDiv', elem => {
+ elem.style.backgroundColor = 'red'
+ })
+ SafeGetElementById('infoMainDiv', elem => {
+ elem.innerText = texts.noServer
+ })
+ SafeGetElementById('ircButton', elem => {
+ elem.style.display = ''
+ })
+ SafeGetElementById('retryButton', elem => {
+ elem.style.display = ''
+ })
+ Log(texts.noServerConsoleMessage)
+ }
+
+ function VersionActions() {
+ // FOR TESTING ONLY
+ // setVal("version15", true);
+ // setVal("firstRun", true);
+ // setVal("version16", true);
+ // throw "asd";
+
+ FreshStart()
+ }
+
+ // : Version action functions {{{
+
+ function FreshStart() {
+ var firstRun = getVal('firstRun') // if the current run is the frst
+ if (firstRun === undefined || firstRun === true) {
+ setVal('firstRun', false)
+ ShowHelp() // showing help
+
+ document.write(texts.freshStartWarning)
+ document.close()
+ throw new Error('something, so this stuff stops')
+ }
+ }
+
+ // : }}}
+
+ // : UI handling {{{
+ function HandleUI(url) {
+ // FIXME: normal string building with localisation :/
+ var newVersion = false // if the script is newer than last start
+
+ try {
+ newVersion = info().script.version !== getVal('lastVerson')
+ } catch (e) {
+ Log('Some weird error trying to set new verison')
}
- // : }}}
-
- 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 {{{
- function ClearAllMessages() {
- overlay.querySelectorAll("#scriptMessage").forEach(x => x.remove())
- }
-
- // 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 === "") {
- // if msg is empty
- return
- }
- msgItem = [
- [
- {
- m: simpleMessageText,
- },
- ],
- ]
- isSimpleMessage = true
- }
-
- var appedtTo = overlay // will be appended here
- var width = window.innerWidth - window.innerWidth / 6 // with of the box
- var startFromTop = 25 // top distance
-
- var mainDiv = document.createElement("div") // the main divider, wich items will be attached to
- mainDiv.setAttribute("id", "messageMainDiv")
- if (funct) {
- // if there is a function as parameter
- addEventListener(mainDiv, "click", funct) // adding it as click
- }
- // lotsa crap style
- SetStyle(mainDiv, {
- position: "fixed",
- zIndex: 999999,
- textAlign: "center",
- width: width + "px",
- padding: "0px",
- background: "#222d32",
- color: "#ffffff",
- borderColor: "#035a8f",
- border: "none",
- top: startFromTop + "px",
- left: (window.innerWidth - width) / 2 + "px",
- opacity: "0.9",
- })
- mainDiv.setAttribute("id", "scriptMessage")
- var matchPercent = msgItem[0][0].p
- if (isSimpleMessage) {
- var simpleMessageParagrapg = document.createElement("p") // new paragraph
- simpleMessageParagrapg.style.margin = defMargin // fancy margin
-
- var mesageNode = document.createElement("p") // new paragraph
- mesageNode.innerHTML = simpleMessageText.replace(/\n/g, "")
- simpleMessageParagrapg.appendChild(mesageNode)
- mesageNode.style.margin = defMargin // fancy margin
-
- mainDiv.appendChild(simpleMessageParagrapg) // adding text box to main div
- } else {
- // if its a fucking complicated message
- // TABLE SETUP ------------------------------------------------------------------------------------------------------------
- var table = document.createElement("table")
- table.style.width = "100%"
- // ROWS -----------------------------------------------------------------------------------------------------
- var rowOne = table.insertRow() // previous suggestion, question text, and prev question
- var rowTwo = table.insertRow() // next question button
- var rowThree = table.insertRow() // next suggetsion button
- // CELLS -----------------------------------------------------------------------------------------------------
- // row one
- var numberTextCell = rowOne.insertCell()
- var questionCell = rowOne.insertCell() // QUESTION CELL
- questionCell.setAttribute("id", "questionCell")
- questionCell.rowSpan = 3
- questionCell.style.width = "90%"
- var prevQuestionCell = rowOne.insertCell()
- // row two
- var percentTextCell = rowTwo.insertCell()
- var nextQuestionCell = rowTwo.insertCell()
- // row three
- var prevSuggestionCell = rowThree.insertCell()
- var nextSuggestionCell = rowThree.insertCell()
- // adding finally
- mainDiv.appendChild(table)
- // PERCENT TEXT SETUP -----------------------------------------------------------------------------------------------------
- var percentTextBox = CreateNodeWithText(percentTextCell, "")
- percentTextBox.setAttribute("id", "percentTextBox")
-
- if (matchPercent) {
- // if match percent param is not null
- percentTextBox.innerText = matchPercent + "%"
- }
- // NUMBER SETUP -----------------------------------------------------------------------------------------------------
- var numberTextBox = CreateNodeWithText(numberTextCell, "1.")
- numberTextBox.setAttribute("id", "numberTextBox")
-
- // ANSWER NODE SETUP -------------------------------------------------------------------------------------------------------------
- var questionTextElement = CreateNodeWithText(
- questionCell,
- "ur question goes here, mister OwO"
- )
- questionTextElement.setAttribute("id", "questionTextElement")
-
- // BUTTON SETUP -----------------------------------------------------------------------------------------------------------
- var currItem = 0
- var currRelevantQuestion = 0
-
- const GetRelevantQuestion = () => {
- // returns the currItemth questions currRelevantQuestionth relevant question
- return msgItem[currItem][currRelevantQuestion]
- }
-
- const ChangeCurrItemIndex = to => {
- currItem += to
- if (currItem < 0) {
- currItem = 0
- }
- if (currItem > msgItem.length - 1) {
- currItem = msgItem.length - 1
- }
- currRelevantQuestion = 0
- }
-
- const ChangeCurrRelevantQuestionIndex = to => {
- currRelevantQuestion += to
- if (currRelevantQuestion < 0) {
- currRelevantQuestion = 0
- }
- if (currRelevantQuestion > msgItem[currItem].length - 1) {
- currRelevantQuestion = msgItem[currItem].length - 1
- }
- }
-
- const SetQuestionText = () => {
- var relevantQuestion = GetRelevantQuestion()
- questionTextElement.innerText = relevantQuestion.m
- if (currItem === 0 && currRelevantQuestion === 0) {
- numberTextBox.innerText = currRelevantQuestion + 1 + "."
- } else {
- numberTextBox.innerText =
- currItem +
- 1 +
- "./" +
- (currRelevantQuestion + 1) +
- "."
- }
- percentTextBox.innerText = relevantQuestion.p + "%"
- }
-
- var buttonMargin = "2px 2px 2px 2px" // uniform button margin
- if (msgItem[currItem].length > 1) {
- // PREV SUGG BUTTON ------------------------------------------------------------------------------------------------------------
- var prevSuggButton = CreateNodeWithText(
- prevSuggestionCell,
- "<",
- "button"
- )
- prevSuggButton.style.margin = buttonMargin // fancy margin
-
- prevSuggButton.addEventListener("click", function() {
- ChangeCurrRelevantQuestionIndex(-1)
- SetQuestionText()
- })
- // NEXT SUGG BUTTON ------------------------------------------------------------------------------------------------------------
- var nextSuggButton = CreateNodeWithText(
- nextSuggestionCell,
- ">",
- "button"
- )
- nextSuggButton.style.margin = buttonMargin // fancy margin
-
- nextSuggButton.addEventListener("click", function() {
- ChangeCurrRelevantQuestionIndex(1)
- SetQuestionText()
- })
- }
- // deciding if has multiple questions ------------------------------------------------------------------------------------------------
- if (msgItem.length === 1) {
- SetQuestionText()
- } else {
- // if there are multiple items to display
- // PREV QUESTION BUTTON ------------------------------------------------------------------------------------------------------------
- var prevButton = CreateNodeWithText(
- prevQuestionCell,
- "^",
- "button"
- )
- prevButton.style.margin = buttonMargin // fancy margin
-
- // event listener
- prevButton.addEventListener("click", function() {
- ChangeCurrItemIndex(-1)
- SetQuestionText()
- })
- // NEXT QUESTION BUTTON ------------------------------------------------------------------------------------------------------------
- var nextButton = CreateNodeWithText(
- nextQuestionCell,
- "ˇ",
- "button"
- )
- nextButton.style.margin = buttonMargin // fancy margin
-
- // event listener
- nextButton.addEventListener("click", function() {
- ChangeCurrItemIndex(1)
- SetQuestionText()
- })
- SetQuestionText()
- }
- }
- appedtTo.appendChild(mainDiv) // THE FINAL APPEND
-
- // setting some events
- // addEventListener(window, 'scroll', function () {
- // mainDiv.style.top = (pageYOffset + startFromTop) + 'px';
- // })
- addEventListener(window, "resize", function() {
- mainDiv.style.left = (window.innerWidth - width) / 2 + "px"
- })
- var timeOut
- if (timeout && timeout > 0) {
- // setting timeout if not zero or null
- timeOut = setTimeout(function() {
- mainDiv.parentNode.removeChild(mainDiv)
- }, timeout * 1000)
- }
- // middle click close event listener
- addEventListener(mainDiv, "mousedown", function(e) {
- if (e.which === 2) {
- mainDiv.parentNode.removeChild(mainDiv)
- if (timeOut) {
- clearTimeout(timeOut)
- }
- }
- })
- } catch (e) {
- Exception(e, "script error at showing message:")
- }
- }
-
- // shows a fancy menu
- function ShowMenu() {
- try {
- var appedtTo = overlay // will be appended here
-
- // mainDiv.style.left = (window.innerWidth - width) / 2 + 'px';
-
- var menuButtonDiv = document.createElement("div")
- menuButtonDiv.setAttribute("id", "menuButtonDiv")
- SetStyle(menuButtonDiv, {
- width: "600px",
- // height: buttonHeight + 'px',
- top: window.innerHeight - 120 + "px",
- left: "10px",
- zIndex: 999999,
- position: "fixed",
- textAlign: "center",
- padding: "0px",
- margin: "0px",
- background: "#262626",
- })
-
- var tbl = document.createElement("table")
- tbl.style.margin = "5px 5px 5px 5px"
- tbl.style.textAlign = "left"
- tbl.style.width = "98%"
- menuButtonDiv.appendChild(tbl)
-
- var buttonRow = tbl.insertRow()
- var buttonCell = buttonRow.insertCell()
- buttonCell.style.textAlign = "center"
-
- let buttonStyle = {
- position: "",
- margin: "5px 5px 5px 5px",
- border: "none",
- backgroundColor: "#222d32",
- color: "#ffffff",
- cursor: "pointer",
- }
- // help button ----------------------------------------------------------------------------------------------------------------
- let helpButton = CreateNodeWithText(
- buttonCell,
- texts.help,
- "button"
- )
- SetStyle(helpButton, buttonStyle)
-
- helpButton.addEventListener("click", function() {
- ShowHelp()
- }) // adding clicktextNode
-
- // site link ----------------------------------------------------------------------------------------------------------------
-
- let contributeLink = CreateNodeWithText(
- buttonCell,
- texts.contribute,
- "button"
- )
- contributeLink.title = texts.contributeTitle
- SetStyle(contributeLink, buttonStyle)
-
- contributeLink.addEventListener("click", function() {
- openInTab(serverAdress + "contribute?scriptMenu", {
- active: true,
- })
- })
-
- // pw request ----------------------------------------------------------------------------------------------------------------
-
- let pwRequest = CreateNodeWithText(
- buttonCell,
- texts.pwRequest,
- "button"
- )
- pwRequest.title = texts.newPWTitle
- SetStyle(pwRequest, buttonStyle)
-
- pwRequest.addEventListener("click", function() {
- openInTab(serverAdress + "pwRequest", {
- active: true,
- })
- })
-
- // site link ----------------------------------------------------------------------------------------------------------------
-
- let siteLink = CreateNodeWithText(
- buttonCell,
- texts.websiteBugreport,
- "button"
- )
- SetStyle(siteLink, buttonStyle)
-
- siteLink.addEventListener("click", function() {
- openInTab(serverAdress + "menuClick", {
- active: true,
- })
- })
-
- // donate link ----------------------------------------------------------------------------------------------------------------
- let donateLink = CreateNodeWithText(
- buttonCell,
- texts.donate,
- "button"
- )
- SetStyle(donateLink, buttonStyle)
-
- donateLink.addEventListener("click", function() {
- openInTab(serverAdress + "donate?scriptMenu", {
- active: true,
- })
- })
-
- addEventListener(window, "resize", function() {
- menuButtonDiv.style.top = window.innerHeight - 70 + "px"
- })
-
- // INFO TABEL --------------------------------------------------------------------
- var itbl = document.createElement("table")
- SetStyle(itbl, {
- margin: "5px 5px 5px 5px",
- textAlign: "left",
- width: "98%",
- })
- menuButtonDiv.appendChild(itbl)
- var ibuttonRow = tbl.insertRow()
- var ibuttonCell = ibuttonRow.insertCell()
- ibuttonCell.style.textAlign = "center"
-
- // INFO DIV ---------------------------------------------------------------------------------
- let infoDiv = CreateNodeWithText(ibuttonCell, texts.loading, "span")
- infoDiv.setAttribute("id", "infoMainDiv")
- SetStyle(infoDiv, {
- color: "#ffffff",
- margin: "5px",
- })
-
- // login div ----------------------------------------------------------------------------------------------------------------
- const loginDiv = document.createElement("div")
- loginDiv.style.display = "none"
- loginDiv.setAttribute("id", "loginDiv")
- const loginButton = document.createElement("button")
- loginButton.innerText = texts.login
- const loginInput = document.createElement("input")
- loginInput.type = "text"
- loginInput.style.width = "400px"
- loginInput.style.textAlign = "center"
- const clientId = getVal("clientId")
- if (clientId && clientId.toString()[0] !== "0") {
- loginInput.value = clientId || ""
- loginButton.innerText = texts.requestPWInsteadOfLogin
- }
- loginDiv.appendChild(loginInput)
- loginDiv.appendChild(loginButton)
-
- SetStyle(loginButton, buttonStyle)
-
- loginInput.addEventListener("keyup", e => {
- console.log(e.target.value)
- if (e.target.value === clientId) {
- loginButton.innerText = texts.requestPWInsteadOfLogin
- } else if (e.target.value !== "") {
- loginButton.innerText = texts.login
- }
- })
-
- loginButton.addEventListener("click", function() {
- if (loginInput.value === clientId.toString()) {
- openInTab(serverAdress + "getVeteranPw?cid=" + clientId, {
- active: true,
- })
- } else {
- Auth(loginInput.value)
- }
- })
-
- ibuttonCell.appendChild(loginDiv)
-
- // irc button ----------------------------------------------------------------------------------------------------------------
- let ircButton = CreateNodeWithText(
- ibuttonCell,
- texts.ircButton,
- "button"
- )
- SetStyle(ircButton, buttonStyle)
- ircButton.style.display = "none"
- ircButton.setAttribute("id", "ircButton")
-
- ircButton.addEventListener("click", function() {
- openInTab(ircAddress, {
- active: true,
- })
- })
-
- // retry button ----------------------------------------------------------------------------------------------------------------
- let retryButton = CreateNodeWithText(
- ibuttonCell,
- texts.retry,
- "button"
- )
- SetStyle(retryButton, buttonStyle)
- retryButton.style.display = "none"
- retryButton.setAttribute("id", "retryButton")
-
- retryButton.addEventListener("click", function() {
- menuButtonDiv.style.background = "#262626"
- infoDiv.innerText = texts.loading
- retryButton.style.display = "none"
- ircButton.style.display = "none"
- ConnectToServer(AfterLoad)
- })
-
- // window resize event listener ---------------------------------------
- addEventListener(window, "resize", function() {
- menuButtonDiv.style.top = window.innerHeight - 70 + "px"
- })
-
- // APPEND EVERYTHING
- appedtTo.appendChild(menuButtonDiv)
- } catch (e) {
- Exception(e, "script error at showing menu:")
- }
- }
-
- // : }}}
-
- // : Generic utils {{{
- function GetId() {
- let currId = getVal("clientId")
- if (currId) {
- return currId
- } else {
- currId = new Date()
- currId =
- currId.getTime() + Math.floor(Math.random() * 1000000000000)
- currId = currId.toString().split("")
- currId.shift()
- currId = "0" + currId.join("")
- setVal("clientId", currId)
- return currId
- }
- }
-
- function SafeGetElementById(id, next) {
- let element = overlay.querySelector("#" + id)
- if (element) {
- next(element)
- } else {
- Log(`Unable to safe get element by id: ${id}`)
- }
- }
-
- function SetStyle(target, style) {
- Object.keys(style)
- .sort()
- .forEach(key => {
- target.style[key] = style[key]
- })
- }
-
- 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 GetXHRInfos() {
- const now = new Date().getTime()
- const lastCheck = getVal("lastInfoCheckTime")
- if (!lastCheck) {
- setVal("lastInfoCheckTime", now)
+ let showMOTD = false
+ if (!SUtils.EmptyOrWhiteSpace(motd)) {
+ var prevmotd = getVal('motd')
+ if (prevmotd !== motd) {
+ showMOTD = true
+ setVal('motdcount', motdShowCount)
+ setVal('motd', motd)
+ } else {
+ var motdcount = getVal('motdcount')
+ if (motdcount === undefined) {
+ setVal('motdcount', motdShowCount)
+ motdcount = motdShowCount
}
- let lastInfo = { result: "noLastInfo" }
- try {
- lastInfo = JSON.parse(getVal("lastInfo"))
- } catch (e) {
- console.info(e)
+ motdcount--
+ if (motdcount > 0) {
+ showMOTD = true
+ setVal('motdcount', motdcount)
+ }
+ }
+ }
+ let isNewVersionAvaible =
+ lastestVersion !== undefined && info().script.version !== lastestVersion
+ var greetMsg = '' // message to show at the end
+ var timeout = null // the timeout. if null, it wont be hidden
+
+ if (isNewVersionAvaible || newVersion || showMOTD) {
+ greetMsg =
+ 'Moodle/Elearning/KMOOC segéd v. ' + info().script.version + '. '
+ }
+ if (isNewVersionAvaible) {
+ timeout = 5
+ greetMsg += 'Új verzió elérhető: ' + lastestVersion
+ timeout = undefined
+ }
+ if (newVersion) {
+ // --------------------------------------------------------------------------------------------------------------
+ greetMsg +=
+ 'Verzió frissítve ' +
+ info().script.version +
+ '-re. Changelog:\n' +
+ texts.lastChangeLog
+ setVal('lastVerson', info().script.version) // setting lastVersion
+ }
+ if (showMOTD) {
+ greetMsg += '\nMOTD:\n' + motd
+ timeout = null
+ }
+
+ ShowMessage(
+ {
+ m: greetMsg,
+ isSimple: true,
+ },
+ timeout
+ ) // showing message. If "m" is empty it wont show it, thats how showSplash works.
+ }
+
+ // : }}}
+
+ // : Answering stuffs {{{
+
+ function HandleQuiz() {
+ var q = QPM.GetQuestionFromTest()
+ var questions = q.q
+ var imgNodes = q.imgnodes
+ // ------------------------------------------------------------------------------------------------------
+ let promises = []
+ questions.forEach(x => {
+ let question = SUtils.EmptyOrWhiteSpace(x)
+ ? ''
+ : SUtils.RemoveUnnecesarySpaces(x) // simplifying question
+ promises.push(
+ GetXHRQuestionAnswer({
+ q: question,
+ data: GetImageDataFromImgNodes(imgNodes),
+ subj: MPM.GetCurrentSubjectName(),
+ })
+ )
+ })
+
+ // FIXME: promise.all promise resolve order same as original?
+ Promise.all(promises).then(res => {
+ let answers = []
+
+ res.forEach((result, j) => {
+ var r = PrepareAnswers(result, j)
+ if (r !== undefined) {
+ answers.push(r)
+ }
+ HighLightAnswer(result, j) // highlights the answer for the current result
+ })
+
+ ShowAnswers(answers, q.q)
+ })
+ }
+
+ function PrepareAnswers(result, j) {
+ assert(result)
+
+ if (result.length > 0) {
+ var allMessages = [] // preparing all messages
+ for (var k = 0; k < result.length; k++) {
+ var msg = '' // the current message
+ if (getVal('showQuestions') === undefined || getVal('showQuestions')) {
+ msg += result[k].q.Q + '\n' // adding the question if yes
+ }
+ msg += result[k].q.A.replace(/, /g, '\n') // adding answer
+ if (result[k].q.data.type === 'image') {
+ msg +=
+ '\n\nKépek fenti válaszok sorrendjében: ' +
+ result[k].q.data.images.join(', ') // if it has image part, adding that too
}
if (
- lastInfo.result !== "success" ||
- now > lastCheck + infoExpireTime * 1000
+ result[k].detailedMatch &&
+ result[k].detailedMatch.matchedSubjName
) {
- return new Promise((resolve, reject) => {
- const url =
- apiAdress +
- "infos?version=true&motd=true&subjinfo=true&cversion=" +
- info().script.version +
- "&cid=" +
- GetId()
-
- xmlhttpRequest({
- method: "GET",
- url: url,
- crossDomain: true,
- xhrFields: { withCredentials: true },
- headers: {
- "Content-Type": "application/json",
- },
- onload: function(response) {
- try {
- setVal("lastInfoCheckTime", now)
- const res = JSON.parse(response.responseText)
- setVal("lastInfo", response.responseText)
- resolve(res)
- } catch (e) {
- Log("Errro paring JSON in GetXHRInfos")
- Log(response.responseText)
- Log(e)
- reject(e)
- }
- },
- onerror: e => {
- Log("Info get Error", e)
- reject(e)
- },
- })
- })
- } else {
- return new Promise((resolve, reject) => {
- try {
- resolve(lastInfo)
- } catch (e) {
- Log(
- "Errro paring JSON in GetXHRInfos, when using old data!"
- )
- Log(e)
- reject(e)
- }
- })
+ msg += '\n(Tárgy: ' + result[k].detailedMatch.matchedSubjName + ')'
}
- }
-
- function GetXHRQuestionAnswer(question) {
- return new Promise((resolve, reject) => {
- let url = apiAdress + "ask?"
- let params = []
- Object.keys(question).forEach(key => {
- let val = question[key]
- if (typeof val !== "string") {
- val = JSON.stringify(val)
- }
- params.push(key + "=" + encodeURIComponent(val))
- })
- url +=
- params.join("&") +
- "&cversion=" +
- info().script.version +
- "&cid=" +
- GetId()
-
- xmlhttpRequest({
- method: "GET",
- url: url,
- onload: function(response) {
- try {
- let res = JSON.parse(response.responseText)
- // FIXME: check if res is a valid answer array
- // res.json({
- // result: r,
- // success: true
- // })
- // ERROR:
- // res.json({
- // message: `Invalid question :(`,
- // result: [],
- // recievedData: JSON.stringify(req.query),
- // success: false
- // })
- resolve(res.result)
- } catch (e) {
- reject(e)
- }
- },
- onerror: e => {
- Log("Errro paring JSON in GetXHRQuestionAnswer")
- Log(e)
- reject(e)
- reject(e)
- },
- })
+ allMessages.push({
+ m: msg,
+ p: result[k].match,
})
+ }
+ return allMessages
}
+ }
- function SendXHRMessage(path, message) {
- // message = SUtils.RemoveSpecialChars(message) // TODO: check this
- if (typeof message === "object") {
- message = JSON.stringify(message)
+ function ShowAnswers(answers, question) {
+ assert(answers)
+
+ if (answers.length > 0) {
+ // if there are more than 0 answer
+ ShowMessage(answers)
+ } else {
+ ShowMessage(
+ {
+ m: texts.noResult,
+ isSimple: true,
+ },
+ undefined,
+ function() {
+ OpenErrorPage({
+ message: 'No result found',
+ stack: JSON.stringify(question),
+ })
}
- const url = apiAdress + path
- return new Promise((resolve, reject) => {
- xmlhttpRequest({
- method: "POST",
- url: url,
- crossDomain: true,
- xhrFields: { withCredentials: true },
- data: message,
- headers: {
- "Content-Type": "application/json",
- },
- onerror: function(e) {
- Log("Data send error", e)
- reject(e)
- },
- onload: resp => {
- try {
- const res = JSON.parse(resp.responseText)
- resolve(res)
- } catch (e) {
- Log("Error paring JSON in SendXHRMessage")
- Log(resp.responseText)
- Log(e)
- reject(e)
- }
- },
- })
- })
+ )
}
+ }
- function OpenErrorPage(e) {
- let path = "lred"
+ // : }}}
+
+ // : Quiz saving {{{
+
+ function HandleResults(url) {
+ SaveQuiz(GetQuiz(), ShowSaveQuizDialog) // saves the quiz questions and answers
+ }
+
+ function ShowSaveQuizDialog(sendResult, sentData, newQuestions) {
+ // FIXME: normal string building with localisation :/
+ var msg = ''
+ if (sendResult) {
+ msg = 'Kérdések elküldve, katt az elküldött adatokért.'
+ if (newQuestions > 0) {
+ msg += ' ' + newQuestions + ' új kérdés'
+ } else {
+ msg += ' Nincs új kérdés'
+ }
+ } else {
+ msg =
+ 'Szerver nem elérhető, vagy egyéb hiba kérdések elküldésénél! (F12 -> Console)'
+ }
+ // showing a message wit the click event, and the generated page
+ ShowMessage(
+ {
+ m: msg,
+ isSimple: true,
+ },
+ null,
+ function() {
+ let towrite = ''
try {
- if (e.message || e.stack) {
- path += "?"
- }
- if (e.message) {
- path += "msg:" + SUtils.SimplifyQuery(e.message)
- }
- if (e.stack) {
- path += "___stack:" + SUtils.SimplifyStack(e.stack)
- }
- path += "___version:" + info().script.version
- path = SUtils.RemoveSpecialChars(path)
+ towrite += 'Elküldött adatok: ' + JSON.stringify(sentData)
} catch (e) {
- Exception(e, "error at setting error stack/msg link")
+ towrite += 'Elküldött adatok: ' + sentData
}
- path = path.replace(/ /g, "_")
- openInTab(serverAdress + path, {
- active: true,
- })
+ document.write(towrite)
+ document.close()
+ }
+ )
+ }
+
+ // this should get the image url from a result page
+ // i is the index of the question
+ // FIXME: move this to RPM class ??? and refactor this
+ function GetImageFormResult(i) {
+ try {
+ var imgElements = RPM.GetResultImage(i) // trying to get image
+ var imgURL = [] // image urls
+ for (var j = 0; j < imgElements.length; j++) {
+ if (!imgElements[j].src.includes('brokenfile')) {
+ var filePart = imgElements[j].src.split('/') // splits the link by "/"
+ filePart = filePart[filePart.length - 1] // the last one is the image name
+ imgURL.push(decodeURI(SUtils.ShortenString(filePart, 30)))
+ }
+ }
+ if (imgURL.length > 0) {
+ return imgURL
+ }
+ } catch (e) {
+ Log("Couldn't get images from result")
+ }
+ }
+
+ function GetDataFormResult(i) {
+ let data = { type: 'simple' }
+
+ let img = GetImageFormResult(i)
+ let grabbox = RPM.GetAnswersFromGrabBox(i)
+ if (img) {
+ data = {
+ type: 'image',
+ images: img,
+ }
+ }
+ if (grabbox) {
+ data = {
+ type: 'grabbox',
+ images: img,
+ grabbox: grabbox,
+ }
}
- // : }}}
+ return data
+ }
- // : Help {{{
-
- // shows some neat help
- function ShowHelp() {
- openInTab(serverAdress + "manual?scriptMenu", {
- active: true,
+ // saves the current quiz. questionData contains the active subjects questions
+ function SaveQuiz(quiz, next) {
+ try {
+ let sentData = {}
+ if (quiz.length === 0) {
+ throw new Error('quiz length is zero!')
+ }
+ try {
+ try {
+ sentData.subj = MPM.GetCurrentSubjectName()
+ } catch (e) {
+ sentData.subj = 'NOSUBJ'
+ Log('unable to get subject name :c')
+ }
+ sentData.version = info().script.version
+ sentData.id = GetId()
+ sentData.quiz = quiz
+ console.log('SENT DATA', sentData)
+ SendXHRMessage('isAdding', sentData).then(res => {
+ next(res.success, sentData, res.newQuestions)
})
+ } catch (e) {
+ Exception(e, 'error at sending data to server.')
+ }
+ } catch (e) {
+ Exception(e, 'script error at saving quiz')
+ }
+ }
+
+ // getting quiz from finish page
+ function GetQuiz() {
+ try {
+ var quiz = [] // final quiz stuff
+ var results = RPM.GetFormResult() // getting results element
+ for (var i = 0; i < results.length - 2; i++) {
+ var question = {} // the current question
+ // QUESTION --------------------------------------------------------------------------------------------------------------------
+ question.Q = RPM.GetQuestionFromResult(i)
+
+ // RIGHTANSWER ---------------------------------------------------------------------------------------------------------------------
+ question.A = RPM.GetRightAnswerFromResultv2(i)
+ if (question.A === undefined) {
+ question.A = RPM.GetRightAnswerFromResult(i)
+ }
+ // DATA ---------------------------------------------------------------------------------------------------------------------
+ question.data = GetDataFormResult(i)
+
+ if (question.A !== undefined) {
+ quiz.push(question) // adding current question to quiz
+ } else {
+ Log(
+ 'error getting queston, no correct answer given, or its incorrect'
+ )
+ Log(question)
+ }
+ }
+ return quiz
+ } catch (e) {
+ Exception(e, 'script error at quiz parsing:')
+ }
+ }
+
+ // : }}}
+
+ // : Helpers {{{
+
+ function GetImageDataFromImgNodes(imgs) {
+ var questionImages = [] // the array for the image names in question
+ for (var i = 0; i < imgs.length; i++) {
+ if (!imgs[i].src.includes('brokenfile')) {
+ var filePart = imgs[i].src.split('/') // splits the link by "/"
+ filePart = filePart[filePart.length - 1] // the last one is the image name
+ questionImages.push(
+ decodeURI(
+ SUtils.RemoveUnnecesarySpaces(SUtils.ShortenString(filePart, 30))
+ )
+ ) // decodes uri codes, and removes exess spaces, and shortening it
+ }
+ }
+ if (questionImages.length > 0) {
+ return {
+ type: 'image',
+ images: questionImages,
+ }
+ } else {
+ return {
+ type: 'simple',
+ }
+ }
+ }
+
+ // adds image names to image nodes
+ function AddImageNamesToImages(imgs) {
+ for (var i = 0; i < imgs.length; i++) {
+ if (!imgs[i].src.includes('brokenfile')) {
+ // TODO: add this to shadowroot
+ var filePart = imgs[i].src.split('/') // splits the link by "/"
+ // console.log(imgs[i].src.split("base64,")[1])
+ // TODO: base64
+ filePart = filePart[filePart.length - 1] // the last one is the image name
+ var appendTo = imgs[i].parentNode // it will be appended here
+ var mainDiv = document.createElement('div')
+ var fileName = SUtils.ShortenString(decodeURI(filePart), 15) // shortening name, couse it can be long as fuck
+ var textNode = document.createTextNode('(' + fileName + ')')
+ mainDiv.appendChild(textNode)
+ appendBelowElement(appendTo, mainDiv)
+ }
+ }
+ }
+
+ // this function adds basic hotkeys for video controll.
+ function AddVideoHotkeys(url) {
+ var seekTime = 20
+ document.addEventListener('keydown', function(e) {
+ try {
+ var video = MPM.GetVideo()
+ var keyCode = e.keyCode // getting keycode
+ if (keyCode === 32) {
+ // if the keycode is 32 (space)
+ e.preventDefault() // preventing default action (space scrolles down)
+ if (video.paused && video.buffered.length > 0) {
+ video.play()
+ } else {
+ video.pause()
+ }
+ }
+ if (keyCode === 39) {
+ // rigth : 39
+ video.currentTime += seekTime
+ }
+ if (keyCode === 37) {
+ // left : 37
+ video.currentTime -= seekTime
+ }
+ } catch (err) {
+ Log('Hotkey error.')
+ Log(err.message)
+ }
+ })
+ var toadd = MPM.GetVideoElement()
+ var node = CreateNodeWithText(toadd, texts.videoHelp)
+ node.style.margin = '5px 5px 5px 5px' // fancy margin
+ }
+
+ // removes stuff like " a. q1" -> "q1"
+ function RemoveLetterMarking(inp) {
+ let dotIndex = inp.indexOf('.')
+ let doubledotIndex = inp.indexOf(':')
+ let maxInd = 4 // inp.length * 0.2;
+
+ if (dotIndex < maxInd) {
+ return SUtils.RemoveUnnecesarySpaces(
+ inp.substr(inp.indexOf('.') + 1, inp.length)
+ )
+ } else if (doubledotIndex < maxInd) {
+ return SUtils.RemoveUnnecesarySpaces(
+ inp.substr(inp.indexOf(':') + 1, inp.length)
+ )
+ } else {
+ return inp
+ }
+ }
+
+ // highlights the possible solutions to the current question
+ function HighLightAnswer(results, currQuestionNumber) {
+ try {
+ if (results.length > 0) {
+ var answers = RPM.GetAllAnswer(currQuestionNumber) // getting all answers
+ var toColor = [] // the numberth in the array will be colored, and .length items will be colored
+ var type = '' // type of the question. radio or ticbox or whatitscalled
+ for (let i = 0; i < answers.length; i++) {
+ // going thtough answers
+ if (
+ answers[i].tagName &&
+ answers[i].tagName.toLowerCase() === 'div'
+ ) {
+ // if its not null and is "div"
+ var correct = results[0].q.A.toLowerCase() // getting current correct answer from data
+ var answer = answers[i].innerText.replace(/\n/g, '').toLowerCase() // getting current answer
+
+ // removing stuff like "a."
+ answer = RemoveLetterMarking(answer)
+
+ if (
+ SUtils.EmptyOrWhiteSpace(correct) ||
+ SUtils.EmptyOrWhiteSpace(answer)
+ ) {
+ continue
+ }
+
+ if (
+ SUtils.NormalizeSpaces(
+ SUtils.RemoveUnnecesarySpaces(correct)
+ ).includes(answer)
+ ) {
+ // if the correct answer includes the current answer
+ toColor.push(i) // adding the index
+ type = MPM.GetInputType(answers, i) // setting the type
+ }
+ }
+ }
+ if (results[0].match === 100) {
+ // if the result is 100% correct
+ if (type !== 'radio' || toColor.length === 1) {
+ // FIXME why not radio
+ for (let i = 0; i < toColor.length; i++) {
+ // going through "toColor"
+ let highlight = createHoverOver(answers[toColor[i]])
+ Object.assign(highlight.style, {
+ border: '7px solid rgba(100, 240, 100, 0.8)',
+ borderRadius: '10px',
+ margin: '-13px 0 0 -8px',
+ })
+ }
+ }
+ } // 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 {{{
+ function ClearAllMessages() {
+ overlay.querySelectorAll('#scriptMessage').forEach(x => x.remove())
+ }
+
+ // 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 === '') {
+ // if msg is empty
+ return
+ }
+ msgItem = [
+ [
+ {
+ m: simpleMessageText,
+ },
+ ],
+ ]
+ isSimpleMessage = true
+ }
+
+ var appedtTo = overlay // will be appended here
+ var width = window.innerWidth - window.innerWidth / 6 // with of the box
+ var startFromTop = 25 // top distance
+
+ var mainDiv = document.createElement('div') // the main divider, wich items will be attached to
+ mainDiv.setAttribute('id', 'messageMainDiv')
+ if (funct) {
+ // if there is a function as parameter
+ addEventListener(mainDiv, 'click', funct) // adding it as click
+ }
+ // lotsa crap style
+ SetStyle(mainDiv, {
+ position: 'fixed',
+ zIndex: 999999,
+ textAlign: 'center',
+ width: width + 'px',
+ padding: '0px',
+ background: '#222d32',
+ color: '#ffffff',
+ borderColor: '#035a8f',
+ border: 'none',
+ top: startFromTop + 'px',
+ left: (window.innerWidth - width) / 2 + 'px',
+ opacity: '0.9',
+ })
+ mainDiv.setAttribute('id', 'scriptMessage')
+ var matchPercent = msgItem[0][0].p
+ if (isSimpleMessage) {
+ var simpleMessageParagrapg = document.createElement('p') // new paragraph
+ simpleMessageParagrapg.style.margin = defMargin // fancy margin
+
+ var mesageNode = document.createElement('p') // new paragraph
+ mesageNode.innerHTML = simpleMessageText.replace(/\n/g, '')
+ simpleMessageParagrapg.appendChild(mesageNode)
+ mesageNode.style.margin = defMargin // fancy margin
+
+ mainDiv.appendChild(simpleMessageParagrapg) // adding text box to main div
+ } else {
+ // if its a fucking complicated message
+ // TABLE SETUP ------------------------------------------------------------------------------------------------------------
+ var table = document.createElement('table')
+ table.style.width = '100%'
+ // ROWS -----------------------------------------------------------------------------------------------------
+ var rowOne = table.insertRow() // previous suggestion, question text, and prev question
+ var rowTwo = table.insertRow() // next question button
+ var rowThree = table.insertRow() // next suggetsion button
+ // CELLS -----------------------------------------------------------------------------------------------------
+ // row one
+ var numberTextCell = rowOne.insertCell()
+ var questionCell = rowOne.insertCell() // QUESTION CELL
+ questionCell.setAttribute('id', 'questionCell')
+ questionCell.rowSpan = 3
+ questionCell.style.width = '90%'
+ var prevQuestionCell = rowOne.insertCell()
+ // row two
+ var percentTextCell = rowTwo.insertCell()
+ var nextQuestionCell = rowTwo.insertCell()
+ // row three
+ var prevSuggestionCell = rowThree.insertCell()
+ var nextSuggestionCell = rowThree.insertCell()
+ // adding finally
+ mainDiv.appendChild(table)
+ // PERCENT TEXT SETUP -----------------------------------------------------------------------------------------------------
+ var percentTextBox = CreateNodeWithText(percentTextCell, '')
+ percentTextBox.setAttribute('id', 'percentTextBox')
+
+ if (matchPercent) {
+ // if match percent param is not null
+ percentTextBox.innerText = matchPercent + '%'
+ }
+ // NUMBER SETUP -----------------------------------------------------------------------------------------------------
+ var numberTextBox = CreateNodeWithText(numberTextCell, '1.')
+ numberTextBox.setAttribute('id', 'numberTextBox')
+
+ // ANSWER NODE SETUP -------------------------------------------------------------------------------------------------------------
+ var questionTextElement = CreateNodeWithText(
+ questionCell,
+ 'ur question goes here, mister OwO'
+ )
+ questionTextElement.setAttribute('id', 'questionTextElement')
+
+ // BUTTON SETUP -----------------------------------------------------------------------------------------------------------
+ var currItem = 0
+ var currRelevantQuestion = 0
+
+ const GetRelevantQuestion = () => {
+ // returns the currItemth questions currRelevantQuestionth relevant question
+ return msgItem[currItem][currRelevantQuestion]
+ }
+
+ const ChangeCurrItemIndex = to => {
+ currItem += to
+ if (currItem < 0) {
+ currItem = 0
+ }
+ if (currItem > msgItem.length - 1) {
+ currItem = msgItem.length - 1
+ }
+ currRelevantQuestion = 0
+ }
+
+ const ChangeCurrRelevantQuestionIndex = to => {
+ currRelevantQuestion += to
+ if (currRelevantQuestion < 0) {
+ currRelevantQuestion = 0
+ }
+ if (currRelevantQuestion > msgItem[currItem].length - 1) {
+ currRelevantQuestion = msgItem[currItem].length - 1
+ }
+ }
+
+ const SetQuestionText = () => {
+ var relevantQuestion = GetRelevantQuestion()
+ questionTextElement.innerText = relevantQuestion.m
+ if (currItem === 0 && currRelevantQuestion === 0) {
+ numberTextBox.innerText = currRelevantQuestion + 1 + '.'
+ } else {
+ numberTextBox.innerText =
+ currItem + 1 + './' + (currRelevantQuestion + 1) + '.'
+ }
+ percentTextBox.innerText = relevantQuestion.p + '%'
+ }
+
+ var buttonMargin = '2px 2px 2px 2px' // uniform button margin
+ if (msgItem[currItem].length > 1) {
+ // PREV SUGG BUTTON ------------------------------------------------------------------------------------------------------------
+ var prevSuggButton = CreateNodeWithText(
+ prevSuggestionCell,
+ '<',
+ 'button'
+ )
+ prevSuggButton.style.margin = buttonMargin // fancy margin
+
+ prevSuggButton.addEventListener('click', function() {
+ ChangeCurrRelevantQuestionIndex(-1)
+ SetQuestionText()
+ })
+ // NEXT SUGG BUTTON ------------------------------------------------------------------------------------------------------------
+ var nextSuggButton = CreateNodeWithText(
+ nextSuggestionCell,
+ '>',
+ 'button'
+ )
+ nextSuggButton.style.margin = buttonMargin // fancy margin
+
+ nextSuggButton.addEventListener('click', function() {
+ ChangeCurrRelevantQuestionIndex(1)
+ SetQuestionText()
+ })
+ }
+ // deciding if has multiple questions ------------------------------------------------------------------------------------------------
+ if (msgItem.length === 1) {
+ SetQuestionText()
+ } else {
+ // if there are multiple items to display
+ // PREV QUESTION BUTTON ------------------------------------------------------------------------------------------------------------
+ var prevButton = CreateNodeWithText(prevQuestionCell, '^', 'button')
+ prevButton.style.margin = buttonMargin // fancy margin
+
+ // event listener
+ prevButton.addEventListener('click', function() {
+ ChangeCurrItemIndex(-1)
+ SetQuestionText()
+ })
+ // NEXT QUESTION BUTTON ------------------------------------------------------------------------------------------------------------
+ var nextButton = CreateNodeWithText(nextQuestionCell, 'ˇ', 'button')
+ nextButton.style.margin = buttonMargin // fancy margin
+
+ // event listener
+ nextButton.addEventListener('click', function() {
+ ChangeCurrItemIndex(1)
+ SetQuestionText()
+ })
+ SetQuestionText()
+ }
+ }
+ appedtTo.appendChild(mainDiv) // THE FINAL APPEND
+
+ // setting some events
+ // addEventListener(window, 'scroll', function () {
+ // mainDiv.style.top = (pageYOffset + startFromTop) + 'px';
+ // })
+ addEventListener(window, 'resize', function() {
+ mainDiv.style.left = (window.innerWidth - width) / 2 + 'px'
+ })
+ var timeOut
+ if (timeout && timeout > 0) {
+ // setting timeout if not zero or null
+ timeOut = setTimeout(function() {
+ mainDiv.parentNode.removeChild(mainDiv)
+ }, timeout * 1000)
+ }
+ // middle click close event listener
+ addEventListener(mainDiv, 'mousedown', function(e) {
+ if (e.which === 2) {
+ mainDiv.parentNode.removeChild(mainDiv)
+ if (timeOut) {
+ clearTimeout(timeOut)
+ }
+ }
+ })
+ } catch (e) {
+ Exception(e, 'script error at showing message:')
+ }
+ }
+
+ // shows a fancy menu
+ function ShowMenu() {
+ try {
+ var appedtTo = overlay // will be appended here
+
+ // mainDiv.style.left = (window.innerWidth - width) / 2 + 'px';
+
+ var menuButtonDiv = document.createElement('div')
+ menuButtonDiv.setAttribute('id', 'menuButtonDiv')
+ SetStyle(menuButtonDiv, {
+ width: '600px',
+ // height: buttonHeight + 'px',
+ top: window.innerHeight - 120 + 'px',
+ left: '10px',
+ zIndex: 999999,
+ position: 'fixed',
+ textAlign: 'center',
+ padding: '0px',
+ margin: '0px',
+ background: '#262626',
+ })
+
+ var tbl = document.createElement('table')
+ tbl.style.margin = '5px 5px 5px 5px'
+ tbl.style.textAlign = 'left'
+ tbl.style.width = '98%'
+ menuButtonDiv.appendChild(tbl)
+
+ var buttonRow = tbl.insertRow()
+ var buttonCell = buttonRow.insertCell()
+ buttonCell.style.textAlign = 'center'
+
+ let buttonStyle = {
+ position: '',
+ margin: '5px 5px 5px 5px',
+ border: 'none',
+ backgroundColor: '#222d32',
+ color: '#ffffff',
+ cursor: 'pointer',
+ }
+ // help button ----------------------------------------------------------------------------------------------------------------
+ let helpButton = CreateNodeWithText(buttonCell, texts.help, 'button')
+ SetStyle(helpButton, buttonStyle)
+
+ helpButton.addEventListener('click', function() {
+ ShowHelp()
+ }) // adding clicktextNode
+
+ // site link ----------------------------------------------------------------------------------------------------------------
+
+ let contributeLink = CreateNodeWithText(
+ buttonCell,
+ texts.contribute,
+ 'button'
+ )
+ contributeLink.title = texts.contributeTitle
+ SetStyle(contributeLink, buttonStyle)
+
+ contributeLink.addEventListener('click', function() {
+ openInTab(serverAdress + 'contribute?scriptMenu', {
+ active: true,
+ })
+ })
+
+ // pw request ----------------------------------------------------------------------------------------------------------------
+
+ let pwRequest = CreateNodeWithText(buttonCell, texts.pwRequest, 'button')
+ pwRequest.title = texts.newPWTitle
+ SetStyle(pwRequest, buttonStyle)
+
+ pwRequest.addEventListener('click', function() {
+ openInTab(serverAdress + 'pwRequest', {
+ active: true,
+ })
+ })
+
+ // site link ----------------------------------------------------------------------------------------------------------------
+
+ let siteLink = CreateNodeWithText(
+ buttonCell,
+ texts.websiteBugreport,
+ 'button'
+ )
+ SetStyle(siteLink, buttonStyle)
+
+ siteLink.addEventListener('click', function() {
+ openInTab(serverAdress + 'menuClick', {
+ active: true,
+ })
+ })
+
+ // donate link ----------------------------------------------------------------------------------------------------------------
+ let donateLink = CreateNodeWithText(buttonCell, texts.donate, 'button')
+ SetStyle(donateLink, buttonStyle)
+
+ donateLink.addEventListener('click', function() {
+ openInTab(serverAdress + 'donate?scriptMenu', {
+ active: true,
+ })
+ })
+
+ addEventListener(window, 'resize', function() {
+ menuButtonDiv.style.top = window.innerHeight - 70 + 'px'
+ })
+
+ // INFO TABEL --------------------------------------------------------------------
+ var itbl = document.createElement('table')
+ SetStyle(itbl, {
+ margin: '5px 5px 5px 5px',
+ textAlign: 'left',
+ width: '98%',
+ })
+ menuButtonDiv.appendChild(itbl)
+ var ibuttonRow = tbl.insertRow()
+ var ibuttonCell = ibuttonRow.insertCell()
+ ibuttonCell.style.textAlign = 'center'
+
+ // INFO DIV ---------------------------------------------------------------------------------
+ let infoDiv = CreateNodeWithText(ibuttonCell, texts.loading, 'span')
+ infoDiv.setAttribute('id', 'infoMainDiv')
+ SetStyle(infoDiv, {
+ color: '#ffffff',
+ margin: '5px',
+ })
+
+ // login div ----------------------------------------------------------------------------------------------------------------
+ const loginDiv = document.createElement('div')
+ loginDiv.style.display = 'none'
+ loginDiv.setAttribute('id', 'loginDiv')
+ const loginButton = document.createElement('button')
+ loginButton.innerText = texts.login
+ const loginInput = document.createElement('input')
+ loginInput.type = 'text'
+ loginInput.style.width = '400px'
+ loginInput.style.textAlign = 'center'
+ const clientId = getVal('clientId')
+ if (clientId && clientId.toString()[0] !== '0') {
+ loginInput.value = clientId || ''
+ loginButton.innerText = texts.requestPWInsteadOfLogin
+ }
+ loginDiv.appendChild(loginInput)
+ loginDiv.appendChild(loginButton)
+
+ SetStyle(loginButton, buttonStyle)
+
+ loginInput.addEventListener('keyup', e => {
+ console.log(e.target.value)
+ if (e.target.value === clientId) {
+ loginButton.innerText = texts.requestPWInsteadOfLogin
+ } else if (e.target.value !== '') {
+ loginButton.innerText = texts.login
+ }
+ })
+
+ loginButton.addEventListener('click', function() {
+ if (loginInput.value === clientId.toString()) {
+ openInTab(serverAdress + 'getVeteranPw?cid=' + clientId, {
+ active: true,
+ })
+ } else {
+ Auth(loginInput.value)
+ }
+ })
+
+ ibuttonCell.appendChild(loginDiv)
+
+ // irc button ----------------------------------------------------------------------------------------------------------------
+ let ircButton = CreateNodeWithText(ibuttonCell, texts.ircButton, 'button')
+ SetStyle(ircButton, buttonStyle)
+ ircButton.style.display = 'none'
+ ircButton.setAttribute('id', 'ircButton')
+
+ ircButton.addEventListener('click', function() {
+ openInTab(ircAddress, {
+ active: true,
+ })
+ })
+
+ // retry button ----------------------------------------------------------------------------------------------------------------
+ let retryButton = CreateNodeWithText(ibuttonCell, texts.retry, 'button')
+ SetStyle(retryButton, buttonStyle)
+ retryButton.style.display = 'none'
+ retryButton.setAttribute('id', 'retryButton')
+
+ retryButton.addEventListener('click', function() {
+ menuButtonDiv.style.background = '#262626'
+ infoDiv.innerText = texts.loading
+ retryButton.style.display = 'none'
+ ircButton.style.display = 'none'
+ ConnectToServer(AfterLoad)
+ })
+
+ // window resize event listener ---------------------------------------
+ addEventListener(window, 'resize', function() {
+ menuButtonDiv.style.top = window.innerHeight - 70 + 'px'
+ })
+
+ // APPEND EVERYTHING
+ appedtTo.appendChild(menuButtonDiv)
+ } catch (e) {
+ Exception(e, 'script error at showing menu:')
+ }
+ }
+
+ // : }}}
+
+ // : Generic utils {{{
+ function GetId() {
+ let currId = getVal('clientId')
+ if (currId) {
+ return currId
+ } else {
+ currId = new Date()
+ currId = currId.getTime() + Math.floor(Math.random() * 1000000000000)
+ currId = currId.toString().split('')
+ currId.shift()
+ currId = '0' + currId.join('')
+ setVal('clientId', currId)
+ return currId
+ }
+ }
+
+ function SafeGetElementById(id, next) {
+ let element = overlay.querySelector('#' + id)
+ if (element) {
+ next(element)
+ } else {
+ Log(`Unable to safe get element by id: ${id}`)
+ }
+ }
+
+ function SetStyle(target, style) {
+ Object.keys(style)
+ .sort()
+ .forEach(key => {
+ target.style[key] = style[key]
+ })
+ }
+
+ 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 GetXHRInfos() {
+ const now = new Date().getTime()
+ const lastCheck = getVal('lastInfoCheckTime')
+ if (!lastCheck) {
+ setVal('lastInfoCheckTime', now)
}
- // : }}}
+ let lastInfo = { result: 'noLastInfo' }
+ try {
+ lastInfo = JSON.parse(getVal('lastInfo'))
+ } catch (e) {
+ console.info(e)
+ }
+ if (
+ lastInfo.result !== 'success' ||
+ now > lastCheck + infoExpireTime * 1000
+ ) {
+ return new Promise((resolve, reject) => {
+ const url =
+ apiAdress +
+ 'infos?version=true&motd=true&subjinfo=true&cversion=' +
+ info().script.version +
+ '&cid=' +
+ GetId()
- // 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,
+ xmlhttpRequest({
+ method: 'GET',
+ url: url,
+ crossDomain: true,
+ xhrFields: { withCredentials: true },
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ onload: function(response) {
+ try {
+ setVal('lastInfoCheckTime', now)
+ const res = JSON.parse(response.responseText)
+ setVal('lastInfo', response.responseText)
+ resolve(res)
+ } catch (e) {
+ Log('Errro paring JSON in GetXHRInfos')
+ Log(response.responseText)
+ Log(e)
+ reject(e)
+ }
+ },
+ onerror: e => {
+ Log('Info get Error', e)
+ reject(e)
+ },
+ })
+ })
+ } else {
+ return new Promise((resolve, reject) => {
+ try {
+ resolve(lastInfo)
+ } catch (e) {
+ Log('Errro paring JSON in GetXHRInfos, when using old data!')
+ Log(e)
+ reject(e)
+ }
+ })
+ }
+ }
- // 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.
+ function GetXHRQuestionAnswer(question) {
+ return new Promise((resolve, reject) => {
+ let url = apiAdress + 'ask?'
+ let params = []
+ Object.keys(question).forEach(key => {
+ let val = question[key]
+ if (typeof val !== 'string') {
+ val = JSON.stringify(val)
+ }
+ params.push(key + '=' + encodeURIComponent(val))
+ })
+ url +=
+ params.join('&') +
+ '&cversion=' +
+ info().script.version +
+ '&cid=' +
+ GetId()
- // 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.
+ xmlhttpRequest({
+ method: 'GET',
+ url: url,
+ onload: function(response) {
+ try {
+ let res = JSON.parse(response.responseText)
+ // FIXME: check if res is a valid answer array
+ // res.json({
+ // result: r,
+ // success: true
+ // })
+ // ERROR:
+ // res.json({
+ // message: `Invalid question :(`,
+ // result: [],
+ // recievedData: JSON.stringify(req.query),
+ // success: false
+ // })
+ resolve(res.result)
+ } catch (e) {
+ reject(e)
+ }
+ },
+ onerror: e => {
+ Log('Errro paring JSON in GetXHRQuestionAnswer')
+ Log(e)
+ reject(e)
+ reject(e)
+ },
+ })
+ })
+ }
- // 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
+ function SendXHRMessage(path, message) {
+ // message = SUtils.RemoveSpecialChars(message) // TODO: check this
+ if (typeof message === 'object') {
+ message = JSON.stringify(message)
+ }
+ const url = apiAdress + path
+ return new Promise((resolve, reject) => {
+ xmlhttpRequest({
+ method: 'POST',
+ url: url,
+ crossDomain: true,
+ xhrFields: { withCredentials: true },
+ data: message,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ onerror: function(e) {
+ Log('Data send error', e)
+ reject(e)
+ },
+ onload: resp => {
+ try {
+ const res = JSON.parse(resp.responseText)
+ resolve(res)
+ } catch (e) {
+ Log('Error paring JSON in SendXHRMessage')
+ Log(resp.responseText)
+ Log(e)
+ reject(e)
+ }
+ },
+ })
+ })
+ }
- // 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.
+ function OpenErrorPage(e) {
+ let path = 'lred'
+ try {
+ if (e.message || e.stack) {
+ path += '?'
+ }
+ if (e.message) {
+ path += 'msg:' + SUtils.SimplifyQuery(e.message)
+ }
+ if (e.stack) {
+ path += '___stack:' + SUtils.SimplifyStack(e.stack)
+ }
+ path += '___version:' + info().script.version
+ path = SUtils.RemoveSpecialChars(path)
+ } catch (e) {
+ Exception(e, 'error at setting error stack/msg link')
+ }
+ path = path.replace(/ /g, '_')
+ openInTab(serverAdress + path, {
+ active: true,
+ })
+ }
- // O deepest wound of all that he should die
- // On that darkest day.
+ // : }}}
+
+ // : Help {{{
+
+ // shows some neat help
+ function ShowHelp() {
+ openInTab(serverAdress + 'manual?scriptMenu', {
+ active: true,
+ })
+ }
+
+ // : }}}
+
+ // I am not too proud to cry that He and he
+ // Will never never go out of my mind.
+ // All his bones crying, and poor in all but pain,
+
+ // Being innocent, he dreaded that he died
+ // Hating his God, but what he was was plain:
+ // An old kind man brave in his burning pride.
+
+ // The sticks of the house were his; his books he owned.
+ // Even as a baby he had never cried;
+ // Nor did he now, save to his secret wound.
+
+ // Out of his eyes I saw the last light glide.
+ // Here among the liught of the lording sky
+ // An old man is with me where I go
+
+ // Walking in the meadows of his son's eye
+ // Too proud to cry, too frail to check the tears,
+ // And caught between two nights, blindness and death.
+
+ // O deepest wound of all that he should die
+ // On that darkest day.
})() // eslint-disable-line