// ==UserScript== // @name Tabbed AtCoder Editorial // @version 0.4 // @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://greasyfork.org/users/808669 // @downloadURL none // ==/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 getTextResponse(href) { return new Promise((resolve) => { const xhr = new XMLHttpRequest(); xhr.onload = (response) => { resolve(response.target.responseText); }; xhr.open("GET", href); xhr.overrideMimeType("text/plain; charset=Shift_JIS"); xhr.send(); }); } async function typical90(id) { const editorial = { "005": 3, "011": 2, "017": 3, "023": 4, "029": 2, "035": 3, "041": 3, "047": 2, "053": 4, "059": 3, "065": 3, "071": 3, "077": 3, "083": 4, "084": 2, "085": 2, "086": 2, "087": 2, "088": 2, "089": 4, "090": 6 }; const source = { "005": "005-03", "011": "011-03", "017": "017-03", "023": "023-04b", "029": "029-03", "035": "035-04", "041": "041-03", "047": "047-02", "053": "053-04", "059": "059-02", "061": "061-02", "065": "065-03", "071": "071-03", "077": "077-04b", "083": "083-02a", "084": "084-02", "089": "089-05", "090": "090-07b" }; let content = `問題文
公式解説 `; if (editorial[id] === undefined) { content += ``; } else { for (let i = 1; i <= editorial[id]; i++) { content += ``; } } const code = await getTextResponse(`https://raw.githubusercontent.com/E869120/kyopro_educational_90/main/sol/${source[id] === undefined ? id : source[id]}.cpp`); content += `
想定ソースコード
${code}
`; return ``; } 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(``); const navdiv = parse(`
`); 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(`
  • ${name}
  • `); 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]); if (location.href.match(/https:\/\/atcoder\.jp\/contests\/typical90\/tasks\/.*\/editorial/) && 1 <= Number(previd) && Number(previd) <= 90) { div.appendChild(parse(await typical90(previd))); if (e.textContent !== "解説がまだありません。") { div.appendChild(e); } else { dummy.appendChild(e); } } else { 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/katex@0.16.0/dist/katex.min.css"); await addScript("https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.js"); await addScript("https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/contrib/auto-render.min.js"); await createTab(); await addScript("https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"); })();