Last active
October 11, 2020 14:40
-
-
Save yumechi/76093bc40b5cb0d339eb to your computer and use it in GitHub Desktop.
AtCoderのSubmitページからプログラムをDLするスクリプト一応完成(使い方読んでね)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Atcoder Submit Code Downloader | |
// @namespace https://twitter.com/yumechi0525 | |
// @description Submition PageからプログラムをDLするスクリプト | |
// @include http://*.atcoder.jp/submissions/* | |
// 使い方: | |
// 1, FileFoxならGreasemonkey、ChromeならTampermonkeyをインストールしてスクリプトを導入する | |
// 2, http://abc033.contest.atcoder.jp/submissions/631975 みたいなsubmitページに移動して、スクリプトを実行する(ボタンが出なかったら再読込してね) | |
// ファイル名フォーマット:コンテスト名_年月日_時分秒_ID_ジャッジステータス.拡張子 | |
// ==/UserScript== | |
(function() { | |
// プログラム名を保持するクラス | |
var ProgramInfo = function() { | |
this.contestname = ""; | |
this.username = ""; | |
this.time = ""; | |
this.judge_status = ""; | |
this.prog_language = ""; | |
} | |
var info = new ProgramInfo(); | |
// refer: save file | |
// http://qiita.com/bump_of_kiharu/items/f41beec668e1f3ea675e | |
// ファイルを保存する | |
function savefile(data, filename) { | |
// refer: defualt value | |
// http://d.hatena.ne.jp/pashango_p/20110425/1303717074 | |
if (typeof filename === 'undefined') filename = "a.txt"; | |
// download | |
var blob = new Blob([data], { | |
type: "text/csv;charset=utf-8;" | |
}); | |
if (window.navigator.msSaveOrOpenBlob) { | |
// for IE | |
navigator.msSaveBlob(blob, file_name); | |
} else { | |
// for Other(Chrome, Firefox) | |
var downloadLink = $('<a></a>'); | |
downloadLink.attr('href', window.URL.createObjectURL(blob)); | |
downloadLink.attr('download', filename); | |
downloadLink.attr('target', '_blank'); | |
$('body').append(downloadLink); | |
downloadLink[0].click(); | |
downloadLink.remove(); | |
} | |
} | |
// refer | |
// http://qiita.com/hrdaya/items/291276a5a20971592216 | |
// http://qiita.com/naoyashiga/items/75bce8cabbbab29b0ccb | |
// http://iwb.jp/pre-tag-html-javascript-escape/ | |
// サニタイズされたものを戻す | |
function unsanitize(data) { | |
// refer | |
// http://pst.co.jp/powersoft/html/index.php?f=3401 | |
var dict = { | |
""": "\"", | |
"&": "&", | |
"<": "<", | |
">": ">", | |
" ": " ", | |
}; | |
for (var key in dict) { | |
data = data.replace(new RegExp(key, "g"), dict[key]); | |
} | |
return data; | |
} | |
// htmlテキストになっているプログラムデータを普通のテキストに変換する | |
function cleanData(data) { | |
var ret = data; | |
ret = ret.replace(/(<\/li>)/gi, '\n'); | |
ret = ret.replace(/<.+?>/gi, ''); | |
ret = unsanitize(ret); | |
return ret; | |
} | |
// プログラム部分を切り出す(htmlテキストになる) | |
function getProgramSource() { | |
var elements = document.querySelectorAll('pre'); | |
return cleanData(elements[0].innerHTML); | |
} | |
// 言語ごとの拡張子を返却する | |
function getLanguageName(data) { | |
var lang_dic = { | |
"Java" : "java", | |
"Python" : "py", | |
"Perl" : "pl", | |
"Pascal" : "pas", | |
"Text" : "txt", | |
"Bash" : "sh", | |
"C" : "c", | |
"C++" : "cpp", | |
"C++11" : "cpp", | |
"PHP" : "php", | |
"D" : "d", | |
"Ruby" : "rb", | |
"JavaScript" : "js", | |
"Python2" : "py", | |
"Python3" : "py", | |
"Scala" : "scala", | |
"Go" : "go", | |
"Scheme" : "scm", | |
"Haskell" : "hs", | |
"CommonLisp" : "lsp", | |
"Clojure" : "clj", | |
"C++14" : "cpp", | |
"Java8" : "java", | |
"OCaml" : "ml", | |
"C#" : "cs", | |
// 新バージョンに合わせた追加 | |
"Fortran" : "f", | |
"VisualBasic" : "vb", | |
"Objective-C" : "m", | |
"Swift" : "swift", | |
"Rust" : "rs", | |
"Sed" : "sed", | |
"Awk" : "awk", | |
"Brainfuck" : "bf", | |
"StandardML" : "sml", | |
"PyPy2" : "py", | |
"PyPy3" : "py", | |
"Crystal" : "cr", | |
"F#" : "fs", | |
"Unlambda" : "unlambda", | |
"Lua" : "lua", | |
"LuaJIT" : "lua", | |
"MoonScript" : "moon", | |
"Ceylon" : "ceylon", | |
"Julia" : "jl", | |
"Octave" : "m", | |
"Nim" : "nim", | |
"TypeScript" : "ts", | |
"Perl6" : "pl", | |
"Kotlin" : "kt", | |
"PHP7" : "php" | |
}; | |
for(var key in lang_dic) { | |
if(key === data) { | |
return lang_dic[key]; | |
} | |
} | |
return "txt"; | |
} | |
// ファイルネームを自動生成 | |
// ファイル名フォーマット:コンテスト名_年月日_時分秒_ID_ジャッジステータス.拡張子 | |
function setFileName() { | |
var filename = "a.txt"; | |
var htmls = document.querySelectorAll('html')[0].innerHTML.split("\n"); | |
var htmllines = htmls.length; | |
info.contestname = function() { | |
var contestURL = location.href; | |
return contestURL.split(".")[0].replace(/.+\//, ""); | |
}(); | |
for(var i = 0; i < htmllines; i++) { | |
// for username | |
if(htmls[i].indexOf("ユーザ名") > 0 && info.username === "") { | |
i++; // next line have user ID | |
// match return array. if nobody hit it, return null | |
var userURL = htmls[i].match(/\".+\"/); | |
if(userURL == null) { | |
info.username = "username"; | |
} else { | |
userURL = userURL[0]; | |
info.username = userURL.split("\/")[2].replace(/\"/, ""); | |
} | |
} | |
// for time | |
if(htmls[i].indexOf("投稿日時") > 0 && info.time === "") { | |
i++; // next line have submition time | |
var timedata = htmls[i].match(/<time.+>.+<\/time>/); | |
if(timedata == null) { | |
info.time = "XXXXXXXX"; | |
} else { | |
timedata = timedata[0].replace(/<.+?>/g, "") | |
var times = timedata.split(" "); | |
info.time = times[0].replace(/\//g, "") + "_" + times[1].replace(/:/g, ""); | |
} | |
} | |
// for prog_language | |
if(htmls[i].indexOf("言語") > 0 && info.prog_language === "") { | |
i++; // next line have language data | |
var language_str = htmls[i].replace(/<.+?>/g, ""); | |
language_str = language_str.replace(/\(.+?\)/g, ""); | |
language_str = language_str.replace(/\s/g, ""); | |
info.prog_language = getLanguageName(language_str); | |
} | |
// for judge_status | |
// "状態" appeared 2 times, we need first time. | |
if(htmls[i].indexOf("状態") > 0 && info.judge_status === "") { | |
i++; // next line have judge status | |
var status_str = htmls[i].replace(/<.+?>/g, ""); | |
status_str = status_str.replace(/\s/g, ""); | |
info.judge_status = status_str; | |
} | |
} | |
filename = info.contestname + "_" | |
+ info.time + "_" | |
+ info.username + "_" | |
+ info.judge_status + "." | |
+ info.prog_language; | |
return filename; | |
} | |
// テキストエリアとボタンを追加する | |
function addInputField() { | |
var filename = setFileName(); | |
// refer: http://blog.livedoor.jp/vtwin_engine/archives/3815328.html | |
// create textarea | |
var filename_textarea = document.createElement("input"); | |
filename_textarea.id = "filename_text"; | |
filename_textarea.type = "text"; | |
filename_textarea.value = filename; | |
document.querySelectorAll('h2')[0].appendChild(filename_textarea); | |
// create button | |
var dl_button = document.createElement("input"); | |
dl_button.id = "dl_button"; | |
dl_button.type = "button"; | |
dl_button.value = "ソースコード保存"; | |
document.querySelectorAll('h2')[0].appendChild(dl_button); | |
// click action | |
document.getElementById("dl_button").onclick=function() { | |
var newfilename = document.getElementById("filename_text").value; | |
if(newfilename === filename) { | |
savefile(getProgramSource(), filename); | |
} else { | |
var extention = (newfilename.search(/\./) >= 0) ? "" : ("." + info.prog_language); | |
savefile(getProgramSource(), newfilename + extention); | |
} | |
} | |
} | |
var main = function() { | |
addInputField(); | |
} | |
main(); | |
})(); |
更新(2016/03/08)
自動的に名前付けする用に改良。
format:コンテスト名_年月日_時間_userID_結果.プログラミング言語の拡張子
埋め込みを頑張りたい。
大体完成(2016/03/15)
GreasemonkeyやTemperMonkeyで動きます。
ファイル名も自動的につくようになりました。
ページ中にダウンロードボタンを埋め込みました。
(追記:2016/04/03)
新しい言語に対応しました
数年動作確認してないので多分動かないんじゃないかなと思う(動いてもメンテする気がないです)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
このスクリプトの説明:
Atcoderのsubmitページ上で簡単にソースコードがダウンロードできるスクリプト。(Greasemonkeyで動かしたい)
を作ろうとしている(完成とは言っていない)
一応、submitページに行き、firebugとかでスクリプトを実行すると動く… っぽい(テストケースが甘いため、なんとも言いがたい)
TODO:
・ファイル名をもう少しかっこよく付けたい(a.txtはクソ)
・ページ中にダウンロードボタンを埋め込みたい
・Greasemonkeyで動かせるようにする