Skip to content

Instantly share code, notes, and snippets.

@yumechi
Last active October 11, 2020 14:40
Show Gist options
  • Save yumechi/76093bc40b5cb0d339eb to your computer and use it in GitHub Desktop.
Save yumechi/76093bc40b5cb0d339eb to your computer and use it in GitHub Desktop.
AtCoderのSubmitページからプログラムをDLするスクリプト一応完成(使い方読んでね)
// ==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 = {
"&quot;": "\"",
"&amp;": "&",
"&lt;": "<",
"&gt;": ">",
"&nbsp;": " ",
};
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();
})();
@yumechi
Copy link
Author

yumechi commented Mar 6, 2016

このスクリプトの説明:
Atcoderのsubmitページ上で簡単にソースコードがダウンロードできるスクリプト。(Greasemonkeyで動かしたい)
を作ろうとしている(完成とは言っていない)
一応、submitページに行き、firebugとかでスクリプトを実行すると動く… っぽい(テストケースが甘いため、なんとも言いがたい)

TODO:
・ファイル名をもう少しかっこよく付けたい(a.txtはクソ)
・ページ中にダウンロードボタンを埋め込みたい
・Greasemonkeyで動かせるようにする

@yumechi
Copy link
Author

yumechi commented Mar 7, 2016

更新(2016/03/08)
自動的に名前付けする用に改良。
format:コンテスト名_年月日_時間_userID_結果.プログラミング言語の拡張子

埋め込みを頑張りたい。

@yumechi
Copy link
Author

yumechi commented Mar 14, 2016

大体完成(2016/03/15)
GreasemonkeyやTemperMonkeyで動きます。
ファイル名も自動的につくようになりました。
ページ中にダウンロードボタンを埋め込みました。

@yumechi
Copy link
Author

yumechi commented Apr 2, 2016

(追記:2016/04/03)
新しい言語に対応しました

@yumechi
Copy link
Author

yumechi commented Oct 11, 2020

数年動作確認してないので多分動かないんじゃないかなと思う(動いてもメンテする気がないです)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment