Greasy Fork is available in English.

spoiled competitive programming

Parse AtCoder problem statement into sections

2024-06-04 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

  1. // ==UserScript==
  2. // @name spoiled competitive programming
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description Parse AtCoder problem statement into sections
  6. // @author Harui (totally helped by GPT-4o)
  7. // @match https://atcoder.jp/contests/*/tasks/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Create buttons for Japanese
  16. const openMarkdownButtonJa = document.createElement('button');
  17. openMarkdownButtonJa.textContent = '新しいタブで日本語のMarkdownを開く';
  18. openMarkdownButtonJa.style.position = 'fixed';
  19. openMarkdownButtonJa.style.left = '10px';
  20. openMarkdownButtonJa.style.bottom = '145px';
  21. openMarkdownButtonJa.style.zIndex = '1000';
  22. openMarkdownButtonJa.style.padding = '10px';
  23. openMarkdownButtonJa.style.backgroundColor = '#4CAF50';
  24. openMarkdownButtonJa.style.color = 'white';
  25. openMarkdownButtonJa.style.border = 'none';
  26. openMarkdownButtonJa.style.borderRadius = '5px';
  27. openMarkdownButtonJa.style.cursor = 'pointer';
  28.  
  29. const openHtmlButtonJa = document.createElement('button');
  30. openHtmlButtonJa.textContent = '新しいタブで日本語のHTMLを開く';
  31. openHtmlButtonJa.style.position = 'fixed';
  32. openHtmlButtonJa.style.left = '10px';
  33. openHtmlButtonJa.style.bottom = '100px';
  34. openHtmlButtonJa.style.zIndex = '1000';
  35. openHtmlButtonJa.style.padding = '10px';
  36. openHtmlButtonJa.style.backgroundColor = '#4CAF50';
  37. openHtmlButtonJa.style.color = 'white';
  38. openHtmlButtonJa.style.border = 'none';
  39. openHtmlButtonJa.style.borderRadius = '5px';
  40. openHtmlButtonJa.style.cursor = 'pointer';
  41.  
  42. // Append the buttons to the body
  43. document.body.appendChild(openMarkdownButtonJa);
  44. document.body.appendChild(openHtmlButtonJa);
  45.  
  46.  
  47.  
  48. // Event listener for open Markdown button (Japanese)
  49. openMarkdownButtonJa.addEventListener('click', () => {
  50. const markdownContent = htmlToMarkdown(extractHtml('ja'));
  51.  
  52. // Open the Markdown content in a new tab
  53. const newTab = window.open();
  54. newTab.document.open();
  55. newTab.document.write('<pre>' + markdownContent + '</pre>');
  56. newTab.document.close();
  57. });
  58.  
  59. // Event listener for open HTML button (Japanese)
  60. openHtmlButtonJa.addEventListener('click', () => {
  61. const htmlContent = htmlToAnkiHtml(extractHtml('ja'));
  62.  
  63. // Open the HTML content in a new tab
  64. const newTab = window.open();
  65. newTab.document.open();
  66. newTab.document.write(htmlContent);
  67. newTab.document.close();
  68. });
  69.  
  70. // Create buttons and style them
  71. const openMarkdownButtonEn = document.createElement('button');
  72. openMarkdownButtonEn.textContent = 'Open English Markdown in New Tab';
  73. openMarkdownButtonEn.style.position = 'fixed';
  74. openMarkdownButtonEn.style.left = '10px';
  75. openMarkdownButtonEn.style.bottom = '55px';
  76. openMarkdownButtonEn.style.zIndex = '1000';
  77. openMarkdownButtonEn.style.padding = '10px';
  78. openMarkdownButtonEn.style.backgroundColor = '#4CAF50';
  79. openMarkdownButtonEn.style.color = 'white';
  80. openMarkdownButtonEn.style.border = 'none';
  81. openMarkdownButtonEn.style.borderRadius = '5px';
  82. openMarkdownButtonEn.style.cursor = 'pointer';
  83.  
  84. const openHtmlButtonEn = document.createElement('button');
  85. openHtmlButtonEn.textContent = 'Open English HTML in New Tab';
  86. openHtmlButtonEn.style.position = 'fixed';
  87. openHtmlButtonEn.style.left = '10px';
  88. openHtmlButtonEn.style.bottom = '10px';
  89. openHtmlButtonEn.style.zIndex = '1000';
  90. openHtmlButtonEn.style.padding = '10px';
  91. openHtmlButtonEn.style.backgroundColor = '#4CAF50';
  92. openHtmlButtonEn.style.color = 'white';
  93. openHtmlButtonEn.style.border = 'none';
  94. openHtmlButtonEn.style.borderRadius = '5px';
  95. openHtmlButtonEn.style.cursor = 'pointer';
  96.  
  97. // Append the buttons to the body
  98. document.body.appendChild(openMarkdownButtonEn);
  99. document.body.appendChild(openHtmlButtonEn);
  100.  
  101. // Function to extract English parts and remove Katex, returning HTML
  102. function extractHtml(lang) {
  103. console.log(lang);
  104. const englishParts = document.querySelectorAll(`span.lang-${lang}`);
  105. const problemTitle = document.title;
  106.  
  107. let htmlContent = `<h1>${problemTitle}</h1>\n\n`;
  108.  
  109. englishParts.forEach(part => {
  110. // Clone the part to avoid modifying the original document
  111. const clone = part.cloneNode(true);
  112.  
  113. // Remove "Copy" buttons
  114. const copyButtons = clone.querySelectorAll('.btn-copy, .btn-pre, .div-btn-copy');
  115. copyButtons.forEach(button => button.remove());
  116.  
  117. // Remove Katex elements
  118. const katexElements = clone.querySelectorAll('.katex');
  119. katexElements.forEach(katex => {
  120. const tex = katex.querySelector('.katex-mathml annotation');
  121. if (tex) {
  122. const textNode = document.createTextNode(tex.textContent);
  123. katex.parentNode.replaceChild(textNode, katex);
  124. }
  125. });
  126.  
  127. // Append HTML content
  128. htmlContent += clone.outerHTML + '\n\n';
  129. });
  130.  
  131. return htmlContent;
  132. }
  133.  
  134. // Event listener for open Markdown button
  135. openMarkdownButtonEn.addEventListener('click', () => {
  136. const markdownContent = htmlToMarkdown(extractHtml('en'));
  137.  
  138. // Open the Markdown content in a new tab
  139. const newTab = window.open();
  140. newTab.document.open();
  141. newTab.document.write('<pre>' + markdownContent + '</pre>');
  142. newTab.document.close();
  143. });
  144.  
  145. // Event listener for open HTML button
  146. openHtmlButtonEn.addEventListener('click', () => {
  147. const htmlContent = extractHtml('en');
  148.  
  149. // Open the HTML content in a new tab
  150. const newTab = window.open();
  151. newTab.document.open();
  152. newTab.document.write(htmlContent);
  153. newTab.document.close();
  154. });
  155.  
  156. // Function to convert HTML to Markdown
  157. function htmlToMarkdown(html) {
  158. // Simple conversion rules
  159. const rules = [
  160. { regex: /<h3>(.*?)<\/h3>/g, replacement: '\n### $1\n' },
  161. { regex: /<h2>(.*?)<\/h2>/g, replacement: '\n## $1\n' },
  162. { regex: /<h1>(.*?)<\/h1>/g, replacement: '\n# $1\n' },
  163. { regex: /<p>(.*?)<\/p>/g, replacement: '$1\n' },
  164. { regex: /<ul>(.*?)<\/ul>/gs, replacement: '$1' },
  165. { regex: /<li>(.*?)<\/li>/g, replacement: '- $1' },
  166. { regex: /<pre.*?>(.*?)<\/pre>/gs, replacement: '\n\n``` \n$1\n```' },
  167. { regex: /<var>(.*?)<\/var>/g, replacement: '`$1`' },
  168. { regex: /<div.*?>(.*?)<\/div>/gs, replacement: '$1' },
  169. { regex: /<span.*?>(.*?)<\/span>/g, replacement: '$1' },
  170. { regex: /<section.*?>(.*?)<\/section>/gs, replacement: '$1' },
  171. { regex: /<hr>/g, replacement: '---' },
  172. { regex: /<br>/g, replacement: '\n' }
  173. ];
  174.  
  175. // Apply rules
  176. let markdown = html;
  177. rules.forEach(rule => {
  178. markdown = markdown.replace(rule.regex, rule.replacement);
  179. });
  180.  
  181. // Remove any remaining HTML tags
  182. markdown = markdown.replace(/<\/?[^>]+(>|$)/g, "");
  183.  
  184. return markdown.trim();
  185. }
  186.  
  187. // Function to convert HTML to Anki HTML
  188. function htmlToAnkiHtml(html) {
  189. // Simple conversion rules
  190. const rules = [
  191. { regex: /<h3>(.*?)<\/h3>/g, replacement: '\n<h3>$1</h3>\n' },
  192. { regex: /<h2>(.*?)<\/h2>/g, replacement: '\n<h2>$1</h2>\n' },
  193. { regex: /<h1>(.*?)<\/h1>/g, replacement: '\n<h1>$1</h1>\n' },
  194. { regex: /<p>(.*?)<\/p>/g, replacement: '<p>$1</p>\n' },
  195. { regex: /<ul>(.*?)<\/ul>/gs, replacement: '$1' },
  196. { regex: /<li>(.*?)<\/li>/g, replacement: '<li>$1</li>' },
  197. { regex: /<pre.*?>(.*?)<\/pre>/gs, replacement: '\n\n<pre>\n$1\n</pre>' },
  198. { regex: /<var>(.*?)<\/var>/g, replacement: '<var>$1</var>' },
  199. { regex: /<div.*?>(.*?)<\/div>/gs, replacement: '<div>$1</div>' },
  200. { regex: /<span.*?>(.*?)<\/span>/g, replacement: '<span><anki-mathjax>$1</anki-mathjax></span>' },
  201. { regex: /<section.*?>(.*?)<\/section>/gs, replacement: '<section>$1</section>' },
  202. { regex: /<hr>/g, replacement: '<hr>' },
  203. { regex: /<br>/g, replacement: '<br>' },
  204. { regex: /\$(.*?)\$/g, replacement: '$1' } // Convert TeX to Anki TeX
  205. ];
  206.  
  207. // Apply rules
  208. let ankiHtml = html;
  209. rules.forEach(rule => {
  210. ankiHtml = ankiHtml.replace(rule.regex, rule.replacement);
  211. });
  212.  
  213. return ankiHtml.trim();
  214. }
  215. })();