您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Parse AtCoder problem statement into sections and create Anki CSV
当前为
// ==UserScript== // @name spoiled competitive programming // @namespace http://tampermonkey.net/ // @version 0.3 // @description Parse AtCoder problem statement into sections and create Anki CSV // @author Harui (totally helped by GPT-4o) // @match https://atcoder.jp/contests/*/tasks/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // Create buttons for Japanese const openMarkdownButtonJa = document.createElement('button'); openMarkdownButtonJa.textContent = '新しいタブで日本語のMarkdownを開く'; openMarkdownButtonJa.style.position = 'fixed'; openMarkdownButtonJa.style.left = '10px'; openMarkdownButtonJa.style.bottom = '145px'; openMarkdownButtonJa.style.zIndex = '1000'; openMarkdownButtonJa.style.padding = '10px'; openMarkdownButtonJa.style.backgroundColor = '#4CAF50'; openMarkdownButtonJa.style.color = 'white'; openMarkdownButtonJa.style.border = 'none'; openMarkdownButtonJa.style.borderRadius = '5px'; openMarkdownButtonJa.style.cursor = 'pointer'; const openHtmlButtonJa = document.createElement('button'); openHtmlButtonJa.textContent = '新しいタブで日本語のHTMLを開く'; openHtmlButtonJa.style.position = 'fixed'; openHtmlButtonJa.style.left = '10px'; openHtmlButtonJa.style.bottom = '100px'; openHtmlButtonJa.style.zIndex = '1000'; openHtmlButtonJa.style.padding = '10px'; openHtmlButtonJa.style.backgroundColor = '#4CAF50'; openHtmlButtonJa.style.color = 'white'; openHtmlButtonJa.style.border = 'none'; openHtmlButtonJa.style.borderRadius = '5px'; openHtmlButtonJa.style.cursor = 'pointer'; // Append the buttons to the body document.body.appendChild(openMarkdownButtonJa); document.body.appendChild(openHtmlButtonJa); // Event listener for open Markdown button (Japanese) openMarkdownButtonJa.addEventListener('click', () => { const markdownContent = htmlToMarkdown(extractHtml('ja')); // Open the Markdown content in a new tab const newTab = window.open(); newTab.document.open(); newTab.document.write('<pre>' + markdownContent + '</pre>'); newTab.document.close(); }); // Event listener for open HTML button (Japanese) openHtmlButtonJa.addEventListener('click', () => { const htmlContent = htmlToAnkiHtml(extractHtml('ja')); // Open the HTML content in a new tab const newTab = window.open(); newTab.document.open(); newTab.document.write(htmlContent); newTab.document.close(); }); // Create buttons and style them const openMarkdownButtonEn = document.createElement('button'); openMarkdownButtonEn.textContent = 'Open English Markdown in New Tab'; openMarkdownButtonEn.style.position = 'fixed'; openMarkdownButtonEn.style.left = '10px'; openMarkdownButtonEn.style.bottom = '55px'; openMarkdownButtonEn.style.zIndex = '1000'; openMarkdownButtonEn.style.padding = '10px'; openMarkdownButtonEn.style.backgroundColor = '#4CAF50'; openMarkdownButtonEn.style.color = 'white'; openMarkdownButtonEn.style.border = 'none'; openMarkdownButtonEn.style.borderRadius = '5px'; openMarkdownButtonEn.style.cursor = 'pointer'; const openHtmlButtonEn = document.createElement('button'); openHtmlButtonEn.textContent = 'Open English HTML in New Tab'; openHtmlButtonEn.style.position = 'fixed'; openHtmlButtonEn.style.left = '10px'; openHtmlButtonEn.style.bottom = '10px'; openHtmlButtonEn.style.zIndex = '1000'; openHtmlButtonEn.style.padding = '10px'; openHtmlButtonEn.style.backgroundColor = '#4CAF50'; openHtmlButtonEn.style.color = 'white'; openHtmlButtonEn.style.border = 'none'; openHtmlButtonEn.style.borderRadius = '5px'; openHtmlButtonEn.style.cursor = 'pointer'; // Append the buttons to the body document.body.appendChild(openMarkdownButtonEn); document.body.appendChild(openHtmlButtonEn); // Create button for Anki CSV download const downloadCsvButton = document.createElement('button'); downloadCsvButton.textContent = 'Anki用CSVをダウンロード'; downloadCsvButton.style.position = 'fixed'; downloadCsvButton.style.left = '10px'; downloadCsvButton.style.bottom = '190px'; downloadCsvButton.style.zIndex = '1000'; downloadCsvButton.style.padding = '10px'; downloadCsvButton.style.backgroundColor = '#4CAF50'; downloadCsvButton.style.color = 'white'; downloadCsvButton.style.border = 'none'; downloadCsvButton.style.borderRadius = '5px'; downloadCsvButton.style.cursor = 'pointer'; // Append the button to the body document.body.appendChild(downloadCsvButton); // Event listener for download CSV button downloadCsvButton.addEventListener('click', () => { const htmlContent = htmlToAnkiHtml(extractHtml('ja')); const csvContent = generateAnkiCsv(htmlContent); // Create a blob from the CSV content const blob = new Blob([csvContent], { type: 'text/csv' }); const url = URL.createObjectURL(blob); // Create a link element and trigger the download const a = document.createElement('a'); a.href = url; a.download = 'anki.csv'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }); // Function to extract parts and remove Katex, returning HTML function extractHtml(lang) { const parts = document.querySelectorAll(`span.lang-${lang}`); const problemTitle = document.title; let htmlContent = `<h1>${problemTitle}</h1>\n\n`; parts.forEach(part => { // Clone the part to avoid modifying the original document const clone = part.cloneNode(true); // Remove "Copy" buttons const copyButtons = clone.querySelectorAll('.btn-copy, .btn-pre, .div-btn-copy'); copyButtons.forEach(button => button.remove()); // Remove Katex elements const katexElements = clone.querySelectorAll('.katex'); katexElements.forEach(katex => { const tex = katex.querySelector('.katex-mathml annotation'); if (tex) { const textNode = document.createTextNode(tex.textContent); katex.parentNode.replaceChild(textNode, katex); } }); // Append HTML content htmlContent += clone.outerHTML + '\n\n'; }); return htmlContent; } // Function to generate Anki CSV function generateAnkiCsv(htmlContent) { const problemId = window.location.pathname.split('/')[2]; const problemAlpha = window.location.pathname.split('/')[4].split('_')[1]; const problemTitle = document.querySelector('title').textContent.trim(); const problemName = `${problemId.toUpperCase()}_${problemAlpha.toUpperCase()} ${problemTitle}`; const problemUrl = window.location.href; // Replace double quotes with double double quotes const escapedHtmlContent = htmlContent.replace(/"/g, '""'); const csvContent = `#separator:tab\n#html:true\n"${problemName}"\t"<a href=""${problemUrl}"">${problemUrl}</a>"\t"${escapedHtmlContent}"`; return csvContent; } // Event listener for open Markdown button (English) openMarkdownButtonEn.addEventListener('click', () => { const markdownContent = htmlToMarkdown(extractHtml('en')); // Open the Markdown content in a new tab const newTab = window.open(); newTab.document.open(); newTab.document.write('<pre>' + markdownContent + '</pre>'); newTab.document.close(); }); // Event listener for open HTML button (English) openHtmlButtonEn.addEventListener('click', () => { const htmlContent = extractHtml('en'); // Open the HTML content in a new tab const newTab = window.open(); newTab.document.open(); newTab.document.write(htmlContent); newTab.document.close(); }); // Function to convert HTML to Markdown function htmlToMarkdown(html) { // Simple conversion rules const rules = [ { regex: /<h3>(.*?)<\/h3>/g, replacement: '\n### $1\n' }, { regex: /<h2>(.*?)<\/h2>/g, replacement: '\n## $1\n' }, { regex: /<h1>(.*?)<\/h1>/g, replacement: '\n# $1\n' }, { regex: /<p>(.*?)<\/p>/g, replacement: '$1\n' }, { regex: /<ul>(.*?)<\/ul>/gs, replacement: '$1' }, { regex: /<li>(.*?)<\/li>/g, replacement: '- $1' }, { regex: /<pre.*?>(.*?)<\/pre>/gs, replacement: '\n\n``` \n$1\n```' }, { regex: /<var>(.*?)<\/var>/g, replacement: '`$1`' }, { regex: /<div.*?>(.*?)<\/div>/gs, replacement: '$1' }, { regex: /<span.*?>(.*?)<\/span>/g, replacement: '$1' }, { regex: /<section.*?>(.*?)<\/section>/gs, replacement: '$1' }, { regex: /<hr>/g, replacement: '---' }, { regex: /<br>/g, replacement: '\n' } ]; // Apply rules let markdown = html; rules.forEach(rule => { markdown = markdown.replace(rule.regex, rule.replacement); }); // Remove any remaining HTML tags markdown = markdown.replace(/<\/?[^>]+(>|$)/g, ""); return markdown.trim(); } // Function to convert HTML to Anki HTML function htmlToAnkiHtml(html) { // Simple conversion rules const rules = [ { regex: /<h3>(.*?)<\/h3>/g, replacement: '\n<h3>$1</h3>\n' }, { regex: /<h2>(.*?)<\/h2>/g, replacement: '\n<h2>$1</h2>\n' }, { regex: /<h1>(.*?)<\/h1>/g, replacement: '\n<h1>$1</h1>\n' }, { regex: /<p>(.*?)<\/p>/g, replacement: '<p>$1</p>\n' }, { regex: /<ul>(.*?)<\/ul>/gs, replacement: '$1' }, { regex: /<li>(.*?)<\/li>/g, replacement: '<li>$1</li>' }, { regex: /<pre.*?>(.*?)<\/pre>/gs, replacement: '\n\n<pre>\n$1\n</pre>' }, { regex: /<var>(.*?)<\/var>/g, replacement: '<var>$1</var>' }, { regex: /<div.*?>(.*?)<\/div>/gs, replacement: '<div>$1</div>' }, { regex: /<span.*?>(.*?)<\/span>/g, replacement: '<span><anki-mathjax>$1</anki-mathjax></span>' }, { regex: /<section.*?>(.*?)<\/section>/gs, replacement: '<section>$1</section>' }, { regex: /<hr>/g, replacement: '<hr>' }, { regex: /<br>/g, replacement: '<br>' }, { regex: /\$(.*?)\$/g, replacement: '$1' } // Convert TeX to Anki TeX ]; // Apply rules let ankiHtml = html; rules.forEach(rule => { ankiHtml = ankiHtml.replace(rule.regex, rule.replacement); }); return ankiHtml.trim(); } })();