Tumblr


2009年04月27日

引越ししてみました livedoor -> pixiv

pixivブログの編集画面が残念すぎるのでアレなんだけど、面白そうだなので。
おっと萌えキャラの悪口はそこまでだ

2009年04月13日

就職活動が残念な状況になってきた

まあ、ある程度予想していたが素直にぶつかり過ぎて玉砕してる感じでションボリすることしきり。
もう弾数が無いのでどうしたものかと悩み中。もう今から増やすのも辛い状況で、
ほんとうにニッチもサッチもいかんです。でも無い内定だけは避けたいので、もう少し頑張るかな。

2009年04月07日

ActiveObjectsを使っていてすっかり忘れていたこと

データベースを触るとき、コネクションプーリングという話があります。参考
ActiveObjectsには当然ながら、コネクションプーリングを使うことが出来ます。
しかし、なぜかActiveObjectsはH2Databaseには対応していません(正直、これは理解不能
EntityManagerの取得に関しては、クラスひとつ作って対応することは出来るものの、
コネクションプーリングは、各DBベンダ毎にenumでハードコーディングされているので、
クラスを作って対応みたいなことは出来ず、ソースから書き換える必要があります。

なので、プロトタイプのつもりで作ったタグ付けサービスはコネクションプーリングを使わずに動かしました。

ウン千データの中から、百程度のデータを抽出する単純な検索を実行したら、4,5秒帰ってこないということで、
何故だろうと思っていたら、後輩の『これ、毎度接続処理してるんじゃね?』と言われて気づきました。
『コネクションプーリングしてなかったじゃん…』と。
コネクションプーリングを使うようにすると、無事一瞬で帰ってくるようになりました。ちゃんちゃん。

2009年04月02日

Tumblrのタグ機能を云々

Tumblrのタグ機能をTagClickにさせる予定だったのだが、
TagClickの扱いにくさに辟易してきたので、タグ付けオンリーの自作サービスを作ることに。
とりあえず、試作でpermalinkに対するタグ付けと、permalinkの検索だけ実装
それに対してグリモンスクリプトを組んで、動くことを確認。
pixivのみを対象にしていたのを、URLとタグを抽出するパーサのリストを作ることで、
ある程度汎用的にタグ付けできるようにしてみた。
ブログの個別記事などは割とタグ付けされた状態のものがあるので、quoteに対してもタグ付けできるようにという考え。
後は、タグ付けの書き込みを許可してるので、書き込み操作にユーザ認証を追加することと、
タグ付けの更新(追加変更など)を簡単に出来るように、グリモンスクリプトを弄ることが必要かな。

あ、ちょうどいい題材だと思うのでタグ付けサービスはCatalyst、もしくはRailsの習作にした。

2009年03月28日

Tumblrのタグ機能をTagClickに肩代わりさせる

Tumblrのタグ検索は日本語がうまく扱えないらしい(タグに日本語が使えない訳では無い)
普段タグを自分で付けたりしないので、あまり気にしてはいなかったのだけど、
ポスト数が増えるにつれ、後から探したいという欲求に耐えられなくなり、
『どげんかせんといけん』と、ようよう対策を講じることにしました。

とりあえず同じ不満を持つ人の対策を真似してみました。Tumblrにタグをつけてみた
ただ、これだけだとタグ付けの面倒は変わらないので、例のGreaseMonkeyを使って極力自動化することに。

1.Tumblrページ(= not Dashbord)のPixivポストを見つける

2.『タグ付けを更新する』ボタンを表示する。(赤網)
  ボタンをクリックすると、必要な情報を最初から入力された状態でTagClickのタグ登録画面へ飛ぶ。

3.TagClickにそれが登録されているか探す。
3.1.無い → Pixivを読み込みタグを見つけ、TagClickへ登録する。
3.2.ある → TagClickからタグを読み込み、Tumblrページに表示する(緑網)

サムネイル3







これで快適なタグ検索ライフを送ることが出来そうです。
まずは350ページ(1ページ10ポストなので約3500ポスト)に及ぶpixiv絵をTumblrからざっと閲覧してタグを登録させる作業を完了させないと^^

一応コードを公開しておく。
ただし、各人のTumblrのXML構造に影響されると思うので、多分汎用は無理。
そもそもTomblooを使ってTumblrにpixiv絵をポストする人専用というね。
素直にpixivのブックマーク使えって話ですよね〜

Tumblr用スクリプト
// ==UserScript==
// @name Tumblr and TagClick Tag add for pixiv
// @namespace http://blog.livedoor.jp/himachoco/
// @description Tumblr and TagClick Tag add for pixiv
// @include http://mix3.tumblr.com/*
// ==/UserScript==
var tumblrUrl = 'http://mix3.tumblr.com/';
var searchUrl = 'mix3_tumblr_search';

(function() {
var load = function(context) {
if(!context){
root = document.getElementById('container').firstChild;
}else{
root = context;
}
while(root){
var aTags = $x('div//a', root);
if(aTags.length){
var title = aTags[1].textContent;
var pixivlink = aTags[1].getAttribute('href');
var permalink = aTags[2].getAttribute('href');
if(pixivlink.match("pixiv")){
var updateUrl = createURL(permalink, title, pixivlink);
var button = buttonRender(updateUrl);
insertAfter(button, root.lastChild);

var pixiv_id= pixivlink.match(/(\&illust_id=)(\d*)$/)[2];
var url = 'http://search.tagclick.net/'+searchUrl+'/keyword?q='+pixiv_id+'&commit=%8C%9F%8D%F5';
tagRender(url, button, updateUrl);
}
}
root = root.nextSibling;
}
}

function setStyle(){
var style =
<><![CDATA[
input.btn{
border: 1px solid #333333;
color: #333333;
background-color: #edeae4;
font-family: Verdana, Geneva, sans-serif;
font-size: 11px;
font-weight: bold;
}
]]></>;
GM_addStyle(style);
}

function createURL(url, title, pixivlink){
title = encodeURI(title);
return 'http://www.tagclick.net/account/entry/post?url='+url+'&title='+title+'&pixivlink=['+pixivlink+']';
}

function buttonRender(url){
var insertButton = document.createElement('input');
insertButton.setAttribute('type', 'button');
insertButton.setAttribute('value', 'タグ付けを更新する');
insertButton.setAttribute('class', 'btn');
insertButton.setAttribute('onclick', 'window.open(\''+url+'\',\'_blank\')');
return insertButton;
}

function autoTagAdd(updateUrl){
GM_xmlhttpRequest({
method: 'GET',
url: updateUrl,
onload: function(res) {
var result = res.responseText.match(/<form[\s\S]*?<\/form>/)[0];
result = result.replace(/<script type=\"text\/javascript\">[\s\S]*?<\/script>/,"");
var formDom = (new DOMParser).parseFromString(result, "text/xml").firstChild;

GM_xmlhttpRequest({
method: 'GET',
url: updateUrl.match(/(\&pixivlink\=\[)(.*?)(\])$/)[2],
onload: function(res) {
var result = res.responseText.match(/<div id=\"tag_area\">[\s\S]*?<\/div>/);
if(result != null){
var tagDom = (new DOMParser).parseFromString(result[0], "text/xml").firstChild.firstChild.nextSibling;

var map = {};
var inputList = formDom.getElementsByTagName('input');
for(var i = 0; i < inputList.length; i++){
map[inputList[i].getAttribute('name')] = encodeURI(inputList[i].getAttribute('value'));
}

var tagString = '';
var tag = tagDom.getElementsByTagName('a');
for(var i = 0; i < tag.length; i++){
tagString += tag[i].textContent + ' ';
}
map['entry_tags'] = tagString;

var option = formDom.getElementsByTagName('option');
var count = 0;
for(var i = 0; i < option.length; i++){
if(option[i].hasAttribute('selected')){
switch (count){
case 0 :
map['posted_at[year]'] = option[i].getAttribute('value');
break;
case 1 :
map['posted_at[month]'] = option[i].getAttribute('value');
break;
case 2 :
map['posted_at[day]'] = option[i].getAttribute('value');
break;
case 3 :
map['posted_at[hour]'] = option[i].getAttribute('value');
break;
case 4 :
map['posted_at[minute]'] = option[i].getAttribute('value');
break;
}
count++;
}
}

map['entry[excerpt]'] = encodeURIComponent(updateUrl.match(/(\&pixivlink\=\[)(.*?)(\])$/)[2]);

/*
map['entry[title]'] = 'test';
map['entry_tags'] = 'test';
map['entry[excerpt]'] = 'test';
map['commit'] = encodeURI('検索');
*/

//log(map);
var mapResult = '';
for(i in map){
mapResult += '&'+i+'='+map[i];
}

//log('http://www.tagclick.net'+formDom.getAttribute('action'));
//log(mapResult.substring(1));
post('http://www.tagclick.net'+formDom.getAttribute('action'), mapResult.substring(1), function(text) {
log('tag update:'+decodeURI(map['entry[title]']));
});

//log(mapResult.substring(1));
}else{
log('illust may be removed');
}
}
});
}
});
}

function tagRender(url, button, updateUrl){
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(res) {
var result = res.responseText.match(/<p class=\"tags\">[\s\S]*?<\/p>/);
if(result != null){
result[0] = result[0].replace(/\ \;/g, "");
var dom = (new DOMParser).parseFromString(result, "text/xml").firstChild;
var aTag = dom.getElementsByTagName('a');
var span = document.createElement('span');
var strong = document.createElement('strong');
strong.innerHTML = ' タグ: ';
span.appendChild(strong);
if(!aTag.length){
strong.innerHTML += 'タグはありません';
}
for(var i = 0; i < aTag.length; i++){
var a = document.createElement('a');
a.setAttribute('href', 'http://search.tagclick.net/'+aTag[i].getAttribute('href'));
a.setAttribute('target', '_blank');
a.innerHTML = aTag[i].textContent + ' ';
span.appendChild(a);
}
insertAfter(span, button);
}else{
autoTagAdd(updateUrl);
}
}
});
}

function addFilter(filter, i) {
i = i || 4;
if(window.AutoPagerize && window.AutoPagerize.addFilter) {
//window.AutoPagerize.addFilter(filter);
window.AutoPagerize.addFilter(function(elements) {
filter(elements[0]);
});
} else if(i > 1) {
setTimeout(arguments.callee, 1000, filter, i -1);
}
}

// Main
setStyle();
load();
addFilter(load);

// Utility
function $x(xpath, context) {
context = context || document;
var res = document.evaluate(xpath, context, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
for(var i, nodes = [] ; i=res.iterateNext() ; nodes.push(i));
return nodes;
}

function insertAfter(newNode, node) {
return node.parentNode.insertBefore(newNode, node.nextSibling);
}

function log(message) {
if (unsafeWindow && unsafeWindow.console) {
unsafeWindow.console.log(message);
}
}

function post(url, data, cb) {
GM_xmlhttpRequest({
method: "POST",
url: url,
headers:{'Content-type':'application/x-www-form-urlencoded'},
data: data,
onload: function(xhr) { cb(xhr.responseText); }
});
}
})();

TagClick用スクリプト
// ==UserScript==
// @name TagClick Tag add for pixiv
// @namespace http://blog.livedoor.jp/himachoco/
// @description TagClick Tag add for pixiv
// @include http://www.tagclick.net/account/entry/post*pixivlink*
// ==/UserScript==

(function() {
var url = window.location.search.match(/(\&pixivlink\=\[)(.*?)(\])$/)[2];

GM_xmlhttpRequest({
method: 'GET',
url: url,
headers: {
'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
'Accept': 'application/atom+xml,application/xml,text/xml',
},
onload: function(res) {
var result = res.responseText.match(/<span id=\"tags\">[\s\S]*?\[\s<a/)[0];
result = result.replace(/\[\s<a/,"");

var dom = (new DOMParser).parseFromString(result, "text/xml").firstChild;
var tag = dom.getElementsByTagName('a');
var tagString = '';
for(var i = 0; i < tag.length; i++){
tagString += tag[i].textContent + ' ';
}

var tagForm = document.getElementById('entry_tags');
tagForm.setAttribute('value', tagString);

var textAreaForm = document.getElementById('entry_excerpt');
textAreaForm.innerHTML = url;
}
});

// Utility
function log(message) {
if (unsafeWindow && unsafeWindow.console) {
unsafeWindow.console.log(message);
}
}
})();


2009年03月26日

とりあえずバージョンアップ

全てのポストを表示するためのボタンを追加。ver2.0
同じスクリプトを繰り返す無駄な処理があったので、ついでに修正。使い方は変わらず。

2009年03月25日

グリモンのスクリプト完成

Tumblrのダッシュボードで『フォローはしてるけど、投稿内容がアレげな感じで、直で見るにはあまり好ましく無いような物ばかりの人』の投稿内容を非表示にするスクリプト。
ボタンで表示出来るようになっています。現状一個一個ボタンで表示させる仕様になっていますので、見たい場合はかなり面倒なスクリプトとなっています。
さすがに面倒すぎると思うので、1ページにつき1ボタンを作り、一気に表示非表示を切り替えるられるような仕様を追加しようと思います。

----------【追記】----------
使い方:firefoxのグリモンであることが前提です。スクリプトの以下の部分を、自分用に修正する必要があります。
// Tumblrで記事を隠すユーザのリスト
var userList = ['消したい人のID1', '消したい人のID2', …, '消したい人のIDN'];
// 自分のID
var mine = '自分のID';
インストール後ユーザスクリプトの管理を開いて、編集ボタンから直接編集してください。

注意:触って確かめた程度のテストしかしていないので、クラッシュしても知りません。後クロスブラウザとか一切考慮してないので、firefox以外でまともに動作するか分かりません。
------------------------------

作ったものはこれです。以下作ったスクリプトの中身。
// ==UserScript==
// @name Tumblr hidden script
// @namespace http://blog.livedoor.jp/himachoco/
// @description Selected user's blog hidden
// @include http://www.tumblr.com/dashboard*
// ==/UserScript==

// Tumblrで記事を隠すユーザのリスト
var userList = ['消したい人のID1', '消したい人のID2', …, '消したい人のIDN'];
// 自分のID
var mine = '自分のID';

(function() {
var repeat = '';
var load = function(context) {
if(!context){
root = document.getElementById('left_column');
}else{
root = context;
}
var liList = $x('ol[@id="posts"]//li', root);
var print = '';
liList.forEach(function(e){
if(checkUserList(getPostUserName(e))){
var id = getId(e);
e.setAttribute('style', 'display:none;');
e.parentNode.insertBefore(scriptRender(id), e);
e.parentNode.insertBefore(buttonRender(id), e);
}
});
}

function getPostUserName(liElement){
if(liElement.className.match(/is_mine\b/)){
return mine;
}
var res = $x('div[@class="post_info"]//a/text()', liElement);
for(var i = 0; i < res.length; i++){
if(repeat = res[i].textContent){
return repeat;
}
}
return repeat;
}

function checkUserList(userName){
for(var i = 0; i < userList.length; i++){
if(userList[i] == userName){
return true;
}
}
return false;
}

function getId(liElement){
return liElement.getAttribute('id');
}

function scriptRender(id){
var insertNode = document.createElement('script');
var textNode = document.createTextNode('function oc(elm, id) {\n'+
'var flag = (elm.innerHTML==\'隠す\');\n'+
'document.getElementById(id).style.display = flag?\'none\':\'block\';\n'+
'elm.innerHTML = flag?\'表示\':\'隠す\';\n'+
'}');
insertNode.setAttribute('type', 'text/javascript');
insertNode.appendChild(textNode);
return insertNode;
}

function buttonRender(id){
var insertNode = document.createElement('button');
var textNode = document.createTextNode('表示');
insertNode.setAttribute('id', 'btn');
insertNode.setAttribute('onclick', 'oc(this, \''+id+'\')');
insertNode.appendChild(textNode);
return insertNode;
}

function setStyle(){
var style =
<><![CDATA[
button#btn{
border: 1px solid #333333;
color: #333333;
background-color: #edeae4;
font-family: Verdana, Geneva, sans-serif;
font-size: 11px;
font-weight: bold;
}
]]></>;
GM_addStyle(style);
}

function addFilter(filter, i) {
i = i || 4;
if(window.AutoPagerize && window.AutoPagerize.addFilter) {
//window.AutoPagerize.addFilter(filter);
window.AutoPagerize.addFilter(function(elements) {
filter(elements[0]);
});
} else if(i > 1) {
setTimeout(arguments.callee, 1000, filter, i -1);
}
}

// Main
setStyle();
load();
addFilter(load);

// Utility
function $x(xpath, context) {
context = context || document;
var res = document.evaluate(xpath, context, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
for(var i, nodes = [] ; i=res.iterateNext() ; nodes.push(i));
return nodes;
}
})();


greasemonkeyのscriptを書いてた

Javascriptの書き方、作法など全然知らないので手探りでゴリゴリ。
で、一旦完成したと思ったら、AutoPagerizeを使ってるので、
AutoPagerizeが読み込んだ先までちゃんと動作するようにしないといけないことに気づいた。
window.AutoPagerize.addFilter(function (docs){
// func
});
こんな感じでaddFilterにfunctionを渡してあげれば良いみたいなのだが、
functionで受け取った引数の扱い方が良く分からず…
ノードの配列らしいのだが上手く処理出来ない…
getElementById()でidからelementを取り出そうとしてもうまくいかず。
何かのサンプルでforEachを使って処理してたので、真似してみるも、getAttribute()で属性値は取り出せるのに、結局getElementById()でelementを取り出そうとすると上手くいかず。

誰かヘルプ!

2009年03月23日

Wicketの本をゲット

矢野勉さんが執筆なさったWicketの本が最近出版され、
今日になって本屋で見かけたので思わず買ってしまった。

さらっと流し読みしたところ、入門書としてとても良さそうな感じだった。
Wicketの生い立ちから、導入、Wicketの開発手法(コンポーネントの紹介)など、
Wicketを最初に触る上で必要なところをざっと書いてある感じ。

さらにAjax、Wicketにおけるテストの方法、
(WicketにはWicketTesterというテストツールが用意されている)
DIコンテナとの連携方法なども書いてあって、Wicketのイメージを掴むのにうってつけのように思う。

そしてなにより重要なのは、日本語w

ただ、実際にWicketを使ってどういうアプリが作れるかという具体例は少なめみたいな感じなので、、
そこはWicket in Actionなど別の資料で補う必要はあるかな。

ということで、この本を執筆した矢野さんに感謝。勉強させていただきます。

2009年03月22日

SRT

この間から描きたい衝動に駆られてたのでスーパーラクガキタイム
中国
美鈴は腰から下の服の表現が苦手で、
どう描いたら良いか結構悩む…
ちなみにこの美鈴は萃夢想仕様

関係ないけど美鈴は俺の嫁!

Profile
mix3
主にIRCに生息

細○○子に「しかし 地獄行く」
と宣告され、練炭自殺を
何度も図る迷惑な人間。
Twitter Space
Tumblr Search
Categories
Recent Comments
Archives search
Recommend books
それでも町は廻っている (1)

それでも町は廻っている (1)


Black lagoon (1) (ブラックラグーン)

Black lagoon (1) (ブラックラグーン)


少女セクト

少女セクト


皇国の守護者 1 (1)

皇国の守護者 1 (1)


蟲師 (1)

蟲師 (1)


謎の彼女X(1)

謎の彼女X(1)


ユリア100式 (1)

ユリア100式 (1)


GUNSLINGER GIRL (1)

GUNSLINGER GIRL (1)


いばらの王 (1)

いばらの王 (1)


エマ (1)

エマ (1)


銭 (1巻)

銭 (1巻)


ふたつのスピカ 1

ふたつのスピカ 1


ブロッケンブラッド

ブロッケンブラッド


惑星(ほし)のさみだれ (1)

惑星(ほし)のさみだれ (1)


くちびるためいきさくらいろ

くちびるためいきさくらいろ


銃夢(Gunnm)Last Order (1)

銃夢(Gunnm)Last Order (1)


銃夢(Gunnm) (1)

銃夢(Gunnm) (1)


ドージンワーク (1)

ドージンワーク (1)


Pumpkin Scissors (1)

Pumpkin Scissors (1)


コイネコ (1)

コイネコ (1)


さんさん録 (1)

さんさん録 (1)


夕凪の街 桜の国

夕凪の街 桜の国


長い道

長い道


Rozen Maiden (1)

Rozen Maiden (1)


E.G.コンバット

E.G.コンバット


猫の地球儀 (焔の章)

猫の地球儀 (焔の章)


鉄コミュニケイション (1)

鉄コミュニケイション (1)


イリヤの空、UFOの夏〈その1〉

イリヤの空、UFOの夏〈その1〉


ミナミノミナミノ

ミナミノミナミノ


キノの旅―The beautiful world

キノの旅―The beautiful world


R.O.D―Yomiko Readman “the paper”

R.O.D―Yomiko Readman “the paper”


LAMPO (ランポ) (1)

LAMPO (ランポ) (1)


隻眼獣ミツヨシ (1)

隻眼獣ミツヨシ (1)


荒川アンダーザブリッジ (1)

荒川アンダーザブリッジ (1)


デトロイト・メタル・シティ (1)

デトロイト・メタル・シティ (1)


仮面のメイドガイ (1)

仮面のメイドガイ (1)


Dear Emily… (1)

Dear Emily… (1)


機動旅団八福神 (1巻)

機動旅団八福神 (1巻)


びんちょうタン (1)

びんちょうタン (1)


Blood alone (1)

Blood alone (1)


帝立第13軍学校歩兵科異常アリ!? (1)

帝立第13軍学校歩兵科異常アリ!? (1)


東京赤ずきん 1

東京赤ずきん 1


きみのカケラ 1

きみのカケラ 1


ツイてるカノジョ (1)

ツイてるカノジョ (1)


少年少女 (1巻)

少年少女 (1巻)


封神演義 (第1部)

封神演義 (第1部)


PSYCHO+ (DRIVE A) GAME START

PSYCHO+ (DRIVE A) GAME START


Worlds―藤崎竜短編集

Worlds―藤崎竜短編集


DEATH NOTE (1)

DEATH NOTE (1)


ヒカルの碁 (1)

ヒカルの碁 (1)


NARUTO (巻ノ1)

NARUTO (巻ノ1)


テニスの王子様 (1)

テニスの王子様 (1)


スーパーマリオくん (1)

スーパーマリオくん (1)


ワイド版 風の谷のナウシカ7巻セットトルメキア戦役バージョン

ワイド版 風の谷のナウシカ7巻セットトルメキア戦役バージョン


初恋姉妹 (1)

初恋姉妹 (1)


ワン ホット ミニット

ワン ホット ミニット


キャット・シット・ワン (Vol.0)

キャット・シット・ワン (Vol.0)


ニアアンダーセブン (1)

ニアアンダーセブン (1)


頭蓋骨のホーリーグレイル

頭蓋骨のホーリーグレイル


月と貴女(あなた)に花束を

月と貴女(あなた)に花束を


旋風のカガリ

旋風のカガリ


陰陽ノ京(みやこ)

陰陽ノ京(みやこ)


パラサイトムーン―風見鳥の巣

パラサイトムーン―風見鳥の巣


吉永さん家(ち)のガーゴイル

吉永さん家(ち)のガーゴイル


A君〈17〉の戦争 (1)

A君〈17〉の戦争 (1)


Missing―神隠しの物語

Missing―神隠しの物語


シンフォニアグリーン

シンフォニアグリーン


アリソン

アリソン


リバーズ・エンド

リバーズ・エンド


毛布おばけと金曜日の階段

毛布おばけと金曜日の階段


ポストガール

ポストガール


ブギーポップは笑わない

ブギーポップは笑わない

livedoor Readerに登録
RSS
livedoor Blog(ブログ)