LearnedLeague forum thread utility

Adds a link to the question on forum posts about a specific question and adds a button to

2022-06-19 या दिनांकाला. सर्वात नवीन आवृत्ती पाहा.

// ==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;
    }
  }
}