|
| 1 | +// ==UserScript== |
| 2 | +// @name atcoder-tools user script |
| 3 | +// @namespace https://github.com/kyuridenamida/atcoder-tools |
| 4 | +// @version 1.0.0 |
| 5 | +// @description write the code generated by atcoder-tools into the AtCoder's textbox |
| 6 | +// @author Kimiyuki Onaka |
| 7 | +// @match *://atcoder.jp/contests/*/tasks/* |
| 8 | +// ==/UserScript== |
| 9 | + |
| 10 | +import QualityResult from './models/QualityResult'; |
| 11 | +import Language from './models/Language'; |
| 12 | + |
| 13 | +declare const $ : any; // of jQuery |
| 14 | + |
| 15 | +function loadAllQualityResults(): Promise<QualityResult[]> { |
| 16 | + const apiUrl = 'https://kyuridenamida.github.io/atcoder-tools/api/all.json'; |
| 17 | + |
| 18 | + return new Promise<QualityResult[]>((resolve, reject) => { |
| 19 | + const xhr = new XMLHttpRequest(); |
| 20 | + xhr.open('GET', apiUrl); |
| 21 | + xhr.responseType = 'json'; |
| 22 | + |
| 23 | + xhr.onload = () => { |
| 24 | + if (xhr.status === 200) { |
| 25 | + const qualityResult: QualityResult[] = xhr.response; |
| 26 | + resolve(qualityResult); |
| 27 | + |
| 28 | + } else { |
| 29 | + reject(Error(xhr.statusText)); |
| 30 | + } |
| 31 | + }; |
| 32 | + xhr.onerror = () => { |
| 33 | + reject(Error("Network Error")); |
| 34 | + }; |
| 35 | + |
| 36 | + xhr.send(); |
| 37 | + }); |
| 38 | +} |
| 39 | + |
| 40 | +function getContestIdAndProblemId(): [string, string] { |
| 41 | + const re = new RegExp('://atcoder\\.jp/contests/([^/]+)/tasks/([^/?&]+)'); |
| 42 | + const found = window.location.href.match(re); |
| 43 | + if (found == null) { |
| 44 | + throw Error("failed to parse URL. something wrong"); |
| 45 | + } |
| 46 | + const contestId = found[1]; |
| 47 | + const problemId = found[2]; |
| 48 | + return [contestId, problemId]; |
| 49 | +} |
| 50 | + |
| 51 | +function getQualityResultForCurrentProblem(allQualityResults: QualityResult[]): QualityResult { |
| 52 | + const [contestId, problemId] = getContestIdAndProblemId(); |
| 53 | + for (const qualityResult of allQualityResults) { |
| 54 | + if (qualityResult.contest.contest_id === contestId && qualityResult.problem.problem_id === problemId) { |
| 55 | + return qualityResult; |
| 56 | + } |
| 57 | + } |
| 58 | + throw Error("QualityResult not found"); |
| 59 | +} |
| 60 | + |
| 61 | +function getLanguageFromAtCoderDisplayName(languageName: string): Language | null { |
| 62 | + if (languageName.startsWith('C++')) { |
| 63 | + return 'cpp'; |
| 64 | + } else if (languageName.startsWith('Java') && ! languageName.startsWith('JavaScript')) { |
| 65 | + return 'java'; |
| 66 | + } else if (languageName.startsWith('Rust')) { |
| 67 | + return 'rust'; |
| 68 | + } else if (languageName.startsWith('Python3') || languageName.startsWith('PyPy3')) { |
| 69 | + return 'python'; |
| 70 | + } else { |
| 71 | + return null; |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +function isErasableCode(code: string, qualityResult: QualityResult): boolean { |
| 76 | + if (!code.trim()) { |
| 77 | + return true; |
| 78 | + } |
| 79 | + for (const generatedCode of Object.values(qualityResult.codes)) { |
| 80 | + if (code.trim() === generatedCode.trim()) { |
| 81 | + return true; |
| 82 | + } |
| 83 | + } |
| 84 | + return false; |
| 85 | +} |
| 86 | + |
| 87 | +async function main() { |
| 88 | + const textarea = document.getElementsByName('sourceCode')[0]; |
| 89 | + const languageId = document.getElementsByName('data.LanguageId')[0] as HTMLSelectElement; |
| 90 | + if (textarea === undefined) { |
| 91 | + throw Error("textarea not found. are you logged in?"); |
| 92 | + } |
| 93 | + |
| 94 | + const allQualityResults = await loadAllQualityResults(); |
| 95 | + const qualityResult = getQualityResultForCurrentProblem(allQualityResults); |
| 96 | + console.log(`QualityResult: ${qualityResult}`); |
| 97 | + |
| 98 | + const editor = $(textarea).data('editor'); // of CodeMirror (https://codemirror.net/) |
| 99 | + const run = () => { |
| 100 | + if (isErasableCode(editor.getValue(), qualityResult)) { |
| 101 | + const languageName = languageId.options[languageId.selectedIndex].textContent; |
| 102 | + const language = languageName === null ? null : getLanguageFromAtCoderDisplayName(languageName); |
| 103 | + editor.setValue(language === null ? "" : qualityResult.codes[language]); |
| 104 | + } |
| 105 | + }; |
| 106 | + run(); |
| 107 | + editor.on('optionChange', run); |
| 108 | +} |
| 109 | + |
| 110 | +main(); |
0 commit comments