Hatena::ブログ(Diary)

5.1さらうどん Twitter

2009/05/06(Wed)

ニコニコ大百科のキーワードをポップアップ表示させるGreaseMonkeyスクリプト『Popup Nico Dict』リリースしました

| 08:43 |

6月1日追記:バグを修正し、動画視聴ページ上でポップアップできるなど新機能を搭載した新バージョンを公開しました。

インストールは以下からどうぞ!

ニコニコ大百科のリンク先をポップアップする『Popup NicoDict』のVer3.0.0をリリースしました。 - 5.1さらうどん

5月13日追記:バグを修正し、動画視聴ページ上でポップアップできるなど新機能を搭載した新バージョンを公開しました。

インストールは以下からどうぞ!

ニコニコ大百科のリンク先をポップアップする『Popup NicoDict』改良版リリースしました! - 5.1さらうどん

http://gyazo.com/6e466b7006d51560426506a987b2062c.png


これは何?

ニコニコ大百科のキーワードをマウスオーバーでポップアップ表示させます。

こんな感じになるようです。

http://gyazo.com/cae6a74961ccdf96d9823674a3020692.png

http://gyazo.com/7a5c295d1e022e1e82b206d743ad0574.png

http://gyazo.com/170f33cef5effd79518afa78268830ab.png

ダウンロード

userscripts.orgからどうぞ。

Popup Nico Dict


GreaseMonkey独自の関数を利用しているため、Operaなどでは動きません。

近日、機能追加を行う予定があるので、アカウントを所持している方はfavoriteに登録しておくといいかも。

不具合のご指摘や改良の報告などお待ちしております。

技術的なおはなし

このスクリプトのほとんどはコンセプト含め、はてなグリースモンキーの『キーワードポップアップ』を参考にしています。

キーワードポップアップ - はてなグリースモンキー 


ただ、はてなキーワードの場合は、キーワードのページ自体がRSSを吐いているのでDOMで取得するのが容易ですが、ニコニコ大百科の場合はそうではないので、正規表現を用いて強引にスクレイピングしました。

その後、記事の整形については独自のアルゴリズムで作成しました。

子見出しを【】でくくったり、目次を消したりとか・・・。


ポップアップの表示部分については、ほとんど『キーワードポップアップ』からの流用です。

最初は既存のツールチップライブラリを用いることも考えましたが、何種類か試してみてもうまく導入できなかったので表示部分はほぼ同じです。

唯一、影を付けたところだけが違うかな・・・。


ソースの方に細かくコメントを付けておいたので、気になる方は参照してください。


既知の問題点

・リダイレクト記事が正常に表示されない

リダイレクト設定されている記事にカーソルを載せると、何も表示されません。

本当はリダイレクト先のページを更に取得できるといいんだけど、取得方法がいまいちわからなかったので・・・。

http://gyazo.com/522e76213d72d115d59343d765ed6352.png


・画面のスクロール位置を考慮していない

表示させるスペースが足りない場合は、カーソルの上にキーワードを表示させるなどの処理を作れば良かったんだけど、これもいまいちよくわからなかったので保留中。

現状だと下が少ないと画面からはみ出してしまいます。

これはちょっと見にくいなあ・・・。

http://gyazo.com/703f5009c9eff8de34f9af2ab641c57e.png

今後の追加機能予定

ニコニコ動画への対応

本当は/watch/ページの大百科アイコン(http://res.nicovideo.jp/img/common/icon/dic_on.gif)にマウスオーバーすることで記事の取得が出来たらよかったんだけど、うまくいかなかったので保留中です。

要望が多いなら作るかも。

・検索結果ページへの対応

実は開発段階では出来ていたのですが、諸般の事情によりカットした。

近いうちに搭載します。


ソースコード

// ==UserScript==
// @name           Popup Nico Dict
// @namespace      http://gigi-net.net
// @include        http://dic.nicovideo.jp/*
// @include        http://www.nicovideo.jp/*
// ==/UserScript==
(function(){
//変数定義一覧
var xpos =0;
var ypos =0;
var flag=0;
var now_target_title="";
var fadeInIntervalID = null;
var fadeOutIntervalID = null;
var show ="";

//ポップアップをフェードイン
function fadein(){
	popup = document.getElementById("ndtkeywordpopup");
	var op = parseFloat(popup.style.opacity);
	var newop =op+0.1;
	popup.style.opacity=newop;
	if(op>=1){
		clearInterval(fadeInIntervalID);
	}
}

//ポップアップをフェードアウト
function fadeout(){
	popup = document.getElementById("ndtkeywordpopup");
	var op = parseFloat(popup.style.opacity);
	var newop =op-0.1;
	popup.style.opacity=newop;
	if(op<0){
		clearInterval(fadeOutIntervalID);
		flag=0;
		document.getElementsByTagName("body")[0].removeChild(popup);
	}
}

//ポップアップを生成する関数
function createPopup(text){
		if(flag==0){
		var popup = document.createElement('div');
		popup.id ="ndtkeywordpopup";
		with(popup.style) {
			visibility = 'visible';
			fontSize = '10pt';
			fontFamily = 'sans-serif';
			textAlign = 'left';
			lineHeight = '110%';
			color = '#333333';
			paddingLeft = '5px';
			paddingRight = '5px';
			backgroundColor = 'cornsilk';
			border = '1px solid #333333';
			opacity = '0';
			position = 'absolute';
			left = xpos+"px";
			top = ypos+25+"px";
			width = '400px';
		}
		//影を生成
		var shadow = document.createElement('div');
		shadow.id ="ndtkeywordpopup_shadow";
		with(shadow.style) {
			visibility = 'visible';
			fontFamily = 'sans-serif';
			fontSize = '10pt'
			textAlign = 'left';
			color = '#333';
			paddingLeft = '5px';
			paddingRight = '5px';
			lineHeight = '110%';
			backgroundColor = '#333';
			border = '1px solid #000';
			opacity = '0.5';
			position = 'absolute';
			left = xpos+5+"px";
			top = ypos+25+5+"px";
			width = '400px';
		}
		popup.innerHTML =text;
		shadow.innerHTML =text;
		flag =1;
		document.getElementsByTagName("body")[0].appendChild(shadow);
		document.getElementsByTagName("body")[0].appendChild(popup);
		fadeInIntervalID =setInterval(fadein,30);
		}
}
//マウスがキーワードから外れたとき
function outKeyword(){
	shadow =document.getElementById("ndtkeywordpopup_shadow");
	document.getElementsByTagName("body")[0].removeChild(shadow);
	fadeOutIntervalID =setInterval(fadeout,30);
}


//記事を取得し、整形する関数
function GetDOM(x){
	var dict = x.responseText;
	//正規表現でarticle以降を取り出す。
	var start =dict.search(/<div class..article.* id..article.*>/);
	var end =dict.search("<hr>");
	var article =dict.substring(start,end);
	//見出しを【】に変換
	article =article.replace(/<h2.*?>/gm,"【");
	article =article.replace(/<.h2>/gm,"】");
	//ページメニューを消去
	article =article.replace(/<div id..page-menu.>.*<.div>/,"");
	//タグを全消去
	article =article.replace(/<.*?>/mg,"");
	//先頭300文字のみ抽出し、省略された場合は...をくわえる
	show =article.substring(0,299);
	if(article.length>=300){
		show +="..."
		}
	//見出しを改行する
	show =show.replace(/【/gm,"<br>【");
	show =show.replace(/】/gm,"】<br>");
	//タイトルを追加
	show ="<h1>"+now_target_title+"</h1>"+show;
	createPopup(show);
}

//キーワードにマウスが乗ったときの関数
function onKeyword(e){
	document.addEventListener('mousemove', setPos, false);
	//タイトルを取得
	now_target_title =decodeURIComponent(e.target);
	now_target_title =now_target_title.replace(/http:..dic.nicovideo.jp.../,"");
	var article_url =e.target+"";
	GM_xmlhttpRequest({
  		method:"GET", 
  		url:article_url,
  		onload:GetDOM
	});
}

//キーワードリンクを配列にまとめる
var links =document.getElementsByTagName("a");
var keywords =new Array;
var urls =new Array;
//現在のページがニコニコ大百科の場合
if(document.domain=="dic.nicovideo.jp"){
	//トップページは除外する
	if(location.href!="http://dic.nicovideo.jp/"){
		for(var i=0;i<links.length;i++){
			//reg =new RegExp("/[av]/.*");
			//var f =links[i].getAttribute("href").match(reg);
			if(links[i].getAttribute("class") =="auto"){
				keywords.push(links[i]);
			}
		}
	}
}else{
//現在のページがニコニコ動画の場合
	for(i=0;i<links.length;i++){	
		if(links[i].getAttribute("href").indexOf("http://dic.nicovideo.jp")!=-1){
			alert(links[i].innerHTML);
			keywords.push(links[i]);
		}
	}
}

//キーワードにマウスが乗ったときのイベント追加
for(i=0;i<keywords.length;i++){
	keywords[i].addEventListener('mouseover',onKeyword,false);
	keywords[i].addEventListener('mouseout', outKeyword, false);
}
//マウスカーソルの座標を同期
function setPos(e) {
		xpos = e.pageX;
		ypos = e.pageY;
}

})();

Sore_0Sore_0 2009/05/06 13:14 リダイレクト先のURL入手の参考ページ
http://d.hatena.ne.jp/Constellation/20080613
表示スペース位置の参考スクリプト
http://userscripts.org/scripts/show/28910
http://d.hatena.ne.jp/gifnksm/20090408/1239147297

通り通り 2009/05/06 15:26 名前は確かに7ですが、内部バージョンはWindows 6.1なのでVistaより劇的に変わることはないです。
というか、やっぱりVista SP2(ry

nao_tsukiyanao_tsukiya 2009/05/07 00:40 たまに出てくるとーぜんのSSは、早いところ更新追いつかせろって意味ですね。わかります。
Windows7、自分もインスコしたんだけど色々と環境整えたいんで全然触ってない。つーかVista再インスコもしたんだけどwww

gigi-netgigi-net 2009/05/07 08:44 >id:Sore_0さん
参考URLありがとうございます。
週末辺りにでも改良してみますね。

>通りすがりの方
ご指摘どうも。
バージョンアップとして物足りないというだけで、OS自体としてはそんなに悪くなさそうという印象。

>id:nao_tsukiyaくん
3年ぐらい前からとーぜんのSS貼りまくってるけど、本人が反応したのは初かもしれないw別に催促はしてない。
こっちもインスコするだけでマンゾクしてしまって、あんまり見た目が変わっていなかったのでちょっとしかいじってないのです。

MnstMnst 2009/05/07 16:22 Popup Nico Dictの不具合報告です。
Popup Nico Dictでは最初半透明状態からじわりと記事がポップアップしますが、その
徐々に透明度を下げている最中にマウスポインタがポップアップ記事上に移動すると
表示が半透明表示で固定されてしまいます。一度その状態になるとポインタが他に
移動してもそのポップアップ記事が消えないし、他の単語からのポップアップも
出来なくなります。ポップアップ記事が消えなくなった時になんらかの方法でポップ
アップを消せる手段がほしいです。
要望ですが、現在は完全にポップアップ表示した後ではポップアップ記事上にポインタを
移動しようとしてもポップアップが消えてしまいますが、この場合は記事が残り続ける
ようにしてほしいです。これが出来ないとポップアップ記事上でのテキストドラッグ&
コピーが出来ないためです。
以上、使ってみて気が付いた点の報告でした。

gigi-netgigi-net 2009/05/07 20:46 ご指摘ありがとうございます。
上記のバグについてはこちらでも把握しているので、バグフィックスと新機能の追加含め、今週末には開発したいと思っております。