Greasy Fork is available in English.

AtCoder Efficient Layout

入力形式やサンプルを横並びにします。

  1. // ==UserScript==
  2. // @name AtCoder Efficient Layout
  3. // @namespace https://atcoder.jp/
  4. // @version 0.3
  5. // @description 入力形式やサンプルを横並びにします。
  6. // @author magurofly
  7. // @match https://atcoder.jp/contests/*/tasks/*
  8. // @match https://atcoder.jp/contests/*/tasks_print
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=atcoder.jp
  10. // @grant unsafeWindow
  11. // @license CC0-1.0 Universal
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. const ioStyleSummary = "入出力形式";
  18. const sampleWidth = 60;
  19. const sampleWidthUnit = "vw";
  20. const sampleMargin = "0 1em";
  21. const sampleSummary = "入出力例";
  22.  
  23. const doc = unsafeWindow.document;
  24.  
  25. // 入出力形式より後にある hr を全部消す
  26. for (const hr of doc.querySelectorAll(".io-style ~ hr")) {
  27. hr.parentElement.removeChild(hr);
  28. }
  29.  
  30. // 入出力形式より後にある .part を取得(入出力例のはず)
  31. const samples = doc.querySelectorAll(".io-style ~ .part");
  32. const lazyContainer = parentElement => {
  33. let row = parentElement.querySelector(".samples-row");
  34. if (!row) {
  35. const container = newDetails(sampleSummary);
  36. container.className = "samples-container";
  37. parentElement.appendChild(container);
  38. row = doc.createElement("div");
  39. row.className = "samples-row";
  40. container.appendChild(row);
  41. }
  42. return row;
  43. };
  44. if (samples.length % 2 == 0) {
  45. // 偶数個なら 2 個ずつまとめて横並びにする
  46. for (let i = 0; i < samples.length; i += 2) {
  47. const input = samples[i];
  48. const output = samples[i + 1];
  49. const container = lazyContainer(input.parentElement, "samples-container");
  50. const col = doc.createElement("div");
  51. col.appendChild(input.parentElement.removeChild(input));
  52. col.appendChild(output.parentElement.removeChild(output));
  53. container.appendChild(col);
  54. }
  55. } else {
  56. // 奇数個ならまとめずに横並びにする
  57. for (let i = 0; i < samples.length; i++) {
  58. const element = samples[i];
  59. const container = lazyContainer(element.parentElement, "samples-container");
  60. container.appendChild(element.parentElement.removeChild(element));
  61. }
  62. }
  63.  
  64. for (const rows of doc.querySelectorAll(".samples-row")) {
  65. rows.style.width = `${sampleWidth * rows.children.length}${sampleWidthUnit}`;
  66. }
  67.  
  68. // 入出力形式を details に入れる
  69. const ioStyle = doc.querySelector(".io-style");
  70. const ioStyleDetails = newDetails(ioStyleSummary);
  71. ioStyle.parentElement.insertBefore(ioStyleDetails, ioStyle);
  72. ioStyleDetails.appendChild(ioStyle.parentElement.removeChild(ioStyle));
  73.  
  74. const globalCSS = `
  75. /* 入出力形式を横に並べる */
  76. .io-style {
  77. display: flex;
  78. justify-content: space-between;
  79. }
  80. .io-style > * {
  81. width: 100%;
  82. }
  83.  
  84. /* サンプルを横に並べる */
  85. .samples-container {
  86. width: 100%;
  87. overflow-x: auto;
  88. }
  89. .samples-row {
  90. display: flex;
  91. }
  92. .samples-row > * {
  93. width: ${sampleWidth}${sampleWidthUnit};
  94. margin: ${sampleMargin};
  95. }
  96. `;
  97.  
  98. doc.head.appendChild(document.createElement("style")).textContent = globalCSS;
  99.  
  100. function newDetails(summaryText, open = true) {
  101. const details = doc.createElement("details");
  102. details.open = open;
  103. const summary = doc.createElement("summary");
  104. summary.textContent = summaryText;
  105. details.appendChild(summary);
  106. return details;
  107. }
  108. })();