<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'><id>tag:blogger.com,1999:blog-1303966254927872449</id><updated>2008-08-18T21:10:56.046+09:00</updated><title type='text'>守破離でいこう! -Let's go with SyuHaRi!-</title><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default?start-index=26&amp;max-results=25'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>49</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-9147913725208734958</id><published>2008-08-18T21:01:00.001+09:00</published><updated>2008-08-18T21:09:42.192+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Rails で、一つのフォームで複数のモデルを扱う</title><content type='html'>&lt;p&gt;先日のRails講習で、いいことを学んだので忘れないうちにメモです・・。&lt;/p&gt;
&lt;p&gt;Rails では、基本、一つの Form に一つの Model なのですが、 fields_for というヘルパーを使用することで複数のモデルを扱えます。&lt;/p&gt;
&lt;p&gt;一対一のモデルを一度に更新する場合などに使えそうです。&lt;/p&gt;

&lt;blockquote cite="http://api.rubyonrails.org/"&gt;
  &lt;pre&gt;&lt;code&gt;  &amp;lt;% form_for @person, :url =&amp;gt; { :action =&amp;gt; "update" } do |person_form| %&amp;gt;
    First name: &amp;lt;%= person_form.text_field :first_name %&amp;gt;
    Last name : &amp;lt;%= person_form.text_field :last_name %&amp;gt;

    &amp;lt;% fields_for @person.permission do |permission_fields| %&amp;gt;

      Admin?  : &amp;lt;%= permission_fields.check_box :admin %&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;参考：&lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#M001741" title="Module: ActionView::Helpers::FormHelper" target="_blank"&gt;Rails Framework Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/08/rails.html' title='Rails で、一つのフォームで複数のモデルを扱う'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=9147913725208734958' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/9147913725208734958'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/9147913725208734958'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-3528405208755304258</id><published>2008-07-17T20:43:00.001+09:00</published><updated>2008-07-18T11:56:17.198+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Rails 2.1.0 をインストールしてみる</title><content type='html'>&lt;p&gt;久しぶりに、Railsの環境を更新しようと下記のコマンドこ実行すると、エラーがでました。&lt;br /&gt;やはり、すんなりといきませんねぇ～。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&gt; gem update --system
&gt; gem update rails
Updating installed gems
Updating rails
ERROR:  While executing gem ... (Gem::InstallError)
 invalid gem format for c:/ruby/lib/ruby/gems/1.8/cache/activesupport-2.1.0.gem&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://www.google.co.jp/search?hl=ja&amp;newwindow=1&amp;rlz=1B3GGGL_ja___JP283&amp;q=invalid+gem+format+activesupport-2.1.0.gem&amp;btnG=%E6%A4%9C%E7%B4%A2&amp;lr=" title="Google Search" target="_blank"&gt;いろいろ調べてみる&lt;/a&gt;と、activesupportを手動でインストールすることで解決できるようです。&lt;/p&gt;
&lt;blockquote cite="http://weblog.rubyonrails.org/2008/6/1/rails-2-1-time-zones-dirty-caching-gem-dependencies-caching-etc"&gt;
 &lt;p&gt;http://rubyforge.org/projects/activesupport/&lt;/p&gt;
 &lt;p&gt;Download gem to a local file, then&lt;/p&gt;
 &lt;p&gt;sudo gem install --local activesupport-2.1.0.gem&lt;/p&gt;
 &lt;p&gt;All fixed.&lt;/p&gt;

 &lt;p&gt;参考：&lt;a href="http://weblog.rubyonrails.org/2008/6/1/rails-2-1-time-zones-dirty-caching-gem-dependencies-caching-etc" title="Riding Rails: Rails 2.1: Time zones, dirty, caching, gem dependencies, caching, etc" target="_blank"&gt;Riding Rails: Rails 2.1: Time zones, dirty, caching, gem dependencies, caching, etc&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/07/rails-210.html' title='Rails 2.1.0 をインストールしてみる'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=3528405208755304258' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/3528405208755304258'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/3528405208755304258'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-4512350725426617097</id><published>2008-07-10T22:06:00.001+09:00</published><updated>2008-07-10T23:55:03.833+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>MySQLで行番号</title><content type='html'>&lt;img src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/mysql_get_row_number-795347.gif" border="0" alt="MySQLで行番号を表示したいです。" /&gt;
&lt;p&gt;データベースにMySQLを使っていて、INSERTするときにソート番号を得たいときなど、「SELECT時に行番号を表示したい」ということがたまにあります。&lt;/p&gt;
&lt;p&gt;ずいぶん昔にそんなことがあって、その時は、一度Excelにコピペして、行番号をつけて、マクロでSQL文を生成、なんてことをやったのですが、MySQLではユーザ変数をつかうことで行番号を表示することが可能でした。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&gt; set @i=0;
mysql&gt; select (@i:=@i+10) as position, name from users;
+----------+-------+
| position | name  |
+----------+-------+
|       10 | hoge1 |
|       20 | hoge2 |
|       30 | hoge3 |
|       40 | hoge4 |
|       50 | hoge5 |
+----------+-------+&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;変数なんて初めて知りました（汗&lt;/p&gt;
&lt;blockquote cite="http://dev.mysql.com/doc/refman/5.1/ja/user-variables.html"&gt;
 &lt;p&gt;ユーザによって定義された変数に値を保存し、後で参照することができます。これで1つのステートメントから次のステートメントに変数を移行させることができます。&lt;/p&gt;
 &lt;p&gt;&lt;a href="http://dev.mysql.com/doc/refman/5.1/ja/user-variables.html" title="MySQL ::   MySQL 5.1 リファレンスマニュアル :: 8.4 ユーザによって定義された変数" target="_blank"&gt;MySQL ::   MySQL 5.1 リファレンスマニュアル :: 8.4 ユーザによって定義された変数&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/07/mysql.html' title='MySQLで行番号'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=4512350725426617097' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/4512350725426617097'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/4512350725426617097'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-5733343896962769517</id><published>2008-06-12T22:11:00.000+09:00</published><updated>2008-06-13T00:52:23.206+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GDD08'/><category scheme='http://www.blogger.com/atom/ns#' term='GDD08JP'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Google Developer Day 2008</title><content type='html'>&lt;p&gt;今年もGoogle Developer Dayに参加してきました。&lt;/p&gt;
&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0775-772685.JPG" title="Google Developer Day 2008 GDD2008"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0775-771808.JPG" border="0" alt="Google Developer Day 2008 GDD2008" title="Google Developer Day 2008 GDD2008" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;基調講演では、Client / Connectivity / Cloud という「3つのC」が次世代Webのキーワードで、Googleではそれぞれ、Gears / Android / Google App Engine を提供しているというお話でした。&lt;/p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0778-782564.JPG" title="Google Developer Day 2008 GDD2008"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0778-782027.JPG" border="0" alt="Google Developer Day 2008 GDD2008" title="Google Developer Day 2008 GDD2008" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0805-748137.JPG" title="Google Developer Day 2008 GDD2008"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0805-747614.JPG" border="0" alt="Google Developer Day 2008 GDD2008" title="Google Developer Day 2008 GDD2008" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0781-799981.JPG" title="Google Developer Day 2008 GDD2008"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0781-799437.JPG" border="0" alt="Google Developer Day 2008 GDD2008" title="Google Developer Day 2008 GDD2008" /&gt;&lt;/a&gt;
&lt;p&gt;どれもまだ触っていないので、近いうちに弄ってみたいと思います。&lt;/p&gt;
&lt;p&gt;午後のセッションでは、今回はコードラボ（その場で実際にコーティングしながらアプリを作る）という新しいタイプのセッションがあったので、最近流行りのOpenSocialのコードラボに参加しました。&lt;/p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0794-719719.JPG" title="Google Developer Day 2008 GDD2008"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_0794-719173.JPG" border="0" alt="Google Developer Day 2008 GDD2008" title="Google Developer Day 2008 GDD2008" /&gt;&lt;/a&gt;
&lt;p&gt;どうせなら前回とはちょっと違ったものにと軽い気持ちだったのですが、OpenSocialをどのように利用するのかいまいちピンと来ていなかった自分には、他のメンバーとディスカッションすることができて、非常に参考になりました。&lt;/p&gt;
&lt;p&gt;作成するアプリのアイディアは次のようなもの&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;自分と友達で動物占い&lt;/li&gt;
    &lt;li&gt;友達にタグ付&lt;/li&gt;
    &lt;li&gt;チャット&lt;/li&gt;
    &lt;li&gt;フィードリーダー&lt;/li&gt;
    &lt;li&gt;地図から友達検索&lt;/li&gt;
    &lt;li&gt;足跡&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;いくつかのグループに分かれてアプリを作成するのですが、上記の中でも一番ソーシャルっぽい占いアプリに参加&lt;/p&gt;
&lt;p&gt;短い時間で、適当に作業担当を決めて作成していくのですが、みんなでワイワイやるといった感覚は普段はまったくないので、非常に新鮮な体験でした。作り終えたときの感動は格別です。&lt;/p&gt;
&lt;p&gt;予習の際、デバッグにひどく苦労したのですが、コードラボではGoogleの方に教えて頂いた、CodeRunnerというアプリのおかげで、すぐさまTry and Errorができ非常に助かりました。&lt;/p&gt;
&lt;p&gt;それと、いろいろなAPIが提供されているOpenSocialですが、やはり（APIだけでは）難しいところは、サーバ側で処理するのが常套手段とのことでした。&lt;/p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/file-734023.png" title="Google Developer Day 2008 GDD2008"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/file-734018.png" border="0" alt="Google Developer Day 2008 GDD2008" title="Google Developer Day 2008 GDD2008" /&gt;&lt;/a&gt;
&lt;p&gt;前回とはまた違った刺激を受けることができました。是非また参加したいと思いました。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/06/google-developer-day-2008.html' title='Google Developer Day 2008'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=5733343896962769517' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/5733343896962769517'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/5733343896962769517'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-8680314062018635761</id><published>2008-05-09T20:57:00.005+09:00</published><updated>2008-05-28T16:37:52.030+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='greasemonkey'/><category scheme='http://www.blogger.com/atom/ns#' term='gmail'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Gmail 2.0 で複数の署名を切り替える</title><content type='html'>&lt;a href="http://mail.google.com/mail" title="Gmail" target="_blank"&gt;&lt;img src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/gmail-767162.jpg" alt="Gmail" border="0" /&gt;&lt;/a&gt;&lt;a href="http://d.hatena.ne.jp/re_guzy/20070904/p1" title="Gmail にテンプレート切り替え機能を付けてみた - 記憶は削除の方向で" target="_blank"&gt;&lt;img src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/GTS-767172.png" alt="Gmail Template Switch" border="0" /&gt;&lt;/a&gt;
&lt;p&gt;僕は普段Gmailを使っていて、&lt;a href="http://firefox.geckodev.org/index.php?Greasemonkey" title="Greasemonkey - Mozilla Firefox まとめサイト" target="_blank"&gt;Greasemonkey&lt;/a&gt;スクリプトの「Gmail Template Switch」を愛用しています。&lt;/p&gt;
&lt;p&gt;複数のアカウントで使用したり、差出人によって署名・挨拶文を変えたい場合に非常に便利なGreasemonkeyスクリプトです。&lt;/p&gt;
&lt;span&gt;Gmail Template Switch&lt;/span&gt;
&lt;blockquote cite="http://d.hatena.ne.jp/re_guzy/20070904/p1"&gt;
    &lt;p&gt;大抵皆そうなんだろうけど、メールを書く場合決まった形があって、あいさつ文・本文・締め言葉・署名という順番で書いている。以前作ったスクリプト で、署名は差出人に応じて自動的に切り替わるようになったけど、あいさつ文や締め言葉は辞書に登録したりして、毎回入力してたわけです。会社で使っていることもあり、社内と社外で定型文が変わってくるので、辞書に登録した語句を忘れたりしてかなり不便だった。これはさすがに面倒なので、さらに Gmail を快適にすべく、テンプレートを切り替えられる Greasemonkey スクリプトを作ってみた。これで署名が複数あっても、定型文が複数あっても平気ですな。&lt;/p&gt;
    &lt;p&gt;&lt;a href="http://d.hatena.ne.jp/re_guzy/20070904/p1" title="Gmail にテンプレート切り替え機能を付けてみた - 記憶は削除の方向で" target="_blank"&gt;Gmail にテンプレート切り替え機能を付けてみた - 記憶は削除の方向で&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;しかしながら、Gmail Template Switch は、Gmail 2.0では動作しないため、動作が快速でラベルの色分け等便利な機能は我慢し、日本語版Gmailを使っていたわけですが、先日ついに、日本語版Gmailも 2.0になってしまいました。&lt;/p&gt;
&lt;p&gt;というわけで、Gmail Template Switch を、Gmail 2.0 で動作するようにハックしてみました。&lt;/p&gt;
&lt;p&gt;&lt;a style="
font-weight: bold;
font-family: inherit;
text-align: center;
color: #2b333c;
background: orange;
border: 1px solid;
border-color: #c4cccc #acb5b5 #6f7777 #acb5b5;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=0, OffY=1, Color=#AAAAAA);
box-shadow: 0 1px 2px #AAA;
padding: .2em 1.5ex;
_padding: .2em 2ex;
_width/**/:1px;
_white-space: nowrap;
overflow: visible; /* fix button width for IE 5.5 to 7 */
cursor: pointer;
_cursor: hand;" href="http://userscripts.org/scripts/show/26426" target="_blank"&gt;Gmail Template Switcher - v 2.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;既存のデータは生かしたかったので、コードはほとんど流用しています。なお、jQueryの使用で意味不明なエラーが出たので、使わないようにハックしてます。&lt;br /&gt;
また、Gmail 2.0は、Greasemonkeyを正式にサポートするらしく、&lt;a href="http://code.google.com/p/gmail-greasemonkey/wiki/GmailGreasemonkey10API" title="GmailGreasemonkey10API - gmail-greasemonkey - Google Code" target="_blank"&gt;GmailGreasemonkey10API&lt;/a&gt;が公開されているので、せっかくなので使ってみました。&lt;/p&gt;
&lt;p&gt;はじめは軽い気持ちでやってみたのですが、Greasemonkeyは初めて、かつ、Gmail 2.0 になってずいぶん変わっているようで、とても苦戦しました・・・（汗&lt;/p&gt;
&lt;p&gt;とくに、ElementのIDがランダムで変わるのと、Reply時の入力フォームが動的に挿入される点でかなりはまりました・・。&lt;/p&gt;
&lt;p&gt;なお、操作方法等は&lt;a href="http://d.hatena.ne.jp/re_guzy/20070904/p1" title="Gmail にテンプレート切り替え機能を付けてみた - 記憶は削除の方向で" target="_blank"&gt;オリジナル&lt;/a&gt;とかわりません。もちろん新しい機能なんてありませんｗ&lt;/p&gt;
&lt;p&gt;以下、コード全文です。&lt;/p&gt;
&lt;p style="font-size:.8em;color:green;"&gt;※最新のコードは&lt;a href="http://userscripts.org/scripts/review/26426" title="Source for &amp;quot;Gmail Template Switcher - v 2.0&amp;quot; &amp;ndash; Userscripts.org" target="_blank"&gt;こちら&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;gmailtemplateswitcherv20.user.js&lt;/span&gt;
&lt;pre&gt;&lt;code class="prettyprint"&gt;// ==UserScript==
// @name           Gmail Template Switcher - v 2.0
// @namespace      http://www.r-stone.net/blogs/ishikawa
// @description    Append the function to apply the mail template, when writing a mail. Modify source code of [Gmail Template Switch] from http://d.hatena.ne.jp/re_guzy by re_guzy
// @version        0.1.20080510.0
// @include        http://mail.google.com/*
// @include        https://mail.google.com/*
// @exclude        http://mail.google.com/mail/help/*
// @exclude        https://mail.google.com/mail/help/*
//
// Copyright (c) 2007-2008, re_guzy &amp;lt;goodspeed.xii@gmail.com&amp;gt;
// Distributed under the MIT license
// http://opensource.org/licenses/mit-license.php
// http://sourceforge.jp/projects/opensource/wiki/licenses%2FMIT_license
//
// Notice : To Uninstall this script, remove "gtssettings0-9" from Gmail contact list.
// Feature: When writing a mail, append a combobox to action. By selecting action,
//          apply template to mail, or add template or remove template. Template
//          is saved to "contact list" named starting with "gtssettings".
// Require: Greasemonkey 0.7.20080121.0
// ==/UserScript==

const DEBUG = false;
const KEY_TOKEN = "gts_token";
const KEY_CACHE = "gts_cache";
const CONTACT_NAME = "gtssettings";
const CONTACT_ID_RE = /\["\w+","(\w+)","gtssettings\d","gtssettings\d",/;
const MSGBODY_RE = /([\s\S]*)\n?(?:^---)(\n[\s\S]+)/m;
const LOCATION_RE = /(https?:\/\/[^\/]+\/(a\/[^\/]+\/)?).*/;
const SELECTOR = {
  'gts' : 'descendant::*[local-name() = "select" or local-name() = "SELECT"][@id = "id_gts_template"]',
  'input_form' : 'descendant::*[local-name() = "form" or local-name() = "FORM"]',
  'body' : 'descendant::*[local-name() = "textarea" or local-name() = "TEXTAREA"][@name = "body"]',
  'subject' : 'descendant::*[local-name() = "input" or local-name() = "INPUT"][@name = "subject"]',
  'to' : 'descendant::*[local-name() = "textarea" or local-name() = "TEXTAREA"][@name = "to"]',
  'from' : 'descendant::*[local-name() = "select" or local-name() = "SELECT"][@name = "from"] | descendant::*[local-name() = "input" or local-name() = "INPUT"][@name = "from"]',
  'cc' : 'descendant::*[local-name() = "textarea" or local-name() = "TEXTAREA"][@name = "cc"]',
  'bcc' : 'descendant::*[local-name() = "textarea" or local-name() = "TEXTAREA"][@name = "bcc"]',
  'gts_undo' : 'descendant::*[contains(concat(" ",@class," "), " gts_undo_option ")]',
  'gts_first' : 'descendant::*[contains(concat(" ",@class," "), " gts_option_first ")]',
  'discard' : 'descendant::*[local-name() = "button" or local-name() = "BUTTON"][count(preceding-sibling::*[local-name() = "button" or local-name() = "BUTTON"]) = 2]',
  'labels' : 'descendant::*[local-name() = "span" or local-name() = "SPAN"]',
  'msg' : 'div/div/div/div/div/div/div/div/div/div/div[3]/div/div[2]/div[2]/div/div[2]/div[2]/div/table/tbody/tr[2]/td[2]'
}

var T = new Array(10);
T[0] = { 'id' : -1, 'num' : 0 };
for (var i=1;i &amp;lt; T.length;i++) {
  T[i] = {
    'id' : -1,
    'num' : i,
    'from' : '',
    'to' : '',
    'cc' : '',
    'bcc' : '',
    'subject' : '',
    'body' : '',
    'body_latter' : ''
  }
}
var Ja = false;
var recentView;

//Initialize gmail and gmonkey objects
window.addEventListener('load', function() {
  if (unsafeWindow.gmonkey) {
    unsafeWindow.gmonkey.load('1.0', function(gmail) {

      function getViewType() {
        var str = '';
        switch (gmail.getActiveViewType()) {
          case 'tl': str = 'Threadlist'; break;
          case 'cv': str = 'Conversation'; break;
          case 'co': str = 'Compose'; break;
          case 'ct': str = 'Contacts'; break;
          case 's': str = 'Settings'; break;
          default: str = 'Unknown';
        }
        return str;
      }

      function getView() {
        return gmail.getActiveViewElement();
      }

      function getMailForm() {
        var a = xpath(SELECTOR['input_form'], getView());
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getGts() {
        var a = xpath(SELECTOR['gts'], getView());
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getGtsOptUndos() {
        return a = xpath(SELECTOR['gts_undo'], getGts());
      }

      function getGtsOptFirst() {
        var a = xpath(SELECTOR['gts_first'], getGts());
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getFrom(form) {
        var a = xpath(SELECTOR['from'], form || getMailForm());
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getTo(form) {
        var a = xpath(SELECTOR['to'], form || getMailForm());
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getCc(form) {
        var a = xpath(SELECTOR['cc'], form || getMailForm());
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getBcc(form) {
        var a = xpath(SELECTOR['bcc'], form || getMailForm());
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getSubject(form) {
        var a = xpath(SELECTOR['subject'], form || getMailForm());
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getBody(form) {
        var a = xpath(SELECTOR['body'], form || getMailForm());
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getDiscard() {
        var a = xpath(SELECTOR['discard'], getMailForm().parentNode.parentNode);
        if (a &amp;&amp; a.length &amp;gt; 0) {
          return a[0];
        } else {
          return null;
        }
      }

      function getLabels(form) {
        return a = xpath(SELECTOR['labels'], form || getMailForm());
      }

      function getCcLabel() {
        var a = getLabels();        
        for (var i=0; i&amp;lt;a.length; i++) {
          if (/Cc/.exec(a[i].innerHTML)) {
            return a[i];
          }
          if (i&amp;gt;1) {
            break;
          }
        }
        return null;
      }

      function getBccLabel() {
        var a = getLabels();        
        for (var i=0; i&amp;lt;a.length; i++) {
          if (/Bcc/.exec(a[i].innerHTML)) {
            return a[i];
          }
          if (i&amp;gt;1) {
            break;
          }
        }
        return null;
      }

      function getChangeLabel() {
        var a = getLabels();        
        for (var i=0; i&amp;lt;a.length; i++) {
          if (/change/.exec(a[i].innerHTML) || /変更/.exec(a[i].innerHTML)) {
            return a[i];
          }
          if (i&amp;gt;1) {
            break;
          }
        }
        return null;
      }

      function switcher() {
        str = getViewType();

        if (str != "Compose" &amp;&amp; str != "Conversation") {
          if (recentView) {
            recentView.removeEventListener('DOMNodeInserted', nodeInsertedHandler, false);
          }
          return;
        }
        recentView = getView();
        window.setTimeout(function() {
          initialize();
        }, 600);
      }

      function nodeInsertedHandler(event) {
        target = event.target;
        if (target.nodeType == 1) {
          tagName = target.tagName.toLowerCase();
          if (tagName == 'form') {
            log('form inserted');
            window.setTimeout(function() {
              initialize();
            }, 500);
            //getView().removeEventListener('DOMNodeInserted', nodeInsertedHandler, false);
          } else if (tagName == 'table') {
            log('table inserted');
            window.setTimeout(function() {
              initialize();
            }, 500);
            //getView().removeEventListener('DOMNodeInserted', nodeInsertedHandler, false);
          }
        }        
      }

      function initialize() {
        log('initialize');

        try {

          if (getGts()) {
            log('already initialized');
            return;
          }

          var form = getMailForm();
          if (!form) {
            log('form not found');
            getView().addEventListener('DOMNodeInserted', nodeInsertedHandler, false);
            return;
          }
          //getView().removeEventListener('DOMNodeInserted', nodeInsertedHandler, false);

          var discard_button = getDiscard();
          var label = discard_button.innerHTML;
          Ja = (label == '破棄');
          discard_button.parentNode.insertBefore(createSelectElement(), discard_button.nextSibling);
          composeCommand(form);
        } catch(e) {
          log('add combobox failure because : ' + e);
          return;
        }
      }

      function createSelectElement() {
        var content = document.createElement('select');
        content.setAttribute("id", "id_gts_template");
        content.setAttribute("style", "margin-left:10px;font-size:.8em;");
        content.innerHTML = toOption('please wait...' , false , true);
        content.addEventListener('change', function(event) {doCommand(event.target)}, true);
        return content
      }

      function toOption(text, value, selected, cls, isDom) {
        if (isDom) {
          var attr = {
            'style' : value ? null : 'color: rgb(119, 119, 119);',
            'disabled' : value ? null : 'disabled',
            'selected' : selected ? 'selected' : null,
            'value' : value ? value : null,
            'class' : cls ? cls : null
          };
          var elm = document.createElement('option');
          for (var i in attr) {
            if (attr[i]) {
              elm.setAttribute(i, attr[i]);
            }
          }
          elm.innerHTML = text;
          return elm;
        } else {
          var attr = {
            'style' : value ? null : '"color: rgb(119, 119, 119);"',
            'disabled' : value ? null : '"disabled"',
            'selected' : selected ? '"selected"' : null,
            'value' : value ? '"' + value + '"' : null,
            'class' : cls ? cls : null
          };
          var a = [];
          for (var i in attr) {
            if (attr[i]) {
              a.push(i + "=" + attr[i]);
            }
          }
          return "&amp;lt;option " + a.join(' ') + "&amp;gt;" + text + "&amp;lt;/option&amp;gt;";
        }
      }

      function composeCommand(form) {
        getTemplates(function /*parseTemplate*/(notes, use_cache) {
          for (var i in notes) {
            if (use_cache) {
              T[i] = notes[i];
            } else {
              var note = notes[i].note ? decode(notes[i].note, false) : "{}";
              try {
                T[notes[i].num] = eval(note);
                T[notes[i].num].id = notes[i].id;
                T[notes[i].num] = decode(T[notes[i].num], true);
              } catch(e) {log("eval failed : " + e);}
            }
          }

          recomposeSelectElement(form);
          applyDefault(form);
          if (notes.length == 0) {
            save();
          } else if (!use_cache) {
            var caches = [];
            for (var i in T) {
              var encoded = encode(T[i], true);
              if (encoded) {
                caches.push(encoded.toSource());
              }        
            }
            GM_setValue(KEY_CACHE, "[" + caches.join(", ") + "]");
          }
        });
      }

      function applyDefault(form) {
        var from = getFrom();
        if (from) {
          var fromvalue = from.value;
          matched = grep(T, function(i) {
            return (i.name + '').indexOf('#') == 0 &amp;&amp; i.from == fromvalue;
          });
          if (matched.length &amp;gt; 0) {
            applyTemplate(matched[0].num, form);
          }
        }
      }

      function recomposeSelectElement(form) {
        var options = [];
        options.push(toOption(trans("Template actions...") , "init" , true, "gts_option_first"));

        var enables = grep(T, function(o) {return (o.name &amp;&amp; o.num != 0);});
        var expand = function(arrays, cmd) {
          var hash = {};
          for (var i in enables) {
            hash[enables[i].from] = hash[enables[i].from] || [];
            hash[enables[i].from].push(enables[i]);
          }
          for (var i in hash) {
            arrays.push(toOption('&amp;nbsp;&amp;lt;' + (i || trans('No from')) + '&amp;gt;'));
            for (var j in hash[i]) {
              arrays.push(toOption('&amp;nbsp;&amp;nbsp;' + hash[i][j].name , cmd + '_' + hash[i][j].num));
            }
          }
        };

        var tmp = [
          {'cmd':'apply', 'exp':trans("Apply"), 'func':expand},
          {'cmd':'add','exp':trans("Append"), 'func':function(arrays, cmd) {
            var used = grep(T , function(o) { return (o.name &amp;&amp; o.num != 0) });
            if (used.length &amp;lt; 9) {
              arrays.push(toOption('&amp;nbsp;&amp;nbsp;' + trans("Includes from") , cmd));
              arrays.push(toOption('&amp;nbsp;&amp;nbsp;' + trans("Excludes from") , cmd + '_ignore_from'));
            } else {
              arrays.push(toOption('&amp;nbsp;&amp;nbsp;' + trans('Quantity limit is 9')));
            }
          }},
          {'cmd':'delete','exp':trans('Remove'), 'func':expand}
        ];
        for (var i in tmp) {
          if (tmp[i].func != expand || enables.length != 0) {
            options.push(toOption('-------'));
            options.push(toOption(trans('verbs', tmp[i].exp) + ':'));
            tmp[i].func(options, tmp[i].cmd);
          }
        }

        var gts = getGts();
        gts.innerHTML = options.join('');
        gts.value = 'init';
      }

      function doCommand(selectNode) {
        var form = getMailForm();
        if (form) {
          if (selectNode.value == 'add') {
            addTemplate(form, true);
          } else if (selectNode.value == 'add_ignore_from') {
            addTemplate(form, false);
          } else if (selectNode.value.match(/apply_(\d+)/)) {
            applyTemplate(RegExp.$1 , form);
          } else if (selectNode.value.match(/delete_(\d+)/)) {
            deleteTemplate(RegExp.$1 , form);
          } else if (selectNode.value == 'undo') {
            if (unsafeWindow.gts_undo) { unsafeWindow.gts_undo(); }
          }
          selectNode.value= 'init';
        } else {
          log('form not found');
        }
      }

      function addTemplate(form, contain_from) {
        var m = trans('Please input the template name.');
        if (contain_from) {
          m += '\n';
          m += trans('If the name is started from "#", it becomes default of the corresponding "from".');
        }
        var name = window.prompt(m, "");
        if (!name) {return;}
        if (grep(T , function(o) { return (o.name == name) }).length &amp;gt; 0) {
          alert(trans('The name already exists.'));
          return;
        }

        var empties = grep(T , function(o) { return (o.name || o.num == 0) }, true);
        var t = empties[0];

        var to = getTo(form);
        var b = getBody(form);
        var c = getCc(form);
        var bc = getBcc(form);
        var f = getFrom(form);
        var s = getSubject(form);
        T[t.num] = {
          'num' : t.num,
          'id' : t.id,
          'name' : name,
          'from' : contain_from ? f.options[f.selectedIndex].value : "",
          'to' : to.innerHTML,
          'cc' : c.innerHTML,
          'bcc' : bc.innerHTML,
          'subject' : s.value,
          'body' : b.innerHTML
        };

        if (MSGBODY_RE.exec(T[t.num].body)) {
          T[t.num].body = RegExp.$1;
          T[t.num].body_latter = RegExp.$2;
        }

        editContact(T[t.num], function() {
          recomposeSelectElement(form);
          msg(trans('appended', name));
        });
      }

      function applyTemplate(num , form) {
        if (typeof T[0]._init == 'undefined') {
          T[0]._init = {};
          var selectors = x('f,s,t,c,bc,bo');
          for (var i in selectors) {
            var j = xpath(selectors[i], form)[0];
            T[0]._init[j.name] = trim(j.value);
          }
        }

        var selectors = x('f,s,t,c,bc');
        for (var i in selectors) {
          var j = xpath(selectors[i], form)[0];
          var tmp = T[0]._init[j.name];
          if (j.name != 'from' || j.value != T[num][j.name]) {
            if (j.type == 'textarea') {
              var targetaddrs = tmp || "";
              //既に含まれているものは追加しない
              var notcontains = grep(T[num][j.name].split(','), function(i) {
                if (trim(i).length == 0) { return; }//空白は無視
                if (/([\w\.+-]+@[\w+-]+(\.[\w+-]+)+)/.exec(i)) {
                  return targetaddrs.indexOf(RegExp.$1) &amp;lt; 0;
                } else {
                  return targetaddrs.indexOf(i) &amp;lt; 0;
                }
              });
              if (tmp) { notcontains.unshift(tmp); }
              j.value = notcontains.join(', ');
            } else {
              j.value = (j.name == 'subject' &amp;&amp; tmp) ? tmp : T[num][j.name];
            }
            if (j.name == 'cc' &amp;&amp;  T[num][j.name]) {
              var cc_label = getCcLabel();
              if (cc_label) {
                emulate_click(cc_label);
              }
            }
            if (j.name == 'bcc' &amp;&amp;  T[num][j.name]) {
              var bcc_label = getBccLabel();
              if (bcc_label) {
                emulate_click(bcc_label);
              }
            }
          }
        }

        var change_label = getChangeLabel();
        if (change_label) {
          emulate_click(change_label);
        }

        var b = getBody(form);
        b.value = grep([
          T[num].body, T[0]._init.body, T[num].body_latter
        ], function(i) { return i; }).join("\n\n");

        var selectors = x('bo,s,t');
        for (var i in selectors) {
          var j = xpath(selectors[i], form)[0];
          if ((j.name == 'body') || !j.value) {
            j.focus();
            j.selectionStart = 0;
            j.selectionEnd = 0;
          }
        }

        var undos = [
          toOption('-------', null, null, "gts_undo_option", true),
          toOption('&amp;nbsp;&amp;nbsp;' + trans("Undo"), 'undo', null, "gts_undo_option", true)
        ];
        var gts_undos = getGtsOptUndos();
        for (var i=0; i&amp;lt;gts_undos.length; i++) {
          gts_undos[i].parentNode.removeChild(gts_undos[i]);
        }
        var gts_first = getGtsOptFirst();
        if (gts_first) {
          gts_first.parentNode.insertBefore( undos[1], gts_first.nextSibling );
          gts_first.parentNode.insertBefore( undos[0], gts_first.nextSibling );
        }

        msg(trans('applied', T[num].name), function() {
          undo(form);
          msg(trans("To apply template was canceled."));
        }, true);
      }

      function undo(form) {
        if (typeof T[0]._init != 'undefined') {
          var selectors = x('f,t,c,bc,bo');
          for (var i in selectors) {
            var j = xpath(selectors[i], form)[0];
            j.value = T[0]._init[j.name];
          }
        }
        delete T[0]._init;
        var gts_undos = getGtsOptUndos();
        for (var i=0; i&amp;lt;gts_undos.length; i++) {
          gts_undos[i].parentNode.removeChild(gts_undos[i]);
        }
      }

      function emulate_click(target) {
        if (target.dispatchEvent) {
          var e = unsafeWindow.document.createEvent("MouseEvents");
          e.initEvent("click", true, true);
          target.dispatchEvent(e);
        }
      }

      function deleteTemplate(num , form) {
        var name = T[num].name;
        if (confirm(trans('remove confirm', name)) != true) {
          return;
        }

        T[num] = {'id' : T[num].id, 'num' : num};
        editContact(T[num], function() {
          recomposeSelectElement(form);
          msg(trans("removed", name));
        });
      }

      function getTemplates(f_parseTemplate) {
        var queryUrl = 'mail/contacts/data/contacts?thumb=false&amp;groups=false&amp;show=ALL&amp;psort=Name&amp;max=300&amp;out=js&amp;rf=&amp;jsx=true';
        ajax(queryUrl, function(req){
          contactPage = req.responseText.replace('while (true); ', '').replace(/&amp;&amp;&amp;START&amp;&amp;&amp;([^&amp;&amp;&amp;]+)&amp;&amp;&amp;END&amp;&amp;&amp;/, "$1");
          response = eval("(" + contactPage + ")");
          if (response.Success) {
            var contacts = response.Body.Contacts;
            var notes = [];
            for(i=0; i&amp;lt;contacts.length; i++) {
              if (contacts[i].Name &amp;&amp; /gtssettings(\d)/.exec(contacts[i].Name) ) {
                num = RegExp.$1;
                note = contacts[i].Notes;
                id = contacts[i].Id;
                notes.push({'num' : num, 'note' : note, 'id' : id});
              }
            }
            var authtoken = response.Body.AuthToken.Value;
            GM_setValue(KEY_TOKEN, authtoken);
            f_parseTemplate(notes);
          } else {
            log("Contacts Request Failed: " + response.Errors[0].Text);
          }
        });
      }

      function encode(tmpl, by_escape) {
        if (by_escape) {
          var escaped = {};
          //encodeURIだと「'」がエンコードされないので、escapeを使う
          for (var i in tmpl) {
            if (i.indexOf('_') != 0) {
              escaped[i] = escape(tmpl[i]);
            }
          }
          return escaped;
        } else {
          //連絡先は「"」で囲まれるため、JSONデータを表すのに「"」を使えない。
          //連絡先から復元するときに使うデータを、REGEXでマッチさせるため「"」を「'」に置換しておく。
          return tmpl.toSource().replace(/\"/g, "'");
        }
      }

      function decode(tmpl, by_unescape) {
        if (by_unescape) {
          var unescaped = {};
          for (var i in tmpl) {
            unescaped[i] = unescape(tmpl[i]);
          }
          return unescaped;
        } else {
          //連絡先に格納するために、「'」に変換しておいた「"」を戻す
          return tmpl.replace(/\\'/g, "\"");
        }
      }

      function editContact(tmpl, f_completed) {
        var authtoken = GM_getValue(KEY_TOKEN);
        if (!authtoken) {
          log('token not found');
          return;
        }
        var escaped = encode(tmpl, true);
        var post_data = param({
          "token" : authtoken, 
          "tok" : authtoken,
          "out" : "js",
          "id" : tmpl.id,
          "action" : "SET",
          "Name" : CONTACT_NAME + tmpl.num,
          "Emails.0.Address" : CONTACT_NAME + tmpl.num + "@gmail.com",
          "Notes" : encode(escaped, false)
        });
        ajax("mail/contacts/update/contact", function(req) {
          var response = eval("(" + req.responseText.replace('while (true); ', '').replace(/&amp;&amp;&amp;START&amp;&amp;&amp;([^&amp;&amp;&amp;]+)&amp;&amp;&amp;END&amp;&amp;&amp;/, "$1") + ")");
          if (response.Success) {
            if (tmpl.id == -1) {
              if (CONTACT_ID_RE.exec(req.responseText)) {
                tmpl.id = RegExp.$1;
                editContact(tmpl, f_completed);
              }
            } else {
              if (tmpl.num != 0) {
                save(f_completed);
              } else {
                f_completed();
              }
            }
          } else {
            log("Update Contact Request Failed: " + response.Errors[0].Text);
          }
        }, 'POST', {'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'}, post_data);

      }

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

      function getCookie(name) {
        var re = new RegExp(name + "=([^;]+)");
        var value = re.exec(document.cookie);
        return (value != null) ? decodeURI(value[1]) : null;
      }

      function msg(message, f_clicked, is_undo) {
        unsafeWindow.gts_undo = f_clicked;
        var a = xpath(SELECTOR['msg'], getView().ownerDocument.body);
        if (a &amp;&amp; a.length &amp;gt; 0) {
          var td = a[0];
          var div = td.parentNode.parentNode.parentNode.parentNode;
          div.style.visibility = "visible";
          td.innerHTML = "GTS : " + message;
          if (is_undo) {
            var span = document.createElement('span');
            span.setAttribute("id", "gts_und");
            span.setAttribute("class", "lk");
            span.innerHTML = trans('Undo link');
            span.addEventListener('click', f_clicked, true);
            td.appendChild(span);
          }
          window.setTimeout(function() {
            div.style.visibility = "hidden";
          }, 60000);
        }
      }

      function save(f_saved) {
        T[0]['num'] = 0;
        T[0].date = new Date();
        editContact(T[0], f_saved ? f_saved : function() {});
      }

      function ajax(request_path, f_load, get_or_post, headers, data) {
        window.setTimeout(function() {
          GM_xmlhttpRequest({
            'method': get_or_post ? get_or_post : "GET",
            'url': getBaseLocation() + request_path,
            'data': data,
            'headers': headers,
            'onload': f_load,
            'onerror': function(req) {
              log("Request Failed in error code: " + req.status);
            }
          });
        }, 0);
      }

      function getBaseLocation() {
        if (LOCATION_RE.exec(document.location)) {//for Google Apps
          return RegExp.$1;
        } else {
          return 'http://mail.google.com/';
        }
      }

      function trans(msg_id, opt) {
        return {
          'Template actions...' : Ja ? 'テンプレートの操作...' : msg_id,
          'Apply' : Ja ? '適用' : msg_id,
          'Append' : Ja ? '追加' : msg_id,
          'Includes from' : Ja ? '差出人を含む' : msg_id,
          'Excludes from' : Ja ? '差出人を除く' : msg_id,
          'Quantity limit is 9' : Ja ? '最大９個です' : msg_id,
          'Remove' : Ja ? '削除' : msg_id,
          'verbs' : Ja ? (opt + 'するテンプレート') : (opt + ' template'),
          'Please input the template name.' : Ja ? 'テンプレート名を入力してください。' : msg_id,
          'If the name is started from "#", it becomes default of the corresponding "from".' :
            Ja ? '名前を「#」から始めると、対応する差出人のデフォルトになります。' : msg_id,
          'The name already exists.' : Ja ? 'その名前は既に存在します。' : msg_id,
          'appended' : Ja ? ("テンプレート「" + opt + "」を追加しました。")
            : ('Template "' + opt + '" was appended.'),
          'applied' : Ja ? ("テンプレート「"+opt+"」を適用しました。")
            : ('Template "' + opt + '" was applied. '),
          'remove confirm' : Ja ? ("テンプレート「" + opt + "」を削除しますか？")
            : ('Is template "' + opt + '" removed?'),
          'removed' : Ja ? ("テンプレート「" + opt + "」を削除しました。")
            : ('Template "' + opt + '" was removed.'),
          'To apply template was canceled.' : Ja ? "テンプレートの適用は取り消されました。" : msg_id,
          'Undo' : Ja ? "適用の取り消し" : msg_id,
          'Undo link' : Ja ? "適用取り消し" : "Undo applied",
          'No from' : Ja ? "差出人なし" : msg_id
        }[msg_id] || msg_id;
      }

      function x(prefix) {
        var result = [];

        for (var i in SELECTOR) {
          if (typeof prefix != 'undefined') {
            var a = grep(prefix.split(','), function(j) { return i.indexOf(j) == 0; });
            if (a.length != 0) { result.push(SELECTOR[i]); }
          } else {
            result.push(SELECTOR[i]);
          }
        }
        return result;
      }

      /*
       * this 'grep' function from jquery-1.2.2.js
       * jQuery 1.2.2 - New Wave Javascript
       */
      function grep( elems, callback, inv ) {
        // If a string is passed in for the function, make a function
        // for it (a handy shortcut)
        if ( typeof callback == "string" )
          callback = eval("false||function(a,i){return " + callback + "}");

        var ret = [];

        // Go through the array, only saving the items
        // that pass the validator function
        for ( var i = 0, length = elems.length; i &amp;lt; length; i++ )
          if ( !inv &amp;&amp; callback( elems[ i ], i ) || inv &amp;&amp; !callback( elems[ i ], i ) )
            ret.push( elems[ i ] );

        return ret;
      }

      /*
       * this 'param' function from jquery-1.2.2.js
       * jQuery 1.2.2 - New Wave Javascript
       */
      function param(a) {
        var s = [];

        // Serialize the key/values
        for ( var j in a )
          // If the value is an array then the key names need to be repeated
          s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );

        // Return the resulting serialization
        return s.join("&amp;").replace(/%20/g, "+");
      }

      /*
       * this 'trim' function from jquery-1.2.2.js
       * jQuery 1.2.2 - New Wave Javascript
       */
      function trim(text) {
        return (text || "").replace( /^\s+|\s+$/g, "" );
      }

      function xpath(query, target) {
        var results = document.evaluate(query, target || document, null,
          XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
        var nodes = [];
        for (var i=0; i&amp;lt;results.snapshotLength; i++) {
          nodes.push(results.snapshotItem(i));
        }
        return nodes;
      }

      gmail.registerViewChangeCallback(switcher);
      switcher();
    });
  }
}, true);
&lt;/code&gt;&lt;/pre&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/05/gmail-20.html' title='Gmail 2.0 で複数の署名を切り替える'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=8680314062018635761' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/8680314062018635761'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/8680314062018635761'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-4085405068703148033</id><published>2008-04-25T21:57:00.004+09:00</published><updated>2008-04-26T01:02:14.910+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='openid'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>ホスト名とOpenID</title><content type='html'>&lt;a href="http://openid.net/" title="OpenID" target="_blank"&gt;&lt;img src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/openidnet_logo-708808.gif" border="0" alt="" /&gt;&lt;/a&gt; &lt;a href="http://www.openid.ne.jp/" title="自分のブログURLをIDで使える!－OpenID.ne.jp(オープンアイディー）" target="_blank"&gt;&lt;img src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/openidnejplogo-770654.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;p&gt;Rails で OpenID を試してみようと思って思わぬところで躓いたのでメモです。&lt;/p&gt;
&lt;p&gt;ホスト名に使用できる文字は、英数字文字（a-zA-Z0-9）および、ハイフン（-）となっています。&lt;/p&gt;
&lt;span&gt;ホスト名について&lt;/span&gt;
&lt;blockquote cite="http://www.y-adagio.com/public/standards/tr_uri_2396/rfc2396-main.htm#3"&gt;
    &lt;p&gt;ホスト名(hostname)は，[RFC1034]の3.及び[RFC1123]の2.1 で示される形式をとる。すなわち，"."によって分離されたドメインラベルの列であって，各々のドメインラベルは，英数字文字(alphanum)で開始及び終了し，"-"文字を含んでもよい。完全限定ドメイン名の最も右にあるドメインラベルは，数字で始まってはならない。そのために，IPv4アドレスとは構文的に区別されるドメイン名になる。&lt;/p&gt;
    &lt;p&gt;&lt;a href="http://www.y-adagio.com/public/standards/tr_uri_2396/rfc2396-main.htm#3" title="Uniform Resource Identifiers (URI): Generic Syntax: Main" target="_blank"&gt;Uniform Resource Identifiers (URI): Generic Syntax: Main（日本語）&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;テストのために自分のOpenIDを取得したのですが、これがまた、上で述べたルールに反したものを取得してしまいました・・。&lt;/p&gt;
&lt;p&gt;取得したIDは、「ishikawa_rs.openid.ne.jp」なのですが、アンダーバーが入っています＞＜&lt;/p&gt;
&lt;p&gt;このIDをOpenIDのプラグインである&lt;a href="http://agilewebdevelopment.com/plugins/openidauthentication" title="Plugins - Open Id Authentication - Agile Web Development" target="_blank"&gt;open_id_authentication&lt;/a&gt;に通すと、「ishikawa_rs.openid.ne.jp is not an OpenID URL」と言われてしまいます。&lt;/p&gt;
&lt;p&gt;エラーが発生する&lt;a href="http://dev.rubyonrails.org/browser/plugins/open_id_authentication/lib/open_id_authentication.rb" title="/plugins/open_id_authentication/lib/open_id_authentication.rb - Rails Trac - Trac" target="_blank"&gt;ところ&lt;/a&gt;をirbで再現してみると下記のようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code class="prettyprint"&gt;irb(main):001:0&gt; require 'uri'
=&gt; true
irb(main):002:0&gt; url = "ishikawa_rs.openid.ne.jp"
=&gt; "ishikawa_rs.openid.ne.jp"
irb(main):003:0&gt; uri = URI.parse(url.to_s.strip)
=&gt; #&lt;URI::Generic:0x18b3c1e URL:ishikawa_rs.openid.ne.jp&gt;
irb(main):004:0&gt; uri = URI.parse("http://#{uri}") unless uri.scheme
URI::InvalidURIError: the scheme http does not accept registry part: ishikawa_rs
.openid.ne.jp (or bad hostname?)
        from c:/ruby/lib/ruby/1.8/uri/generic.rb:195:in `initialize'
        from c:/ruby/lib/ruby/1.8/uri/http.rb:78:in `initialize'
        from c:/ruby/lib/ruby/1.8/uri/common.rb:488:in `new'
        from c:/ruby/lib/ruby/1.8/uri/common.rb:488:in `parse'
        from (irb):4
irb(main):005:0&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;bad hostnameです。&lt;/p&gt;
&lt;p&gt;そもそも、なぜこのIDになったかというと、「ishikawa.rs」にしようとしたところ、「ドット（.）はだめです。アンダーバー（_）は使えるよ。」と言われたからでした・・。&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/userid-795784.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/userid-795781.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;OpenIDを取得する際、ユーザIDがホスト名になる場合はご注意ください。&lt;/p&gt;
&lt;p style="font-size:small"&gt;※そして、困ったことにopenid.ne.jpではアカウントの削除ができないようです。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/04/openid.html' title='ホスト名とOpenID'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=4085405068703148033' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/4085405068703148033'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/4085405068703148033'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-6222691434042028509</id><published>2008-03-28T21:51:00.001+09:00</published><updated>2008-04-06T09:09:32.710+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='air'/><title type='text'>Rails と AIR で、付箋紙アプリ</title><content type='html'>&lt;a href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/stickynotes-783863.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/stickynotes-783849.jpg" border="0" alt="Stickynotes" /&gt;&lt;/a&gt;
&lt;p&gt;Rails の最新は 2.0.2 ですし、AIR は正式版の 1.0 が公開されました。&lt;br /&gt;
情報はある程度追ってはいるものの、やはり実際に試してみないとなかなか身につきません。&lt;/p&gt;
&lt;p&gt;というわけで、以前から気になっていた、&lt;a href="http://www.thinkit.co.jp/free/article/0709/3/1/" title="[Think IT] 第1回：付箋紙アプリケーションを作ろう！" target="_blank"&gt;[Think IT] 第1回：付箋紙アプリケーションを作ろう！&lt;/a&gt;を参考に、Ruby on RailsとAIRによるデスクトップ付箋紙アプリケーションを作ってみました。&lt;/p&gt;
&lt;p&gt;Adobe AIR のインストールは&lt;a href="http://get.adobe.com/air/" title="Adobe - Adobe AIR Download Center" target="_blank"&gt;こちら&lt;/a&gt;から。&lt;/p&gt;
&lt;p&gt;サンプル付箋紙アプリをお試しいただく場合は、こちらから。&lt;br /&gt;
ちなみに、このアプリはユーザ管理はしておりません。ので、大変ソーシャルな付箋アプリです（汗&lt;/p&gt;
&lt;p&gt;&lt;a style="
font-weight: bold;
font-family: inherit;
text-align: center;
color: #2b333c;
background: orange;
border: 1px solid;
border-color: #c4cccc #acb5b5 #6f7777 #acb5b5;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=0, OffY=1, Color=#AAAAAA);
box-shadow: 0 1px 2px #AAA;
padding: .2em 1.5ex;
_padding: .2em 2ex;
_width/**/:1px;
_white-space: nowrap;
overflow: visible; /* fix button width for IE 5.5 to 7 */
cursor: pointer;
_cursor: hand;" href="http://www.r-stone.net/blogs/ishikawa/resources/stickynotes-air.air"&gt;Download Stickynotes AIR&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;基本的な動作は、&lt;a href="http://www.thinkit.co.jp/free/article/0709/3/1/" title="[Think IT] 第1回：付箋紙アプリケーションを作ろう！" target="_blank"&gt;[Think IT] 第1回：付箋紙アプリケーションを作ろう！&lt;/a&gt;と、&lt;a href="http://www.pokeal.com/2008/01/ruby-on-railsadobe-air.html" title="Ruby on RailsとAdobe AIRでデスクトップアプリを作る - Pokeal.COM" target="_blank"&gt;Ruby on RailsとAdobe AIRでデスクトップアプリを作る - Pokeal.COM&lt;/a&gt;をベースに、なんとなく&lt;a href="http://www.pokeal.com/2008/01/ruby-on-railsadobe-air.html" title="Develogger: AIRでWindowsのシステムトレイにアイコンを表示" target="_blank"&gt;タスクトレイアイコンも使って&lt;/a&gt;みました。ついでに、常に前面表示も可能です。&lt;/p&gt;
&lt;p&gt;Railsに関しては、実質、次の2行のみです。これでRestfulなバックエンドアプリのできあがりです・・。&lt;/p&gt;
&lt;pre&gt; $ ruby script/generate scaffold sticky body:text x:float y:float width:integer height:integer
 $ rake db:migrate&lt;/pre&gt;
&lt;p&gt;なお、Railsアプリは、&lt;a href="http://www.r-stone.net/blogs/ishikawa/2008/02/ruby-on-rails-heroku.html" title="守破離でいこう! -Let's go with SyuHaRi!-: ブラウザだけでRuby on Rails - Heroku -" target="_blank"&gt;先日紹介&lt;/a&gt;した、&lt;a href="http://heroku.com/" title="Heroku" target="_blank"&gt;Heroku&lt;/a&gt;で構築しています。&lt;/p&gt;
&lt;p&gt;AIRでは、透過するTextFieldを作るのにちょっと苦労しました。&lt;br /&gt;ポイントは、blendModeをLAYERにしたSpriteでした。&lt;/p&gt;
&lt;span&gt;&lt;a href="http://www.kirupa.com/forum/showthread.php?t=268284" title="dynamic textfield alpha problem - kirupaForum" target="_blank"&gt;dynamic textfield alpha problem - kirupaForum&lt;/a&gt;&lt;/span&gt;
&lt;blockquote cite="http://www.kirupa.com/forum/showthread.php?t=268284"&gt;
 &lt;p&gt;I am not sure if this is quite what you are looking for but here is a solution I found for adjusting alpha on a TextField object.&lt;/p&gt;
    &lt;pre&gt;// create an empty sprite
var rect:Sprite = new Sprite();
rect.blendMode = BlendMode.LAYER;

addChild(rect);

var txtField:TextField = new TextField();
txtField.txtColor = 0x000000;
txtField.alpha = .5;
txtField.appendText("SOME TEXT");

rect.addChild(txtField);&lt;/pre&gt;
 &lt;p&gt;the key is to set the blendMode property on the sprite to LAYER.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;以下、参考までにソースコードです。&lt;/p&gt;
&lt;h4&gt;Menu.mxml&lt;/h4&gt;
&lt;pre&gt;&lt;code class="prettyprint"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="300" height="200" creationComplete="onCreationComplete();" closing="closing(event);"&amp;gt;
  &amp;lt;mx:Script&amp;gt;
 &amp;lt;![CDATA[
   import mx.core.BitmapAsset;

   [Embed(source="icons/broken-16x16.png")]
   private var icon16:Class;

   private var stickies:Array = [];

   private function create():void {
  setStatus("&amp;gt;&amp;gt; Create new sticky");

  var sticky:Sticky = new Sticky();

  sticky.setAlwaysInFront(isAlwaysInFront.selected);
  sticky.save();
  sticky.show();
  stickies.push(sticky);

   }

   private function onCreationComplete():void {
  setStatus("&amp;gt;&amp;gt; Initializing...");

  // is supports system tray icon
  if (NativeApplication.supportsSystemTrayIcon) {

    var images:Array = [];
    images.push((new icon16() as BitmapAsset).bitmapData);
    nativeApplication.icon.bitmaps = images;

    var systemTrayIcon:SystemTrayIcon = (nativeApplication.icon as SystemTrayIcon);

    systemTrayIcon.tooltip = "Stickynotes";

    var nativeMenu:NativeMenu = new NativeMenu();
    var menuItemNew:NativeMenuItem = new NativeMenuItem("New Stickynote");
    menuItemNew.addEventListener(Event.SELECT, function(e:Event):void {
   create();
    });
    var menuItemReload:NativeMenuItem = new NativeMenuItem("Reload Stickynotes");
    menuItemReload.addEventListener(Event.SELECT, function(e:Event):void {
   load();
    });
    var menuItemRestore:NativeMenuItem = new NativeMenuItem("Restore window");
    menuItemRestore.addEventListener(Event.SELECT, function(e:Event):void {
   restore();
    });
    var menuItemExit:NativeMenuItem = new NativeMenuItem("Exit");
    menuItemExit.addEventListener(Event.SELECT, function(e:Event):void {
   exit();
    });
    nativeMenu.addItem(menuItemNew);
    nativeMenu.addItem(menuItemReload);
    nativeMenu.addItem(menuItemRestore);
    nativeMenu.addItem(menuItemExit);
    systemTrayIcon.menu = nativeMenu;

  }

  load();
   }

   private function load():void {
  setStatus("&amp;gt;&amp;gt; Loading from http://stickynotes.heroku.com ...");

  closing(null);

  var request:URLRequest = new URLRequest("http://stickynotes.heroku.com/stickies.xml");
  request.method = 'GET';
  var loader:URLLoader = new URLLoader();
  loader.addEventListener(Event.COMPLETE, function(e:Event):void {
    messages.text += e.target.data + "\n";

    var xml:XML = new XML(e.target.data);
    for each (var element:Object in xml.sticky) {
   var sticky:Sticky = new Sticky();
   sticky.id = element.id;
   sticky.editor.text = element.body;
   sticky.window.x = element.x * Capabilities.screenResolutionX;
   sticky.window.y = element.y * Capabilities.screenResolutionY;
   sticky.window.width = element.width;
   sticky.window.height = element.height;
   sticky.setAlwaysInFront(isAlwaysInFront.selected);
   sticky.updateStatus();
   sticky.show();

   stickies.push(sticky);
    }

    clearStatus();
  });
  loader.load(request);
   }

   private function setStatus(s:String):void {
  this.status = s;
   }
   private function clearStatus():void {
  this.status = "";
   }

   private function closing(e:Event):void {
  for each (var sticky:Sticky in stickies) {
    if (sticky) {
   sticky.window.close();
    }
  }
  stickies = [];
   }

   private function saveAll():void {
  setStatus("&amp;gt;&amp;gt; Save stickies");

  for each(var sticky:Sticky in stickies) {
    sticky.save();
  }
   }

   private function onChangeHandle():void {
  for each (var sticky:Sticky in stickies) {
    sticky.setAlwaysInFront(isAlwaysInFront.selected);
  }
   }

 ]]&amp;gt;
  &amp;lt;/mx:Script&amp;gt;
  &amp;lt;mx:Button left="10" top="10" label="New" id="new_btn" click="create();" /&amp;gt;
  &amp;lt;mx:Button left="60" top="10" label="Reload" id="load_btn" click="load();" /&amp;gt;
  &amp;lt;mx:Button right="10" top="10" label="Save" id="save_btn" click="saveAll();" /&amp;gt;
  &amp;lt;mx:CheckBox left="10" top="40" label="always in front" id="isAlwaysInFront" change="onChangeHandle();" /&amp;gt;
  &amp;lt;mx:TextArea right="10" top="70" left="10" bottom="10" id="messages" /&amp;gt;
&amp;lt;/mx:WindowedApplication&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Sticky.as&lt;/h4&gt;
&lt;pre&gt;&lt;code class="prettyprint"&gt;package {
  import flash.text.*;
  import flash.display.*;
  import flash.events.*;
  import flash.system.*;
  import flash.net.*;
  import mx.controls.*;

  public class Sticky {
  public var window:NativeWindow; // sticky window
  public var editor:TextField; // sticky edit area
  public var id:Number; // primary key
  private var button:SimpleButton = new SimpleButton(); // cloase button
  private var resizeHandle:SimpleButton = new SimpleButton(); // resize handle
  private var x:Number; // sticky x
  private var y:Number; // sticky y
  private var height:Number; // sticky height
  private var width:Number; // sticky width
  private var body:String; // sticky body
  private var sprite:Sprite = new Sprite();
  private static var RESIZE_HANDLE_SIZE:int = 20;

  /* create sticky window */
  public function Sticky():void {
 var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
 initOptions.systemChrome = NativeWindowSystemChrome.NONE;
 initOptions.transparent = true;
 initOptions.type = NativeWindowType.LIGHTWEIGHT;

 window = new NativeWindow(initOptions);
 window.alwaysInFront = false;
 window.stage.align = StageAlign.TOP_LEFT;
 window.stage.scaleMode = StageScaleMode.NO_SCALE;

 // layer for transparent editor
 sprite.blendMode = BlendMode.LAYER;
 window.stage.addChild(sprite);

 // for edit area
 editor = new TextField();
 editor.x = editor.y = 0;
 editor.selectable = true;
 editor.border = false;
 editor.type = TextFieldType.INPUT;
 editor.multiline = true;
 editor.background = true;
 editor.wordWrap = true;
 editor.backgroundColor = 0xE6E082;
 editor.alpha = 1;
 sprite.addChild(editor);

 // window position of center
 window.x = 0.5 * Capabilities.screenResolutionX - 300 * 0.5
 window.y = 0.5 * Capabilities.screenResolutionY - 100 * 0.5

 // size for window and edit area
 window.width = editor.width = 300;
 window.height = editor.height = 100;

 // resize for window and edit area
 window.stage.addEventListener(Event.RESIZE, function(e:Event):void {
   resized();
 });

 // move and resize
 window.stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
   var x:Number = e.stageX;
   var y:Number = e.stageY;
   if (y &amp;gt; window.height - RESIZE_HANDLE_SIZE &amp;&amp; x &amp;gt; window.width - RESIZE_HANDLE_SIZE) {
  //window.startResize(NativeWindowResize.BOTTOM_RIGHT);
   } else {
  window.startMove();
   }
 });

 window.stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {
   if (isChanged()) {
  save();
   }
 });

 editor.addEventListener(FocusEvent.FOCUS_OUT, function(e:FocusEvent):void {
   if (isChanged()) {
  save();
   }
 });

  } // end of function Sticky

  // show window
  public function show():void {
 // create close button
 button.x = window.width - 15;
 button.y = 5;
 button.upState = createBox(0xE6E082, 10, 0.3);
 button.overState = createBox(0xE6E082, 10, 1);
 button.downState = createBox(0xCCCCCC, 10, 1);
 button.hitTestState = button.upState;
 button.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
   var request:URLRequest = new URLRequest("http://stickynotes.heroku.com/stickies/" + id + ".xml");
   request.method = 'DELETE';
   var loader:URLLoader = new URLLoader();
   loader.load(request);
   window.close();
 });
 window.stage.addChild(button);

 // create resize handle
 resizeHandle.x = window.width - 15;
 resizeHandle.y = window.height - 15;
 resizeHandle.upState = createResizeHandle(0xE6E082, 10, 0.3);
 resizeHandle.overState = createResizeHandle(0xE6E082, 10, 1);
 resizeHandle.downState = createResizeHandle(0xCCCCCC, 10, 1);
 resizeHandle.hitTestState = button.upState;
 resizeHandle.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
   window.startResize(NativeWindowResize.BOTTOM_RIGHT);
 });
 window.stage.addChild(resizeHandle);

 window.visible = true;
  }

  // call after window resized
  public function resized():void {
 editor.width = window.width;
 editor.height = window.height;
 button.x = window.width - 15;
 button.y = 5;
 resizeHandle.x = window.width - 15;
 resizeHandle.y = window.height - 15;
  }

  // save sticky
  public function save():void {
 // create URL of sticky
 var request:URLRequest
 if (!id) {
   request = new URLRequest("http://stickynotes.heroku.com/stickies.xml");
   request.method = 'POST';
 } else {
   request = new URLRequest("http://stickynotes.heroku.com/stickies/" + id + ".xml");
   request.method = 'PUT';
 }

 // create paramater for edit
 var variables:URLVariables = new URLVariables();
 variables['sticky[x]'] = window.x / Capabilities.screenResolutionX;
 variables['sticky[y]'] = window.y / Capabilities.screenResolutionY;
 variables['sticky[width]'] = window.width;
 variables['sticky[height]'] = window.height;
 variables['sticky[body]'] = editor.text;
 request.data = variables;

 // send
 var loader:URLLoader = new URLLoader();
 loader.addEventListener(Event.COMPLETE, function(e:Event):void {
   var xml:XML = new XML(e.target.data);
   if (!id) {
  id = xml.id;
   }
   updateStatus();
 });
 loader.load(request);
  }

  public function setAlwaysInFront(value:Boolean):void {
 window.alwaysInFront = value;
 if (value) {
   editor.alpha = 0.85;
 } else {
   editor.alpha = 1;
 }
  }

  public function updateStatus():void {
 // sticky status
 x = window.x;
 y = window.y;
 width = window.width;
 height = window.height;
 body = editor.text;
  }

  /* private methods ***********/

  // close button
  private function createBox(color:uint, radius:Number, vis:Number):Shape {
 var xShape:Shape = new Shape();
 xShape.graphics.lineStyle(1, 0x000000);
 xShape.graphics.beginFill(color);
 xShape.graphics.drawRect(0, 0, radius, radius);
 xShape.graphics.moveTo(0, radius);
 xShape.graphics.lineTo(radius, 0);
 xShape.graphics.moveTo(radius, radius);
 xShape.graphics.lineTo(0, 0);
 xShape.graphics.endFill();
 xShape.alpha = vis;
 return xShape;
  }

  private function createResizeHandle(color:uint, radius:Number, vis:Number):Shape {
 var xShape:Shape = new Shape();
 xShape.graphics.lineStyle(1, 0x000000);
 xShape.graphics.beginFill(color);
 xShape.graphics.moveTo(0, radius);
 xShape.graphics.lineTo(radius, 0);
 xShape.graphics.lineTo(radius, radius);
 xShape.graphics.lineTo(0, radius);
 xShape.graphics.endFill();
 xShape.alpha = vis;
 return xShape;
  }

  // is paramater changed
  private function isChanged():Boolean {
 return (x != window.x || y != window.y ||
   width != window.width || height != window.height ||
   body != editor.text);
  }

  } // end of class Sticky

} // end of package
&lt;/code&gt;&lt;/pre&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/03/rails-air.html' title='Rails と AIR で、付箋紙アプリ'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=6222691434042028509' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/6222691434042028509'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/6222691434042028509'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-7295874929618012884</id><published>2008-03-11T22:02:00.000+09:00</published><updated>2008-03-12T08:27:14.151+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='server'/><category scheme='http://www.blogger.com/atom/ns#' term='zabbix'/><title type='text'>CentOS 4.6 で、ZABBIX 1.4.4</title><content type='html'>&lt;p&gt;社内のサーバ監視に、&lt;a href="http://www.zabbix.com/" title="Homepage of ZABBIX :: An Enterprise-Class Open Source Distributed Monitoring Solution" target="_blank"&gt;ZABBIX&lt;/a&gt;（&lt;a href="http://www.zabbix.jp/" title="ZABBIX-JP - Un-Official Support Page" target="_blank"&gt;日本語サイト&lt;/a&gt;）を使用しているのですが、ZABBIX 1.4 では、「WEB monitoring」というWebサイトを簡単に監視できる機能が搭載されたようなので、この機能を使いたいがため、1.1.3 からアップデートしてみました。&lt;/p&gt;
&lt;span&gt;&lt;a href="http://www.zabbix.com/features.php" title="New in ZABBIX 1.4 FEATURES" target="_blank"&gt;New in ZABBIX 1.4 FEATURES&lt;/a&gt;（&lt;a href="http://www.zabbix.jp/modules/news/article.php?storyid=65" title="ZABBIX-JP - ZABBIX 1.4リリースノート翻訳" target="_blank"&gt;日本語サイト&lt;/a&gt;）&lt;/span&gt;
&lt;blockquote cite="http://www.zabbix.com/features.php"&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Installation Wizard&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; Installation Wizard automatically checks pre-requisites, database connectivity and generates configuration file for WEB front end. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Support of new database engines&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; Support of SQLite has been implemented. It allows use of ZABBIX in embedded environments. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;WEB interface improvements&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; Speed and usability of WEB interface has been improved very much. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;New notification methods&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; Native support of Jabber messaging has been introduced. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Distributed monitoring&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; ZABBIX distributed monitoring is made for complex environments consisting of different locations.
   ZABBIX supports monitoring of unlimited number of nodes. Centralized configuration allows easy configuration of all nodes from a single location. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Auto-discovery&lt;/b&gt;&lt;/li&gt;
 ZABBIX distributed monitoring module allows easy deployment of ZABBIX systems. The discovery support IP ranges, service checks, agent and SNMP checks for efficient auto-discovery.
 &lt;p&gt; &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Many-to-many template linkage&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; More flexible host-template linkage saves time and make configuration of hosts more flexible and straight forward. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Database watchdog&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; ZABBIX server will automatically warn group of users if database is down and continues normal operations when database is back. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;WEB monitoring&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; WEB monitoring module allows flexible and easy monitoringof availability and performanceof WEB sites and WEB based applications. It supports passing of GET and POST variables. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;XML data import/export&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; New XML data import and export functionality is an excellent way of sharing templates, hosts configuration and items/triggers related information. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Support of Windows Vista&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; ZABBIX Windows agent supports Windows Vista, both 32 and 64 bit versions. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;More flexible actions&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; Multiple operations (notifications, script execution) per action are supported. Choice of action calculation algorithm was introduced. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Server-side external checks&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; Server-side external checks can be used to introduce custom checks executed on ZABBIX server side. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;New user permission schema&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; Old user permission schema is no longer support. It was replaced by new more efficient, yet simple, schema working on level of user groups and host groups. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Support of hysteresis&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; ZABBIX support use of different trigger expressions for going to ON and OFF states. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Support of slide show&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; Several screens can be grouped into a slide show for better presentation. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;ZABBIX server can spread load across several servers&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; Groups of server side processes (discoverer, poller, HTTP poller, trapper, etc) can be located on different physical servers for better performance and availability. &lt;/p&gt;
  &lt;/ul&gt;
  &lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Other improvements&lt;/b&gt;&lt;/li&gt;
 &lt;p&gt; See Release Notes for a complete list of improvements. &lt;/p&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;CentOS 4.6 の環境で、いくつか躓いたところがあったので、メモです。&lt;/p&gt;
&lt;h3&gt;CURLのバージョン&lt;/h3&gt;
&lt;p&gt;Web Monitoring機能を有効にするには、--with-libcurlオプションを付けてインストールします。&lt;/p&gt;
&lt;pre&gt;$ ./configure --enable-server --with-mysql --with-net-snmp --with-libcurl&lt;/pre&gt;
&lt;p&gt;すると、次のようなエラーが出て止まってしまいます。&lt;/p&gt;
&lt;pre&gt;checking for libcurl &amp;gt;= version 7.13.1... no
configure: error: Not found Curl library
&lt;/pre&gt;
&lt;p&gt;CentOS 4.x では、curlのバージョンは7.12なのでダメなようです。&lt;br /&gt;
いろいろ調べてみると、結局、&lt;a href="http://www.zabbix.com/forum/showthread.php?t=8319" title="Not found Curl library - ZABBIX Forums" target="_blank"&gt;curlを自身でバージョンアップするしかない&lt;/a&gt;ようです。&lt;br /&gt;
以下のようにして解決です。&lt;/p&gt;
&lt;span&gt;&lt;a href="http://www.zabbix.com/wiki/doku.php?id=centosinstall&amp;s=curl" title="centosinstall" target="_blank"&gt;centosinstall&lt;/a&gt;&lt;/span&gt;
&lt;blockquote cite="http://www.zabbix.com/wiki/doku.php?id=centosinstall&amp;s=curl"&gt;
 &lt;pre&gt;$ cd ~/src/
$ wget ftp://ftp.planetmirror.com/pub/curl/libcurl4-devel-7.16.2-1.i386.rpm
$ wget ftp://ftp.planetmirror.com/pub/curl/libcurl4-7.16.2-1.i386.rpm
$ sudo yum install openssl096b
$ sudo yum remove curl-devel
$ sudo rpm -i libcurl4* 
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;h3&gt;PHP4&lt;/h3&gt;
&lt;p&gt;ZABBIXのフロントエンドはPHPなのですが、やっとインストールが成功して管理画面にアクセスすると以下のエラーが発生。&lt;/p&gt;
&lt;pre&gt;PHP Parse error: parse error, unexpected T_STATIC, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}' in /u02/zabbix/web/include/copt.lib.php on line 112&lt;/pre&gt;
&lt;p&gt;調べてみると、PHP5用のコードになっているのが原因のようです。&lt;br /&gt;
以下のようにして解決です。&lt;/p&gt;
&lt;span&gt;&lt;a href="http://www.zabbix.com/forum/showthread.php?t=8996&amp;highlight=Parse+error" title="Blank web install - ZABBIX Forums" target="_blank"&gt;Blank web install - ZABBIX Forums&lt;/a&gt;&lt;/span&gt;
&lt;blockquote cite="http://www.zabbix.com/forum/showthread.php?t=8996&amp;highlight=Parse+error"&gt;
    &lt;p&gt;includes/copt.inc.php を修正し、'static function' を 'function' にすべて置き換えます。&lt;/p&gt;
 &lt;pre&gt;$ vi includes/copt.inc.php
:%s/static function/function/g&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;これで、無事にアップデートすることができました。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/03/centos-46-zabbix-144.html' title='CentOS 4.6 で、ZABBIX 1.4.4'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=7295874929618012884' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/7295874929618012884'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/7295874929618012884'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-2598067242372441904</id><published>2008-03-09T21:18:00.001+09:00</published><updated>2008-03-10T01:20:33.692+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='api'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>RubyでGoogle Data APIs</title><content type='html'>&lt;p&gt;Googleが提供するさまざまなサービスには、&lt;a href="http://code.google.com/apis/gdata/" title="Google Data APIs - Google Code" target="_blank"&gt;Google Data APIs&lt;/a&gt;を利用して、データの取得や更新を行うことができます。&lt;/p&gt;
&lt;p&gt;現在、以下のサービスへのAPIが提供されています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/apps/" title="Google Apps APIs - Google Code" target="_blank"&gt;Google Apps APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/base/" title="Google Base data API - Google Code" target="_blank"&gt;Google Base data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/blogger/" title="Blogger data API - Google Code" target="_blank"&gt;Blogger data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/calendar/" title="Google Calendar data API - Google Code" target="_blank"&gt;Google Calendar data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/codesearch/" title="Google Code Search data API - Google Code" target="_blank"&gt;Google Code Search data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/notebook/" title="Google Notebook data API - Google Code" target="_blank"&gt;Google Notebook data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/spreadsheets/" title="Google Spreadsheets data API - Google Code" target="_blank"&gt;Google Spreadsheets data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/picasaweb/" title="Picasa Web Albums data API - Google Code" target="_blank"&gt;Picasa Web Albums data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/documents/" title="Google Documents List data API - Google Code" target="_blank"&gt;Google Documents List data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/apis/youtube/" title="YouTube data API - Google Code" target="_blank"&gt;YouTube data API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;JavaScriptからSpreadsheetsを操作できないかと調べてみたのですが、JavaScript client libraryは、現在、Google Calendar と Bloggerのみのようです。&lt;/p&gt;
&lt;p&gt;そこで、Rubyのライブラリを探してみると、&lt;a href="http://rubyforge.org/projects/gdata-ruby/" title="RubyForge: Google GData API access via Ruby wrapper: Project Info" target="_blank"&gt;gdata-ruby&lt;/a&gt;というライブラリがありました。&lt;br /&gt;ただ、このライブラリは現在開発がストップしているらしく、SpreadsheetとBloggerのみに対応しているようです。&lt;/p&gt;
&lt;p&gt;というわけで、せっかくなのでRubyでGoogle Spreadsheets data APIを試してみました。&lt;/p&gt;
&lt;p&gt;ruby-gdataのインストール&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gem install GData&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;書き込みテスト&lt;/p&gt;
&lt;pre&gt;&lt;code class="prettyprint"&gt;#!/usr/bin/env ruby

require 'gdata/spreadsheet'

gdata_user = 'xxx@gmail.com'
gdata_pass = 'xxx'
gs_key = 'xxx'

gs = GData::Spreadsheet.new(gs_key)
gs.authenticate(gdata_user, gdata_pass)

gs.add_to_cell 'sin(0.2)'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;残念ながら、使用感はいまいちでした。他によいライブラリはないのでしょうか・・・？&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/03/rubygoogle-data-apis.html' title='RubyでGoogle Data APIs'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=2598067242372441904' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/2598067242372441904'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/2598067242372441904'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-3608319790103166090</id><published>2008-02-29T22:14:00.002+09:00</published><updated>2008-03-05T12:25:41.405+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='api'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='chart'/><title type='text'>Google Chart API を使ってみる</title><content type='html'>&lt;p&gt;しばらく前に、&lt;a href="http://code.google.com/apis/chart/" title="Developer&amp;#39;s Guide - Google Chart API - Google Code" target="_blank"&gt;Google Chart API&lt;/a&gt; が公開されたのですが、後れ馳せながら使ってみました。&lt;/p&gt;
&lt;p&gt;Google Chart APIは、&lt;a href="http://www.r-stone.net/blogs/ishikawa/2008/02/google-static-maps-api.html" title="Google Static Maps APIを使ってみる - 守破離でいこう! -Let's go with SyuHaRi!-"&gt;Google Static Maps API&lt;/a&gt;と同じようにグラフを画像（PNGフォーマット）で返してくれるAPIです。（Google Chart APIが先ですが・・）&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;img src="http://chart.apis.google.com/chart?cht=p3&amp;chd=s:hW&amp;chs=400x200&amp;chl=Hello|World" alt="Google Chart API - Hello World" /&gt;&lt;br /&gt;&lt;span style="color:green;font-size:.8em"&gt;http://chart.apis.google.com/chart?cht=p3&amp;amp;chd=s:hW&amp;amp;chs=400x200&amp;amp;chl=Hello|World&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Google Chart API のURLは、次の形式になっていて、&lt;em&gt;parameters&lt;/em&gt;部分は以下のようになっています。※グラフの種類によって違ってきます。&lt;/p&gt;
&lt;style type="text/css" media="screen"&gt;
 .paramkey {
  color: green;
 }
 .required {
  color: #666;
  font-size: 0.8em;
 }
 .example {
  color: #666;
  font-size: 0.9em;
 }
 .note {
  color: #666;
  font-size: 0.9em;

 }
 dl {
  margin-left: 1em;
 }
 dd {

 }
&lt;/style&gt;
&lt;pre&gt;http://chart.apis.google.com/chart?parameters&lt;/pre&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;span class="paramkey"&gt;chs&lt;/span&gt;&lt;/dt&gt;
  &lt;dd&gt;グラフのサイズ&lt;/dd&gt;
  &lt;dd&gt;&lt;span class="example"&gt;e.g. 400x250&lt;/span&gt;&lt;/dd&gt;
  &lt;dd&gt;&lt;span class="note"&gt;※最大値は1000x1000&lt;/span&gt;&lt;/dd&gt;
  &lt;dt&gt;&lt;span class="paramkey"&gt;chd&lt;/span&gt;&lt;/dt&gt;
  &lt;dd&gt;グラフのデータ&lt;/dd&gt;
  &lt;dd&gt;&lt;span class="example"&gt;e.g. s:helloWorld&lt;/span&gt;&lt;/dd&gt;
  &lt;dd&gt;&lt;span class="note"&gt;※&lt;a href="http://code.google.com/apis/chart/#chart_data" title="Google Chart API - Chart data" target="_blank"&gt;データをエンコードした文字列&lt;/a&gt;を設定します&lt;/span&gt;&lt;/dd&gt;
  &lt;dt&gt;&lt;span class="paramkey"&gt;cht&lt;/span&gt;&lt;/dt&gt;
  &lt;dd&gt;グラフの種類&lt;/dd&gt;
  &lt;dd&gt;&lt;span class="example"&gt;e.g. lc&lt;/span&gt;&lt;/dd&gt;
  &lt;dt&gt;&lt;span class="paramkey"&gt;chxt&lt;/span&gt;&lt;/dt&gt;
  &lt;dd&gt;軸ラベルの表示&lt;/dd&gt;
  &lt;dd&gt;&lt;span class="example"&gt;e.g. x,y&lt;/span&gt;&lt;/dd&gt;
  &lt;dt&gt;&lt;span class="paramkey"&gt;chxl&lt;/span&gt;&lt;/dt&gt;
  &lt;dd&gt;軸ラベル&lt;/dd&gt;
  &lt;dd&gt;&lt;span class="example"&gt;e.g. 0:|Mar|Apr|May|June|July|1:||50+Kb&lt;/span&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;上記の例だと、このようなグラフになります。&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;img src="http://chart.apis.google.com/chart?chs=400x250&amp;chd=s:helloWorld&amp;cht=lc&amp;chxt=x,y&amp;chxl=0:|Mar|Apr|May|June|July|1:||50+Kb" /&gt;&lt;br /&gt;&lt;span style="color:green;font-size:.8em"&gt;http://chart.apis.google.com/chart?chs=400x250&amp;amp;chd=s:helloWorld&amp;amp;cht=lc&amp;amp;chxt=x,y&amp;amp;chxl=0:|Mar|Apr|May|June|July|1:||50+Kb&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;表示できるグラフの種類は、円・棒・折れ線グラフはもちろん、面グラフや散布図といったものまで表示でき、大概のグラフは表示できそうです。&lt;/p&gt;
&lt;p&gt;これまでグラフを貼り付けるのに、Excelで作成したものを貼り付けたり、Flashでやっていたものが、簡単に貼り付けられるようになったのは素晴らしいと思います。ただ、今のところ（2008/02/29現在）、ラベルに日本語はうまく表示できないようなのがちょっと残念。&lt;/p&gt;
&lt;p&gt;なお、利用にあたっては、ユーザあたり50,000リクエスト/日（query limit of 50,000 queries per user per day）という制限があります。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/02/google-chart-api.html' title='Google Chart API を使ってみる'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=3608319790103166090' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/3608319790103166090'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/3608319790103166090'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-2810339923271601894</id><published>2008-02-28T22:25:00.006+09:00</published><updated>2008-03-05T12:26:09.240+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maps'/><category scheme='http://www.blogger.com/atom/ns#' term='api'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Google Static Maps APIを使ってみる</title><content type='html'>&lt;p&gt;先日、より簡単にGoogleマップを表示できるようになる、&lt;a href="http://code.google.com/apis/maps/documentation/staticmaps/" title="Google Static Maps API" target="_blank"&gt;Google Static Maps API&lt;/a&gt;が&lt;a href="http://googlemapsapi.blogspot.com/2008/02/google-maps-without-scripting.html" title="Official Google Maps API Blog: Google Maps Without the Scripting" target="_blank"&gt;公開&lt;/a&gt;されました。&lt;/p&gt;
&lt;p&gt;特定の地図をimageデータ（GIFフォーマット）として返してくれるようで、いろいろと使い道があるのではないでしょうか。（特にモバイル）&lt;/p&gt;
&lt;p&gt;というわけで、さっそく使ってみました。&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;img src="http://maps.google.com/staticmap?center=44.019684,144.26526&amp;markers=44.019684,144.26526,bluer&amp;zoom=9&amp;size=500x400&amp;key=ABQIAAAA8ZpiZm-gfGul8RlB5tWW8RTpDJjqT8UXk8rchgTOum6qLViMchRfSgbpJe_xDHQEkmkQPmBh4jRE4A" alt="River Stone Inc." /&gt;&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;span class=""&gt;携帯で表示(240x270)&lt;/span&gt;&lt;br /&gt;&lt;img src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/gstaticmaps_qr-727420.png" alt="QRコード" /&gt;&lt;br /&gt;&lt;a href="http://www.r-stone.net/blogs/ishikawa/google_static_maps_api_mobile.html" title="google static maps api mobile - River Stone Inc." target="_blank"&gt;http://www.r-stone.net/blogs/ishikawa/google_static_maps_api_mobile.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;使い方は非常に簡単で、&lt;a href="http://gmaps-samples.googlecode.com/svn/trunk/simplewizard/makestaticmap.html" title="Static Map Wizard" target="_blank"&gt;Static Map Wizard&lt;/a&gt;で手軽に作成できます。&lt;/p&gt;
&lt;p&gt;Google Static Maps API のURLは、次の形式になっていて、&lt;em&gt;parameters&lt;/em&gt;部分は以下のようになっています。&lt;/p&gt;
&lt;style type="text/css" media="screen"&gt;
    .paramkey {
  color: green;
 }
 .required {
  color: #666;
  font-size: 0.8em;
 }
 .example {
  color: #666;
  font-size: 0.9em;
 }
 .note {
  color: #666;
  font-size: 0.9em;
  
 }
 dl {
  margin-left: 1em;
 }
 dd {
  
 }
&lt;/style&gt;
&lt;pre&gt;http://maps.google.com/staticmap?parameters&lt;/pre&gt;
&lt;dl&gt;
    &lt;dt&gt;&lt;span class="paramkey"&gt;center&lt;/span&gt;&lt;span class="required"&gt;（必須）&lt;/span&gt;&lt;/dt&gt;
 &lt;dd&gt;マップの中央の座標&lt;/dd&gt;
 &lt;dd&gt;&lt;span class="example"&gt;e.g. 40.714728,-73.998672&lt;/span&gt;&lt;/dd&gt;
 &lt;dt&gt;&lt;span class="paramkey"&gt;zoom&lt;/span&gt;&lt;span class="required"&gt;（必須）&lt;/span&gt;&lt;/dt&gt;
 &lt;dd&gt;ズームレベル&lt;/dd&gt;
 &lt;dd&gt;&lt;span class="example"&gt;e.g. 17&lt;/span&gt;&lt;/dd&gt;
 &lt;dt&gt;&lt;span class="paramkey"&gt;size&lt;/span&gt;&lt;span class="required"&gt;（必須）&lt;/span&gt;&lt;/dt&gt;
 &lt;dd&gt;画像のサイズ&lt;/dd&gt;
 &lt;dd&gt;&lt;span class="example"&gt;e.g. 500x400&lt;/span&gt;&lt;/dd&gt;
 &lt;dd&gt;&lt;span class="note"&gt;※最大値は512x512&lt;/span&gt;&lt;/dd&gt;
 &lt;dt&gt;&lt;span class="paramkey"&gt;maptype&lt;/span&gt;&lt;span class="required"&gt;（オプション）&lt;/span&gt;&lt;/dt&gt;
 &lt;dd&gt;&lt;span class="paramkey"&gt;roadmap&lt;/span&gt;&lt;span class="required"&gt;（デフォルト）&lt;/span&gt;:通常のマップ&lt;/dd&gt;
 &lt;dd&gt;&lt;span class="paramkey"&gt;mobile&lt;/span&gt;:表示文字等が簡略化されたモバイル用&lt;/dd&gt;
 &lt;dt&gt;&lt;span class="paramkey"&gt;markers&lt;/span&gt;&lt;span class="required"&gt;（オプション）&lt;/span&gt;&lt;/dt&gt;
 &lt;dd&gt;マーカーの位置,色,文字&lt;/dd&gt;
 &lt;dd&gt;&lt;span class="note"&gt;※"|"で区切ることで複数マーカーが可能&lt;/span&gt;&lt;/dd&gt;
 &lt;dd&gt;&lt;span class="note"&gt;{latitude},{longitude},{color}{alpha-character}&lt;/span&gt;&lt;/dd&gt;
 &lt;dd&gt;&lt;span class="example"&gt;e.g. 40.702147,-74.015794,blues|40.711614,-74.012318,greeng&lt;/span&gt;&lt;/dd&gt;
 &lt;dt&gt;&lt;span class="paramkey"&gt;key&lt;/span&gt;&lt;span class="required"&gt;（必須）&lt;/span&gt;&lt;/dt&gt;
 &lt;dd&gt;APIキー&lt;/dd&gt;
 &lt;dd&gt;&lt;span class="note"&gt;Google Maps APIと同じ&lt;/span&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;なお、利用にあたって、1ユーザ(1IPアドレス)当たり、表示は1日1,000種類の画像までという制限（※&lt;a href="http://code.google.com/apis/maps/documentation/staticmaps/index.html#Limits" title="Google Static Maps API - Google Code" target="_blank"&gt;原文&lt;/a&gt;によると「1000 unique (different) image requests per viewer per day」）があるので要注意です。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/02/google-static-maps-api.html' title='Google Static Maps APIを使ってみる'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=2810339923271601894' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/2810339923271601894'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/2810339923271601894'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-327450645485597244</id><published>2008-02-24T21:51:00.001+09:00</published><updated>2008-02-25T12:49:09.463+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>ブラウザだけでRuby on Rails - Heroku -</title><content type='html'>&lt;a href="http://heroku.com/" title="Heroku" target="_blank"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/logo-732545.png" border="0" alt="" /&gt;&lt;/a&gt;
&lt;p&gt;以前、&lt;a href="http://jp.techcrunch.com/archives/heroku-lifts-ruby-on-rails-development-to-the-cloud/" title="TechCrunch Japanese  アーカイブ    &amp;raquo; Herokuが、ブラウザー内でRuby on Railsの開発を可能に" target="_blank"&gt;TechCrunchの記事&lt;/a&gt;で&lt;a href="http://heroku.com/" title="Heroku" target="_blank"&gt;Heroku&lt;/a&gt;を知って、すぐにベータテスタとして登録したのですが、先日アカウント登録のメールが届いたのでさっそく使ってみました。&lt;/p&gt;
&lt;p&gt;Herokuとは、ブラウザの中だけで、Railsのアプリケーションを開発できるサービスです。また、作成したアプリは、Amazon EC2上で簡単にホストすることもできるようです。&lt;/p&gt;
&lt;p&gt;気になるRailsのバージョンは「2.0.2」になるようです。また、データベースはPostgresなようです。&lt;/p&gt;
&lt;a href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/Heroku--untitled-16c42a-793674.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/Heroku--untitled-16c42a-793668.png" border="0" alt="" /&gt;&lt;/a&gt;
&lt;a href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/Heroku--untitled-16c42a-db-753266.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/Heroku--untitled-16c42a-db-753258.png" border="0" alt="" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/Heroku--untitled-16c42a-con-792236.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/Heroku--untitled-16c42a-con-792233.png" border="0" alt="" /&gt;&lt;/a&gt;
&lt;p&gt;ほんとうに簡単に開発からデプロイまでできるので、ちょっとしたアプリを作るにはもってこいですね。&lt;br /&gt;それに、後に公開されるプレミアム版では、サブドメインではなく、独自ドメインも使用可能になるようですので、要注目です。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/02/ruby-on-rails-heroku.html' title='ブラウザだけでRuby on Rails - Heroku -'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=327450645485597244' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/327450645485597244'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/327450645485597244'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-8071304833694871873</id><published>2008-02-10T22:31:00.000+09:00</published><updated>2008-02-11T02:46:04.038+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>クールに画像をズームする[FancyZoom]</title><content type='html'>&lt;p&gt;クールに画像をズームできる&lt;a href="http://www.cabel.name/2008/02/fancyzoom-10.html" title="Smooth Javascript Image Zooming For Your Web Pages" target="_blank"&gt;FancyZoom&lt;/a&gt;というJavascriptによるスクリプトを発見したのでメモです。&lt;/p&gt;
&lt;p&gt;画像をかっこよくズームするのに、Lightboxをはじめ、&lt;a href="http://www.designwalker.com/2008/01/lightbox.html" title="Lightboxスクリプトいろいろ - DesignWalker" target="_blank"&gt;多数のスクリプト&lt;/a&gt;がありますが、久しぶりにビビっときたので使ってみました。&lt;/p&gt;
&lt;p style="text-align:center"&gt;
&lt;a title="Valentine's Day Night | Vladstudio.com - free desktop wallpapers, widescreen, dual monitors, iPhone wallpapers, backgrounds for mobile phones, wallpaper clock" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/vladstudio_valentine_night_1280x1024-734643.jpg"&gt;&lt;img src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/vladstudio_valentine_night_1280x1024-734626.jpg" border="0" alt="" /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;使い方は簡単で、&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://www.cabel.name/2008/02/fancyzoom-10.html" title="Smooth Javascript Image Zooming For Your Web Pages" target="_blank"&gt;サイト&lt;/a&gt;よりZipファイルをダウンロードします&lt;/li&gt;
&lt;li&gt;FancyZoom.jsの冒頭の以下の部分を適当に置き換えます&lt;br /&gt;
&lt;p&gt;var zoomImagesURI   = '/images-global/zoom/';&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;Webサイトにファイルをアップロードします。&lt;/li&gt;
&lt;li&gt;HTMLの&amp;lt;head&amp;gt;セクションに次の行を加えます。&lt;br /&gt;
&lt;p&gt;&amp;lt;script src="/path/to/FancyZoom.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script src="/path/to/FancyZoomHTML.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
※/path/to/部分は適当に置き換えます
&lt;/li&gt;
&lt;li&gt;&amp;lt;body&amp;gt;タグにonload="setupZoom()"を加えます&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
あとは、次のように画像がリンク先になっているものを検出し自動でやってくれます。
&lt;p style="margin-left:1em"&gt;&amp;lt;a href="image.jpg"&amp;gt;&amp;lt;img src="image-thumbnail.jpg" /&amp;gt;&amp;lt;/a&amp;gt; will zoom up image.jpg when clicked.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;
ちなみに、ズームしたくないものがある場合は、以下のようにrel="nozoom"を指定します。&lt;/p&gt;
&lt;p style="margin-left:1em"&gt;&amp;lt;a href="image.jpg" rel="nozoom"&amp;gt;&amp;lt;img src="image-thumbnail.jpg" /&amp;gt;&amp;lt;/a&amp;gt; will zoom up image.jpg when clicked.&lt;/p&gt;
&lt;p&gt;また、aタグのtitle属性を指定することで、ズームウィンドウにキャプションを設定することもできます。&lt;/p&gt;
&lt;p style="margin-left:1em"&gt;&amp;lt;a href="image.jpg" rel="nozoom" title="will zoom up image.jpg when clicked."&amp;gt;&amp;lt;img src="image-thumbnail.jpg" /&amp;gt;&amp;lt;/a&amp;gt; will zoom up image.jpg when clicked.&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/02/fancyzoom.html' title='クールに画像をズームする[FancyZoom]'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=8071304833694871873' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/8071304833694871873'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/8071304833694871873'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-1107670951536954283</id><published>2008-02-04T22:04:00.000+09:00</published><updated>2008-02-04T23:01:07.608+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogger'/><title type='text'>Bloggerでのラベル一覧表示スクリプトを修正しました</title><content type='html'>&lt;p&gt;以前、&lt;a href="http://www.r-stone.net/blogs/ishikawa/2007/12/bloggerftp.html" title="[再] Bloggerのクラシックテンプレート(FTP公開)でもラベル一覧を表示したい"&gt;[再] Bloggerのクラシックテンプレート(FTP公開)でもラベル一覧を表示したい&lt;/a&gt;を紹介したのですが、BloggerのAPIでエラーが発生してしまい、動作しなくなってしまったので修正しました。&lt;/p&gt;
&lt;p&gt;具体的には、&lt;br /&gt;
&lt;a href="http://www.blogger.com/feeds/16294798617244656433/blogs/6893113891512020766?alt=json-in-script&amp;callback=listLabels" target="_blank"&gt;http://www.blogger.com/feeds/&lt;em&gt;UserID&lt;/em&gt;/blogs/&lt;em&gt;BlogID&lt;/em&gt;?alt=json-in-script&amp;callback=listLabels&lt;/a&gt;&lt;br /&gt;
とリクエストしても、次のエラーが表示されます。&lt;br /&gt;
The &amp;#39;callback&amp;#39; parameter is only supported on feeds.
&lt;/p&gt;
&lt;p&gt;
この現象は、どうやらBloggerAPIの問題として投稿されているようです。&lt;br /&gt;
&lt;a href="http://groups.google.co.jp/group/bloggerDev/browse_thread/thread/283f76934d4baff" title="Public Metafeed Callback Error - Blogger Data API | Google グループ" taget="_blank"&gt;Public Metafeed Callback Error - Blogger Data API | Google グループ&lt;/a&gt;&lt;br&gt;
&lt;a href="http://code.google.com/p/gdata-issues/issues/detail?id=341" title="Issue 341 - gdata-issues - Google Code" taget="_blank"&gt;Issue 341 - gdata-issues - Google Code&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.r-stone.net/blogs/ishikawa/2007/12/bloggerftp.html" title="[再] Bloggerのクラシックテンプレート(FTP公開)でもラベル一覧を表示したい"&gt;[再] Bloggerのクラシックテンプレート(FTP公開)でもラベル一覧を表示したい&lt;/a&gt;では、アップデートとしてユーザのブログ一覧から絞り込む方法を紹介しましたが、Google グループのコメントにもあるように、callbackパラメータを省略した場合にデフォルトで設定される gdata.io.handleScriptLoaded を自身で定義することでも対応できます。
&lt;/p&gt;
&lt;pre&gt;&lt;code class="prettyprint"&gt;var gdata = function() {}
gdata.io = function() {}
gdata.io.handleScriptLoaded = function(root) {
 var baseURL = '/blogs/ishikawa/labels/';
 var baseHeading = "ラベル";
 var isFTP = true;
 var llDiv = document.getElementById('labelList');
 var h2 = document.createElement('h2');
 h2.className = 'sidebar-title';
 var h2t = document.createTextNode(baseHeading);
 h2.appendChild(h2t);
 llDiv.appendChild(h2);
 var ul = document.createElement('ul');
 ul.id = 'label-list';
 var regBlogId = new RegExp(/blog-(\d+)/);
 var labelSort = new Array();
 var entry = root.entry;
 for (var p in entry.category) {
  var category = entry.category[p];
  labelSort[labelSort.length] = category.term;
 }
 labelSort.sort();
 for (var r=0; r &amp;lt; labelSort.length; r++){
  var li = document.createElement('li');
  var a = document.createElement('a');
  var label = new String(labelSort[r]);
  var l = encodeURIComponent(label);
  if (isFTP) {
   if (label.indexOf('Blogger') &amp;gt;= 0) {
  l = l.replace(/%/g, '=');
   } else if (encodeURIComponent(label.replace(/[!-~\s]/g, '')).indexOf('%', 0) &amp;gt;= 0) {
  l = Base64.encode(new String(label));
  l = l.replace(/\//g, "__");
   }
  }
  a.href = baseURL + l +'.html';
  a.innerHTML = label + ' ';
  li.appendChild(a);
  ul.appendChild(li);
  abnk = document.createTextNode(' ');
  ul.appendChild(abnk);
 }
 llDiv.appendChild(ul);
}
&amp;lt;/script&amp;gt;
&amp;lt;script type="text/javascript" src="http://www.blogger.com/feeds/08755644619888194912/blogs/1303966254927872449?alt=json-in-script" &amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;しばらくは、様子を見る必要がありそうです。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/02/blogger.html' title='Bloggerでのラベル一覧表示スクリプトを修正しました'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=1107670951536954283' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/1107670951536954283'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/1107670951536954283'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-47249223871573894</id><published>2008-01-25T12:02:00.000+09:00</published><updated>2008-01-25T22:32:56.533+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Google ブックマークのデータが消えている</title><content type='html'>&lt;p&gt;どこでも共通のブックマークが使えるように、ブックマークにはGoogle ブックマークを使っているのですが、今朝(2008/01/25現在)アクセスしてみると、いくつかのブックマークを除き、ほとんどが消えてしまっています。&lt;br /&gt;
はじめは、自分の誤操作かとも疑いましたが、ここ最近のものを除いたすべてが消えているようです。&lt;br /&gt;
これはおかしいと思い調べてみると、同様の現象に見舞われている人がいるようです。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://okwave.jp/qa3711587.html" title="Google ツールバーのブックマークが消えた -OKWave" target="_blank"&gt;Google ツールバーのブックマークが消えた -OKWave&lt;/a&gt;
&lt;blockquote cite="http://okwave.jp/qa3711587.html"&gt;
&lt;p&gt;昨晩まで使えていた Google ツールバー のブックマークが今朝、ただひとつのブックマーク（直近に追加したもの）だけを残して消えていました（同じツールバーの検索タイプの方は登録内容が残っています）。&lt;br /&gt;
ツールバーの[ブックマーク]−[更新]を何度か試みましたが復活しません。登録内容はエクスポートしていません。元に戻す方法ないでしょうか？&lt;/P&gt;
&lt;/blockquote&gt;
&lt;p&gt;
上記サイトにあるように、僕の場合もGoogle Notebookにデータ自体あったのですが、手動での復旧は億劫なので、Googleの対応を待ったほうがよさそうです・・・。&lt;/p&gt;
&lt;p&gt;
データがどんどんネットに保存されて便利になっていく一方で、それが壊れたときの衝撃はすさまじいものだと痛感しました。&lt;br /&gt;
サービス提供者として、肝に銘じておかなければなりません。&lt;/p&gt;
&lt;p&gt;
&lt;span style="font-weight: bold"&gt;Update:&lt;/span&gt;&lt;br /&gt;
既に復活しているようです。夕方くらいには復活したようですね。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/01/google.html' title='Google ブックマークのデータが消えている'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=47249223871573894' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/47249223871573894'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/47249223871573894'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-8595772466267150988</id><published>2008-01-20T15:32:00.000+09:00</published><updated>2008-01-21T09:34:04.265+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vista'/><category scheme='http://www.blogger.com/atom/ns#' term='pc'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Windows Vistaのスリープ</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/sleep-761322.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/sleep-761317.jpg" border="0" alt="" /&gt;&lt;/a&gt;
&lt;p&gt;ノートPC（Windows Vista）を使うようになり、シャットダウンはあまり行わず、スリープを使用するようになりました。（ノートPCをパタンと閉じて作業終了。開いて作業開始）&lt;br /&gt;
起動・終了が高速で、非常に便利です。&lt;/p&gt;
&lt;p&gt;
そんなスリープですが、そもそもどんな機能なのか気になり、調べてみました。&lt;/p&gt;
&lt;a href="http://pc.nikkeibp.co.jp/article/NPC/20070820/279904/" title="Vistaの「スリープ」は何が違う？【お答えします100題】：日経パソコンオンライン" target="_blank"&gt;Vistaの「スリープ」は何が違う？【お答えします100題】：日経パソコンオンライン&lt;/a&gt;&lt;br /&gt;
&lt;blockquote cite="http://pc.nikkeibp.co.jp/article/NPC/20070820/279904/"&gt;
    &lt;p&gt;休止状態では、メモリーの中の作業用データをハードディスクに保存し、パソコン全体の電源を切ります。CPUやメモリーにも給電されないので、ほとんど電力を消費しません。一方で、ハードディスクを読み書きする手間が必要となるので休止状態に切り替わるまでの時間や復帰するまでの時間が若干長くなります。&lt;/p&gt;
 &lt;p&gt;Windows Vistaのスリープは、速度と安全性を両立した方式です。従来のスタンバイと休止状態を組み合わせた方式なので「ハイブリッド スリープ」とも呼ばれます。具体的には、メモリー内の作業用データをそのまま保持したまま、ハードディスクにも同じデータを記録するのです。こうすることで、省電力状態からの復帰が速くなるほか、メモリーへの電源供給が切れてしまった場合でも復帰できるのです。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
また、スリープ時にはメモリ以外は電源がOFFになるので、持ち運び中にHDDが動作し故障する心配はないようです。&lt;/p&gt;
&lt;p&gt;
しかしながら、しばらく起動したままでいるとだんだん動作があやしくなるのは変わっていないようですので(笑)、たまには再起動をしたほうがいいようです。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/01/windows-vista.html' title='Windows Vistaのスリープ'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=8595772466267150988' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/8595772466267150988'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/8595772466267150988'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-3667023203148525564</id><published>2008-01-15T18:16:00.000+09:00</published><updated>2008-01-16T08:51:54.888+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='pc'/><title type='text'>新しい仕事道具</title><content type='html'>&lt;p&gt;今年もよろしくお願いいたします。&lt;/p&gt;
&lt;p&gt;さて、僕の仕事道具といえばやはりPCなのですが、昨年末に新調しました。&lt;/p&gt;
&lt;p style="text-align:center"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_1532-770456.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://www.r-stone.net/blogs/ishikawa/uploaded_images/IMG_1532-770025.JPG" border="0" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;いつでもどこでも仕事ができるように・・・ということでノートを選択したわけでして、&lt;br /&gt;
13インチ以下、HDDの速度、ある程度のビデオ性能、価格等々を考慮し、DELL M1330をチョイス。&lt;/p&gt;
&lt;p&gt;同時にデュアルディスプレイ環境にしたことで、非常に快適な仕事道具となりました。&lt;/p&gt;
&lt;p&gt;次は、机・椅子も新調し、より快適な仕事空間を築けたらと妄想したりしています。&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2008/01/blog-post.html' title='新しい仕事道具'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=3667023203148525564' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/3667023203148525564'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/3667023203148525564'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-3731259688212559684</id><published>2007-12-27T22:14:00.000+09:00</published><updated>2007-12-27T22:14:58.665+09:00</updated><title type='text'>End Of 2007年</title><content type='html'>&lt;p&gt;
早いもので、今年も残すところあとわずかとなりました。
&lt;/p&gt;
&lt;p&gt;
まずは、本年お世話になったお客様に感謝です。&lt;br /&gt;
本年もご愛顧いただきまことにありがとうございます。&lt;br /&gt;
来年も変わらぬご愛顧のほど、よろしくお願い申しあげます。
&lt;/p&gt;
&lt;p&gt;
今年を振り返ってみると、瞬く間に過ぎ去っていった一年でした。&lt;br /&gt;
昨年の反省から、今年は、よりアンテナを伸ばし情報収集を怠らず、時代の流れに素早く乗っていこうとの思いでやってきましたが、結局、情報に埋もれて身動きが取れなかった気がします。&lt;br /&gt;
Googel Readerで、未読フィード数が100+から1000+になって、バージョンアップしたことに気がついたようでは駄目ですね。
&lt;/p&gt;
&lt;p&gt;
来年は、情報を的確にさばき、受信のみならず、さらなる発信も行っていきたいと思います。
&lt;/p&gt;
&lt;p&gt;
そして、このブログのタイトルにもある「守破離」ですが、これまでは、「守」を意識してきましたが、来年からはそろそろ「破」の段階にいかねばなりません。&lt;br /&gt;
これまでの経験を活かし、さらなる精進を重ねていこうと思う次第です。
&lt;/p&gt;
&lt;span&gt;&lt;a href="http://web.cc.yamaguchi-u.ac.jp/~c066eb/senjin.syuhari.htm" title="先人の教え　守破離" target"_blank"&gt;守破離とは&lt;/a&gt;&lt;/span&gt;
&lt;blockquote cite="http://web.cc.yamaguchi-u.ac.jp/~c066eb/senjin.syuhari.htm"&gt;
&lt;p align="center"&gt;「守」とは、&lt;/p&gt;
&lt;p align="center"&gt;師匠の教えを正確かつ忠実に守り、剣道における基本の作法、礼法、技法を身に付ける、いわば「学び」の段階をいいます。&lt;/p&gt;
&lt;p align="center"&gt;「破」とは、&lt;/p&gt;
&lt;p align="center"&gt;それまで身に付けた技や形をさらに洗練させ、自己の個性を創造する段階をいい、&lt;/p&gt;
&lt;p align="center"&gt;「離」とは、&lt;/p&gt;
&lt;p align="center"&gt;さらに前進させ、自らの新しい独自の道を確立させる最終段階のことをいいます。自己を常に発展させるべく修行、精進を重ねていく終わりのない道です。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
それでは、皆様、よいお年を！&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.r-stone.net/blogs/ishikawa/2007/12/end-of-2007.html' title='End Of 2007年'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1303966254927872449&amp;postID=3731259688212559684' title='0 件のコメント'/><link rel='replies' type='application/atom+xml' href='http://www.r-stone.net/blogs/ishikawa/atom.xml' title='コメントの投稿'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/3731259688212559684'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1303966254927872449/posts/default/3731259688212559684'/><author><name>naoki</name><uri>http://www.blogger.com/profile/08755644619888194912</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1303966254927872449.post-962654933407770742</id><published>2007-12-20T23:59:00.000+09:00</published><updated>2007-12-27T14:07:53.205+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><title type='text'>はてなダイアリーキーワードのふりがなを表示する、Firefoxの拡張機能(Extension)を作ってみました</title><content type='html'>&lt;h2&gt;HatenaKeywordTrans - Firefox Extension&lt;/h2&gt;
&lt;p&gt;
Blog等を読んでいるとき、「これ、なんて読むんだろう？」と思う単語（キーワード）に出会うことがよくあります。&lt;br /&gt;
そんなとき、都度、きちんと調べられるといいのですが、面倒なのでなかなかそうはいきません。&lt;br /&gt;
この間も、「Leopard」や「Erlang」に遭遇し、適当に「れおぱーど」「えあらんぐ」などと脳内変換していました。orz&lt;/p&gt;
&lt;p&gt;
そんなわけで、Firefoxで選択したキーワードのふりがなを表示する拡張機能を作ってみました。&lt;br /&gt;
ふりがなの取得は、&lt;a href="http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%c0%a5%a4%a5%a2%a5%ea%a1%bc%a5%ad%a1%bc%a5%ef%a1%bc%a5%c9?kid=1#p3" title="はてなダイアリーキーワードとは - はてなダイアリー" target="_blank"&gt;はてなダイアリーキーワード&lt;/a&gt;の&lt;a href="http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%ad%a1%bc%a5%ef%a1%bc%a5%c9API" title="はてなキーワードAPIとは - はてなダイアリー" target="_blank"&gt;API&lt;/a&gt;を使っています。&lt;/p&gt;
&lt;p&gt;
Firefox2からは、RSSを簡単に扱える、&lt;a href="http://developer.mozilla.org/ja/docs/Feed_content_access_API" title="RSS および Atom フィードをアクセスおよびパースできる API" target="_blank"&gt;Feed content access API&lt;/a&gt;が使えるのではじめはこれを使おうと思ったのですが、はてなダイアリーキーワードのAPI（RSS)では、ふりがなはhatena名前空間による拡張（hatena:furigana）により定義されているので、nsIFeedContainer::fieldsのgetProperty("hatena:furigana")で取得します（と思う）。&lt;br /&gt;
が、これがなぜかうまくいかなかったので、Feed content access APIを使うのは諦めました（涙&lt;/p&gt;
&lt;p&gt;
また、日本語にロケールする場合、日本語はJavaユニコード(Unicode)形式に変換する必要があるので、&lt;a href="http://code.cside.com/3rdpage/jp/" title="3rdpageSearch 