This commit is contained in:
parent
69fb65deb7
commit
9c0c80902e
7 changed files with 156 additions and 113 deletions
18
.gitea/workflows/lint.yml
Normal file
18
.gitea/workflows/lint.yml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
name: "Linting"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
javadoc:
|
||||||
|
name: "Javadoc"
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
container:
|
||||||
|
image: "git.euph.dev/actions/runner-js-latest:latest"
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: "https://git.euph.dev/actions/checkout@v3"
|
||||||
|
- name: "Prettier"
|
||||||
|
run: npm_config_yes=true npx prettier src/ types/ --check --log-level=error
|
10
.prettierrc.json
Normal file
10
.prettierrc.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"useTabs": false,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"bracketSameLine": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
2
Makefile
Normal file
2
Makefile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
lint:
|
||||||
|
@npx prettier src/ types/ --write
|
|
@ -1,24 +1,24 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Array<Answer>} answerData
|
* @param {Array<Answer>} answerData
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function answerQuestion(answerData){
|
function answerQuestion(answerData) {
|
||||||
const question = document.querySelector(".question:not(.hidden)");
|
const question = document.querySelector('.question:not(.hidden)');
|
||||||
if (!question) {
|
if (!question) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const questionTextDom = question.querySelector(".questionText .mattext");
|
const questionTextDom = question.querySelector('.questionText .mattext');
|
||||||
if (!questionTextDom) return;
|
if (!questionTextDom) return;
|
||||||
const questionText = questionTextDom.textContent.trim();
|
const questionText = questionTextDom.textContent.trim();
|
||||||
|
|
||||||
const answersDom = question.querySelector("ul.coreContent");
|
const answersDom = question.querySelector('ul.coreContent');
|
||||||
if (!answersDom) return;
|
if (!answersDom) return;
|
||||||
const answers = answersDom.children;
|
const answers = answersDom.children;
|
||||||
|
|
||||||
for (let answer of Array.from(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;
|
||||||
}
|
}
|
||||||
|
@ -29,17 +29,17 @@ function answerQuestion(answerData){
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const answer of correctAnswers) {
|
for (const answer of correctAnswers) {
|
||||||
const input = answer.querySelector("input");
|
const input = answer.querySelector('input');
|
||||||
if (!input) continue;
|
if (!input) continue;
|
||||||
input.checked = true;
|
input.checked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Array<Answer>} answerData
|
* @param {Array<Answer>} answerData
|
||||||
* @param {string} questionText
|
* @param {string} questionText
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function findAnswers(answerData, questionText, answers) {
|
function findAnswers(answerData, questionText, answers) {
|
||||||
if (answerData === null) {
|
if (answerData === null) {
|
||||||
|
@ -51,7 +51,12 @@ function findAnswers(answerData, questionText, answers) {
|
||||||
if (matchAnswer(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 (matchAnswer(availableAnswer.textContent.trim(), possibleAnswer)) {
|
if (
|
||||||
|
matchAnswer(
|
||||||
|
availableAnswer.textContent.trim(),
|
||||||
|
possibleAnswer
|
||||||
|
)
|
||||||
|
) {
|
||||||
correctAnswers.push(availableAnswer);
|
correctAnswers.push(availableAnswer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,9 +69,9 @@ function findAnswers(answerData, questionText, answers) {
|
||||||
|
|
||||||
function matchAnswer(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.answerQuestion = answerQuestion;
|
window.answerQuestion = answerQuestion;
|
||||||
|
|
157
src/fetch.js
157
src/fetch.js
|
@ -5,22 +5,22 @@ const QUESTION_REGEX = /^[0-9]+\. (.*)$/;
|
||||||
* @param {string} [answerURL=""] - The URL to fetch answers from.
|
* @param {string} [answerURL=""] - The URL to fetch answers from.
|
||||||
* @returns {Promise<Array<Answer>>} A Promise that resolves with the fetched answers.
|
* @returns {Promise<Array<Answer>>} A Promise that resolves with the fetched answers.
|
||||||
*/
|
*/
|
||||||
function fetchAnswers(answerURL = "") {
|
function fetchAnswers(answerURL = '') {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
GM_xmlhttpRequest({
|
GM_xmlhttpRequest({
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
url: answerURL,
|
url: answerURL,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/html",
|
'Content-Type': 'text/html'
|
||||||
},
|
},
|
||||||
onload: function (response) {
|
onload: function (response) {
|
||||||
parseAnswers(response, resolve);
|
parseAnswers(response, resolve);
|
||||||
},
|
},
|
||||||
onerror: function (error) {
|
onerror: function (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
},
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,19 +29,19 @@ function fetchAnswers(answerURL = "") {
|
||||||
* @param {(value: Answer[] | PromiseLike<Answer[]>) => void} resolve
|
* @param {(value: Answer[] | PromiseLike<Answer[]>) => void} resolve
|
||||||
*/
|
*/
|
||||||
function parseAnswers(response, resolve) {
|
function parseAnswers(response, resolve) {
|
||||||
const results = [];
|
const results = [];
|
||||||
const allAnswersElement = getAllAnswersElement(response);
|
const allAnswersElement = getAllAnswersElement(response);
|
||||||
|
|
||||||
let index = -1;
|
let index = -1;
|
||||||
for (let child of Array.from(allAnswersElement.children)) {
|
for (let child of Array.from(allAnswersElement.children)) {
|
||||||
index++;
|
index++;
|
||||||
const result = parseAnswerElement(index, child, allAnswersElement);
|
const result = parseAnswerElement(index, child, allAnswersElement);
|
||||||
if (result != undefined) {
|
if (result != undefined) {
|
||||||
results.push(result);
|
results.push(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
resolve(results);
|
resolve(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,14 +49,17 @@ function parseAnswers(response, resolve) {
|
||||||
* @returns {Element}
|
* @returns {Element}
|
||||||
*/
|
*/
|
||||||
function getAllAnswersElement(response) {
|
function getAllAnswersElement(response) {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const virtualDOM = parser.parseFromString(response.responseText, "text/html");
|
const virtualDOM = parser.parseFromString(
|
||||||
|
response.responseText,
|
||||||
|
'text/html'
|
||||||
|
);
|
||||||
|
|
||||||
let answersElement = virtualDOM.querySelector(".pf-content");
|
let answersElement = virtualDOM.querySelector('.pf-content');
|
||||||
if (!answersElement) {
|
if (!answersElement) {
|
||||||
answersElement = virtualDOM.querySelector(".thecontent");
|
answersElement = virtualDOM.querySelector('.thecontent');
|
||||||
}
|
}
|
||||||
return answersElement;
|
return answersElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,38 +69,38 @@ function getAllAnswersElement(response) {
|
||||||
* @returns {Answer}
|
* @returns {Answer}
|
||||||
*/
|
*/
|
||||||
function parseAnswerElement(index, element, allAnswersElement) {
|
function parseAnswerElement(index, element, allAnswersElement) {
|
||||||
// Check for Possible Tags
|
// Check for Possible Tags
|
||||||
if (
|
if (
|
||||||
!(element.tagName === "P" || element.tagName === "STRONG") ||
|
!(element.tagName === 'P' || element.tagName === 'STRONG') ||
|
||||||
!element.innerHTML
|
!element.innerHTML
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Get Question Element
|
|
||||||
/** @type {Element} */
|
|
||||||
let questionElement = element.querySelector("strong");
|
|
||||||
if (questionElement === null) {
|
|
||||||
if (!element.textContent) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
questionElement = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Question
|
// Get Question Element
|
||||||
const questionText = parseQuestion(questionElement);
|
/** @type {Element} */
|
||||||
if (questionText === null) {
|
let questionElement = element.querySelector('strong');
|
||||||
return;
|
if (questionElement === null) {
|
||||||
}
|
if (!element.textContent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
questionElement = element;
|
||||||
|
}
|
||||||
|
|
||||||
// Get Awsners
|
// Get Question
|
||||||
const answersElement = getAnswersElement(index, allAnswersElement);
|
const questionText = parseQuestion(questionElement);
|
||||||
if (answersElement === null || answersElement.tagName !== "UL") return;
|
if (questionText === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
// Get Awsners
|
||||||
question: questionText,
|
const answersElement = getAnswersElement(index, allAnswersElement);
|
||||||
answers: getAnswers(answersElement),
|
if (answersElement === null || answersElement.tagName !== 'UL') return;
|
||||||
};
|
|
||||||
|
return {
|
||||||
|
question: questionText,
|
||||||
|
answers: getAnswers(answersElement)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,9 +108,9 @@ function parseAnswerElement(index, element, allAnswersElement) {
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
function parseQuestion(questionElement) {
|
function parseQuestion(questionElement) {
|
||||||
const textContent = questionElement.textContent.trim();
|
const textContent = questionElement.textContent.trim();
|
||||||
const matches = textContent.match(QUESTION_REGEX);
|
const matches = textContent.match(QUESTION_REGEX);
|
||||||
return matches !== null ? matches[1] : null;
|
return matches !== null ? matches[1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,12 +119,12 @@ function parseQuestion(questionElement) {
|
||||||
* @returns {Element}
|
* @returns {Element}
|
||||||
*/
|
*/
|
||||||
function getAnswersElement(index, allAnswersElement) {
|
function getAnswersElement(index, allAnswersElement) {
|
||||||
let answersElement = allAnswersElement.children[index + 1];
|
let answersElement = allAnswersElement.children[index + 1];
|
||||||
|
|
||||||
if (answersElement.tagName === "P") {
|
if (answersElement.tagName === 'P') {
|
||||||
answersElement = allAnswersElement.children[index + 2];
|
answersElement = allAnswersElement.children[index + 2];
|
||||||
}
|
}
|
||||||
return answersElement;
|
return answersElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,16 +132,18 @@ function getAnswersElement(index, allAnswersElement) {
|
||||||
* @returns {Array<string>}
|
* @returns {Array<string>}
|
||||||
*/
|
*/
|
||||||
function getAnswers(answersElement) {
|
function getAnswers(answersElement) {
|
||||||
const answers = [];
|
const answers = [];
|
||||||
for (let answerDom of Array.from(answersElement.querySelectorAll("strong"))) {
|
for (let answerDom of Array.from(
|
||||||
let answerText = answerDom.textContent.trim();
|
answersElement.querySelectorAll('strong')
|
||||||
if (answerText.endsWith("*")) {
|
)) {
|
||||||
answerText = answerText.substring(0, answerText.length - 1);
|
let answerText = answerDom.textContent.trim();
|
||||||
|
if (answerText.endsWith('*')) {
|
||||||
|
answerText = answerText.substring(0, answerText.length - 1);
|
||||||
|
}
|
||||||
|
answers.push(answerText);
|
||||||
}
|
}
|
||||||
answers.push(answerText);
|
|
||||||
}
|
|
||||||
|
|
||||||
return answers;
|
return answers;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.fetchAnswers = fetchAnswers;
|
window.fetchAnswers = fetchAnswers;
|
||||||
|
|
|
@ -14,25 +14,28 @@
|
||||||
// @author Dominik Säume
|
// @author Dominik Säume
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|
||||||
const URL_STORAGE_KEY = "itexamanswers.net URL";
|
const URL_STORAGE_KEY = 'itexamanswers.net URL';
|
||||||
/** @type {Array<Answer>} */
|
/** @type {Array<Answer>} */
|
||||||
let answerData;
|
let answerData;
|
||||||
|
|
||||||
window.addEventListener("keydown", async (event) => {
|
window.addEventListener('keydown', async event => {
|
||||||
switch(event.key){
|
switch (event.key) {
|
||||||
case "p":
|
case 'p':
|
||||||
const oldAnswersURL = GM_getValue(URL_STORAGE_KEY);
|
const oldAnswersURL = GM_getValue(URL_STORAGE_KEY);
|
||||||
const newAnswersURL = prompt("Please input the answer url (itexamanswers.net)", oldAnswersURL);
|
const newAnswersURL = prompt(
|
||||||
GM_setValue(URL_STORAGE_KEY, newAnswersURL);
|
'Please input the answer url (itexamanswers.net)',
|
||||||
answerData = await window.fetchAnswers(newAnswersURL);
|
oldAnswersURL
|
||||||
break;
|
);
|
||||||
|
GM_setValue(URL_STORAGE_KEY, newAnswersURL);
|
||||||
|
answerData = await window.fetchAnswers(newAnswersURL);
|
||||||
|
break;
|
||||||
|
|
||||||
case "n":
|
case 'n':
|
||||||
document.getElementById("next").click();
|
document.getElementById('next').click();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "a":
|
case 'a':
|
||||||
window.answerQuestion(answerData);
|
window.answerQuestion(answerData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function GM_getValue(storageKey) {
|
function GM_getValue(storageKey) {
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue