Entry: commons markerの件数をgoogle検索結果に表示するグリモンをつくった
commons markerの件数をgoogle検索結果に表示するグリモンをつくった
[2010.01.15追記] 無駄な処理が多かったのでソースを少し整理した。
恥ずかしながら、commons markerというサービスを最近まで知らなかった。前々から出来たらいいなと思っていたことにすごく近いので使い始めてみた。
ところで、はてなブックマークのFirefoxアドオンはとても便利で、とくにGoogle検索の結果にブクマ件数が表示されるのは、クリックすべきリンクのアタリをつけるのに大変便利なのですが、同じ発想でcommons markerの件数も参考データとして意味があるかもしれないと思い、早速グリモンスクリプトで実現してみた。
googleResultWithMarker.user.jsをインストール

// ==UserScript== // @name googleResultWithMarker // @namespace http://jamadam.com/blog/ // @description googleResultWithMarker // @include http://*.google.co.jp/search* // ==/UserScript== (function() { var $; var jversion = '1.3.2'; var jexist = (typeof unsafeWindow.jQuery != 'undefined'); var conflict = (jexist && unsafeWindow.jQuery.fn.jquery != jversion); // Add jQuery if not loaded if (! jexist || conflict) { var GM_JQ = document.createElement('script'); GM_JQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/' + jversion + '/jquery.js'; GM_JQ.type = 'text/javascript'; document.getElementsByTagName('body')[0].appendChild(GM_JQ); } GM_wait(); // Check if jQuery's loaded function GM_wait() { if (typeof unsafeWindow.jQuery == 'undefined' || unsafeWindow.jQuery.fn.jquery != jversion) { window.setTimeout(GM_wait,100); } else { if (conflict) { $ = unsafeWindow.jQuery.noConflict(true); } else { $ = unsafeWindow.jQuery; } letsJQuery(); } } // All your GM code must be inside this function function letsJQuery() { var array = $('#res li.g'); var procExist = false; var i = 0; var loop = function() { if (i < array.length) { if (procExist) {return;} procExist = true; var href = array.eq(i).find('h3 a').attr("href"); var pos = array.eq(i).find('span.gl'); var url = encodeURIComponent(href); var jsurl = "http://commonsmarker.com/tools/latest_info?url=" + url; unsafeWindow["__jsonp__aD3jHjaf27mZQxt"] = function(a) { if (! a || ! a.marks || ! a.marks.length) { return; } $("<a>" + a.marks.length + " markers</a>") .attr('href', "http://commonsmarker.com/page/" + href) .css({ backgroundColor:"#f5d0d0", color:"#ff0000", fontFamily:"arial,sans-serif", fontWeight:"bold", marginLeft:"4px", padding:"1px", fontSize:"85%" }) .insertAfter(pos); } $.ajax({ type: "GET", url: jsurl, dataType: "script", complete: function() { i++; procExist = false; loop(); } }); } } loop(); } })();
commons markerの公式のインターフェースはまだ機能不足なため他のユーザーの動向がよく掴めなかったんだけど、こうしてスクリプトで簡単に件数を把握できるようにしてみて分かったことは、「全然利用されてない」ということでした。予想はしていたけど。
なお、commons markerはこの手の外部スクリプトへのインターフェースや仕様を一切公開していません。今回のスクリプトはとても強引な処理と憶測で実装しているので、サーバーの仕様が変わって動かなくなることが大いに予想されます。
Entry: ついったらータグs API(仮)
ついったらータグs API(仮)
「ついったらータグs API(仮)」は、ついったらータグs(仮)のデータベースへの問い合わせをアプリケーションから利用するためのインターフェースを提供します。ついったらータグs(仮)のデータベースにはTwitterの各ユーザーページに対する、はてなブックマークタグの情報を格納しています。本APIを利用することで、任意のユーザーに対するタグ付けの情報はもちろんのこと、任意のタグがどのユーザーに適用されているかという情報を取得することができます。また、対象となるデータベースとして、独自のルールでタグ文字列の正規化を行ったテーブルを対象にすることもできます(任意)。
全てのAPIは下記のURLで提供されます。
http://jamadam.com/th/
現在サポートされている操作は、タグ-ユーザー検索(パラメータt=api/tag.json)とユーザー-タグ検索(パラメータt=api/user.json)です。いずれもレスポンスとしてJSONを返します。また、クエリーとしてcallbackを渡すことでJSONPを受け取ることもできます。
タグ-ユーザー検索
任意のタグ文字列を指定することで、このタグがつけられたユーザーのスクリーンネームのリストを得ます。
パラメータ
- t (必須)
"api/tag.json"を指定します。 - str (必須)
任意のタグを指定します。 - nn (オプション)
正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。
リクエスト例
/th/?t=api/tag.json&str=it
レスポンス例
{"result":[
{"user":"takapon_jp","occurance":"2"},
{"user":"m_kumagai","occurance":"1"},
{"user":"huehara88","occurance":"1"}
]}
※実際はレスポンスに改行は含まれません。
ユーザー-タグ検索
任意のスクリーンネームを指定することで、このユーザーに付与されたタグの一覧を得ます。
パラメータ
- t (必須)
"api/user.json"を指定します。 - str (必須)
任意のスクリーンネームを指定します。 - nn (オプション)
正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。
リクエスト例
/th/?t=api/user.json&str=jamadam
レスポンス例
{"result":[
{"tag":"twitter","occurance":"38"},
{"tag":"音楽","occurance":"9"},
{"tag":"有名人","occurance":"4"},
{"tag":"webサービス","occurance":"4"}
]}
※実際はレスポンスに改行は含まれません。
実装例
ユーザー-タグ検索を利用してタグ一覧を取得するPerlコードは以下のようになります。
use LWP::UserAgent; use JSON::XS; my $tags = _twitterer_tags('jamadam'); foreach my $entry (@$tags) { print($entry->{tag}); print(":"); print($entry->{occurance}."回"); print("¥n"); } sub _twitterer_tags { my $user = shift; my $ua = LWP::UserAgent->new; my $res = $ua->get('http://jamadam.com/th/?t=api/user.json&str='. $user); my $obj = decode_json $res->content(); return $obj->{result}; }
Entry: 縮 REST API
縮 REST API
「縮 REST API」は、URL短縮/転送サービス「縮.jp」の機能を、アプリケーションから利用するためのインターフェースを提供します。
全てのAPIは下記のURL以下で提供されます。
http://api.xn--jj0a.jp/
現在サポートされている操作は、短縮URLの生成(generate.jsonへのPOST)と短縮URLの展開(lookup.jsonへのGET)です。いずれもレスポンスとしてJSONを返します。また、クエリーとしてcallbackを渡すことでJSONPを受け取ることもできるかもしれません。
GENERATE
任意のURLを渡すことで、新規に短縮URLを生成します。
パラメータ
- fUrl(必須)
短縮対象のURL。http://かhttps://で始まる必要があります。 - customId(オプション)
任意の漢字の組み合わせで短縮URLの識別子を指定します。utf8でパーセントエンコードしてください。 - offset(オプション)
fUrlで指定したURLに対する既設の短縮URLがひとつ以上存在する場合、このパラメータに0以上の数値を指定することで任意の候補を選択することができます。既設URLは文字数の短い順に並んでいます。offsetが既設URLの数より大きいとき、新しいURLが生成されます。このパラメータは特殊な目的がない場合は指定しないでください。
リクエスト例
POST /generate.json
fUrl=http://example.com&id=%E4%B8%83
レスポンス例
{"result":{"fUrl":"http://example.com/","generated":"http://縮.jp/七"}}
LOOKUP
既存の短縮URLの識別子を受け取り、元のURLを返します。識別子はUTF-8でパーセントエンコードしてください。
パラメータ
- id(必須)
URLの識別子を指定します。 - hash
後方互換性のための、idの別名です。(廃止の予定)
リクエスト
GET /lookup.json?id=%E4%B8%83
レスポンス
{"result":{"fUrl":"http://example.com"}}
共通
HTTPレスポンスコード
正常/エラーに関わらず「200 OK」を返します。
エラー時のレスポンスボディ
下記の書式でJSONデータを返します。
{"error":"[エラーコード] [説明]"}
エラーコード一覧
- 400: generateの際、fUrlの書式が正しくない、customIdに不正な文字が含まれる、など。
- 409: generateの際、customIdがすでに使用されている、など。
- 404: lookupの際、指定のidが存在しない、など。
実装例
generate.jsonを利用して短縮URLを取得するPerlコードは以下のようになります。
use LWP::UserAgent; use JSON::XS; my $generated = _shuku('http://example.com'); sub _shuku { my $ua = LWP::UserAgent->new; my $res = $ua->post('http://api.xn--jj0a.jp/generate.json', {'fUrl' => $_[0]}); my $obj = decode_json $res->content(); my $decoded = $obj->{result}->{generated}; utf8::decode($decoded); return $decoded; }
Entry: ブログにはてなブックマーク窓をつけた
Entry: AJAXデータの取得先URLをどう管理するかという話
AJAXデータの取得先URLをどう管理するかという話
AJAX満載のサイトでアンカーに紐づいたAJAXデータの取得先URLをどう管理するかという話。with jQuery1.3.2。
HTML
<a href="./path/to/parmalink.html">通常リンク</a> <a href="./path/to/snippet.html" class="widget-opener">動的コンテンツ</a>
Javascript
$('a.widget-opener').live('click', function () { $.get($(this).attr('href'), function(html){ //appendなりprependなり }); return false; });
上記の場合、javascriptオフの人が「動的コンテンツ」を叩くとHTMLスニペットやjsonテキストが画面いっぱいに広がるページに遷移してしまったりする。 また、検索クローラーはリンク先のページを収集してしまうかもしれない。
かといって、href="#"とでもして、すべてのアンカーにidをふって、script内でURLを関連づけていくのも煩雑だ。
HTML
<a id='id1' href="#" class="widget-opener">記事1</a> <a id='id2' href="#" class="widget-opener">記事2</a> <a id='id3' href="#" class="widget-opener">記事3</a>
Javascript
url.id1 = './path/to/snippet1.html'; url.id2 = './path/to/snippet2.html'; url.id3 = './path/to/snippet3.html';
そこで、アンカータグのonclickとjQueryの$.data()メソッドでAJAX URLをインラインで管理するようにしてみた。 $.data()はDOM要素に紐づいたデータを管理することが目的という、本件におあつらえ向きなメソッド。
HTML
<a href="#" class="widget-opener" onclick="$.data(this, 'ajaxURL', './ajax1.html')">記事1</a> <a href="#" class="widget-opener" onclick="$.data(this, 'ajaxURL', './ajax3.html')">記事2</a> <a href="#" class="widget-opener" onclick="$.data(this, 'ajaxURL', './ajax3.html')">記事3</a>
Javascript
$('a.widget-opener').live('click', function () { $.get($.data(this, 'ajaxURL'), function(html){ //appendなりprependなり }); return false; });
onclickとliveでバインドしたイベントとの実行順が気になったけど、意図した順番に実行されている。ここで念のためonmousedownとかに逃げてしまうと、 今度は.trigger()が使えなくなってしまうのでonclickしかない。実行順が覆るケースがあるようなら、click.not_inlineというようにnamespacedイベントを利用して onclickイベントとの実行順を制御することもできる。
また、パーマリンクが存在するアンカーであれば、当然hrefにそれをかける。
<a href='./parmalink1.html' class="widget-opener" onclick="$.data(this, 'ajaxURL', './ajax1.html')">記事</a>
こうすることで、新規タブでリンクを開く可能性に配慮した仕組みも実現できる。実際、このブログの「最近の記事」のボタンは左クリックで新規ウィジェットを起動するけど、コンテキストメニューから新規タブでパーマリンクを開くこともできる。そして、この2つのURLをひとつのアンカータグ内で管理している。
まあ、HTML内に書くのには$.data()はちょっとだけ汚らしいというのは認める。
Entry: Photoshopでsocket通信
Photoshopでsocket通信
Adobe ExtendScriptでsocket通信にトライ。結構苦労してサーバーから日本語を含むJSONデータを正常に取得することができた。
国内の解説サイトでsocket通信に言及しているのは2件。うち一件はメールの送信について解説していて、もう一件は日本語サイトの取得に失敗したとのことで、他には見つからない。
成功するポイントは、openメソッドの第二引数は必ずbinaryにすることです。UTF8などを指定すると長いコンテンツが途中で切れてしまう。どうやらマルチバイトのデータサイズをreadメソッドがきちんと処理してくれてないことが原因。また、openメソッドをテキストモードにすると改行コードも変更されてしまってとても混乱。
それから、エンティティボディの処理はきちんとチャンクサイズに基づいて行うこと。マルチバイトのあるなしに関わらず、ある程度以上のサイズを一気にreadまたはreadlnしようとすると途切れる。なので、サンプルスクリプトやAdobeのドキュメントに書いてあるread(999999)みたいなのは実用的には全く使えない。
結局、マルチバイトのエンコードもchunkedなコンテンツのデコードも全て自前の処理になるので、普通に面倒だけど寧ろそれが普通か。なんか以前にもPerlで同じコードを書いた気がする。
Entry: Javascript - newtooltip version 0.1
Javascript - newtooltip version 0.1
先日から勉強がてら改造していたjqueryプラグインがひと段落したのでアップしてみる。その名もjquery.newtooltip.js。半分くらい原型をとどめてないので、もともとの機能が維持できてるか謎。特に、背景画像のIE対応とか未確認。世の中にはtooltipのjqueryプラグインはたくさんあるので、すでにいいものがあるのかも知れないです。あくまで勉強。
動作イメージはこちら。
下記のようにセットアップすると、title属性のあるタグは全部、マウスオーバーに反応するようになる。
// 一番簡単な使い方 $(document).ready(function(){ $.tooltip_setup(); });
様々な初期設定。
// 様々な初期設定(下記はデフォルト) $.tooltip_setup({ in_json : false, // json書式を使用するか否か auto : true, // title属性から全自動で設定 handler_on : 'mouseover', // tooltip表示用ハンドラ handler_off : 'mouseout', // tooltip非表示用ハンドラ track : true, // mousemoveに追従する設定 fade : 200, // フェード(msec) top : 15, // ポインタからの距離 left : 15, // ポインタからの距離 id : "TOOLTIP" // tooltip用id auto_css : true, // CSSをデフォルト値に設定 });
newtooltip.min.js
圧縮ファイル。
newtooltip.org.js
非圧縮ファイル。
Subscribe to my RSS feed