Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
// ==UserScript==
// @name ShogiDB2 DownloadLinkCreator
// @description 将棋DB2の検索結果一覧ページに棋譜ダウンロードボタンを追加します。
// @include https://shogidb2.com/latest*
// @include https://shogidb2.com/floodgate*
// @include https://shogidb2.com/tournament/*
// @include https://shogidb2.com/player/*
// @include https://shogidb2.com/strategy/*
// @require https://cdn.rawgit.com/polygonplanet/encoding.js/master/encoding.min.js
// @namespace knoa.jp
// @version 1.1
// ==/UserScript==
(function () {
console.log('ShogiDB2 DownloadLinkCreator');
var KEYWORDS = ['竜王戦','名人戦','順位戦','王位戦','王座戦','棋王戦','王将戦','棋聖戦','十段戦','九段戦'];/*対局情報に含まれていればハイライト*/
var TITLES = ['竜王','名人','王位','王座','棋王','王将','棋聖','叡王','朝日杯','銀河','NHK杯','JT杯'];/*対局者名から取り除く称号*/
var FILETYPE = 'KIF';/*棋譜ファイルの形式(CSA,KIF,KI2)*/
var ENCODING = 'SJIS';/*棋譜ファイルの文字コード変換(SJIS,UTF8,...)※ブックマークレット実行時は無効*/
var INTERVAL = 1000;/*短時間連続読み込みの抑制*/
var LIMIT = 9999;/*取得数制限(デバッグ用)*/
var AUTO = false;/*自動ダウンロード(キーワードが含まれる棋譜のみ)*/
var games = document.getElementsByClassName('list-group-item');/*検索結果一覧*/
var start = document.createElement('a');/*起動ボタン*/
var core = {
/*初期化*/
'initialize': function(){
console.log('initialize...');
var year, script;
core.attatchStyle();
start.className = 'make-downloads btn btn-primary';
start.textContent = AUTO ? '自動ダウンロード開始' : 'ダウンロードボタン生成';
start.addEventListener('click', function(e){
this.classList.add('disabled');
for(var i=0; games[i]; i++){
core.markKeywords(games[i]);
}
/*自動ダウンロード時は先に取得予定リストを揃えておく*/
if(AUTO){
year = prompt('何年度までさかのぼりますか?', (new Date()).getFullYear());
core.paginate(document.querySelector('a[aria-label="Next"]').href, year);
}
/*棋譜データ処理スクリプトを取得してからボタン生成を始める*/
script = document.createElement('script');
script.src = '/js/shogiboard.min.js';
script.addEventListener('load', function(e){
core.createLink(0);
});
document.getElementsByTagName('head')[0].appendChild(script);
});
document.querySelector('h2').appendChild(start);
},
/*スタイル付与*/
'attatchStyle': function(){
console.log('attatchStyle...');
var style, css = '';
css += 'a.keywords{background:#ccddee!important;}';
css += 'a.make-downloads{position:absolute;right:15px;}';
css += 'a.download{position:absolute;right:30px;margin-top:-45px;}';
style = document.createElement('style');
style.textContent = css;
document.getElementsByTagName('head')[0].appendChild(style);
},
/*キーワードを含む棋譜を特定*/
'markKeywords': function (game) {
if(game.textContent.match(new RegExp(KEYWORDS.join('|'))) != null){
game.classList.add('keywords');
}
},
/*自動ページネーション*/
'paginate': function (url, year) {
console.log('paginate...', url);
var xhr, d, gs;
setTimeout(function () {
xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'document';
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
d = xhr.responseXML;
gs = d.querySelectorAll('a.list-group-item');
for(var i=0; gs[i]; i++){
if(year+'年04月' <= gs[i].querySelector('p:nth-child(2)').textContent.replace(/\b([1-9])()/g, '0$1$2')){
core.markKeywords(gs[i]);
games[0].parentNode.insertBefore(gs[i], null);
}else{
core.markKeywords();
return;
}
if(!gs[i+1] && d.querySelector('a[aria-label="Next"]')){
core.paginate(d.querySelector('a[aria-label="Next"]').href, year);
}
}
}
};
xhr.send();
}, INTERVAL);
},
/*棋譜ダウンロードボタン生成*/
'createLink': function (i) {
console.log('createLink...', i);
var a, xhr, d, table, date, title, sente, gote, kifu;
/*自動モードのときはキーワードを含まない棋譜は無視する*/
if(AUTO && !games[i].classList.contains('keywords')){
if (!games[i+1] || LIMIT <= i+1){
start.textContent = '完了';
console.log('done.');
return;
}
core.createLink(i+1);
return;
}
/*無効状態のボタンを作っておく*/
a = document.createElement('a');
a.className = 'download btn btn-primary disabled';
a.textContent = FILETYPE + '形式';
games[i].parentNode.insertBefore(a, games[i].nextSibling);
setTimeout(function () {
xhr = new XMLHttpRequest();
xhr.open('GET', games[i].href);
xhr.responseType = 'document';
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
d = xhr.responseXML;
/*ここからファイル名構築*/
table = d.querySelector('table');
date = table.querySelector('tr:nth-child(1) td').textContent.replace(/\b([1-9])(|)/g, '0$1$2');
title = table.querySelector('tr:nth-child(2) td').textContent;
sente = core.normalizeName(table.querySelector('tr:nth-child(4) td').textContent);
gote = core.normalizeName(table.querySelector('tr:nth-child(5) td').textContent);
a.download = [date, title, sente, gote].join('-') + '.' + FILETYPE.toLowerCase();
/*ここから棋譜取得*/
eval(d.querySelector('body>script[type]').textContent);/*"var data = {(json)}"であることを信頼*/
kifu = core.exportKifu(data);
/*ここからボタン有効化*/
a.href = core.kifuToObjectURL(kifu);
if(AUTO && games[i].classList.contains('keywords')){
a.click();
a.textContent = "済";
}else{
a.classList.remove('disabled');
}
}
};
xhr.send();
if (!games[i+1] || LIMIT <= i+1){
start.textContent = '完了';
console.log('done.');
return;
}
core.createLink(i+1);
}, INTERVAL);
},
/*段位タイトルを削除*/
'normalizeName': function(name){
var dan = new RegExp('((女流|奨励会)?[初一二三四五六七八九・](級|段|冠)|' + TITLES.join('|') + ')$', 'g');
name = name.replace(dan, '');
name = name.replace(/ | /g, '');
return name;
},
/*指定形式で棋譜ファイル生成*/
'exportKifu': function(data){
switch(FILETYPE){
case 'CSA':
return new CSAExporter(data).export();
case 'KIF':
return new KifExporter(data).export();
case 'KI2':
return new KI2Exporter(data).export();
}
},
/*棋譜ファイルからobjectURL生成*/
'kifuToObjectURL': function(kifu){
var kifuArray;
if(typeof Encoding === 'undefined' || ENCODING == 'UTF8'){
kifuArray = [new Uint8Array([0xEF, 0xBB, 0xBF])/*BOM追加*/, kifu];
}else{
kifuArray = [new Uint8Array(Encoding.convert(kifu, {to:ENCODING, from:'AUTO', type:'array'}))];
}
return window.URL.createObjectURL(new Blob(kifuArray));
}
};
core.initialize();
}) ();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment