// ==UserScript==
// @name Tabbed AtCoder Editorial
// @version 0.5
// @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");
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 = `<a href="https://github.com/E869120/kyopro_educational_90/blob/main/problem" rel="nofollow">問題文</a>
<img src="https://raw.githubusercontent.com/E869120/kyopro_educational_90/main/problem/${id}.jpg" style="max-width: 100%;">
<hr><a href="https://github.com/E869120/kyopro_educational_90/blob/main/editorial" rel="nofollow">公式解説</a>
`;
if (editorial[id] === undefined) {
content += `<img src="https://raw.githubusercontent.com/E869120/kyopro_educational_90/main/editorial/${id}.jpg" style="max-width: 100%;">`;
} else {
for (let i = 1; i <= editorial[id]; i++) {
content += `<img src="https://raw.githubusercontent.com/E869120/kyopro_educational_90/main/editorial/${id}-${String(i).padStart(2, "0")}.jpg" style="max-width: 100%;">`;
}
}
const code = await getTextResponse(`https://raw.githubusercontent.com/E869120/kyopro_educational_90/main/sol/${source[id] === undefined ? id : source[id]}.cpp`);
content += `<hr><a href="https://github.com/E869120/kyopro_educational_90/tree/main/sol" rel="nofollow">想定ソースコード</a><pre class="prettyprint linenums"><code class="language-cpp">${code}</code></pre>`;
return `<ul><li>${content}</li></ul>`;
}
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]);
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/[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");
})();