Tabbed AtCoder Editorial Debug

display atcoder editorial in tabs

当前为 2022-06-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Tabbed AtCoder Editorial Debug
  3. // @version 0.3
  4. // @description display atcoder editorial in tabs
  5. // @match https://atcoder.jp/contests/*/editorial
  6. // @match https://atcoder.jp/contests/*/editorial?*
  7. // @grant GM_addStyle
  8. // @license MIT
  9. // @namespace https://greasyfork.org/users/808669
  10. // ==/UserScript==
  11.  
  12. /* jshint esversion:8 */
  13. (async function () {
  14. 'use strict';
  15.  
  16. const katexoption = {
  17. delimiters: [
  18. { left: "$$", right: "$$", display: true },
  19. { left: "$", right: "$", display: false },
  20. { left: "\\(", right: "\\)", display: false },
  21. { left: "\\[", right: "\\]", display: true }
  22. ],
  23. ignoredTags: ["script", "noscript", "style", "textarea", "code", "option"],
  24. ignoredClasses: ["prettyprint", "source-code-for-copy"],
  25. throwOnError: false
  26. };
  27.  
  28. async function addScript(src) {
  29. return new Promise((resolve) => {
  30. const script = document.createElement("script");
  31. script.type = "text/javascript";
  32. script.src = src;
  33. script.onload = resolve;
  34. document.getElementsByTagName("head")[0].appendChild(script);
  35. });
  36. }
  37.  
  38. async function addLink(href) {
  39. return new Promise((resolve) => {
  40. const link = document.createElement("link");
  41. link.rel = "stylesheet";
  42. link.href = href;
  43. link.onload = resolve;
  44. document.getElementsByTagName("head")[0].appendChild(link);
  45. });
  46. }
  47.  
  48. async function getEditorial(link) {
  49. return new Promise((resolve) => {
  50. const xhr = new XMLHttpRequest();
  51. xhr.responseType = "document";
  52. xhr.onload = (response) => {
  53. const editorial = response.target.responseXML.querySelector("#main-container > div.row > div:nth-child(2) > div:nth-child(3)");
  54. if (editorial) {
  55. renderMathInElement(editorial, katexoption);
  56. link.parentNode.appendChild(editorial);
  57. }
  58. resolve();
  59. };
  60. xhr.open("GET", link.href);
  61. xhr.send();
  62. });
  63. }
  64.  
  65. async function createTab() {
  66. const parser = new DOMParser();
  67. const parse = s => parser.parseFromString(s, "text/html").body.firstChild;
  68. const nav = document.querySelector("#main-container > div.row > div:nth-child(2)");
  69. const dummy = document.createElement("div");
  70. const navul = parse(`<ul class="nav nav-tabs" role="tablist"></ul>`);
  71. const navdiv = parse(`<div class="tab-content"></div>`);
  72.  
  73. let previd = "dummy";
  74. let isactive = true;
  75. let prevhead = -1;
  76. let kaisetsu = -1;
  77.  
  78. while (nav.children.length > 0) {
  79. const e = nav.firstChild;
  80. const summary = e.textContent.trimStart().split(/\s+/)[0];
  81. if (e.tagName === "DIV" && summary === "解説") {
  82. kaisetsu = dummy.children.length;
  83. dummy.appendChild(e);
  84. } else if (e.tagName === "DIV" || e.tagName === "H3") {
  85. const cond = e.textContent === "コンテスト全体の解説";
  86. const name = cond ? "全体" : summary;
  87. const id = cond ? "all" : summary;
  88. const li = parse(`<li role="presentation">
  89. <a href="#editorial-${id}" aria-controls="editorial-${id}" role="tab" data-toggle="tab">${name}</a>
  90. </li>`);
  91. if (isactive) li.classList.add("active");
  92. navul.appendChild(li);
  93. previd = id;
  94. prevhead = dummy.children.length;
  95. dummy.appendChild(e);
  96. } else if (e.tagName === "UL" || e.tagName == "P") {
  97. const div = document.createElement("div");
  98. div.role = "tabpanel";
  99. div.classList.add("tab-pane");
  100. if (isactive) div.classList.add("active");
  101. div.id = "editorial-" + previd;
  102. div.appendChild(dummy.children[prevhead]);
  103. div.appendChild(e);
  104. navdiv.appendChild(div);
  105. isactive = false;
  106. } else {
  107. dummy.appendChild(e);
  108. }
  109. }
  110.  
  111. if (kaisetsu >= 0) nav.appendChild(dummy.children[kaisetsu]);
  112. nav.appendChild(navul);
  113. nav.appendChild(navdiv);
  114. return Promise.all(
  115. Array.prototype.filter.call(
  116. navdiv.getElementsByTagName('a'),
  117. e => e.href.match(/https:\/\/atcoder\.jp\/contests\/.*\/editorial\//)
  118. ).map(e => getEditorial(e))
  119. );
  120. }
  121.  
  122. GM_addStyle("pre code { tab-size: 4; }");
  123. await addLink("https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css");
  124. await addScript("https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.js");
  125. await addScript("https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/contrib/auto-render.min.js");
  126. await createTab();
  127. await addScript("https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js");
  128. })();