// ==UserScript==
// @name LearnedLeague forum thread utility
// @namespace Violentmonkey Scripts
// @match https://www.learnedleague.com/viewtopic.php*
// @match https://www.learnedleague.com/question.php*
// @grant none
// @version 1.0
// @author BlumE
// @license MIT
// @description Adds a link to the question on forum posts about a specific question and adds a button to
// ==/UserScript==
let processFunction;
if (window.location.pathname === "/question.php") {
processFunction = addTemplate;
} else if (window.location.pathname === "/viewtopic.php") {
processFunction = addLink;
} else {
return;
}
// If page is still loading, queue function for when it is done
if (document.readyState !== 'loading') {
processFunction();
} else {
document.addEventListener('DOMContentLoaded', processFunction);
}
/**
* Adds a link on forum topics that include the proper LL question header.
* e.g. LL83 MD9Q6 (KINGSTON) to the question in question.
*/
function addLink() {
const reLeagueNumber = /[Ll][Ll](\d+)/;
const reMatchDayNumber = /[Mm][Dd](\d+)/;
const reQuestionNumber = /[Qq](\d+)/;
let postHeader = document.querySelector("#pageheader a");
if (postHeader) {
let postName = postHeader.innerHTML;
let leagueNumber = reLeagueNumber.exec(postName);
let matchDayNumber = reMatchDayNumber.exec(postName);
let questionNumber = reQuestionNumber.exec(postName);
if (leagueNumber && matchDayNumber && questionNumber) {
let linkURL = `/question.php?${leagueNumber[1]}&${matchDayNumber[1]}&${questionNumber[1]}`
let newLink = document.createElement('a');
newLink.href = linkURL;
newLink.title = "Link";
newLink.text = " Link >>";
newLink.style = "border-bottom: dotted 1px;color: #336666;"
postHeader.parentElement.appendChild(newLink);
}
}
}
/**
* Adds a button to copy a template for a question to your clipboard for use in a new
* forum post including the post subject and a nicely formatted body with the question text
* included.
*/
function addTemplate() {
let answerDiv = document.querySelector('#xyz > span');
if (!answerDiv) {
return;
}
let [leagueNumber, matchDayNumber, questionNumber] = window.location.search.substr(1).split('&');
if (leagueNumber && matchDayNumber && questionNumber) {
let answer = answerDiv.textContent.trim();
let postSubject = `LL${leagueNumber} MD${matchDayNumber}Q${questionNumber} (${answer})`;
let postBody = templateBody();
let postContent = postSubject + "\\n" + postBody;
let templateHeader = document.createElement('h3');
templateHeader.innerHTML = "Forum Post Template";
let templateCopyDiv = document.createElement('div');
templateCopyDiv.style = "margin-bottom:1.5em;margin-left:0.5em;";
let templateCopy = document.createElement('a');
templateCopy.id = "subjectToggle";
templateCopy.innerHTML = "Click here to copy template";
templateCopy.href = `javascript:navigator.clipboard.writeText("${postContent}")`;
templateCopyDiv.appendChild(templateCopy);
const statsDiv = document.querySelector('#main > div > div.indivqContent > div > div:nth-child(5) > div');
statsDiv.appendChild(templateHeader);
statsDiv.appendChild(templateCopyDiv);
}
}
function templateBody() {
let questionDiv = document.querySelector('.indivqQuestion');
let answerDiv = document.querySelector('#xyz > span');
let answer = answerDiv.textContent.trim();
let postBody = `[quote="Question"]${questionDiv.innerHTML.trim()}[/quote] Answer: [spoiler]${answer}[/spoiler]`;
let escapedBody = postBody;
let template = document.createElement('template');
template.innerHTML = escapedBody;
let content = [...template.content.childNodes].map(node => processNode(node)).join("");
content = content.replace(/"/g, '\\\"');
return content;
}
function processNode(node) {
let val;
if (node.hasChildNodes()) {
val = [...node.childNodes].map(node => processNode(node)).join("");
} else {
val = node.textContent;
}
if (node.nodeType === node.TEXT_NODE) {
return val
} else {
const equivalentElements = ['b', 'i', 'u'];
if (equivalentElements.indexOf(node.localName) !== -1) {
return `[${node.localName}]${val}[/${node.localName}]`;
} else if (node.localName === 'em') {
return `[i]${val}[/i]`;
} else if (node.localName === 'a') {
return `[url=${node.href}]${val}[/url]`;
} else if (node.localName === 'br') {
return '\\n'
} else if (node.localName === 'sup') {
const superscriptMap = new Map([['0', '⁰'], ['1', '¹'], ['2', '²'], ['3', '³'], ['4', '⁴'], ['5', '⁵'], ['6', '⁶'], ['7', '⁷'], ['8', '⁸'],
['9', '⁹'], ['+', '⁺'], ['-', '⁻'], ['=', '⁼'], ['(', '⁽'], [')', '⁾'], ['n', 'ⁿ'], ['i', 'ⁱ']]);
if ([...val].every(char => superscriptMap.has(char))) {
return [...val].map(char => superscriptMap.get(char)).join('');
} else if (
node.children.length === 1
&& equivalentElements.indexOf(node.firstChild.localName) !== -1
&& node.firstChild.children.length === 0
&& [...node.firstChild.textContent].every(char => superscriptMap.has(char))
) {
// Handle edge case of <sup><i>123</i></sup>
return `[${node.firstChild.localName}]${[...node.firstChild.textContent].map(char => superscriptMap.get(char)).join('')}[/${node.firstChild.localName}]`;
} else {
return "^" + val;
}
} else if (node.localName === 'sub') {
const subscriptMap = new Map([['0', '₀'], ['1', '₁'], ['2', '₂'], ['3', '₃'], ['4', '₄'], ['5', '₅'], ['6', '₆'], ['7', '₇'], ['8', '₈'],
['9', '₉'], ['+', '₊'], ['-', '₋'], ['=', '₌'], ['(', '₍'], [')', '₎'], ['a', 'ₐ'], ['e', 'ₑ'], ['o', 'ₒ'],
['x', 'ₓ'], ['h', 'ₕ'], ['k', 'ₖ'], ['l', 'ₗ'], ['m', 'ₘ'], ['n', 'ₙ'], ['p', 'ₚ'], ['s', 'ₛ'], ['t', 'ₜ'], ['ə', 'ₔ']]);
if ([...val].every(char => subscriptMap.has(char))) {
return [...val].map(char => subscriptMap.get(char)).join('');
} else if (
node.children.length === 1
&& equivalentElements.indexOf(node.firstChild.localName) !== -1
&& node.firstChild.children.length === 0
&& [...node.firstChild.textContent].every(char => superscriptMap.has(char))
) {
// Handle edge case of <sub><i>123</i></sub>
return `[${node.firstChild.localName}]${[...node.firstChild.textContent].map(char => subscriptMap.get(char)).join('')}[/${node.firstChild.localName}]`;
} else {
return "_" + val + "_";
}
} else {
console.log('unknown node ', node);
return val;
}
}
}