« Ajaxアプリケーションに変身 | メイン | 関数のデバッグトレース »

Flickrとのマッシュアップ! (PopBoxとdhtmlxGrid) その3はてなブックマークに追加 livedoorクリップに追加 Yahoo!ブックマークに追加 del.icio.usに追加 イザ!ブックマーク ニフティクリップに追加

こんにちは、JavaScript担当の竹村です。

2回に渡ってお送りしてきた『画像検索マッシュアップ - 僕のお気に入り』ですが、今回で一旦終了とします。

前回までをご覧になっていない方は下記でおさらいしてみてください。

前回のおさらい

できあがりイメージ

さて、今回は今までのライブラリの使い方を生かして、コンテンツ化していきます。
ちょっとずつ修正していった最終的なできあがりは↓このようになります。

▼僕のお気に入り -完成系
image.jpg
http://okra.ark-web.jp/~takemura/public/js/my_favorite_v2.1/

では、現状から完成系までの調整箇所を説明していきましょう。

現状

まず、現状の確認です。
↓このようになっていました。

▼画像検索マッシュアップ - 僕のお気に入り
image2.jpg

初期画面で動きを持たせる

上記、現状の画像はページを開いた初期画面です。何も表示されていなくて寂しいのと、
ページを開いた時に、まずは検索して欲しいわけですが、それが分かりづらいです。

そこで、初回はデフォルトで指定した文言で検索しにいくようにしてみましょう。

// 初期化
document.observe("dom:loaded", initialize, false);
function initialize() {
  (...)
  // 初回検索
  searchPhoto();
}

searchPhoto関数は、[検索]ボタンをクリックした時の処理と同じものです。

これで、検索結果が表示されるようになったので、ユーザに画像周辺への興味を
持ってもらうことができます。

PopBoxの特性を生かす

画像をクリックしたら、PopBoxで拡大を表示する部分は前に作りました。PopBoxは
次々に画像をクリックしていったら、どんどん拡大処理していくことができます。

さて、[Myリスト追加]でMyリストに追加できますが、追加されたら該当画像は消えて
くれた方が、PopBoxでどんどん拡大させてどんどんMyリストに追加できそうですね。

// Flickr APIのコールバック関数
function jsonFlickrApi(oJson) {
  (...)
  // 画像を設定して、画像表示部分へ追加書き込み
  var sImg = '<img id="popbox_#{id}" class="PopBoxImageSmall" src="http://static.flickr.com/#{server}/#{id}_#{secret}_s.jpg" '+
    ' pbsrc="http://static.flickr.com/#{server}/#{id}_#{secret}.jpg" '+
    ' pbCaption="<a href=\'javascript: addMylist('+ i +');\'>[Myリスト追加]</a>" '+
    ' title="#{title}" '+
    ' onclick="Pop(this, 100, \'PopBoxImageLarge\')" />';
  new Insertion.Bottom(oDiv, sImg.interpolate(aPhotoList[i]));
  (...)
}
// Mylistに追加する
function addMylist(no) {
  (...)
  Revert('popcopy'+'popbox_'+oPhoto['id'], 100, 'PopBoxImageSmall');
}

このようにimgタグにid属性をつけて、PopBoxのRevert関数で画像を元の位置に
縮小することができます。その際に注意点としてid属性名を直接指定するのではなく、
前に『popcopy』という名前を指定します。

dhtmlxGridのaddRowの引数指定

ここにきて、ふと気づきました。。
addRowの第1引数と第3引数は 1 固定になっています。修正しましょう。

function addMylist(no) {
  var oPhoto = gaPhotoList[no];
  var sImg = '<img alt="" src="http://static.flickr.com/#{server}/#{id}_#{secret}_s.jpg"/>'.interpolate(oPhoto);
  var iNextRow = goMylistGrid.getRowsNum();
  goMylistGrid.addRow(iNextRow, sImg+',#{title}^http://www.flickr.com/photos/#{owner}/#{id}/'.interpolate(oPhoto), iNextRow);
  Revert('popcopy'+'popbox_'+oPhoto['id'], 100, 'PopBoxImageSmall');
}

goMylistGrid.getRowsNum() により、現在の行数を取得し、それを使って
挿入するようにしました。これで、常に行の最後に追加されることになります。

登録済みチェック

次は、Myリストに同じ画像を登録しようとしたらエラー、という制御をしてみます。

まずは、Myリストに『(flickrの)id』を表示する列を追加しましょう。

function initialize() {
  (...)
  // Myリスト用DataGridを生成
  goMylistGrid = new dhtmlXGridObject('mylists');
  goMylistGrid.imgURL = "js/dhtmlxGrid/imgs/";
  goMylistGrid.setHeader("id,image, タイトル");
  goMylistGrid.setInitWidths("100,100,250");
  goMylistGrid.setColAlign("right,left,left");
  goMylistGrid.setColTypes("ro,ro,link");
  goMylistGrid.setColSorting("str,str,str")
  goMylistGrid.setColumnColor("white,white,#d5f1ff")
  goMylistGrid.init();
  (...)
}
(...)
function addMylist(no) {
  var oPhoto = gaPhotoList[no];
  var sRow;
  sRow = '#{id},';
  sRow += ',';
  sRow += '#{title}^http://www.flickr.com/photos/#{owner}/#{id}/';
  var iNextRow = goMylistGrid.getRowsNum();
  goMylistGrid.addRow(iNextRow, sRow.interpolate(oPhoto), iNextRow);
  (...)
}

ではエラーを出す部分と、検索する関数を作ります。

function addMylist(no) {
  var oPhoto = gaPhotoList[no];
  // Mylistを検索して、同じidが登録されていないか確認する
  if (searchMylist(oPhoto['id']) == true) {
    alert('既に登録されています。');
    return;
  }
  (...)
}
(...)
// Mylistを検索して、同じidが登録されていないか確認する
function searchMylist(id) {
  for (var i = 0 ; i < goMylistGrid.getRowsNum() ; i++ ) {
    if (goMylistGrid.cells(i,0).getValue() == id)
      return true;
  }
  return false;
}

goMylistGrid.cells(i,0).getValue() で値を取得しています。

先ほどの『(Flickrの)id』は0番目の要素に入れたので、0を指定しています。
あとはループで検索して、該当があったらエラーを表示するだけです。

一言コメントを入れられるように!

では、各画像に一言入れられるようにしてみましょう。

テキストフィールドを表示してもよいですが、dhtmlxGridの特性上、
表の上で直接変更できるようにしてみます。

function initialize() {
  (...)
  // Myリスト用DataGridを生成
  goMylistGrid = new dhtmlXGridObject('mylists');
  goMylistGrid.imgURL = "js/dhtmlxGrid/imgs/";
  goMylistGrid.setHeader("id,image,タイトル,一言");
  goMylistGrid.setInitWidths("100,100,250,250");
  goMylistGrid.setColAlign("right,left,left,left");
  goMylistGrid.setColTypes("ro,ro,link,ed");
  goMylistGrid.setColSorting("str,str,str,str")
  goMylistGrid.setColumnColor("white,white,#d5f1ff,white")
  goMylistGrid.init();
}

addMylist関数で行を追加する時の処理は省略しておきます。省略すると空になります。
これで、あとはMyリストに行を追加して、「一言」のCellをダブルクリックするか、
F2キーを押せば編集モードに移行し、文字入力をすることができます。

内容をサーバへ送信

最後に保存です。登録した内容をサーバに送信できないことにはWebサービスになりませんね。

// Mylistをサーバに保存する
function saveMylist() {
  var sParam = '';
  for (var i = 0 ; i < goMylistGrid.getRowsNum() ; i++ ) {
    var aRecord = new Array();
    for (var j = 0 ; j < goMylistGrid.getColumnCount() ; j++) {
      aRecord.push(goMylistGrid.cells(i,j).getValue());
    }
    sParam += aRecord.join(',') + "\n";
  }
  
  // Ajaxで保存する
  var myAjax = new Ajax.Request(
      'save.php',
      {
        method: 'post',
        parameters: {grid: sParam},
        onComplete: saveFinish
      });
}
// 保存完了
function saveFinish(oResponse) {
  if (oResponse.status != 200) {
    alert('送信失敗しました。');
  }
  if (oResponse.responseText == 'validate faild') {
    alert('データが不正です。');
  }
  if (oResponse.responseText == 'save faild') {
    alert('保存できませんでした。');
  }
  alert(oResponse.responseText);
}

既におなじみの goMylistGrid.getRowsNum() で行の分だけループしています。
列は、goMylistGrid.getColumnCount() で取れます。

上記行列の分だけ、goMylistGrid.cells(i,j).getValue()で、Cellの中身を1行ずつ取得しています。

取得した内容を prototype.js のAjax.Requestでサーバへ送信、レスポンスをチェックしてエラーがあれば alert を表示するようにしています。

また、save.phpでは、受け取ったパラメータをDBへ保存しています。

途中経過

ここまでのサンプルが↓こちらです。

▼僕のお気に入り -途中経過
image3.jpg

Tips:Firefoxでリストに追加するとheightが伸びる?!

上記のサンプルをFirefoxでご覧いただくと再現するのですが、画像をクリックして
Myリストに追加すると、リストのheightが伸びてしまい、[保存]ボタンが
下にさがってしまいます。

Firebugでトレースしてみたところ、どうもdivタグの書き方がマズかったようです。

■失敗例:
<p>Myリスト</p>
<div id="mylists" width="700" height="250"></div>

■成功例:
<p>Myリスト</p>
<div id="mylists" style="width:700px; height:250px;"></div>

上記のように、スタイルシートで指定したところ問題なくなりました。

サーバからDBの内容をXMLで返す

ここまでやったら、DBへ登録した内容を表示させたいものです。
DBへ登録した内容を「他人のリスト」として表示するようにします。

まずは、他人のリスト表示用の設定をします。

// グローバル変数
var goOtherlistGrid;// 他人のリスト用DataGridオブジェクトのインスタンス
(...)
function initialize() {
  (...)
  // 他人のリスト用DataGridを生成
  goOtherlistGrid = new dhtmlXGridObject('otherlists');
  goOtherlistGrid.imgURL = "js/dhtmlxGrid/imgs/";
  goOtherlistGrid.setHeader("id,image,タイトル,一言");
  goOtherlistGrid.setInitWidths("100,100,250,250");
  goOtherlistGrid.setColAlign("right,left,left,left");
  goOtherlistGrid.setColTypes("ro,ro,link,ro");
  goOtherlistGrid.setColSorting("str,str,str,str")
  goOtherlistGrid.setColumnColor("white,white,#d5f1ff,white")
  goOtherlistGrid.init();
  (...)
}
(...)
<p>他人のリスト</p>
<div id="otherlists" width="700" height="250"></div>

次に、initializeのタイミングと、新しくMyリストを[保存]した時のタイミングで
他人のリストを書き換えるようにします。

function initialize() {
  (...)
  // 他人のリスト読み込み
  loadOtherlist();
}
(...)
// 保存完了
function saveFinish(oResponse) {
  (...)
  // 他人のリスト読み込み
  loadOtherlist();
}
// ロード
function loadOtherlist() {
  // 一旦データを破棄
  goOtherlistGrid.clearAll(false);
  
  // 他人のリスト用DataGridを生成
  goOtherlistGrid.loadXML("show_xml.php");
}

loadOtherlist関数では、他人のリストを一旦破棄してXMLの再読み込みをしています。

リストの破棄は goOtherlistGrid.clearAll() 引数は、ヘッダーも削除するかどうかなのでfalseです。次の、goOtherlistGrid.loadXML() でXMLをロードします。

show_xml.php は、サーバから最新の10ユーザのお気に入りをXML形式で出力しています。この処理だけ切り出すと↓このように出力されます。

http://okra.ark-web.jp/~takemura/public/js/my_favorite_v2.1/show_xml.php 例:

<?xml version="1.0"?>
<rows>
<row id="1">
<cell>2099860332</cell>
<cell><![CDATA[<img title="" alt="" src="http://static.flickr.com/.../..._..._s.jpg">]]></cell>
<cell><![CDATA[クリスマスツリー^http://www.flickr.com/photos/...@.../.../]]></cell>
<cell><![CDATA[めちゃキレイ☆]]></cell></row>
(...)
</rows>

タグが入る箇所は CDATAセクションで文字とみなすようにしておく必要があります。

デコレーションして完成☆

あとは、CSSでデコレーションして完成です。

▼僕のお気に入り -完成系
image.jpg
http://okra.ark-web.jp/~takemura/public/js/my_favorite_v2.1/

この他にもまだ手を入れたい箇所はたくさんあるんですが、
とりあえず今回の目的である「JavaScriptを使って"コンテンツ"を構築」という
部分が実装できたので良しとします。

save.phpとshow_xml.phpを含む、最終系のコードを置いておきます。
コンテンツ構築の参考にされてみてください。

僕のお気に入り ダウンロード