update
This commit is contained in:
parent
d7a20264fb
commit
f9383a99c9
6 changed files with 205 additions and 100 deletions
|
@ -1,4 +1,4 @@
|
||||||
function awnserQuestion(awnserData){
|
function answerQuestion(answerData){
|
||||||
const question = document.querySelector(".question:not(.hidden)");
|
const question = document.querySelector(".question:not(.hidden)");
|
||||||
if (!question) {
|
if (!question) {
|
||||||
return;
|
return;
|
||||||
|
@ -12,15 +12,14 @@ function awnserQuestion(awnserData){
|
||||||
if (!answersDom) return;
|
if (!answersDom) return;
|
||||||
const answers = answersDom.children;
|
const answers = answersDom.children;
|
||||||
|
|
||||||
for (let answer of answers) {
|
for (let answer of Array.from(answers)) {
|
||||||
const input = answer.querySelector("input");
|
const input = answer.querySelector("input");
|
||||||
if (!input) continue;
|
if (!input) continue;
|
||||||
input.checked = false;
|
input.checked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const correctAnswers = findAnswers(awnserData, questionText, answers);
|
const correctAnswers = findAnswers(answerData, questionText, answers);
|
||||||
if (correctAnswers.length === 0) {
|
if (correctAnswers.length === 0) {
|
||||||
GM_log("no awnser")
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,18 +30,17 @@ function awnserQuestion(awnserData){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function findAnswers(awnserData, questionText, answers) {
|
function findAnswers(answerData, questionText, answers) {
|
||||||
if (awnserData === null) {
|
if (answerData === null) {
|
||||||
alert("No chapter data loaded. Maybe the fetch failed?!");
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const correctAnswers = [];
|
const correctAnswers = [];
|
||||||
for (let entry of awnserData) {
|
for (let entry of answerData) {
|
||||||
if (matchAwnser(questionText.trim(), entry.question.trim())) {
|
if (matchAnswer(questionText.trim(), entry.question.trim())) {
|
||||||
for (let availableAnswer of answers) {
|
for (let availableAnswer of answers) {
|
||||||
for (let possibleAnswer of entry.answers) {
|
for (let possibleAnswer of entry.answers) {
|
||||||
if (matchAwnser(availableAnswer.textContent.trim(), possibleAnswer)) {
|
if (matchAnswer(availableAnswer.textContent.trim(), possibleAnswer)) {
|
||||||
correctAnswers.push(availableAnswer);
|
correctAnswers.push(availableAnswer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,11 +51,11 @@ function findAnswers(awnserData, questionText, answers) {
|
||||||
return correctAnswers;
|
return correctAnswers;
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchAwnser(textA, textB) {
|
function matchAnswer(textA, textB) {
|
||||||
const replaceRegex = /[^\w]/gi;
|
const replaceRegex = /[^\w]/gi;
|
||||||
textA = textA.replace(replaceRegex, "");
|
textA = textA.replace(replaceRegex, "");
|
||||||
textB = textB.replace(replaceRegex, "");
|
textB = textB.replace(replaceRegex, "");
|
||||||
return (textA === textB);
|
return (textA === textB);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.awnserQuestion = awnserQuestion;
|
window.answerQuestion = answerQuestion;
|
166
fetch.js
166
fetch.js
|
@ -1,57 +1,131 @@
|
||||||
function fetchAwnsers(awnserURL = "") {
|
const IS_QUESTION_REGEX = /^[0-9]+\. (.*)$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches answers from the specified URL.
|
||||||
|
* @param {string} [answerURL=""] - The URL to fetch answers from.
|
||||||
|
* @returns {Promise<Array<Answer>>} A Promise that resolves with the fetched answers.
|
||||||
|
*/
|
||||||
|
function fetchAnswers(answerURL = "") {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
GM_xmlhttpRequest({
|
GM_xmlhttpRequest({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: awnserURL,
|
url: answerURL,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/html",
|
"Content-Type": "text/html",
|
||||||
},
|
},
|
||||||
onload: function (response) {
|
onload: function (response) {
|
||||||
awnserImgs = new Map();
|
parseAnswers(response, resolve);
|
||||||
const results = [];
|
},
|
||||||
const parser = new DOMParser();
|
onerror: function (error) {
|
||||||
const virtDom = parser.parseFromString(
|
reject(error);
|
||||||
response.responseText,
|
},
|
||||||
"text/html"
|
});
|
||||||
);
|
});
|
||||||
|
|
||||||
let answersDom = virtDom.querySelector(".pf-content");
|
|
||||||
if (!answersDom) {
|
|
||||||
answersDom = virtDom.querySelector(".thecontent");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {GMXMLHttpRequestResponse} response
|
||||||
|
* @param {(value: Answer[] | PromiseLike<Answer[]>) => void} resolve
|
||||||
|
*/
|
||||||
|
function parseAnswers(response, resolve) {
|
||||||
|
const results = [];
|
||||||
|
const allAnswersElement = getAllAnswersElement(response);
|
||||||
|
|
||||||
let index = -1;
|
let index = -1;
|
||||||
for (let childDom of answersDom.children) {
|
for (let child of Array.from(allAnswersElement.children)) {
|
||||||
index++;
|
index++;
|
||||||
|
const result = parseAnswerElement(index, child, allAnswersElement);
|
||||||
if (childDom.tagName === "P" || childDom.tagName === "STRONG") {
|
results.push(result);
|
||||||
// maybe a question question
|
|
||||||
let innerDom = childDom.querySelector("strong");
|
|
||||||
if (innerDom === null) {
|
|
||||||
if (!childDom.textContent) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
innerDom = childDom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const textContent = innerDom.textContent.trim();
|
resolve(results);
|
||||||
const matches = textContent.match(/^[0-9]+\. (.*)$/);
|
|
||||||
if (matches !== null) {
|
|
||||||
const questionText = matches[1];
|
|
||||||
|
|
||||||
// most likely a question
|
|
||||||
let nextChild = answersDom.children[index + 1];
|
|
||||||
|
|
||||||
if (nextChild.tagName === "P") {
|
|
||||||
nextChild = answersDom.children[index + 2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextChild === null) continue;
|
/**
|
||||||
|
* @param {GMXMLHttpRequestResponse} response
|
||||||
|
* @returns {Element}
|
||||||
|
*/
|
||||||
|
function getAllAnswersElement(response) {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const virtualDOM = parser.parseFromString(response.responseText, "text/html");
|
||||||
|
|
||||||
if (nextChild.tagName === "UL") {
|
let answersElement = virtualDOM.querySelector(".pf-content");
|
||||||
// most likely the awnser
|
if (!answersElement) {
|
||||||
|
answersElement = virtualDOM.querySelector(".thecontent");
|
||||||
|
}
|
||||||
|
return answersElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} index
|
||||||
|
* @param {Element} allAnswersElement
|
||||||
|
* @param {Element} element
|
||||||
|
* @returns {Answer}
|
||||||
|
*/
|
||||||
|
function parseAnswerElement(index, element, allAnswersElement) {
|
||||||
|
// Check for Possible Tags
|
||||||
|
if (!(element.tagName === "P" || element.tagName === "STRONG")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Question Element
|
||||||
|
/** @type {Element} */
|
||||||
|
let questionElement = element.querySelector("strong");
|
||||||
|
if (questionElement === null) {
|
||||||
|
if (!element.textContent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
questionElement = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Question
|
||||||
|
const questionText = parseQuestion(questionElement);
|
||||||
|
if (questionText === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Awsners
|
||||||
|
const answersElement = getAnswersElement(index, allAnswersElement);
|
||||||
|
if (answersElement === null || answersElement.tagName === "UL") return;
|
||||||
|
|
||||||
|
return {
|
||||||
|
question: questionText,
|
||||||
|
answers: getAnswers(answersElement),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Element} questionElement
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
function parseQuestion(questionElement) {
|
||||||
|
const textContent = questionElement.textContent.trim();
|
||||||
|
const matches = textContent.match(IS_QUESTION_REGEX);
|
||||||
|
return matches !== null ? matches[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} index
|
||||||
|
* @param {Element} allAnswersElement
|
||||||
|
* @returns {Element}
|
||||||
|
*/
|
||||||
|
function getAnswersElement(index, allAnswersElement) {
|
||||||
|
let answersElement = allAnswersElement.children[index + 1];
|
||||||
|
|
||||||
|
if (answersElement.tagName === "P") {
|
||||||
|
answersElement = allAnswersElement.children[index + 2];
|
||||||
|
}
|
||||||
|
return answersElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Element} answersElement
|
||||||
|
* @returns {Array<string>}
|
||||||
|
*/
|
||||||
|
function getAnswers(answersElement) {
|
||||||
const answers = [];
|
const answers = [];
|
||||||
for (let answerDom of nextChild.querySelectorAll("strong")) {
|
for (let answerDom of Array.from(answersElement.querySelectorAll("strong"))) {
|
||||||
let answerText = answerDom.textContent.trim();
|
let answerText = answerDom.textContent.trim();
|
||||||
if (answerText.endsWith("*")) {
|
if (answerText.endsWith("*")) {
|
||||||
answerText = answerText.substring(0, answerText.length - 1);
|
answerText = answerText.substring(0, answerText.length - 1);
|
||||||
|
@ -59,21 +133,7 @@ function fetchAwnsers(awnserURL = "") {
|
||||||
answers.push(answerText);
|
answers.push(answerText);
|
||||||
}
|
}
|
||||||
|
|
||||||
results.push({
|
return answers;
|
||||||
question: questionText,
|
|
||||||
answers: answers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve(results);
|
|
||||||
},
|
|
||||||
onerror: function(error) {
|
|
||||||
reject(error)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.fetchAwnsers = fetchAwnsers;
|
window.fetchAnswers = fetchAnswers;
|
||||||
|
|
7
jsconfig.json
Normal file
7
jsconfig.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": true,
|
||||||
|
"target": "es6",
|
||||||
|
"lib": ["dom", "es6"]
|
||||||
|
}
|
||||||
|
}
|
19
main.user.js
19
main.user.js
|
@ -6,27 +6,24 @@
|
||||||
// @match *://www.google.com/*
|
// @match *://www.google.com/*
|
||||||
// @match *://www.google.de/*
|
// @match *://www.google.de/*
|
||||||
// @require https://git.euph.dev/SZUT-Dominik/CCNA_Autofill_Userscript/raw/branch/main/fetch.js
|
// @require https://git.euph.dev/SZUT-Dominik/CCNA_Autofill_Userscript/raw/branch/main/fetch.js
|
||||||
// @require https://git.euph.dev/SZUT-Dominik/CCNA_Autofill_Userscript/raw/branch/main/awnser.js
|
// @require https://git.euph.dev/SZUT-Dominik/CCNA_Autofill_Userscript/raw/branch/main/answer.js
|
||||||
// @grant GM_setValue
|
// @grant GM_setValue
|
||||||
// @grant GM_getValue
|
// @grant GM_getValue
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
// @grant GM_log
|
// @version 0.0.12
|
||||||
// @version 0.0.11
|
|
||||||
// @author Dominik Säume
|
// @author Dominik Säume
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|
||||||
const URL_STORAGE_KEY = "itexamanswers.net URL";
|
const URL_STORAGE_KEY = "itexamanswers.net URL";
|
||||||
let awnserData;
|
let answerData;
|
||||||
|
|
||||||
console.log = console.__proto__.log;
|
|
||||||
|
|
||||||
window.addEventListener("keydown", async (event) => {
|
window.addEventListener("keydown", async (event) => {
|
||||||
switch(event.key){
|
switch(event.key){
|
||||||
case "p":
|
case "p":
|
||||||
const oldAwnsersURL = GM_getValue(URL_STORAGE_KEY);
|
const oldAnswersURL = GM_getValue(URL_STORAGE_KEY);
|
||||||
let newAwnsersURL = prompt("Please input the answer url (itexamanswers.net)", oldAwnsersURL);
|
const newAnswersURL = prompt("Please input the answer url (itexamanswers.net)", oldAnswersURL);
|
||||||
GM_setValue(URL_STORAGE_KEY, newAwnsersURL);
|
GM_setValue(URL_STORAGE_KEY, newAnswersURL);
|
||||||
awnserData = await window.fetchAwnsers(newAwnsersURL);
|
answerData = await window.fetchAnswers(newAnswersURL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "n":
|
case "n":
|
||||||
|
@ -34,7 +31,7 @@ window.addEventListener("keydown", async (event) => {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "a":
|
case "a":
|
||||||
window.awnserQuestion(awnserData);
|
window.answerQuestion(answerData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
37
types/GM_xmlhttpRequest.d.js
Normal file
37
types/GM_xmlhttpRequest.d.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* Makes an HTTP request using GM.xmlHttpRequest.
|
||||||
|
* @param {Object} details - Details of the HTTP request.
|
||||||
|
* @param {string} details.method - The HTTP request method (e.g., 'GET', 'POST').
|
||||||
|
* @param {string} details.url - The URL to send the request to.
|
||||||
|
* @param {Object<string, string>} [details.headers] - Additional headers to include in the request.
|
||||||
|
* @param {string|FormData|Document|Blob|ArrayBuffer} [details.data] - The data to send with the request.
|
||||||
|
* @param {string} [details.overrideMimeType] - A MIME type to specify with the request (e.g., "text/html; charset=ISO-8859-1").
|
||||||
|
* @param {string} [details.password] - Password to use for authentication purposes.
|
||||||
|
* @param {string} [details.responseType] - Decode the response as specified type. Accepted values are "", "arraybuffer", "blob", "document", "json", "text", "ms-stream". Default value is "text". See XMLHttpRequest responseType.
|
||||||
|
* @param {boolean} [details.synchronous] - Defaults to false. When true, this is a synchronous request. Be careful: The entire Firefox UI will be locked and frozen until the request completes. In this mode, more data will be available in the return value.
|
||||||
|
* @param {number} [details.timeout] - The number of milliseconds to wait before terminating the call; zero (the default) means wait forever.
|
||||||
|
* @param {Object} [details.upload] - Object containing optional function callbacks (onabort, onerror, onload, onprogress) to monitor the upload of data. Each is passed one argument, the Response Object.
|
||||||
|
* @param {string} details.url - Required. The URL to make the request to. Must be an absolute URL, beginning with the scheme. May be relative to the current page.
|
||||||
|
* @param {string} [details.user] - User name to use for authentication purposes.
|
||||||
|
* @param {function(GMXMLHttpRequestResponse):void} [details.onload] - Optional. Will be called when the request has completed successfully. Passed one argument, the Response Object.
|
||||||
|
* @param {Function} [details.onerror] - Optional. Will be called if an error occurs while processing the request. Passed one argument, the Response Object.
|
||||||
|
* @param {Function} [details.onabort] - Optional. Will be called when the request is aborted. Passed one argument, the Response Object.
|
||||||
|
* @param {Function} [details.ontimeout] - Optional. Will be called if/when the request times out. Passed one argument, the Response Object.
|
||||||
|
* @param {Function} [details.onprogress] - Optional. Will be called when the request progress changes. Passed one argument, the Response Object.
|
||||||
|
* @param {Function} [details.onreadystatechange] - Optional. Will be called repeatedly while the request is in progress. Passed one argument, the Response Object.
|
||||||
|
* @returns {undefined}
|
||||||
|
*/
|
||||||
|
function GM_xmlhttpRequest(details) {
|
||||||
|
// Implementation is not necessary for a declaration file
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response object for the HTTP request.
|
||||||
|
* @typedef {Object} GMXMLHttpRequestResponse
|
||||||
|
* @property {number} readyState - The state of the request.
|
||||||
|
* @property {string} responseHeaders - The response headers.
|
||||||
|
* @property {string} responseText - The response body as text.
|
||||||
|
* @property {number} status - The HTTP status code of the response.
|
||||||
|
* @property {string} statusText - The status message corresponding to the status code.
|
||||||
|
* @property {Object<string, string>} responseHeaders - The response headers.
|
||||||
|
*/
|
6
types/awnser.d.js
Normal file
6
types/awnser.d.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* Object representing a question with answers.
|
||||||
|
* @typedef {Object} Answer
|
||||||
|
* @property {string} question - The question.
|
||||||
|
* @property {Array<string>} answers - An array of answers.
|
||||||
|
*/
|
Loading…
Reference in a new issue