<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>村田佑介.com</title>
	<atom:link href="http://www.muratayusuke.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.muratayusuke.com</link>
	<description>muratayusuke official site</description>
	<lastBuildDate>Thu, 03 Nov 2011 05:39:33 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
		<item>
		<title>Google Developer Day (GDD) 2011に行ってきた</title>
		<link>http://www.muratayusuke.com/2011/11/03/googledeveloperday2011/</link>
		<comments>http://www.muratayusuke.com/2011/11/03/googledeveloperday2011/#comments</comments>
		<pubDate>Thu, 03 Nov 2011 05:39:33 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[勉強会]]></category>
		<category><![CDATA[GDD]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=295</guid>
		<description><![CDATA[Google主催のGoogle Developer Dayに参加させてもらえたので、例によってチームへのフィードバックがてら、個人的に面白かった部分をピックアップしてまとめました。 GDDの詳細はこちら：http://www.google.co.jp/intl/ja/events/developer [...]]]></description>
			<content:encoded><![CDATA[<p>Google主催のGoogle Developer Dayに参加させてもらえたので、例によってチームへのフィードバックがてら、個人的に面白かった部分をピックアップしてまとめました。</p>

<p>GDDの詳細はこちら：<a href="http://www.google.co.jp/intl/ja/events/developerday/2011/tokyo/index.html" target="_blank">http://www.google.co.jp/intl/ja/events/developerday/2011/tokyo/index.html</a></p>

<p>どっかにスライドまとまってないかな・・・</p>

<h3>基調講演</h3>
<p>Webの歴史からGoogleの色んな製品紹介的な感じ。個人的に面白かったのは下記。</p>
<p class="quote">
・アンドロイド<br />
　　-アンドロイドビーム：NFCで簡単に情報共有<br />
　　-顔認識アンロック：パスワード不要。便利<br />
・Chrome Dev Tool<br />
　　-後述。<br />
・<a href="http://www.google.com/chromeframe?hl=ja&prefersystemlevel=true" target="_blank">Chrome frame</a><br />
　　→IEでもクロームの機能が使えるプラグイン。IEユーザを切り捨てずにHTML5の機能をゴリゴリ使える。使う時はIEの人はこのプラグイン入れてね的な表示を出すんやろなあ。
</p>

<h3>HTML5最前線</h3>
<p>HTML5でこんなことできます的な。知らんのが多くて面白かった。</p>
<p class="quote">
・アニメーション（<a href="http://d.hatena.ne.jp/calpo/20110523/p1" target="_blank">参考サイト</a>）<br />
・ページがアクティブかどうかのリスナー？（<a href="http://greenido.wordpress.com/2011/10/10/html5-lastest-features/" target="_blank">参考サイト</a>）<br />
・プリレンダー（<a href="http://greenido.wordpress.com/2011/10/10/html5-lastest-features/" target="_blank">参考サイト</a>）<br />
・オンラインかどうかの判定、リスナー<br />
　　→navigato.onLineで判定できて、window.addEventLister("online", hoge)とかもできるらしい<br />
・Web Intent<br />
　　→よくわからんかったけどサービスをまたいで画像を共有できる的な<br />
・オーディオ＆ビデオ<br />
　　→window.navigator.getUserMedia<br />
・フルスクリーン<br />
　　→特定のDivをフルスクリーンにするとか。リスナーもあったかな<br />
・Web RTC（Real Time Communication）<br />
　　→なんとHTMLでビデオ会議ができます<br />
・HTML5Rocksというサイトにいっぱい情報が載ってるらしい
</p>

<h3>Chrome Developer Tool</h3>
<p>ChromeのDev Toolでこんなことできます的な。これも知らんのがいっぱいあった。</p>
<p class="quote">
・実はウェブアプリ<br />
　　→Dev Toolの中身をDev Toolで見られる<br />
・CSSのパラメータを上下キーでいじれる＆Shift+上下キーで10ずついじれる<br />
・Styles横の+ボタンで選択中のクラスにスタイルをつけられる<br />
・その右のボタンでホバー状態とかを作れる<br />
・右下の歯車アイコンで細かい設定ができる<br />
・consoleコマンドはFirebug Commandline APIがほぼ全部使える<br />
　　→dir()とか$0とか地味に便利そう<br />
・リロードなしでCSSはもちろんjsもいじって実行できる<br />
・その場でいじったjsやcssをそのまま保存できる
</p>

<h3>Googleのクライシスレスポンス</h3>
<p>3月11日の後、何をしたか。これから何をすべきか。一番気持ちがこもってた。Hack for Japanに入りたくなった。</p>
<p class="quote">
・何が起きてるかを知る、それを知らせる。一括りに被災地と言っても車で３０分も走れば全く状況が変わる。<br />
・スキルを活かす。でもスキルありきではない。キレイなシステムを作るより中古PCのセットアップが喜ばれるところもある。<br />
・汎用性はいらない。ミニマルに作る。<br />
・速く出してオープンにする。２週間で必要なものは変わるし、オープンにすれば誰かが改善してくれるかもしれない。<br />
・やったこと、考えたことを記録する。明日役立つかもしれないし、３００年後に役立つかもしれない。伝えることが大事。
</p>

<h3>クラウドコンパイラ</h3>
<p>Chromiumのコンパイル時間を大幅削減するために、クライド上でコンパイル。細かいとこはよくわからんかった。</p>
<p class="quote">
・ビルドにめっちゃ時間かかってビルドしてる間にどんどん新しいコミットがやってくる。<br />
　　→並列処理をしたらある程度速くなるけど限界がある。<br />
　　→PCを増やしたらある程度速くなるけどセットアップ大変。<br />
　　→じゃあクラウドでやっちゃえ！<br />
・クラウドにすると、<br />
　　-無限にPCが使える<br />
　　-他人のキャッシュを使える<br />
　　→速い！<br />
・ネットワークの問題。タイムアウトどうする？<br />
　　-短すぎるとちゃんとやってんのにまた投げちゃう。<br />
　　-長すぎると一瞬で終わるはずのやつを待ち続けちゃう。<br />
　　→ローカルとリモート同時に走らせて速く終わった方を使う！
</p>

<h3>グーグルエンジニアの日常</h3>
<p>今年の３月にGoogleに転職したYuguiさんのGoogle生活。</p>
<p class="quote">
・入社して半年<br />
　　-最初の一ヶ月はひたすら慣れることに専念<br />
　　-３ヶ月目にアメリカ本社で研修<br />
　　-半年位で一人前のGoogle社員<br />
・全てのコードが一つのリポジトリに入っていて、どのコードでも読める<br />
・１つのビルドシステムを使っているので環境作るのに苦労することもあんまりない<br />
・オープンソース界で普通のことがこの規模で普通にできているのはGoogleの凄さ
</p>

<h3>HTML5 vs Flash</h3>
<p>Youtube高速化の事例に見る、HTML5 vs Flashの話。</p>
<p class="quote">
・Flash<br />
　　-Content Protectionに優れてる<br />
　　-ベンチマーク上は、実際に再生するまでの時間が短い<br />
・HTML5<br />
　　-タブキーで再生ボタンなどを選択できる<br />
　　　　→アクセシビリティが高い<br />
　　-ベンチマーク上は、表示時間が短い<br />
・Preload<br />
　　→imgエレメントのsrcに動画のURLを貼っておく<br />
・Player API<br />
・速くするために・・・<br />
　　-キャッシュ重要<br />
　　-lazy load
</p>

<h3>感想</h3>
<p>実際に業務ですぐ使えるかどうかは置いといて、なんかこうトレンドに色々触れられた印象でした。</p>

<h3>おまけ</h3>
<p>Bar Android<br /><img src="http://a1.sphotos.ak.fbcdn.net/hphotos-ak-snc7/302968_310495328975916_100000463531765_1327978_1335087041_n.jpg" alt="Bar Android"></p>
<p>基調講演<br /><img src="http://a7.sphotos.ak.fbcdn.net/hphotos-ak-ash4/301091_310495352309247_100000463531765_1327979_608784555_n.jpg" alt="基調講演"></p>
<p>休憩スペース<br /><img src="http://a6.sphotos.ak.fbcdn.net/hphotos-ak-ash4/321107_310495362309246_100000463531765_1327980_1338428037_n.jpg" alt="休憩スペース"></p>
<p>スタッフの皆様<br /><img src="http://a4.sphotos.ak.fbcdn.net/hphotos-ak-snc7/299013_310495372309245_100000463531765_1327981_661028729_n.jpg" alt="スタッフの皆様"></p>
<p>みなとみらい<br /><img src="http://a8.sphotos.ak.fbcdn.net/hphotos-ak-ash4/312947_310495402309242_100000463531765_1327983_1045263640_n.jpg" alt="みなとみらい"></p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2011/11/03/googledeveloperday2011/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Perl歴2週間やけどYAPCに行ってきた</title>
		<link>http://www.muratayusuke.com/2011/10/18/yapc2011/</link>
		<comments>http://www.muratayusuke.com/2011/10/18/yapc2011/#comments</comments>
		<pubDate>Mon, 17 Oct 2011 16:16:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[勉強会]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=278</guid>
		<description><![CDATA[ということで、YAPCなるPerlのカンファレンスに1日だけ参加してきたので、個人的に勉強になったことをつらつら振り返ります。チーム内で簡単に共有しようと思ってまとめてみたらわりとボリュームが出たから記事にしちゃえっていう感じです。 YAPCについて詳しく知りたい方は本家にどうぞ。 YAPC::As [...]]]></description>
			<content:encoded><![CDATA[<p>ということで、YAPCなるPerlのカンファレンスに1日だけ参加してきたので、個人的に勉強になったことをつらつら振り返ります。チーム内で簡単に共有しようと思ってまとめてみたらわりとボリュームが出たから記事にしちゃえっていう感じです。</p>
<p>YAPCについて詳しく知りたい方は本家にどうぞ。<br />
<a href="http://yapcasia.org/2011/">YAPC::Asia Tokyo 2011</a></p>

<p>タイムテーブルは<a href="http://yapcasia.org/2011/timetable.html">こちら</a>。ほとんどのセッションのスライドも見れます。</p>

<p>あくまで個人的な所感であって正確なまとめではないことを予めご了承ください。Perl特有の話はよくわからんからだいぶ一般化してます。</p>

<h3>Webアプリケーション高速化の話</h3>
<p>スライド：<a href="http://ma.la/files/yapcasia2011">http://ma.la/files/yapcasia2011</a></p>

<p>livedoor Readerの中の人が、いかにして高速化してるかの話。</p>
<p class="quote">
・プロファイリング<br />
　　→プロファイラでボトルネックを探す<br />
・キャッシュ、バッチ<br />
　　-バッチ処理で作るか、動的に作ってキャシュするか<br />
　　-適切なレイヤーで適切なストレージを使う<br />
　　　　-オブジェクトならmemcachedとか<br />
　　　　-HTTPレスポンスならNginxとか<br />
　　　　-静的コンテンツはフロントで処理する（Nginxとか）<br />
　　-キャッシュのウォームアップ<br />
　　　　-よく使われるデータをキャッシュに載せる<br />
　　　　　例：RSSリーダーの未読フィード<br />
・まとめて取得、まとめて更新<br />
　　→ループで毎回SQLを叩くのではなく、最後に一回で叩く<br />
・Bloom Filter<br />
　　-trueが返ってきてもあるかないかわからないが、falseが返ってきたら絶対ない<br />
　　-ないかもしれない大量のデータを問い合わせる前にこれを使って絶対ないやつを間引く<br />
　　　例：リンクを全てスキャンしてRSSフィードを吐いてるページを探す
</p>
<p>高速化っていう括りで体系的にまとめつつ色んなTipsが散りばめられてて、今回見た中で一番面白かった気がします。</p>


<h3>新はてなダイアリーの裏側</h3>
<p>スライド：<a href="http://www.slideshare.net/onishi/ss-9726535">http://www.slideshare.net/onishi/ss-9726535</a></p>

<p>はてなダイアリー大幅リニューアルの話。</p>
<p class="quote">
・名前がはてなダイアリーからはてなブログに<br />
・クロスドメインでiframe内のコンテンツと通信が可能<br />
　　⇒window.postMessageを使う
</p>
<p>技術的な話はいっぱいあったんですが、そんなんできたんや！ってなったのがクロスドメインの話でした。Chrome Extensionでやろうとして挫折した気がする。気が向いたらもっかいちゃんと調べてみよう。</p>


<h3>Webアプリでパスワード保護はどこまでやればいいか</h3>
<p>スライド：<a href="http://www.slideshare.net/ockeghem/how-to-guard-your-password">http://www.slideshare.net/ockeghem/how-to-guard-your-password</a></p>

<p>徳丸本の人のセキュリティの話。Perlは一切関係なかった。</p>
<p class="quote">
・クラック手法<br />
　　-総当り攻撃<br />
　　-辞書攻撃<br />
　　-ジョーアカウント探索<br />
　　-逆走あたり攻撃⇒一番危ない<br />
・ハッシュだからといって安全なわけではない<br />
　　⇒saltとストレッチングは最低限やるべき
</p>
<p>ハッシュを平文に復元する方法はいくらでもあるからできる限りのことはやっときましょうねっていうお話。なるほど。</p>


<h3>Perlで構築された中規模サイトのDC引っ越し記録</h3>
<p>スライド：<a href="http://dl.dropbox.com/u/224433/YAPC2011/index.html">http://dl.dropbox.com/u/224433/YAPC2011/index.html</a></p>

<p>カヤックの中規模サービス「こえ部」DC引越しのお話。</p>
<p class="quote">
・単一障害点をなくす<br />
・段階的に移行する<br />
・DC間にVPNを貼る。DBのレプリケーションも可能<br />
・音声ファイル等はNFSを利用<br />
・ユーザはメンテに「今まで不満だった部分が解消される」ことを期待する
</p>
<p>サーバ台数とか使ってるミドルウェアも結構な数があるサービスをいかに止めずに移行させるか。実際の移行手順はなかなか面白かった。DBも新DCに旧DCのslaveをいくつか立てて、完全移行する時に新DCのslaveの1つをmasterにしちゃうとか。音声とか画像とかのファイルの処理とか。</p>


<h3>Mobage オープンプラットフォームの事件簿</h3>
<p>スライド：<a href="http://www.slideshare.net/zigorou/yapc-asia-2011zigorou">http://www.slideshare.net/zigorou/yapc-asia-2011zigorou</a></p>

<p>モバゲーの内のケーススタディ集。失敗学。</p>
<p class="quote">
・失敗を次に活かすために<br />
　　-原因究明<br />
　　-失敗防止<br />
　　-知識配布
</p>
<p>トラブルが起こった時にいかに再発防止するか。ぶっちゃけ中身は全然理解できひんかった。</p>


<h3>Mobageソーシャルゲームにおける大規模サーバ運用 with Perl</h3>
<p>スライド：<a href="http://www.slideshare.net/zigorou/yapc-asia-2011zigorou">http://www.slideshare.net/zigorou/yapc-asia-2011zigorou</a></p>

<p>モバゲーのインフラ周りのお話。</p>
<p class="quote">
・インフラチューニングの話<br />
・インフラエンジニアもアプリエンジニアや事業部との協業、情報共有が大事
</p>
<p>短期間で急激に成長したサービスをいかにスケールしてきたか。これも具体的な内容はあんまよくわからんかった。あとセクショナリズムはよくないっていう話。うちにも当てはまりまくる。</p>


<h3>感想</h3>
<p>Perlに特化した話を避けてたらなんかインフラ系の話が多くなってしまいましたが、色々といい気づきは得られました。こういうベストプラクティスとか考え方って普段コード書いてて身につくもんじゃないし、1日だけやけど参加してよかったです。</p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2011/10/18/yapc2011/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AjaxでAccess-Control-Allow-Originのエラーを回避する方法</title>
		<link>http://www.muratayusuke.com/2011/05/28/access-control-allow-origin/</link>
		<comments>http://www.muratayusuke.com/2011/05/28/access-control-allow-origin/#comments</comments>
		<pubDate>Sat, 28 May 2011 06:22:40 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[Ajax]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=271</guid>
		<description><![CDATA[Ajaxには欠かせない、XMLHttpRequest。最近こいつが使いにくくなってます。 var XHR = new XMLHttpRequest(); query = &#34;http://www.muratayusuke.com/&#34;; XHR.open(&#34;GET&#038;quot [...]]]></description>
			<content:encoded><![CDATA[<p>Ajaxには欠かせない、XMLHttpRequest。最近こいつが使いにくくなってます。</p>

<pre class="brush: jscript; title: ;">
var XHR = new XMLHttpRequest();

query = &quot;http://www.muratayusuke.com/&quot;;
XHR.open(&quot;GET&quot;,query,true);
XHR.onreadystatechange = function(){
	if (XHR.readyState == 4 &amp;&amp; XHR.status == 200){
		alert(&quot;hoge&quot;);
	}
};
XHR.send(null);
</pre>

<p>上記のコードなんかよく見るXMLHttpRequestのコードだと思いますが、最近のChromeでこいつがエラーを吐くようになりました。そう、<br>
"XMLHttpRequest cannot load http://www.muratayusuke.com/. Origin http://muratest is not allowed by Access-Control-Allow-Origin."<br>
ってやつです。要は、ドメインが違うサイトのデータを勝手に取れなくなってしまったのです。Same Origin Policyというらしいです。<br>
これを回避するには、取られる側のサイトにAccess-Control-Allow-Origin ヘッダーというのを足してやらんとだめみたいで、これを設定してないサイトのデータはXMLHttpRequestで取れないのです。</p>

<p>最近作ってる<a href="http://bit.ly/maw2zd" target="_blank">Chrome拡張</a>とか<a href="http://www.muratayusuke.com/mannaka/" target="_blank">まんなかSearch</a>とかはAjaxしまくりで影響受けまくりだったので、なんとか回避策を練らないといけませんでした。</p>

<p>そこで、やや強引ですが、Access-Control-Allow-Origin ヘッダーをつけた自前のPHPを一枚噛ますことにしました。こんな感じ。</p>

<pre class="brush: jscript; title: ;">
var XHR = new XMLHttpRequest();

query = &quot;./getmurata.php?url=http://www.muratayusuke.com/&quot;;
XHR.open(&quot;GET&quot;,query,true);
XHR.onreadystatechange = function(){
	if (XHR.readyState == 4 &amp;&amp; XHR.status == 200){
		console.log(XHR.responseText);
	}
};
XHR.send(null);
</pre>

<pre class="brush: php; title: ;">
/*getmurata.php*/

require_once(&quot;dataget.php&quot;);
$data = data_get($GET['url']);

header(&quot;Access-Control-Allow-Origin: *&quot;);
print $data;
</pre>

<p>dataget.phpの中身は<a href="http://www.muratayusuke.com/works/#PHPSocket" target="_blank">こちら</a>。PHPでソケット通信して丸々結果を取ってくるfunctionが入ってます。それがdata_get()。HTTP通信が一回増えるので単純に考えて約二倍の時間がかかりますが、とりあえずこれでどんなサイトでもAjaxで取ってこれます。</p>

<p>あと最近知ったjQueryの$.getを使うともっとjavascriptの部分がすっきりします。</p>

<pre class="brush: jscript; title: ;">
query = &quot;./getmurata.php?url=http://www.muratayusuke.com/&quot;;
$.get(query,function(data){
	console.log(data);
});
</pre>

<p>お試しあれ。</p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2011/05/28/access-control-allow-origin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GWにマッシュアップサービスを作った</title>
		<link>http://www.muratayusuke.com/2011/05/15/mannakaearch/</link>
		<comments>http://www.muratayusuke.com/2011/05/15/mannakaearch/#comments</comments>
		<pubDate>Sat, 14 May 2011 20:52:08 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[プログラム]]></category>
		<category><![CDATA[まんなかSearch]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=260</guid>
		<description><![CDATA[とある普通の平日に、@JunichiTokusaさんが「例えば品川と新宿の真ん中らへんをいい感じに計算して、その周辺のお店も出してくれるサービスがあったらいいのに」とぼそっと呟いたのを聞いて、それはいいかも！ってことで、去るゴールデンウィークにさくっと作ってみました。 その名も、「まんなかSearc [...]]]></description>
			<content:encoded><![CDATA[<p>
とある普通の平日に、<a href="http://twitter.com/#!/JunichiTokusa" target="_blank">@JunichiTokusa</a>さんが「例えば品川と新宿の真ん中らへんをいい感じに計算して、その周辺のお店も出してくれるサービスがあったらいいのに」とぼそっと呟いたのを聞いて、それはいいかも！ってことで、去るゴールデンウィークにさくっと作ってみました。<br>
<br>
その名も、「<a href="http://www.muratayusuke.com/mannaka/" target="_blank">まんなかSearch</a>」。<br>
<br>
世に言う「WEB2.0」というのをまじめにやってみました。GoogleMapと食べログ、HeartRailsの最寄り駅検索、さらに路線案内（はAPIじゃないけど・・・）を駆使してがっつりマッシュアップしてます。
</p>

<h3>使い方</h3>
<p>まずは飲み会や食事のアポを取ります。もしくは、飲みたい人を想定します。<br>
次に、下記URLをクリックしてページを開きます。<br>
<a href="http://www.muratayusuke.com/mannaka/" target="_blank">http://www.muratayusuke.com/mannaka/</a><br>
すると、下記のような画面が開きます。</p>

<a href="http://www.muratayusuke.com/wp-content/uploads/d472d8660c8e1df3147da1328fbc65a3.jpg"><img src="http://www.muratayusuke.com/wp-content/uploads/d472d8660c8e1df3147da1328fbc65a3.jpg" alt="開始画面" width="600" class="alignnone size-medium wp-image-261" /></a>

<p>ページが開いたらとりあえず何も考えずに「いいね！」ボタンを押してください。<br>
そして自分の最寄り駅と、飲み相手の最寄り駅を入力します。３人以上入力したい場合は「＋」のボタンを押してください。</p>

<p>入力が完了したら「まんなかを調べる」ボタンを押します。</p>

<p>すると地図上に、入力した地点が赤いピンで、そのまんなかが黄色いピンで表示されます。<br>
さらにそれぞれの地点からまんなかまでのルートや所要時間が左上に、まんなか付近のお食事処がその下に表示されます。マッシュアップ。</p>

<p>まんなかが不満な場合は、黄色いピンを動かすことができます。動かした瞬間にそこまでのルート情報とお店情報が更新されます。</p>

<p>また、左上のルート内の駅名をクリックするとそこがまんなかになり、同じくルート情報とお店情報が更新されます。</p>

<p>まだバグもいろいろありますが、おいおい直していきます。何かあればご報告ください。<br>
今後追加したい機能としては、</p>

<p class="quote">
・いくつかの候補のうち、お店の数が多いところをおすすめする<br>
・iPhone対応
</p>

<p>・・・とりあえずそんなもんかな？入力値によっていろいろおかしくなることもあるので、その辺を洗い出したりおかしくなった時のエラーメッセージ的なやつもちゃんと出したい。<br>
そんな感じで、ぜひぜひご利用ください。<br>
<br>
<a href="http://www.muratayusuke.com/mannaka/" target="_blank">まんなかSearch</a></p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2011/05/15/mannakaearch/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Chrome拡張のContentScriptでjQueryを使う方法</title>
		<link>http://www.muratayusuke.com/2011/02/13/chrome_extension_jquery/</link>
		<comments>http://www.muratayusuke.com/2011/02/13/chrome_extension_jquery/#comments</comments>
		<pubDate>Sun, 13 Feb 2011 04:44:08 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Chrome Extension]]></category>
		<category><![CDATA[JQuery]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[拡張]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=220</guid>
		<description><![CDATA[Chrome拡張のContentScriptでDOM操作できるから、ばりばりjQuery使おう！と思ったけど、あれ、どうやんねん？ってちょっとなったので備忘録。 正解は、manifest.jsonに書いちゃう、でした。 { ・・・ &#34;content_scripts&#34;: [ { &#038; [...]]]></description>
			<content:encoded><![CDATA[<p>Chrome拡張のContentScriptでDOM操作できるから、ばりばりjQuery使おう！と思ったけど、あれ、どうやんねん？ってちょっとなったので備忘録。</p>

<p>正解は、manifest.jsonに書いちゃう、でした。</p>

<pre class="brush: jscript; title: ;">
{
	・・・
	&quot;content_scripts&quot;: [
		{
			&quot;matches&quot;: [
				&quot;http://*/*&quot;,
				&quot;https://*/*&quot;
			],
			&quot;js&quot;: [&quot;jquery.js&quot;,&quot;script.js&quot;],
			&quot;run_at&quot;: &quot;document_end&quot;
		}
	],
	・・・
}
</pre>

<p>この"js"のところが順番に読み込まれるらしく、逆にすると動きませんでした。</p>

<p>そんだけ。</p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2011/02/13/chrome_extension_jquery/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>【LT資料】Chrome拡張でBeat Amazon!</title>
		<link>http://www.muratayusuke.com/2011/01/22/lt_slide_beatamazon/</link>
		<comments>http://www.muratayusuke.com/2011/01/22/lt_slide_beatamazon/#comments</comments>
		<pubDate>Sat, 22 Jan 2011 08:18:22 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Chrome Extension]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[LT]]></category>
		<category><![CDATA[拡張]]></category>
		<category><![CDATA[楽天ブックス]]></category>
		<category><![CDATA[資料]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=212</guid>
		<description><![CDATA[先日、TechTalkという楽天の社内勉強会でLTをしてきました。テーマはAmazon to Rakuten。完全に自分が便利なように作ったChrome拡張ですが、みんなおんなじ不満を感じてたんやなあってことでかなり大ウケやったんちゃうかと思います。せっかくなので、資料を晒しておきます。 [2011 [...]]]></description>
			<content:encoded><![CDATA[<p>先日、TechTalkという楽天の社内勉強会でLTをしてきました。テーマは<a href="https://chrome.google.com/extensions/detail/dedhofliljhpidkfhdlocoecddblhidc?hl=ja#" target="_blank">Amazon to Rakuten</a>。完全に自分が便利なように作ったChrome拡張ですが、みんなおんなじ不満を感じてたんやなあってことでかなり大ウケやったんちゃうかと思います。せっかくなので、資料を晒しておきます。</p>

<div style="width:500px;margin-bottom:20px;" id="__ss_6659185">
	<strong style="display:block;margin:12px 0 4px">
		<a href="http://www.slideshare.net/muratayusuke/20110119chromebeat-amazon-in-techtalk" title="[2011/01/19]Chrome拡張でBeat Amazon! (in TechTalk)">[2011/01/19]Chrome拡張でBeat Amazon! (in TechTalk)</a>
	</strong>
	<object id="__sse6659185" width="500" height="418"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=110119techtalklt-110121221510-phpapp01&rel=0&stripped_title=20110119chromebeat-amazon-in-techtalk&userName=muratayusuke" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse6659185" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=110119techtalklt-110121221510-phpapp01&rel=0&stripped_title=20110119chromebeat-amazon-in-techtalk&userName=muratayusuke" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="500" height="418"></embed></object>
</div>

<p>今後やりたいこととして色々書いてますが、色んな人にフィードバックをもらって今後やりたいことがさらに増えました。まとめると、こんな感じです。</p>

<div class="quote">
1.書籍名ではなくISBNコードで検索<br>
2.AmazonへのリンクにOnMouseOverで楽天へのリンクがポップアップする<br>
3.楽天市場にも対応<br>
4.Firefox版作成
</div>

<p>1.は、楽天ブックス総合検索APIにはなかったんですが書籍検索にあったので、これに切り替えたいと思います。タイトル検索かからん時もあったりするし。<br>
ただまあ3.の市場対応をするならCDとかDVD対応もしたいし、一旦書籍専用でISBN検索しつつ、どのAPIを使うかっていう判断をして全部対応できるような処理もどっかで足したいですね。<br>
2.は勉強会に来てた学生のアイデアでそれはいい！ってなったので、実装しようと思います。<br>
4.のFirefox版は今も作成中ですが、むずい。</p>

<p>そんな感じで、随時実装していくぜい。</p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2011/01/22/lt_slide_beatamazon/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Chrome拡張に買い物かごボタンを追加した</title>
		<link>http://www.muratayusuke.com/2010/12/19/add_cart/</link>
		<comments>http://www.muratayusuke.com/2010/12/19/add_cart/#comments</comments>
		<pubDate>Sun, 19 Dec 2010 11:35:37 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Chrome Extension]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[拡張]]></category>
		<category><![CDATA[楽天ブックス]]></category>
		<category><![CDATA[買い物かご]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=208</guid>
		<description><![CDATA[いやもう、タイトルの通りでございます。いつものAmazon to Rakutenに買い物かごをしれっと追加しました。まあ、つけた方が個人的に便利やし。そこで工夫した点など。 必要な情報 まず、実際の楽天ブックスの商品ページの買い物かごボタンを見て、どんなデータをPOSTしてやればいいのかを調べます。 [...]]]></description>
			<content:encoded><![CDATA[<p>いやもう、タイトルの通りでございます。いつもの<a href="https://chrome.google.com/extensions/detail/dedhofliljhpidkfhdlocoecddblhidc?hl=ja#" target="_blank">Amazon to Rakuten</a>に買い物かごをしれっと追加しました。まあ、つけた方が個人的に便利やし。そこで工夫した点など。</p>

<h3>必要な情報</h3>
<p>まず、実際の楽天ブックスの商品ページの買い物かごボタンを見て、どんなデータをPOSTしてやればいいのかを調べます。</p>

<pre class="brush: xml; title: ;">
&lt;form method=&quot;post&quot; action=&quot;https://books.step.rakuten.co.jp/rms/mall/book/bs/Cart&quot;&gt;
	&lt;div&gt;
		&lt;span class=&quot;unit&quot;&gt;個数&amp;nbsp; &lt;/span&gt;
		&lt;input value=&quot;1&quot; type=&quot;text&quot; size=&quot;4&quot; name=&quot;units&quot; id=&quot;units&quot;&gt;
		&lt;input value=&quot;買い物かごに入れる&quot; type=&quot;submit&quot;&gt;
		&lt;input type=&quot;hidden&quot; value=&quot;213310&quot; name=&quot;shop_bid&quot;&gt;
		&lt;input type=&quot;hidden&quot; value=&quot;14034556&quot; name=&quot;item_id&quot; id=&quot;ScItemGet&quot;&gt;
		&lt;input type=&quot;hidden&quot; value=&quot;1&quot; name=&quot;inventory_flag&quot;&gt;
	&lt;/div&gt;
&lt;/form&gt;
</pre>

<p>見た感じ、いじる必要がありそうなとこは、</p>

<div class="quote">
1.「個数」をhiddenで1個に固定<br />
2."item_id"を商品に合わせて変更
</div>

<p>の２点。楽天ブックスは楽天市場の１店舗なので、"shop_bid"は全部一緒のはず。"inventory_flag"は、まあようわからんけどこのままでいいっしょ。</p>

<h3>実装</h3>

<p>ということで、実質コードを書く必要があるのは"item_id"だけ。ところが厄介なことに、"item_id"はなんとAPIに入っていないので、もっかいXMLHttpRequestを叩いて無理矢理取ってくることに。http通信２回分なんて待ってられないので、一回目のAPI通信で取得したデータはすぐ表示しつつ、続けてitem_idが取得出来次第ボタンを表示っていう仕様にしました。</p>

<pre class="brush: jscript; title: ;">
chrome.extension.onConnect.addListener(function(port) {
  console.assert(port.name == &quot;AtoR&quot;);
  port.onMessage.addListener(function(msg) {
		if(msg.status == &quot;start&quot;){
			//ページアクションのアイコンを表示
			chrome.pageAction.show(port.sender.tab.id);
			
			//楽天APIから商品を検索
			query = &quot;http://api.rakuten.co.jp/rws/3.0/json?&quot; +
					&quot;developerId=&quot; + devId +
					&quot;&amp;affiliateId=&quot; + afiId +
					&quot;&amp;operation=&quot; + opr +
					&quot;&amp;version=&quot; + ver+
					&quot;&amp;keyword=&quot; + encodeURI(msg.title);
					
			api.open(&quot;GET&quot;,query,true);
			api.onreadystatechange = sourceGet(port);
			api.send(null);
		}
		else if(msg.status == &quot;null&quot;){
			content = &quot;このページでは使用できません。&quot;;
		}
		else{
			sourceGet(port);
		}
  });
});


//タブが変更された時の処理
chrome.tabs.onSelectionChanged.addListener(function(tabid){
	chrome.tabs.getSelected(null, function(tab) {
		・・・
	});
});


function sourceGet(port){
	if (api.readyState == 4 &amp;&amp; api.status == 200){
		response = eval('[' + api.responseText + ']')[0];
		
		if(response['Header']['Status'] == 'Success'){
			items = response['Body']['BooksTotalSearch']['Items']['Item'];
			
			//商品データを１つずつhtmlに出力
			content = '&lt;table width=&quot;300&quot;&gt;';
			for(i=0;i&lt;items.length;i++){			
				content = content+'&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;'+items[i]['affiliateUrl']+'&quot;&gt;&lt;img src=&quot;'+items[i]['mediumImageUrl']+'&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;td style=&quot;width:200px;vertical-align:top;&quot;&gt;タイトル：&lt;a href=&quot;'+items[i]['affiliateUrl']+'&quot; target=&quot;_blank&quot;&gt;'+items[i]['title']+'&lt;/a&gt;&lt;br /&gt;著者：'+items[i]['author']+'&lt;br /&gt;価格（税込）：'+setComma(items[i]['itemPrice'])+'円&lt;br /&gt;ポイント：'+Math.floor(items[i]['itemPrice']/1.05/100)+'ポイント&lt;br /&gt;&lt;img src=&quot;img/star_';
				if(items[i]['reviewAverage'] == 0) content = content + '0';
				else if(items[i]['reviewAverage'] &gt; 0 &amp;&amp; items[i]['reviewAverage'] &lt; 1) content = content + '0.5';
				else if(items[i]['reviewAverage'] == 1) content = content + '1';
				else if(items[i]['reviewAverage'] &gt; 1 &amp;&amp; items[i]['reviewAverage'] &lt; 2) content = content + '1.5';
				else if(items[i]['reviewAverage'] == 2) content = content + '2';
				else if(items[i]['reviewAverage'] &gt; 2 &amp;&amp; items[i]['reviewAverage'] &lt; 3) content = content + '2.5';
				else if(items[i]['reviewAverage'] == 3) content = content + '3';
				else if(items[i]['reviewAverage'] &gt; 3 &amp;&amp; items[i]['reviewAverage'] &lt; 4) content = content + '3.5';
				else if(items[i]['reviewAverage'] == 4) content = content + '4';
				else if(items[i]['reviewAverage'] &gt; 4 &amp;&amp; items[i]['reviewAverage'] &lt; 5) content = content + '4.5';
				else if(items[i]['reviewAverage'] == 5) content = content + '5';
				content = content + '.png&quot; /&gt;('+items[i]['reviewCount']+')&lt;br /&gt;買い物かごに入れる&lt;/td&gt;&lt;/tr&gt;';
				
				rakuten.open(&quot;GET&quot;,items[i][&quot;itemUrl&quot;],true);
				rakuten.onreadystatechange = function(){
					if (rakuten.readyState == 4 &amp;&amp; rakuten.status == 200){
						rr = rakuten.responseText;
						rr.match(/value=&quot;([^&quot;]*)&quot;[^&gt;]*id=&quot;ScItemGet&quot;/);
						rr = RegExp.$1;
						
						//買い物かご
						cart = '&lt;form method=&quot;post&quot; action=&quot;https://books.step.rakuten.co.jp/rms/mall/book/bs/Cart&quot; target=&quot;_blank&quot;&gt;';
						cart = cart + '&lt;div&gt;';
						cart = cart + '&lt;input value=&quot;1&quot; type=&quot;hidden&quot; name=&quot;units&quot; id=&quot;units&quot;&gt;';
						cart = cart + '&lt;input value=&quot;買い物かごに入れる&quot; type=&quot;submit&quot;&gt;';
						cart = cart + '&lt;input type=&quot;hidden&quot; value=&quot;213310&quot; name=&quot;shop_bid&quot;&gt;';
						cart = cart + '&lt;input type=&quot;hidden&quot; value=&quot;'+rr+'&quot; name=&quot;item_id&quot; id=&quot;ScItemGet&quot;&gt;';
						cart = cart + '&lt;input type=&quot;hidden&quot; value=&quot;1&quot; name=&quot;inventory_flag&quot;&gt;'
						cart = cart + '&lt;/div&gt;'
						cart = cart + '&lt;/form&gt;'
						
						content = content.replace(&quot;買い物かごに入れる&quot;, cart);
						viewContent();
					}
				};
				rakuten.send(null);
			}
			content = content+'&lt;/table&gt;';
			viewContent();
		}
		・・・
	}
	・・・
}
</pre>

<p>61行目で、まずベタ文字で「買い物かごに入れる」って表示しつつ、63行目以下で２度目の通信を行ってます。item_idは67行目の正規表現で強引にゲット。正規表現が一発で決まると気持ちいい。<br />
無事にitem_idが取れたらベタ文字をformタグで置換。めでたしめでたし。</p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2010/12/19/add_cart/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>楽天ブックス総合検索APIの出力結果</title>
		<link>http://www.muratayusuke.com/2010/11/07/rakuten_books_api/</link>
		<comments>http://www.muratayusuke.com/2010/11/07/rakuten_books_api/#comments</comments>
		<pubDate>Sun, 07 Nov 2010 14:19:38 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[プログラム]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[楽天ブックス]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=201</guid>
		<description><![CDATA[楽天ブックス総合検索APIを使ってて、出力結果の構造がわかりにくかったので、綺麗にインデントしたものを作りました。一個一個の意味はドキュメントをご覧いただくことにして、ここではぱっと目的の値に辿り着けるように出力結果の例だけ載せておきます。 /* json形式 */ { &#34;Body&#038;quo [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://webservice.rakuten.co.jp/api/bookstotalsearch/" target="_blank">楽天ブックス総合検索API</a>を使ってて、出力結果の構造がわかりにくかったので、綺麗にインデントしたものを作りました。一個一個の意味は<a href="http://webservice.rakuten.co.jp/api/bookstotalsearch/" target="_blank">ドキュメント</a>をご覧いただくことにして、ここではぱっと目的の値に辿り着けるように出力結果の例だけ載せておきます。</p>

<pre class="brush: jscript; title: ;">
/* json形式 */

{
	&quot;Body&quot;:{
		&quot;BooksTotalSearch&quot;:{
			&quot;Items&quot;:{
				&quot;Item&quot;:[
					{
						&quot;hardware&quot;:&quot;&quot;,
						&quot;limitedFlag&quot;:0,
						&quot;author&quot;:&quot;ガー・レイノルズ&quot;,
						&quot;booksGenreID&quot;:&quot;001006004006/001006018002/001006009001&quot;,
						&quot;title&quot;:&quot;プレゼンテーションZenデザイン&quot;,
						&quot;listPrice&quot;:&quot;&quot;,
						&quot;itemCaption&quot;:&quot;プレゼン・デザインの原則とテクニック。ビジュアル・コミュニケーション成功の秘訣。&quot;,
						&quot;publisherName&quot;:&quot;ピアソン桐原&quot;,
						&quot;isbn&quot;:&quot;9784894713994&quot;,
						&quot;largeImageUrl&quot;:&quot;http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/8947/89471399.jpg?_ex=200x200&quot;,
						&quot;jan&quot;:&quot;&quot;,
						&quot;mediumImageUrl&quot;:&quot;http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/8947/89471399.jpg?_ex=120x120&quot;,
						&quot;availability&quot;:&quot;1&quot;,
						&quot;os&quot;:&quot;&quot;,&quot;postageFlag&quot;:1,
						&quot;salesDate&quot;:&quot;2010-07-01&quot;,
						&quot;smallImageUrl&quot;:&quot;http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/8947/89471399.jpg?_ex=64x64&quot;,
						&quot;label&quot;:&quot;&quot;,
						&quot;itemPrice&quot;:2520,
						&quot;affiliateUrl&quot;:&quot;http://hb.afl.rakuten.co.jp/hgc/0cec7c34.60401a8a.0cec7c35.b91eba24/_RTwebs10000001?pc=http%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F6527030%2F&quot;,
						&quot;reviewCount&quot;:2,
						&quot;artistName&quot;:&quot;&quot;,
						&quot;discountRate&quot;:0,
						&quot;reviewAverage&quot;:5,
						&quot;itemUrl&quot;:&quot;http://books.rakuten.co.jp/rb/6527030/&quot;
					}
				]
			},
			&quot;pageCount&quot;:1,
			&quot;hits&quot;:1,
			&quot;last&quot;:1,
			&quot;count&quot;:1,
			&quot;page&quot;:1,
			&quot;carrier&quot;:0,
			&quot;first&quot;:1
		}
	},
	&quot;Header&quot;:{
		&quot;Status&quot;:&quot;Success&quot;,
		&quot;Args&quot;:{
			&quot;Arg&quot;:{
				&quot;apiVersion&quot;:{
					&quot;content&quot;:true,
					&quot;value&quot;:&quot;30&quot;
				},
				&quot;operation&quot;:{
					&quot;content&quot;:true,
					&quot;value&quot;:&quot;BooksTotalSearch&quot;
				},
				&quot;developerId&quot;:{
					&quot;content&quot;:true,
					&quot;value&quot;:&quot;&quot;//自分のデベロッパーIDが入る
				},
				&quot;keyword&quot;:{
					&quot;content&quot;:true,
					&quot;value&quot;:&quot;プレゼンテーションzenデザイン &quot;
				},
				&quot;User-Agent&quot;:{
					&quot;content&quot;:true,
					&quot;value&quot;:&quot;Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.11 Safari/534.10&quot;
				},
				&quot;affiliateId&quot;:{
					&quot;content&quot;:true,
					&quot;value&quot;:&quot;&quot;//自分のアフィリエイトIDが入る
				},
				&quot;version&quot;:{
					&quot;content&quot;:true,
					&quot;value&quot;:&quot;2010-03-18&quot;
				}
			}
		},
		&quot;StatusMsg&quot;:&quot;&quot;
	}
}
</pre>

<p>最初から公式のドキュメントに載せといてくれたらいいのに。<br />
ちなみに、検索に失敗するとBodyがnullになってHeaderのStatusにNotFoundやらServerErrorやらが入ります。</p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2010/11/07/rakuten_books_api/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Chrome拡張でContent ScriptとBackground Pageのメッセージのやり取りをする</title>
		<link>http://www.muratayusuke.com/2010/10/31/chrome_extensioncontent-script_background-page/</link>
		<comments>http://www.muratayusuke.com/2010/10/31/chrome_extensioncontent-script_background-page/#comments</comments>
		<pubDate>Sat, 30 Oct 2010 15:49:58 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Chrome Extension]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[拡張]]></category>
		<category><![CDATA[楽天ブックス]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=143</guid>
		<description><![CDATA[以前の記事でやりたいこととして挙げていた、 1.やっぱりContent ScriptとMessageを使いこなしたい。 を、ようやく実装できました。 Amazon to Rakuten Chrome拡張「Amazon to Rakuten」は当初下記の流れで作っていました。 1.chrome.tab [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.muratayusuke.com/2010/10/24/amazon_to_rakuten/">以前の記事</a>でやりたいこととして挙げていた、</p>
<div class="quote">1.やっぱりContent ScriptとMessageを使いこなしたい。</div>
<p>を、ようやく実装できました。<br />
<br>
<a href="https://chrome.google.com/extensions/detail/dedhofliljhpidkfhdlocoecddblhidc?hl=ja#" target="_blank">Amazon to Rakuten</a><br>
<br />
Chrome拡張「Amazon to Rakuten」は当初下記の流れで作っていました。</p>

<div class="quote">
1.chrome.tabs.getSelectedで今開いてるamazonのページのURLを取得<br />
2.XMLHttpRequestで取得したURLのHTMLを取ってきて、本のタイトルを正規表現で取得<br />
3.<a href="http://webservice.rakuten.co.jp/api/bookstotalsearch/" target="_blank">楽天ブックス総合検索API</a>に取得したタイトルを投げる<br />
4.検索結果をPopupに表示。表示されたやつをクリックすると無事楽天ブックスの商品詳細ページにジャンプ
</div>
<p>
が、この1.と2.の部分が無駄なのとbackgroundPageを使ってみたかったのとで、次のような流れにしました。</p>

<div class="quote">
1.Content&nbsp;ScriptでDOM解析し、今開いているamazonの詳細ページから商品タイトルを取得<br />
2.取得したタイトルをMessageでbackground.htmlに送信<br />
3.background.html上で<a href="http://webservice.rakuten.co.jp/api/bookstotalsearch/" target="_blank">楽天ブックス総合検索API</a>に、Content&nbsp;Scriptから受信した商品タイトルを投げて返ってきた結果を処理し、popup.htmlに表示したい内容を変数に格納<br />
4.popup.htmlには3.で格納したbackground.html内の変数の中身を表示するスクリプトを書いておく
</div>

<p>こうすることで、amazonの商品ページを表示した瞬間に裏側でDOM解析＆API検索を行い、アイコンをクリックしたらその処理結果を表示するだけになるので、アイコンクリック後の待ち時間がほぼなくなります。今回も色んなところで詰まりまくった。</p>

<h3>1.Content&nbsp;Scriptで商品タイトルを引っ張ってくる</h3>
<pre class="brush: jscript; title: ;">
/* manifest.json */

{
  ・・・
	&quot;background_page&quot;: &quot;background.html&quot;,
	&quot;content_scripts&quot;: [
		{
			&quot;matches&quot;: [
				&quot;http://www.amazon.co.jp/*&quot;,
				&quot;http://www.amazon.com/*&quot;
			],
			&quot;js&quot;: [&quot;script.js&quot;],
			&quot;run_at&quot;: &quot;document_end&quot;
		}
	],
	&quot;browser_action&quot;: {
		&quot;default_icon&quot;: &quot;icon.png&quot;,
		&quot;popup&quot;: &quot;popup.html&quot;
	},
  ・・・
}
</pre>
<p>まずはここから。Content&nbsp;Scriptを使うには、manifest.jsonに上記のように記述する必要があります。<br />
"matches"はContent&nbsp;Scriptを動かすサイトの条件を設定する項目。<br />
"js"は動かしたいJavaScriptファイルのファイル名。<br />
"run_at"はどのタイミングでスクリプトを実行するかを決める項目。今回はDOM構築完了時にスクリプトを動かす"document_end"にしてます。<br />
他にも何個かオプションがあります。詳細は<a href="http://dev.screw-axis.com/doc/chrome_extensions/guide/content_script/" target="_blank">こちら</a>。</p>

<pre class="brush: jscript; title: ;">
/* script.js */

title = document.getElementById('btAsinTitle').firstChild.nodeValue;
title.match(/([^\(]*)/);
title = RegExp.$1;
</pre>
<p>こうすると、amazonの詳細ページでDOM構築が終わった瞬間script.jsが実行されて、titleの中に商品タイトルが入るって寸法です。<br>
これをそのままAPIに投げたいところですが、Content&nbsp;ScriptではXMLHttpRequestが使えないそうなので、background.htmlに商品タイトルを送ってやる必要があります。</p>

<h3>2.Content&nbsp;Scriptからbackground.htmlにメッセージを送信</h3>
<pre class="brush: jscript; title: ;">
/* script.js */

title = document.getElementById('btAsinTitle').firstChild.nodeValue;
title.match(/([^\(]*)/);
title = RegExp.$1;

var port = chrome.extension.connect({name: &quot;AtoR&quot;});
port.postMessage({title: title,status:&quot;start&quot;});
port.onMessage.addListener(function(msg) {
	if (msg.status == &quot;loading&quot;){
		port.postMessage({status: &quot;loading&quot;});
	}
});
</pre>

<pre class="brush: jscript; title: ;">
/* background.html */

chrome.extension.onConnect.addListener(function(port) {
  console.assert(port.name == &quot;AtoR&quot;);
  port.onMessage.addListener(function(msg) {
		if(msg.status == &quot;start&quot;){
			//楽天APIから商品を検索
			query = &quot;http://api.rakuten.co.jp/rws/3.0/json?&quot; +
					&quot;developerId=&quot; + devId +
					&quot;&amp;affiliateId=&quot; + afiId +
					&quot;&amp;operation=&quot; + opr +
					&quot;&amp;version=&quot; + ver+
					&quot;&amp;keyword=&quot; + encodeURI(msg.title);
					
			api.open(&quot;GET&quot;,query,true);
			api.onreadystatechange = sourceGet(port);
			api.send(null);
		}
		else{
			sourceGet(port);
		}
  });
});

function sourceGet(port){
	console.log(&quot;sourceGet&quot;);
	if (api.readyState == 4 &amp;&amp; api.status == 200){
		//APIからのレスポンスがきちんと返ってきた時の処理
		・・・
	}
	else{
		if (loading === false){
			loading = true;
			content = '&lt;img src=&quot;loading.gif&quot; /&gt;';
		}
		port.postMessage({status: &quot;loading&quot;});
	}
}
</pre>
<p>例によって詳細は<a href="http://dev.screw-axis.com/doc/chrome_extensions/guide/message_passing/" target="_blank">ChromeAPIのメッセージのページ</a>を見てください。<br>
今回はXMLHttpRequestを使うせいか単発通信ではうまくいかなかったので、永続通信で下記の流れでやり取りさせてます。</p>

<div class="quote">
まずContentScriptでタイトルとstatus:startを送信<br>
　　　　　　　　↓<br>
background.htmlがstartを受け取ったら楽天APIにタイトルを投げてsourceGetを実行。通信中の場合はstatus:loadingをContentScriptに返す<br>
　　　　　　　　↓<br>
ContentScriptにstatus:loadingが返ってきたら再びstatus:loadingを投げ返し、background.htmlにお伺いを立てる<br>
　　　　　　　　↓<br>
background.htmlで再びsourceGetを実行。APIからの返事がまだならもっかいstatus:loadingを投げ返す、返事が来てたら処理終了
</div>

<p>ってな具合です。</p>

<h3>3.popup.htmlに表示したい内容を変数に格納</h3>
<pre class="brush: jscript; title: ;">
/* background.html */

function sourceGet(port){
	console.log(&quot;sourceGet&quot;);
	if (api.readyState == 4 &amp;&amp; api.status == 200){
		response = eval('[' + api.responseText + ']')[0];
		
		if(response['Header']['Status'] == 'Success'){
			items = response['Body']['BooksTotalSearch']['Items']['Item'];
			
			//商品データを１つずつhtmlに出力
			content = '&lt;table width=&quot;300&quot;&gt;';
			for(i=0;i&lt;items.length;i++){
				content = content+'&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;'+items[i]['affiliateUrl']+'&quot;&gt;&lt;img src=&quot;'+items[i]['mediumImageUrl']+'&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;td style=&quot;width:200px;vertical-align:top;&quot;&gt;タイトル：&lt;a href=&quot;'+items[i]['affiliateUrl']+'&quot; target=&quot;_blank&quot;&gt;'+items[i]['title']+'&lt;/a&gt;&lt;br /&gt;著者：'+items[i]['author']+'&lt;br /&gt;価格（税込）：'+setComma(items[i]['itemPrice'])+'円&lt;br /&gt;ポイント：'+Math.floor(items[i]['itemPrice']/1.05/100)+'ポイント&lt;/td&gt;&lt;/tr&gt;';
			}
			content = content+'&lt;/table&gt;';
		}
		else if(response['Header']['Status'] == 'NotFound'){
			content = '見つかりませんでした。';
		}
		else{
			content = 'エラーが発生しました。';
		}
	}
	else if (api.readyState == 4){
		content = &quot;接続に失敗しました。&quot;;
	}
	else{
		if (loading === false){
			//ロード中画像を表示
			loading = true;
			content = '&lt;img src=&quot;loading.gif&quot; /&gt;';
		}
		port.postMessage({status: &quot;loading&quot;});
	}
}
</pre>
<p>実際のsourceGetはこんな感じです。汚い。<br>
ポップアップに表示したいものは全部変数contentに格納して、後から引っ張ってきます。</p>

<h3>4.background.htmlのcontentに格納した内容をpopup.htmlに表示</h3>
<pre class="brush: xml; title: ;">
/* popup.html */

&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id=&quot;content&quot;&gt;&lt;/div&gt;
&lt;script&gt;
bg = chrome.extension.getBackgroundPage();
document.getElementById('content').innerHTML = bg.content;
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>なんと、たったの２行で、backgound.html上の変数をpopup.htmlでも使うことができてしまいます。<br>
逆だとちょっとめんどいです。詳細は<a href="http://dev.screw-axis.com/doc/chrome_extensions/guide/background_pages/" target="_blank">こちら</a>。<br>
ちなみに、script.jsは外から呼べなかったです。なぜ。</p>

<h3>思わぬ落とし穴</h3>
<p>これでようやく思ったとおりの挙動になって万々歳！と思った矢先、思わぬ落とし穴が。<br>
このbackground.htmlはタブを何個開いてても共通で使われるので、例えば次のような場合。</p>

<div class="quote">
amazonで商品１のページを見ている状態で、商品２のページを別タブで開く<br>
　　　　　　　　↓<br>
商品２が読み込まれた瞬間background.htmlに商品２のデータが格納される<br>
　　　　　　　　↓<br>
商品１のタブに戻って拡張のアイコンをクリック<br>
　　　　　　　　↓<br>
background.htmlには商品２のデータが格納されたままなので、そっちが表示されてしまう
</div>

<p>というわけで、タブを切り替える度にタイトルを取得してAPIを叩き直さんとあかんことに気づく。<br>
ですが予想通りタブ切り替えイベントは用意されていたので、ささっと対応しました。</p>
<pre class="brush: jscript; title: ;">
/* background.html */

・・・

//タブが変更された時の処理
chrome.tabs.onSelectionChanged.addListener(function(tabid){
	chrome.tabs.getSelected(null, function(tab) {
		chrome.tabs.sendRequest(tab.id, {status: &quot;changed&quot;}, function(response) {});
	});
});

・・・
</pre>

<pre class="brush: jscript; title: ;">
/* script.js */

AtoR();

chrome.extension.onRequest.addListener(
  function(request, sender, sendResponse) {
    AtoR();
  }
);

function AtoR(){
	title = document.getElementById('btAsinTitle').firstChild.nodeValue;
	title.match(/([^\(]*)/);
	title = RegExp.$1;
	
	var port = chrome.extension.connect({name: &quot;AtoR&quot;});
	port.postMessage({title: title,status:&quot;start&quot;});
	port.onMessage.addListener(function(msg) {
		if (msg.status == &quot;loading&quot;){
			port.postMessage({status: &quot;loading&quot;});
		}
	});
}
</pre>

<p>chrome.tabs.onSelectionChangedがscript.js側で使えればよかったんですが、ContentScript上では使えませんって怒られたのでbackground.htmlに記述して例によってメッセージで送信。<br>
chrome.extension.onRequest.addListenerの中がメッセージ受信時、つまりタブが切り替わった時に実行されるアクションです。</p>

<h3>そんなこんなで</h3>
<p>あれやこれやと試行錯誤を繰り返し、なんとかやりたいことを１つ実装できました。今後もちょくちょくアップデートしていく予定です。<br>
Chrome拡張はインストールしたらソースコード全部見られるので、興味ある方はぜひ。<br>
<br>
<a href="https://chrome.google.com/extensions/detail/dedhofliljhpidkfhdlocoecddblhidc?hl=ja#" target="_blank">Amazon to Rakuten</a></p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2010/10/31/chrome_extensioncontent-script_background-page/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>amazonの商品ページから2クリックで楽天ブックスの商品ページに飛ぶChrome拡張を作った</title>
		<link>http://www.muratayusuke.com/2010/10/24/amazon_to_rakuten/</link>
		<comments>http://www.muratayusuke.com/2010/10/24/amazon_to_rakuten/#comments</comments>
		<pubDate>Sun, 24 Oct 2010 09:27:02 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Chrome Extension]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[拡張]]></category>
		<category><![CDATA[楽天ブックス]]></category>

		<guid isPermaLink="false">http://www.muratayusuke.com/?p=66</guid>
		<description><![CDATA[ぼくは昔からずっとamazonユーザーだったんですが、打倒アマゾンを掲げる楽天入社を機に、本とかCDとかは楽天ブックスで買うようになりました。 ところが、RSSに多数登録してる書評ブログから商品をクリックすると、大半がamazonにジャンプしてしまいます。 仕方がないので別タブで楽天ブックスを開き、 [...]]]></description>
			<content:encoded><![CDATA[<p>ぼくは昔からずっとamazonユーザーだったんですが、打倒アマゾンを掲げる楽天入社を機に、本とかCDとかは楽天ブックスで買うようになりました。<br />
ところが、RSSに多数登録してる書評ブログから商品をクリックすると、大半がamazonにジャンプしてしまいます。<br />
仕方がないので別タブで楽天ブックスを開き、本のタイトルをコピペして検索をかけ、やっとその商品の詳細ページに辿り着けるというめんどくさいステップを踏んでました。<br />
これをなんとか楽にしたいという1楽天ブックスユーザーの熱い思いを、今回Google&nbsp;Chromeの拡張機能という形にしてみました。<br>
<br>
<a href="https://chrome.google.com/extensions/detail/dedhofliljhpidkfhdlocoecddblhidc?hl=ja#" target="_blank">Amazon to Rakuten</a><br>
<br>
これで自分は便利になるし打倒アマゾンにも一役買えるんじゃないかというのもあるんですが、色んなとこでハマったので忘れないうちにその辺書いときます。</p>

<h3>大枠</h3>
<p>実はこれだいぶ前に作ろうとしたんですが、全然やり方がわからず放置してました。<br />
ところが先日うちのインターン生でChrome&nbsp;Extensionを作るという女の子が。色々聞いてみて「これならできそう！」ってなってやってみたらできたっていう感じです。<br />
<br />
流れはこんな感じ。<br />
<br />
1.chrome.tabs.getSelectedで今開いてるamazonのページのURLを取得<br />
2.XMLHttpRequestで取得したURLのHTMLを取ってきて、本のタイトルを正規表現で取得<br />
3.<a href="http://webservice.rakuten.co.jp/api/bookstotalsearch/" target="_blank">楽天ブックス総合検索API</a>に取得したタイトルを投げる<br />
4.検索結果をPopupに表示。表示されたやつをクリックすると無事楽天ブックスの商品詳細ページにジャンプ<br />
<br />
まあ正直4.はPopup使わずに一番上の検索結果に自動的に飛ばそうかと思ったんですが、運悪く違うページに飛んじゃったらどうしようもないので1クッション挟みました。<br />
<br />
この流れを見て、「なんで<a href="http://dev.screw-axis.com/doc/chrome_extensions/guide/content_script/" target="_blank">Content&nbsp;Script</a>使わへんの？」と思った方、鋭い。実はほぼ完成してからぼーっとAPI見てたらContent Scriptの存在に気づいて、ちょっとやってみたけど<a href="http://dev.screw-axis.com/doc/chrome_extensions/guide/message_passing/" target="_blank">メッセージの送受信</a>がうまくいかずとりあえず上の流れで公開したっていう感じです。そのうち修正したい。<br>
<br>
というわけで、以下具体的に。</p>

<h3>1.今開いているページのURLを取得</h3>
<pre class="brush: jscript; title: ;">
/* manifest.json */

{
  ・・・
  &quot;browser_action&quot;: {
    &quot;default_icon&quot;: &quot;icon.png&quot;,
    &quot;popup&quot;: &quot;popup.html&quot;
  },
  &quot;permissions&quot;: [
		&quot;tabs&quot;,
		・・・
  ],
	・・・
}
</pre>

<p>まずはmanifest.jsonにchrome.tabsの使用を宣言しなければなりません。permissionsの中にtabsを足します。</p>

<pre class="brush: jscript; title: ;">
/* popup.html */

chrome.tabs.getSelected(null, function(tab) {
	var url = tab.url;
	・・・
});
</pre>

<p>すると、上記のようにchrome.tabs.getSelectedでタブ情報を取ってこれます。tabの中に各種情報が入っていて、tab.urlで現在開いているタブのURLが取れるという具合です。</p>

<h3>2.取得したURLのHTMLデータを取得</h3>

<pre class="brush: jscript; title: ;">
/* popup.html */

var req = new XMLHttpRequest();
var loading = false;

chrome.tabs.getSelected(null, function(tab) {
  var url = tab.url;
	req.open(&quot;GET&quot;,url,true);
	req.onreadystatechange = sorceget;
	req.send(null);
});

function sorceget(){
	if (req.readyState == 4 &amp;amp;&amp;amp; req.status == 200){
		//読み込みが成功した時の処理
		・・・
	}
	else{
		//読み込み中、もしくは読み込みに失敗した時の処理
		
		if(loading == false){
			//ロード中画像を表示
			loadingImg = document.createElement('img');
			loadingImg.id = &quot;loading&quot;;
			loadingImg.src = &quot;loading.gif&quot;;
				
			var objBody = document.getElementsByTagName(&quot;body&quot;).item(0);
  	  objBody.appendChild(loadingImg);
			
			loading = true;
		}
	}
}
</pre>

<p>今度は先程取得したURLの中身をXMLHttpRequestで引っ張ってきます。<br>
req.openの第一引数は取得のmethod、第二引数は対象のURL、第三引数はとりあえず省略orTRUEでOK。<br>
req.onreadystatechangeは、読み込みのステータスが変わる度に呼ばれるfunctionです。if (req.readyState == 4 && req.status == 200)で読み込み完了かつ読み込み成功の時の処理を書きます。elseにロード中の画像を表示するスクリプトを書いとくといい感じな気がします。<br>
req.sendはURLにGETかPOSTで値を渡すのに使うらしい。渡さなくてもnullでsendしとかんとあかんらしい。<br>
その他詳細は<a href="http://ponpon-village.net/ajax/xmlhttp.htm" target="_blank">こちら</a>。<br>
<br>
あとはこれで持ってきたHTMLデータから正規表現でタイトルを抜き取れば第一段階完了。ただamazonがちょっとタイトル部分のソースを変えたらアウト。</p>

<h3>3.楽天ブックス総合検索APIに取得したタイトルを投げる</h3>
<p>まあ、ここはいいでしょう。XMLHttpRequestで別オブジェクトを作ってクエリを投げれば結果が返ってきます。<br>
APIの詳細は<a href="http://webservice.rakuten.co.jp/api/bookstotalsearch/" target="_blank">こちら</a>。</p>

<pre class="brush: jscript; title: ;">
/* popup.html */

items = eval('[' + api.responseText + ']')[0]['Body']['BooksTotalSearch']['Items']['Item'];
</pre>

<p>ただ、返ってきたJSONをオブジェクトに変換する方法を知らなかったのでメモ。<br>
ちなみにこのコードでapi.responseTextに入ってた検索結果の商品がitemsに配列で入るので、forしてitems[i]['title']でタイトルが取れたり、そんな感じです。<br>
これを一行で済ませてしまわずに検索結果が空やった時の分岐とかを本来は作らないといけないとは思う。まあそのうち。</p>

<h3>4.楽天APIの検索結果を表示</h3>
<p>今回は、popup.htmlにdocument.writeでガリガリHTML書いてその中にitemsのデータをいちいち埋め込んでってやりました。<br>
スマートじゃないけど、とりあえず表示できればOK。<br>
中にtarget="_blank"のリンクを埋め込んだら普通に新規タブで開いてくれました。<br>
現在のタブに表示させようとするとちょっとめんどいっぽい？</p>

<h3>これからやりたいこと</h3>
<p>そんな感じで、なんとか動くものができました。やるかどうかはわからんけどこれからやりたいことを。<br>
<br>
1.やっぱりContent&nbsp;ScriptとMessageを使いこなしたい。<br>
2.検索結果が無かった時の分岐。<br>
3.2の結果が空やった時、もしくは違う商品しか表示されなかった時のために「お探しの商品が見つかりませんか？」的なメッセージと共に検索ワードを手で変えて検索し直せるようにしたい。<br>
4.ブラウザアクションじゃなくページアクションで。<br>
5.商品ジャンルも取ってきてAPIを使い分ければ本やCD以外の商品も楽天市場APIとかで取ってこれるかも。<br>
6.デザインを綺麗に。<br>
<br>
とりあえずこんな感じかなあ。<br>
1.は言わずもがな。HTML通信を一回減らせるのは大きい。今回はbackground_pageを使ってないので、amazonのページが表示された瞬間に裏側でContent&nbsp;ScriptがDOM解析でタイトルを取ってきて、裏側で検索かけつつ、アイコンをクリックしたら一瞬でpopupに表示されるとかできたら素敵。できるんかな。<br>
2.は、今ロード中画像がぐるぐる出続けるだけやし。バグと言っていいぐらい。<br>
3.は実際取ってこれへん商品もちょこちょこあるし、余計な文言を省いて検索し直せたらもっと便利。<br>
4.はよくわからずにブラウザアクション（右側にアイコンがずっと出てるやつ）で作ったけど、amazonの詳細ページでしか使えないのでページアクション（特定のサイトを見てる時だけアドレスバーの中の右端にアイコンが出るやつ）の方がいいかも。<br>
5.は、今はブックスのAPIしか使ってませんがamazonも色んな商品置いてるので、使える幅が増えたらいいなあ。<br>
6.は、誰かデザインして・・・<br>
<br>
はじめてのChrome拡張ということでまだまだ基本機能ができただけですが、ちまちまアップデートしていくかもしれないのでよかったら使ってみてください。<br>
<br>
<a href="https://chrome.google.com/extensions/detail/dedhofliljhpidkfhdlocoecddblhidc?hl=ja#" target="_blank">Amazon to Rakuten</a></p>]]></content:encoded>
			<wfw:commentRss>http://www.muratayusuke.com/2010/10/24/amazon_to_rakuten/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

