View All Editorials

View all editorials of the AtCoder contest in one page.

As of 2022-10-15. See the latest version.

// ==UserScript==
// @name            View All Editorials
// @name:ja         解説ぜんぶ見る
// @description     View all editorials of the AtCoder contest in one page.
// @description:ja  AtCoderコンテストの解説ページに、すべての問題の解説をまとめて表示します。
// @version         1.2.2
// @icon            https://www.google.com/s2/favicons?domain=atcoder.jp
// @match           https://atcoder.jp/contests/*/editorial
// @match           https://atcoder.jp/contests/*/editorial?*
// @grant           GM_addStyle
// @namespace       https://gitlab.com/w0mbat/user-scripts
// @author          w0mbat
// ==/UserScript==

(async function () {
  'use strict';
  console.log(`🐻 "View All Editorials" start execution. 🐻`)

  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 loadMathJax() {
    await addScript("https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML");
    MathJax.Hub.Config({ messageStyle: "none", tex2jax: { skipTags: ["script", "noscript", "style", "textarea", "code"], inlineMath: [['$', '$'], ['\\(', '\\)']] } });
  }

  async function loadPrettifier() {
    await addScript("https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js");
  }

  async function getEditorial(link) {
    return new Promise((resolve) => {
      const xhr = new XMLHttpRequest()
      xhr.responseType = "document";
      xhr.onload = (response) => {
        const dom = response.target.responseXML;
        const editorialDom = dom.querySelector("#main-container > div.row > div:nth-child(2) > div:nth-child(3)");
        editorialDom && link.parentNode.appendChild(editorialDom);
        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)));
  }

  GM_addStyle("pre code { tab-size: 4; }");
  await getAllEditorials();
  await loadMathJax();
  await loadPrettifier();

  console.log(`🐻 "View All Editorials" end execution. 🐻`)
})();