diff --git a/stable.user.js b/stable.user.js
index 0cea968..91a7e9d 100755
--- a/stable.user.js
+++ b/stable.user.js
@@ -31,6 +31,7 @@
// @match https://qmining.frylabs.net/*
// @match http://qmining.frylabs.net/*
// @noframes
+// @run-at document-start
// @grant GM_getResourceText
// @grant GM_info
// @grant GM_getValue
@@ -38,6 +39,7 @@
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @grant GM_openInTab
+// @grant unsafeWindow
// @license GNU General Public License v3.0 or later
// @supportURL qmining.frylabs.net
// @contributionURL qmining.frylabs.net
@@ -208,6 +210,86 @@
// 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(unsafeWindow.Error.hasOwnProperty('stackTraceLimit')) {
+ Reflect.defineProperty(unsafeWindow.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 = unsafeWindow.Element.prototype.attachShadow;
+ const attachShadowProxy = new Proxy(original_attachShadow, shadowGetHandler);
+ unsafeWindow.Element.prototype.attachShadow = attachShadowProxy;
+
+ const getShadowRootProxy = new Proxy(Object.getOwnPropertyDescriptor(unsafeWindow.Element.prototype, 'shadowRoot').get, shadowGetHandler);
+ Object.defineProperty(unsafeWindow.Element.prototype, 'shadowRoot', { get: getShadowRootProxy });
+
+ const getHostHandler = { apply: function() { let result = apply(...arguments); return (result === shadowRootNewHost) ? shadowRootHost : result; } };
+ const getHostProxy = new Proxy(Object.getOwnPropertyDescriptor(unsafeWindow.ShadowRoot.prototype, 'host').get, getHostHandler);
+ Object.defineProperty(unsafeWindow.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 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') }
@@ -681,7 +763,12 @@
console.time('main')
timerStarted = true
- Init()
+ if(document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', Init)
+ }
+ else {
+ Init()
+ }
}
function AfterLoad () {
@@ -716,8 +803,8 @@
}
if (forceTestPage || forceResultPage || forceDefaultPage) {
- if (document.getElementById('scriptMessage')) {
- document.getElementById('scriptMessage').style.background = 'green'
+ if (overlay.querySelector('#scriptMessage')) {
+ overlay.querySelector('#scriptMessage').style.background = 'green'
}
}
}
@@ -852,7 +939,7 @@
lastestVersion = inf.version
motd = inf.motd
subjInfo = inf.subjinfo
- document.getElementById('infoMainDiv').innerText = `${subjInfo.subjects} tárgy, ${subjInfo.questions} kérdés. Felh #${inf.uid}`
+ 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(() => {
@@ -1299,7 +1386,12 @@
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"
- answers[toColor[i]].style.backgroundColor = '#8cff66'
+ 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
@@ -1328,11 +1420,7 @@
// : Minor UI stuff {{{
function ClearAllMessages () {
- let elem = document.getElementById('scriptMessage')
- while (elem) {
- elem.parentNode.removeChild(elem)
- elem = document.getElementById('scriptMessage')
- }
+ overlay.querySelectorAll('#scriptMessage').forEach(x => x.remove())
}
// shows a message with "msg" text, "matchPercent" tip and transp, and "timeout" time
@@ -1360,7 +1448,7 @@
isSimpleMessage = true
}
- var appedtTo = document.body // will be appended here
+ var appedtTo = overlay // will be appended here
var width = window.innerWidth - window.innerWidth / 6 // with of the box
var startFromTop = 25 // top distance
@@ -1551,7 +1639,7 @@
// shows a fancy menu
function ShowMenu () {
try {
- var appedtTo = document.body // will be appended here
+ var appedtTo = overlay // will be appended here
// mainDiv.style.left = (window.innerWidth - width) / 2 + 'px';
@@ -1763,7 +1851,7 @@
}
function SafeGetElementById (id, next) {
- let element = document.getElementById(id)
+ let element = overlay.querySelector('#' + id)
if (element) {
next(element)
} else {