Tabbed AtCoder Editorial Debug

display atcoder editorial in tabs

Od 30.06.2022.. Pogledajte najnovija verzija.

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

/* jshint esversion:8 */
(async function () {
  'use strict';

  const katexoption = {
    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
  };

  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) {
          renderMathInElement(editorial, katexoption);
          link.parentNode.appendChild(editorial);
        }
        resolve();
      };
      xhr.open("GET", link.href);
      xhr.send();
    });
  }

  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") {
        const cond = e.textContent === "コンテスト全体の解説";
        const name = cond ? "全体" : summary;
        const id = cond ? "all" : summary;
        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;
        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(/https:\/\/atcoder\.jp\/contests\/.*\/editorial\//)
      ).map(e => getEditorial(e))
    );
  }

  GM_addStyle("pre code { tab-size: 4; }");
  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 createTab();
  await addScript("https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js");
})();