WebOS Goodies

WebOS の未来を模索する、ゲームプログラマあがりの Web 開発者のブログ。

WebOS Goodies へようこそ! WebOS はインターネットの未来形。あらゆる Web サイトが繋がり、共有し、協力して創り上げる、ひとつの巨大な情報システムです。そこでは、あらゆる情報がネットワーク上に蓄積され、我々はいつでも、どこからでも、多彩なデバイスを使ってそれらにアクセスできます。 WebOS Goodies は、さまざまな情報提供やツール開発を通して、そんな世界の実現に少しでも貢献するべく活動していきます。
Subscribe       

iGoogle ガジェットの作り方 : 基本ライブラリ

本日は iGoogle ガジェット作成方法の続きです。この調子でなるべく早急にこのシリーズを完成させてしまうつもりですので、よろしくお願いします。本日からいよいよ JavaScript を使っていきます。 iGoogle ガジェットでは、特有の機能を利用するため、もしくはガジェットの開発効率を高めるために、多数の JavaScript ライブラリが用意されています。本日はその中から、特別な指定をしなくても利用できる基本ライブラリの機能をご紹介しようと思います。任意のサイトへの HTTP リクエストをはじめとして、多彩な機能がありますので、ぜひご覧ください!

ユーザー設定の取得・変更

ユーザー設定の詳細でもご紹介しましたが、ユーザー設定の取得・変更を JavaScript から行うことができます。基本的には以前ご紹介した内容の繰り返しになってしまいますが、復習ということでご了承ください。

_IG_Prefs オブジェクトの作成

ユーザー設定関連の機能は _IG_Prefs クラスのメソッドとして実装されています。従って、使用する前に _IG_Prefs オブジェクトを作成する必要があります。基本的に以下のように記述するだけで OK です。

var prefs = new _IG_Prefs(__MODULE_ID__);

__MODULE_ID__ はサーバーサイドで個々のガジェットの ID に置換されます。上記の行は完全に定型句として覚えてしまって問題ないと思います(ただし、 URL ガジェットは除く)。

このような手続きを踏むのは、おそらくインラインガジェットで対象となるガジェットを識別するためでしょう。インラインガジェットはすべて iGoogle のメインページ上で動作するので、どのガジェットからの呼び出しかを暗黙的に判断するのが難しいのだと思います。

_IG_Prefs オブジェクトのメソッド

_IG_Prefs には以下のメソッドがあります。

getString(name)
name で指定したユーザー設定の値を文字列形式で取得します。
getInt(name)
name で指定したユーザー設定の値を整数形式で取得します。
getBool(name)
name で指定したユーザー設定の値を bool 値で取得します。
getArray(name)
name で指定したユーザー設定の値を文字列配列形式で取得します。主に detatype 属性が list であるユーザー設定の取得に使用します。
getMsg(name)
これだけユーザー設定と関係ありませんが、 message bundle から name に対応する文字列を取得します。詳細は国際化に対応する(後編)を参照してください。
set(name, value)
name で指定したユーザー設定に value を保存します。インラインガジェット以外で利用する際は、こちらの記事でご紹介している方法で SetPrefs ライブラリを読み込む必要があります。
setArray(name, array)
name で指定したユーザー設定に array で指定した文字列配列を保存します。主に datatypelist であるユーザー設定の値の保存に使用します。インラインガジェット以外で利用する際は、こちらの記事でご紹介している方法で SetPrefs ライブラリを読み込む必要があります。
dump()
すべてのユーザー設定を document.writeln() を使ってドキュメントに書き出します。主にデバッグ用です。

これらのメソッドで自由にユーザー設定を操作できます。その気になれば標準の設定ダイアログに頼らずにガジェット内に設定用の UI を表示することもできますね。もちろん、小容量の簡易ストレージとして利用することもできます。ユーザー設定そのものに関する情報や具体的な記述例はユーザー設定の詳細をご参照ください。

外部コンテンツの取得

外部サイトと連携するガジェットを作成するには、どうしても外部サイトとの通信が必要になります。しかし、ご存知のように通常の XMLHttpRequest にはクロスドメイン制約があるので、 URL ガジェット以外では使い物になりません。この問題を回避するため、外部コンテンツの取得関数が新たに追加されています。これを使えば、ドメインを越えて他のサイトのコンテンツが自由に取得できます。凝ったガジェットを作成する際の要となる機能なので、ちょっと詳しくご紹介しますね。

※URL ガジェットではこの機能は利用できません。本来のサーバー側で同様のプロキシーを用意することで対処してください。

関数リスト

外部コンテンツの取得関数は、取得するデータタイプによって以下の 3 つが用意されています。いずれも第 1 引数は取得するコンテンツの URL、第 2 引数はレスポンスを受け取った際に呼び出されるコールバック関数です。

_IG_FetchContent(url, func)
コンテンツを文字列として取得します。
_IG_FetchXmlContent(url, func)
コンテンツを DOM オブジェクトとして取得します。
_IG_FetchFeedAsJSON(url, func, num_entries, get_summaries)
RSS / Atom フィードの内容を JavaScript オブジェクトとして取得します。 num_entries は取得するアイテムの数で 1 から 100 までの整数で指定します。 get_summaries に true を渡すとアイテムの要約が取得できます。

基本的に、 _IG_FetchContent はテキストファイルの取得、 _IG_FetchXmlContent は通常の HTMLXML ファイルの取得、 _IG_FetchFeedAsJSONRSS / Atom フィードの取得に使うと考えておけばよいと思います。もちろん、 _IG_FetchXmlContent で Atom フィードを取得することも可能ですので、場合によって使い分けてください。

コールバック関数の詳細

上記の外部コンテンツ取得関数は、いずれも非同期で動作します。つまり、これらの関数はリクエストを投げるだけですぐに終了し、コンテンツが取得できていない状態でその後の処理が続行されます。そして、レスポンスが届いた時点で第 2 引数 func で指定された関数が呼び出され、取得したコンテンツに対する処理を行うことになります。したがって、外部コンテンツを利用する処理はすべて func の中に記述する必要があります。 _IG_FetchContent などを呼び出した関数内で直接処理することはできません。

func には以下の形式の関数を指定します。

function(contents) { ... }

引数の contents には取得したコンテンツが格納されています。 _IG_FetchContent なら文字列、 _IG_FetchXmlContent なら DOM オブジェクト、 _IG_FetchFeedAsJSON なら以下の値を格納した JavaScript オブジェクトです。

ErrorMsg
エラーが発生した場合は、その内容を示す文字列です。正常終了なら undefined となります。
URL
フィードの URL を示す文字列です。
Title
フィードのタイトルを示す文字列です。
Description
フィードの説明、もしくはタグを示す文字列です。
Link
フィードに関連するホームページの URL を示す文字列です。
Author
フィードの作者を示す文字列です。
Entry
フィードの各アイテムの情報を格納した JavaScript オブジェクトの配列。

上記の Entry には以下の値を格納した JavaScript オブジェクトが取得したアイテムの数だけ格納されています。

Title
アイテムのタイトルを示す文字列です。
Link
アイテムに対応するページの URL を示す文字列です。
Summary
アイテムの要約です。 _IG_FetchFeedAsJSON 関数の第 4 引数に true を指定しないと利用できません。
Date
JavaScript の Date オブジェクトを直接初期化できる形式の日付文字列。

_IG_FetchFeedAsJSON を使えば、とても簡単に RSS / ATOM フィードが利用できそうですね!以下で実例をご紹介しますので、参考にしてください。

キャッシュコントロール

上記の _IG_Fetch〜 系のメソッドで指定した URL の内容は Google のサーバーでキャッシュされ、送信元サーバーの負荷軽減とレスポンスの改善が自動的に行われます。しかし、場合によってはこのキャッシュをバイパスして最新の内容を取得したいときもあるでしょう。そのため、 refreshInterval というオプションパラメータ(2007/7/18 日現在の日本語ドキュメントには未掲載)が追加されました。例えば _IG_FetchContent の場合、以下のような書式になります。

_IG_FetchContent(url, func, { refreshInterval: 秒数 })

これで、少なくとも指定した秒数内に取得した内容が取得できます。秒数を 0 にすることでキャッシュを完全に無効にすることもできますが、レスポンスが低下する上にデータの提供元のサーバーに高負荷をかける可能性があるので、推奨されていません。許容できる範囲でなるべく長めの秒数を指定するようにしましょう。

追記 (2007/8/4)
_IG_FetchFeedAsJSON はキャッシュコントロールをサポートしていないそうです。今後の改良に期待。

実例

実例として、 _IG_FetchFeedAsJSON を使って Google 公式 blog の最新エントリー 5 個を表示するガジェットを作ってみました。

以下が全リストです。

<?xml version="1.0" encoding="UTF-8" ?> 
<Module>
<ModulePrefs title="Google公式blog"
             title_url="http://googleblog.blogspot.com/"
             height="200" />
<Content type="html">
<![CDATA[

<div id="feed_titles"></div>

<script type="text/javascript">
function set_content(content)
{
  var html = ""
  for(var i in content.Entry)
  {
    html += '<a href="' + _hesc(content.Entry[i].Link) + '">'
    html += _hesc(content.Entry[i].Title) + '</a><br/>';
  }
  _gel("feed_titles").innerHTML = html;
}
_IG_FetchFeedAsJSON("http://googleblog.blogspot.com/atom.xml", set_content, 5, false);
</script>

]]> 
</Content> 
</Module>

なんと、これだけで簡易フィードリーダーの完成です。最低限の記述しかしていないので見栄えはイマイチですが、 CSS をちょっと追加してやればだいぶマシになると思います。あとは工夫次第で複数のフィードをマージして表示したり、タイトルに特定のキーワードが含まれるエントリーのみを表示したりと自由自在です。皆さんも自分のニーズに合ったフィードリーダーを作ってみませんか!

onload ハンドラの指定

とくにインラインガジェットでは、 onload ハンドラも iGoogle 本体と共有するので、下手に設定すると誤動作を招きます。そのため、ページロード時に実行する処理を登録するための関数が新設されています。インラインガジェットでは window.onload などは使わず、必ず以下の関数を使ってください。

_IG_RegisterOnloadHandler(func)

func で指定したコールバック関数がページの読み込み時に呼び出されるので、これを onload ハンドラの代わりにすることができます。 func は引数なしの関数でなければなりません。例えば以下のように記述します。

_IG_RegisterOnloadHandler(function() { /*何らかの処理*/ });

インラインでなければ普通に window.onload なども使えますが、なるべく上記の関数を使ったほうが無難です。

画像などのキャッシング

ガジェットが多数の画像などを含んでいると、ガジェットの表示ごとにそれらのリクエストがサーバーに送られ、サーバーに非常に高い負荷がかかります。結果としてガジェットのロード時間が長くなり、ひどいときにはサーバーが落ちかねません。そこで、ガジェットで使用する画像などを Google のサーバーにキャッシュする方法が用意されています。 Google さん、太っ腹です(^^;

キャッシュですから、動的に更新されるような画像には向きません。そのような画像に対して適用した場合、変更が正しく反映されずにトラブルの元になります。逆にほとんど更新されない静的な画像に対しては積極的に利用していきましょう。元がよっぽどネットワーク的に近いサーバーでもない限り、レスポンスが改善されるはずです。

関数リスト

キャッシング関連の関数は以下の 3 つです。いずれも url はキャッシュ対象の画像などの URL です。

_IG_GetImage(url)
url で指定した画像のキャッシュバージョンを表示する IMG 要素を返します。これを DOM API の appendChild などでドキュメントツリーに追加すれば画像が表示できます。
_IG_GetImageUrl(url)
url で指定した画像のキャッシュバージョンの URL を返します。 CSS の background-image など、 IMG 要素以外の場所で使うのに便利です。
_IG_GetCachedUrl(url)
url で指定したファイルのキャッシュバージョンの URL を返します。詳細なドキュメントがないのですが、おそらく画像以外のファイルをキャッシュするためのものかと思われます。

画像を表示するときに必ず JavaScript を通さなければならないのは少々面倒ですが、使い方自体は難しくないはずです。とくに頻繁に表示される画像に対しては、できるだけキャッシュを通すようにしてください。

サンプルコード

関数リストを見れば使い方はほぼ予想がつくと思いますが、参考までにサンプルコードを載せておきます。まずは _IG_GetImage から。以下のようにして DOM ツリーに追加すれば OK です。

<div id="container"></div>

<script type="text/javascript">
_gel("container").appendChild(_IG_GetImage("http://domain.com/image.png"));
</script>

次は _IG_GetImageUrl です。こちらは URL が文字列として返りますから、画像の URL を受け付ける場所ならどこででも使うことが可能です。

<img id="image"/>

<script type="text/javascript">
_gel("image").src = _IG_GetImageUrl("http://domain.com/image.png");
</script>

_IG_GetCachedUrl も対象が画像に限定されないだけで、同様に使えます。

ユーティリティー関数

ガジェット制作の利便性向上を図るため、使用頻度の高い処理がユーティリティー関数としてあらかじめ用意されています。以下がそのリストです。

_gel(id)
document.getElementById(id) と等価。
_gelstn(tagname)
document.getElementByTagName(tagname) と等価。
_toggle(element)
呼び出すたびに element.style.display の値を block と none に交互に変更。
_esc(str)
strURL エンコードした文字列を返す。
_unesc(str)
strURL エンコードを解除した文字列を返す。
_hesc(str)
strHTML エスケープして返す。
_trim(str)
str の先頭と末尾の空白を取り除く。
_uc(str)
str のアルファベットをすべて大文字にして返す。
_min(val1, val2)
val1val2 のうち小さいほうを返す。等しければ val2 を返す。
_max(val1, val2)
val1val2 のうち大きいほうを返す。等しければ val2 を返す。
_args()
ページの URLCGI パラメータを連想配列で返す。例えば "?foo=bar&cat=dog" なら {"foo":"bar", "cat":"dog"} が返る。

使い方は説明するまでもないと思います。上記の Google 公式 blog のガジェットでも少し使っていますので、参考にしてください。これらのユーティリティー関数を有効に活用すれば、より短時間でガジェットが作成でき、メンテナンス性の向上も期待できます。とくにインラインガジェットでは、 prototype.js などのライブラリは利用せずに上記の関数を使うべきです。

インラインガジェットでの名前空間の分離

すべてのインラインガジェットは同じ名前空間上で実行されるので、不用意にグローバル変数やグローバル関数を定義すると名前がコンフリクトして誤動作する可能性があります。可能ならすべてのコードを無名関数に収めてしまうのが確実ですが、それが難しい場合は以下のように名前に "__MODULE_ID__" を含む名前空間を定義し、すべてのグローバルオブジェクトをその中に収めるのがよいでしょう。

// 名前空間の定義
var mod__MODULE_ID__ = new Object;
 
// グローバル変数の定義
mod__MODULE_ID__.foo = 0;
 
// グローバル関数の定義
mod__MODULE_ID__.bar = function() {
  // なんらかの処理
};

前述のとおり、 "__MODULE_ID__" はサーバーサイドで固有 ID に置き換えられるので、 "mod__MODULE_ID__" は完全にユニークであることが期待できるでしょう。

URL ガジェットでの拡張関数の利用

HTML ガジェット(およびインラインガジェット)なら、 JavaScript ライブラリを読み込むための script 要素がサーバーサイドで自動生成されるため、なにもしなくても上記の関数群が利用できます。しかし、 URL ガジェットではそうはいきません。表示内容の HTML に明示的にライブラリを読み込む script 要素を埋め込まなくてはなりません。

ライブラリファイルのパス名は以下のような CGI パラメータとして URL 経由で渡されます。相対パスなので、実際に読み込む際は "http://www.google.com/ig/f/" を頭に追加する必要があります。

http://...&libs=o1yhoxrBnv8/lib/libcore.js...

このファイルを読み込む script 要素をなんらかの方法で生成すれば、 URL ガジェットでも上記の関数群が利用可能になります(外部コンテンツの取得関数を除く)。 script 要素の挿入はサーバーサイドで処理するのがベストだと思いますが、 JavaScript で実装してもかまいません。例えば、以下の JavaScript をガジェットの HTML の先頭あたりに記述すれば、必要なライブラリが読み込めるはずです。

if(document.location.href.match(/[?&]libs=([-0-9a-zA-Z_,\/\.]+)/))
{
  var libs = RegExp.$1.match(/[^,]+/g);
  for(var i = 0 ; i < libs.length ; i++)
  {
    document.write('<scr' + 'ipt src="http://www.google.com/ig/f/' + libs[i] + '"></scr' + 'ipt>');
  }
}

少し話がそれますが、次回以降でご紹介する機能別ライブラリを使用する場合は、複数の JavaScript ライブラリを読み込む必要があります。その際はコンマ区切りでパスが渡されますので、適切に処理してください。上記のコードも対応しています。いるはずです(^^;

最後に、ライブラリを利用するために必要になる "__MODULE_ID__" も当然 URL ガジェットでは使えません。その代わりとして、やはり CGI パラメータとしてモジュール ID が渡されます。パラメータ名は "mid" です。モジュール ID が必要な関数にはこれを渡してください。ライブラリを読み込んだ後なら _args 関数が使えるはずなので、簡単に値を取得できるでしょう。

以上、今回は Google Universal ガジェットで利用できる基本ライブラリの利用方法をご紹介しました。次回以降では、さらに機能別ライブラリを読み込むことで利用できる高度な機能をご紹介します。タブによる表示の切り替えやドラッグ&ドロップといったリッチな UI が簡単に実現できますので、お楽しみに!

関連記事

この記事にコメントする

Recommendations
Books
「Closure Library」の入門書です。
詳しくはこちらの記事をどうぞ!
Categories
Recent Articles