スクリプトを書く
今回は前回の続きでWikipedia検索のガジェットの実装です。前回はガジェット本体の見た目の部分を作ったので、今度は実際に検索ボタンを押して検索、結果の表示の動作をするJavaScriptを説明していきます。
SimpleAPIのWikipedia検索を使ってみる
SimpleAPIとWikipediaAPIの説明
Wikipediaを検索するガジェットであるため、どうにかしてWikipediaを検索させるのですが、実のところWikipedia自体は検索するためのAPIを提供してはいません。
そのため「APIを使って検索できない(=一筋縄ではいかない)のでは?」となりそうですが、Wikipediaのページ名を検索できるAPIがSimpleAPIサービスでWikipediaAPIとして提供されています。
SimpleAPIとは、WikipediaAPI以外にもウェブサイトのスクリーンショットサムネイルや最寄り駅検索のためのAPIを無償で公開しているWebサービスです。SimpleAPIで公開されているAPIの説明の特徴は、無償で提供されていること、呼び出す方法が簡単であること、レスポンスの形式としてXMLやJSON、PHPシリアライズなど柔軟に選択できることがあります。
今回はこのサービスをありがたく利用させていただき、ガジェットから検索することにします。
検索結果を取得する関数を作る
まずは、検索して検索結果を取得する関数を作成します。search("検索文字列");とすると、検索結果が戻るような関数です。
具体的な処理内容は検索文字列を受け取って、XMLHttpRequestを利用してWikipediaAPIを呼び出し、検索結果のレスポンスを返す、となります。
関数が返す値はSimpleAPIはレスポンス形式にJSONを選択できるので、手間を省く意味でもそれを評価してそのまま返すということにします。
- XMLHttpRequestって何?
Ajaxがどうと話題だった昨今、XMLHttpRequestを知らないという人はあまりいないのではないかと思いますが、JavaScriptからHTTPのリクエストを行うための仕組みのことです。なお、Webブラウザから利用する場合には同ドメイン内のみへリクエストできるという制限がありますが、ガジェットから利用する場合にはその制限は受けません。
- JSONって何?
JSONとはJavaScript Object Notationの略称で「JavaScriptの文法を使ったデータ構造表現方法」です。実際にはJavaScriptそのものではなくサブセットなのですが、JavaScriptの形をとるデータを返せばJavaScriptで簡単に読み込むことができるようになるのが特徴です。
例えば、JSONのデータは以下のような形になります。
このようなテキストをJavaScriptで受け取って、
などという形でevalを利用して文字列を評価すると、スクリプトから
という形で簡単に利用できるようになります。
JavaScript以外の言語からJSONを読み書きするためのライブラリはすでに多く実装されているため、JavaScript以外からも扱いやすくなっています。
実装
ではXMLHttpRequestとJSONの簡単な説明が済んだところで、受け取った引数でWikipediaのページを検索する関数を実装します。
なお今回も、CSSと同じようにHTMLの中に直接書いてしまいます。前回書いたHTMLのstyle要素の下にscript要素を追加して、その中に書いてください。
まず、関数の外側を書いておきます。引数には検索文字列が来るのでをkeyword変数としておきます。
はじめにXMLHttpRequestクラスを使うのでインスタンスを作ります。
次にWikipediaAPIを呼び出すリクエストのための指定を行います。
WikipediaAPIの使い方はhttp://wikipedia.simpleapi.net/に書いてあるので見てみましょう。「SimpleAPI Wikipedia 入力仕様」というところに、リクエストする先のURIとパラメータについての解説があります。
- APIのURI : http://wikipedia.simpleapi.net/api
-
パラメータは、GETあるいはPOSTで指定します。文字コードは入力は現在UTF-8のみ。出力はUTF-8固定。
- keyword:キーワード。エイリアスとして、qも指定可能
- output:出力方式(xml,rss,json,html,javascript,php,tsvを指定可能。デフォルトはxml)
- callback:出力形式がJSONP時の名称を指定可能
- lang:現在未実装。現在は日本語(ja)のみ。
- search:現在未実装につき1のみ。(0.その特定キーワードのみを取り出す、1.前方一致、2.後方一致、3.前後方一致、4.FULLTEXT)
引用:SimpleAPI Wikipedia 入力仕様
ベースとなるURIはhttp://wikipedia.simpleapi.net/apiで、リクエストのメソッドはGETでよさそうです。
肝心のパラメータはlangとsearchは利用できないので不要として、callbackも今回は利用しないので不要です。残った二つのkeywordとoutputですが、keywordには検索文字列を、outputには結果としてJSONがほしいのでjsonを指定する必要があります。
ということで、まとめるとGETメソッドで
にリクエストを投げればよいということになります。
試しにhttp://wikipedia.simpleapi.net/api?keyword=Windows&output=jsonをブラウザで開いてみましょう。以下のようなテキストがずらずらと出てくるはずです。
これがレスポンスのJSONです。正しく返ってこなかった場合にはリクエストの文字列を確認するか、サービスが停止していないかなどを確認してみてください。
どこにリクエストを投げればよいのかわかったところで、リクエスト先の指定をします。リクエスト先の指定はXMLHttpRequestのopenメソッドで行います。openメソッドは以下の3つの引数をとります。
- 第一引数:HTTPメソッド(HEAD,GET,POST)
- 第二引数:URL
- 第三引数:同期・非同期かどうかのフラグ(リクエストが完了するまでブロックする)
以上を踏まえてここのコードは以下のようになります。なお今回は非同期処理としないので第三引数はfalseにします。
URLのkeywordパラメータに関数の引数のkeyword変数(検索文字列)をくっつけているのですが、実はこのままではダメです。このままでは一部の文字や日本語などが正しく通らないため、URLエンコードする必要があるのです。URLエンコードするには、組み込みのencodeURI関数を使えばよいので以下のように修正します。
次にリクエストを実際に送信します。送信にはsendメソッドを使います。sendメソッドは第一引数に送信するデータを取りますが、今回はGETで取得するだけなので空を表すnullを指定しておきます。
リクエストの実行後、検索結果が返ってくるのでレスポンスのJSONを評価して返します。レスポンスの中身はXMLHttpRequestのresponseTextに入っているのでそれを直接evalします。
以上がWikipediaAPIを呼び出す流れになります。まとめると以下のようになります。
エラー時の処理などは今回省略していますが、それなりのものを作る場合にはエラー時には何らかのアラートを表示するなどを盛り込んでください。
動作確認
ところでこれ、ちゃんと動くのでしょうか? ということで、ちょっと試してみましょう。以下のコードをsearch関数の下に追加して、Main.htmlをInternet Explorerで開いてみます。情報バーが出てスクリプトの実行がとめられた場合には、情報バーに従って実行を許可してください。
このコードはWindows VistaでWikipediaのページを検索して一件目のページのサマリをalertで表示しています。
何かサマリのようなものが表示されれば、ちゃんと検索結果を取得できています。スクリプトエラーが発生した場合には、ブラウザのエラーメッセージを頼りにコードをチェックしてみてください。
取得できることを確認したら今書いたalertの行は不要なので削除してください。
これで取得の関数は終わりです。XMLHttpRequestとWikipediaAPIの説明でちょっと長くなってしまいました。
検索結果を表示する部分を作る
次に検索結果を表示する部分を作ります。検索結果を表示する部分の動作に関してはここでは作らず、表示するのに必要なものを作ります。
Flyoutの説明
検索結果の表示は完成スクリーンショットにある通り、ガジェットからぴょこっと飛び出した部分に表示します。このぴょこっと飛び出したところを、Windowsサイドバーでは「Flyout」と呼んでいます。
Flyoutはガジェット本体とは別のHTMLファイルで、まったく別のドキュメントとして扱われます。表示はガジェットから任意のタイミングで行え、表示中はガジェット本体側からFlyoutのドキュメントへアクセス、また逆にFlyoutからガジェット側にアクセス可能です。
今回のようにガジェット部分だけでは表示しきれないような情報などを表示するのに利用します。
HTMLを書く
それでは、Flyout用のHTMLをガジェット用のHTMLとは別に作成します。ファイル名はそのままFlyout.htmlとしておきます。
検索結果はガジェット側から差し込むことにしたいので、図2のようにul要素に検索結果一件ずつをli要素にして表示することを考えたHTMLにします。li要素自体をガジェット側から差し込む感じです。本当はli要素の中身まで作っておいてテンプレート的にすると良いのですが、今回はそこまでは手をかけません。
以上を踏まえて、検索結果を表示するHTMLは以下のようになります。
配置や見た目の細かい部分に関しての説明はあまり必要ないと思うので省きますが、ガジェットと同様body要素に対して表示するサイズ(widthとheightプロパティ)の指定が必要だということには注意してください。また、このFlyoutでは検索結果が表示されるため、縦に長くなるのでbody要素にoverflow: autoを指定し、高さを超えた場合にはスクロールするようにしています。
ガジェット側から操作する必要があるため、2つの要素にidを振っています。
一つ目はh1要素下にある、span要素のresultInfoです。これは検索結果の件数を表示するための部分です。
二つ目はul要素のresultsです。これは検索結果を表示するための要素です。検索結果のヒットごとをひとつのli要素としてul要素に追加して表示することを考え、idを振ります。
今回のFlyoutではスクリプトを使わないため、HTMLはこれでおしまいです。
Flyoutを表示設定と表示方法
Flyoutの利用方法を知る意味でも、試しにガジェットのほうから呼び出して表示してみます。
まず、Flyoutがどのファイルであるのかを設定する必要があります。設定は組み込みのSystem.Gadget.Flyoutオブジェクトのfileプロパティにファイル名を指定します。
先ほど作ったFlyoutの名前はFlyout.htmlとしたので、以下のように指定します。この行をMain.htmlのscript要素の先頭に追加します。
そして実際に表示するにはSystem.Gadget.Flyoutオブジェクトのshowプロパティにtrueを指定します。
試しにこの行をファイル指定の後に追加して、ガジェットを追加してみてください。ガジェットがサイドバーに出た直後、シュルシュルと結果の入っていないFlyoutが表示されるはずです。
そして別のアプリケーションなどにフォーカスを移すとシュルシュルと消えてゆきます。Flyoutはフォーカスが外れると自動的に非表示となります。
表示できることを確認したら、showの行は削除しておいてください。検索した結果を表示すればよいので最初は非表示にしておきます。
なおここで出てきたSystem.Gadget.Flyoutオブジェクトはその名の通りFlyout関連のオブジェクトで、fileプロパティ、showプロパティ以外にもFlyoutが表示されたら発生するonShowイベントや、Flyoutのドキュメントにアクセスするdocumentプロパティが用意されています。
これでFlyoutの準備はできました。あとは検索して結果を反映して、適切なタイミングで表示すればよいことになります。
ボタンを押して検索できるようにする
今度はまたガジェット本体にコードを書いていきます。そもそも、まだ「ボタンを押して検索」という動作すら実装していませんので、まずはそれを実装します。
イベントハンドラをつける
ボタンを押したら検索されるようにするには、フォームの送信イベントであるonsubmitイベントで関数を呼び出すようにします。「ボタンを押したら」であれば「ボタンのinput要素のonclickイベントで呼び出せばよいのでは?」と思われるかもしれませんが、検索文字列入力欄のinput要素に入力してEnterを押した場合に検索できません。それを簡単に回避するために、フォームの送信のタイミングを利用します。
それではイベント発生時に処理を行うために、以下の属性をform要素に追加します。
onsubmitイベントではdoSearch関数を呼んで、falseを返します。doSeachはこの後実際に中身を書く予定の関数名です。
次にfalseを返している理由ですが、onsubmitは送信直前のイベントでありfalseを返すことで送信をキャンセルできます。今回はXMLHttpRequestで通信するため、フォームから送信する必要がないのでfalseを返してキャンセルしています。
検索文字列入力にidをつけておく
入力された検索文字列をスクリプトから取得できるようにしなければいけません。そのためinput要素にkeywordというidを振っておきます。
検索ボタンを押されたときの処理を書く
ボタンが押され、イベントが発生したら関数が呼ばれるようにしたので実際の処理を書きます。先ほどdoSearch関数という名前で呼ぶようにしたのでdoSearch関数を実装します。
doSearch関数で行うことは次のようになります。
- 入力文字列の取得
- 検索
- Flyoutの表示と検索結果更新(Flyoutが表示されていない場合)
- Flyoutの検索結果更新(Flyoutが表示されている場合)
Flyoutの検索結果更新処理は若干長めで、Flyoutが表示されている場合と非表示な場合で別々なところで同じ処理を行うため、updateFlyoutという関数に記述します。
この処理のコードは短いですが、説明するにはそれなりの長さになっています。小分けにしてでは全体が把握しづらくなってしまいますし、DOM操作ばかりで難しいところはほとんどないので、実際のコードをまとめて載せてコメントと重要なところは後から細かく解説します。
それでは重要そうなところを解説します。まず、先頭の行についてです。
System.Gadget.FlyoutオブジェクトのonShowイベントはFlyoutが表示されたときに呼ばれるイベントで、ここではFlyoutが表示されたらupdateFlyout関数を呼ぶようイベントハンドらをセットしています。
onShowイベントを使う都合上、更新がdoSearch関数の外から呼ばれることになり、検索結果などを別な関数に渡す必要があるので、グローバルな変数keywordとsearchResultを用意しています。
その後doSearch関数は大体コメントの通りです。その次はFlyoutの検索結果を更新するupdateFlyout関数です。関数の頭の一文はFlyoutを操作するために必要な文です。
前のFlyoutの節でも触れましたが、System.Gadget.FlyoutオブジェクトのdocumentプロパティでFlyoutのドキュメント要素を取得できます。普通のdocumentオブジェクトと同じですが、ガジェットのものではなくFlyoutのdocumentオブジェクトです。このdocumentオブジェクトを介して、FlyoutのHTMLなどを操作できます。
そして検索結果の追加周りはほぼDOM操作ですが、そもそもsearch関数(WikipediaAPIの検索)の結果として「何が返ってきていているのか?」ということを確認しなければなりません(検索結果を表示するのに、何が返ってきているのかわからないのでは表示のしようがないからです)。
検索した結果返ってくるものはWikipediaAPIのJSONを評価したそのものなので、見つかったページの情報(オブジェクト)の配列です。以下のような構造のデータが返ってきます。
配列なので検索個数はsearchResult.lengthで取得でき、n件目のデータはsearchResult[n]で取得、タイトルならsearchResult[n].titleで取得できます。
また結果が一件もない場合にはnullが返ってきます。それを踏まえてupdateFlyout関数内でFlyoutに検索結果を表示するべく要素を組み立てて、検索結果一覧に追加します。
一通りできたので確認
後半大分駆け足感がありましたが、これで動作に必要なものは一通りそろいました。ガジェットをサイドバーに追加して、好きな言葉で検索して検索結果が表示されたら完成です。おつかれさまでした。
なお、ここまでのサンプルがダウンロードできますので、ご利用ください。
次回は、作成したガジェットを配布する方法についての説明を書く予定です。