Tabbed AtCoder Editorial

display atcoder editorial in tabs

Per 27-06-2022. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Tabbed AtCoder Editorial
// @version      0.2
// @description  display atcoder editorial in tabs
// @match        https://atcoder.jp/contests/*/editorial
// @grant        GM_addStyle
// @license      MIT
// @namespace https://greasyfork.org/users/808669
// ==/UserScript==

/* jshint esversion:8 */
(async function () {
  'use strict';
  async function addScript(src) {
    return new Promise((resolve) => {
      const script = document.createElement("script");
      script.type = "text/javascript";
      script.src = src;
      script.onload = resolve;
      document.getElementsByTagName("head")[0].appendChild(script);
    });
  }

  async function addLink(href) {
    return new Promise((resolve) => {
      const link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = href;
      link.onload = resolve;
      document.getElementsByTagName("head")[0].appendChild(link);
    });
  }

  async function getEditorial(link) {
    return new Promise((resolve) => {
      const xhr = new XMLHttpRequest();
      xhr.responseType = "document";
      xhr.onload = (response) => {
        const editorial = response.target.responseXML.querySelector("#main-container > div.row > div:nth-child(2) > div:nth-child(3)");
        if (editorial) link.parentNode.appendChild(editorial);
        resolve();
      };
      xhr.open("GET", link.href);
      xhr.send();
    });
  }

  async function getAllEditorials() {
    return Promise.all(Array.prototype.filter.call(document.getElementsByTagName('a'), e => e.href.match(/\/editorial\//))
      .map(e => getEditorial(e)));
  }

  async function createTab() {
    const parser = new DOMParser();
    const parse = s => parser.parseFromString(s, "text/html").body.firstChild;
    const nav = document.querySelector("#main-container > div.row > div:nth-child(2)");
    const dummy = document.createElement("div");
    const navul = parse(`<ul class="nav nav-tabs" role="tablist"></ul>`);
    const navdiv = parse(`<div class="tab-content"></div>`);

    let previd = "dummy";
    let isactive = true;
    let prevhead = -1;
    let kaisetsu = -1;

    while (nav.children.length > 0) {
      const e = nav.firstChild;
      const summary = e.textContent.trimStart().split(/\s+/)[0];
      if (e.tagName === "DIV" && summary === "解説") {
        kaisetsu = dummy.children.length;
        dummy.appendChild(e);
      } else if (e.tagName === "DIV" || e.tagName === "H3") {
        console.log(e);
        const cond = e.textContent === "コンテスト全体の解説";
        const name = cond ? "全体" : e.textContent.trimStart().split(/\s+/)[0];
        const id = cond ? "all" : e.textContent.trimStart().split(/\s+/)[0];
        const li = parse(`<li role="presentation">
            <a href="#editorial-${id}" aria-controls="editorial-${id}" role="tab" data-toggle="tab">${name}</a>
          </li>`);
        if (isactive) li.classList.add("active");
        navul.appendChild(li);
        previd = id;
        prevhead = dummy.children.length;
        dummy.appendChild(e);
      } else if (e.tagName === "UL" || e.tagName == "P") {
        const div = document.createElement("div");
        div.role = "tabpanel";
        div.classList.add("tab-pane");
        if (isactive) div.classList.add("active");
        div.id = "editorial-" + previd;
        console.log(prevhead, dummy);
        div.appendChild(dummy.children[prevhead]);
        div.appendChild(e);
        navdiv.appendChild(div);
        isactive = false;
      } else {
        dummy.appendChild(e);
      }
    }

    if (kaisetsu >= 0) nav.appendChild(dummy.children[kaisetsu]);
    nav.appendChild(navul);
    nav.appendChild(navdiv);
    return Promise.all(Array.prototype.filter.call(navdiv.getElementsByTagName('a'), e => e.href.match(/\/editorial\//))
      .map(e => getEditorial(e)));
  }

  GM_addStyle("pre code { tab-size: 4; }");
  await createTab();
  await addLink("https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css");
  await addScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js");
  await addScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js");
  await addScript("https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js");
  renderMathInElement(document.body, {
    delimiters: [
      { left: "$$", right: "$$", display: true },
      { left: "$", right: "$", display: false },
      { left: "\\(", right: "\\)", display: false },
      { left: "\\[", right: "\\]", display: true }
    ],
    ignoredTags: ["script", "noscript", "style", "textarea", "code", "option"],
    ignoredClasses: ["prettyprint", "source-code-for-copy"],
    throwOnError: false
  });
})();