チュートリアル

はてなブックマークに追加 livedoorクリップに追加 Yahoo!ブックマークに追加 del.icio.usに追加 イザ!ブックマーク ニフティクリップに追加 Choixにブックマーク Buzzurlに追加

今日から作れる ガジェットって何?をクリアした人のための ガジェットHOW TO MAKE

サンプルで学ぶ「Y! Widget」の作り方 Y! Widget ガジェット

Yahoo! Widgets版GadgetsPepperをサンプルにしながら、Yahoo! Widgetsの作り方について学びます。先ず、ウィジェットを使ってみて動作を確認したいという方は「ウィジェットのインストール方法」へ、主要な6つの機能がどのように実装されているか概要を知りたい方は「主要機能の実装方法へ」、ソースコードをじっくり読みたい方は「ソースコードの詳説」へとお進みください。ソースコードの詳説では、解説アイコンにマウスをあてると、その部分の解説が表示されます。


ガジェットのインストール方法

Yahoo! Widgetsの実行にはYahoo! Widgets Engineが必要です。Yahoo! Widgets Engineのインストールはhttp://widgets.yahoo.co.jp/から行ってください。
通常のウィジェットは、拡張子".widget"のファイルをダブルクリックするだけ実行できます。GadgetsPepperの場合は、専用のインストーラーが用意されているので、インストーラーを実行してインストールを行ってください。実行もスタートメニューに登録されたアイコンから行うことができます。専用のインストーラはこちらからダウンロードして下さい。

主要機能の実装方法

主要機能の実装方法
機能 実装方法
(1)地図を移動 地図表示にはGoogle Maps APIを利用しています。テキストボックスに地名を入力し、エンターキーもしくは検索ボタンを押すと、findAddress()が呼び出されます。地名から緯度経度への変換(ジオコーディング)にはGoogleのジオコーディングAPIであるGClientGeocoderオブジェクトを使用しています。中心点の再設定は、
308: googleMaps.setCenter(point, initZoom, G_NORMAL_MAP)
で行っています。
(2)ジャンルを選択 ウィジェットロード処理load()内の、
151: oReq.open( "GET", urlGenreMaster );
でジャンルマスターを検索し、返ってきたXMLをonGenreLoad()でパースします。GenreName(ジャンル名)ノード、GenreCD(ジャンルコード)ノードを抽出し、その2つを使って、プルダウンリストの内容をセットします。
(3)グルメ店を検索 読込ボタンが押されると、onFindShops()が呼び出されます。実際の検索はfindAtPoint()内で行っており、地図中心点の緯度経度とジャンルコードを用いURLを組み立て、
272: oReq.open( "GET", url );
でグルメサーチAPIを呼び出します。結果のXMLにはグルメ店情報が含まれます。
(4)詳細画面を表示 グルメ店の緯度経度を用い、地図上にグルメ店の位置を表すマーカーをセットします。この時、マーカーにイベントリスナーを追加し、それがクリックされたときにsetShopInfo()が呼び出されるようにします。setShopInfo()内で、グルメ店の詳細表示用のレイヤをvisibleにし、グルメ店詳細情報を表示します。
(5)背景画像の変更 ガジェットにどのようなオプション設定ができるかは、先ず、タグでグループを作り、その中にタグを作って定義します。GadgetsPepperでは背景画像の選択ができるようにしてあります。選択されたオプションは
41: it.Skin = preferences.Skin.value;
のように参照できます。
(6)状態の保存と復帰 最後に行った検索の条件や背景画像設定の保存はunload()が呼ばれるタイミングで行います。実際の処理はsaveState()内で行われ、緯度経度、ジャンル等をデリミッタ"%00"でつなげた文字列を作り、それをCookieに保存します。
173: document.cookie = "pepper=" + ckstr + "; expires=Tue, 31-Dec-2030 23:59:59; ";
保存した内容の復帰はload()が呼ばれるタイミングでloadState()内で行います。

ソースコード詳説

code
  1: GadgetsPepper.kon
  2:
  3: <?xml version="1.0" encoding="UTF-8"?>
  4: <widget version="1.1" name="Gadgets Pepper">
  5: <debug>off</debug>
  6: <minimumVersion>3.0</minimumVersion>
  7:   <!-- メインウィンドウ(非表示) -->
  8:   <window> 解説
  9:     <name>mainWindow</name>
 10:     <title>GadgetsPepper</title>
 11:     <width>0</width>
 12:     <height>0</height>
 13:     <visible>false</visible>
 14:   </window>
 15:   <!-- 設定グループ -->
 16:     <prefGroup>
 17:     <name>generalPanel</name>
 18:     <title>全般</title>
 19:     <order>1</order>
 20:     </prefGroup>
 21:   <!-- 設定項目 -->
 22:   <preference name="Skin"> 解説
 23:     <title>背景画像:</title>
 24:     <type>popup</type>
 25:     <option>モザイク模様(赤)</option>
 26:     <optionValue>1</optionValue>
 27:     <option>モザイク模様(緑)</option>
 28:     <optionValue>2</optionValue>
 29:     <option>空と雲の風景</option>
 30:     <optionValue>3</optionValue>
 31:     <option>花柄模様</option>
 32:     <optionValue>4</optionValue>
 33:     <option>バブル模様(パープル)</option>
 34:     <optionValue>5</optionValue>
 35:     <defaultValue>1</defaultValue>
 36:     <group>generalPanel</group>
 37:   </preference>
 38:   <!-- 環境設定変更時のイベント処理 -->
 39:   <action trigger="onPreferencesChanged">
 40:     <![CDATA[
 41:       it.Skin = preferences.Skin.value;
 42:     ]]>
 43:   </action>
 44:   <!-- 画面ロード時のイベント処理 -->
 45:   <action trigger="onLoad">
 46:     <![CDATA[
 47:     var it;
 48:
 49:     // 画面が移動したときのイベント処理
 50:     //
 51:     function Com_Move(xpos, ypos){
 52:       mainWindow.hOffset = xpos;
 53:       mainWindow.vOffset = ypos;
 54:     }
 55:
 56:     // コンテキストメニュー<ウィジェットの環境設定>の処理
 57:     //
 58:     function Com_Setting(){
 59:       showWidgetPreferences();
 60:     }
 61:
 62:     // コンテキストメニューこのウィジェットを閉じる>の処理
 63:     //
 64:     function Com_Close(){
 65:       closeWidget();
 66:     }
 67:
 68:     // COMオブジェクト生成
 69:     try {
 70:       it = COM.createObject("GadgetsPepper.ComClass1"); 解説
 71:     } catch(e){
 72:       print( "\nError: " + e + "\n" ) ;
 73:       closeWidget();
 74:       exit;
 75:     }
 76:     // ファイル展開
 77:     widget.extractFile( "Resources/button1.png" );
 78:     widget.extractFile( "Resources/button2.png" );
 79:     widget.extractFile( "Resources/close.png" );
 80:     widget.extractFile( "Resources/HotScript.js" );
 81:     widget.extractFile( "Resources/HotStyle.css" );
 82:     widget.extractFile( "Resources/next.png" );
 83:     widget.extractFile( "Resources/prev.png" );
 84:     widget.extractFile( "Resources/shop_undock.png" );
 85:     widget.extractFile( "Resources/title.png" );
 86:     widget.extractFile( "Resources/b1.png" );
 87:     widget.extractFile( "Resources/b2.png" );
 88:     widget.extractFile( "Resources/b3.png" );
 89:     widget.extractFile( "Resources/b4.png" );
 90:     widget.extractFile( "Resources/b5.png" );
 91:     // 画面設定
 92:     it.Skin = preferences.Skin.value; 解説
 93:     it.X = mainWindow.hOffset;
 94:     it.Y = mainWindow.vOffset;
 95:     // COMオブジェクト接続
 96:     COM.connectObject( it, "Com_" ); 解説
 97:     // 画面表示
 98:     var url = convertPathToPlatform(widget.extractFile( "Resources/pepper.html" ));
 99:     it.Show(url);
100:     ]]>
101:   </action>
102: </widget>
103:
104: pepper.html
105: ?<html>
106:  <head>
107:   <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
108:   <title>Gadgets Pepper</title>
109:   <link href="HotStyle.css" rel="stylesheet" type="text/css" />
110:   <script src="http://maps.google.com/maps?file=api&v=2&key=(API KEY)" type="text/javascript" charset="utf-8"></script>
111:   <script type="text/javascript" src="HotScript.js"></script> 解説
112:   <script type="text/javascript">
113:   var urlGourmetSearch = "http://api.hotpepper.jp/GourmetSearch/V110/?key=guest";
114:   var urlGenreMaster = "http://api.hotpepper.jp/Genre/V110/?key=guest";
115:   var googleMaps = null;
116:   var shopNodeList = null;
117:   var curShopNode = 0;
118:   var lastSearchPoint = new GLatLng( 35.681074, 139.767079 );
119:   var lastSearchGenre = null;
120:   var initZoom = 13;
121:   var idIntervalTimer = null;
122:   var skinno = 0;
123:   var initZoom = 13;
124:   var idIntervalTimer = null;
125:   var geocoder = null;
126:
127: // HTML bodyのロード時
128: // 各種初期化処理
129: //
130:   function load() { 解説
131:     try{
132:       // Google Mapsの作成
133:       loadState();
134:       if( GBrowserIsCompatible() ){
135:         googleMaps = new GMap2( document.getElementById( "map" ) );
136:         googleMaps.addControl( new GSmallMapControl() );
137:         googleMaps.addControl( new GScaleControl() );
138:         googleMaps.enableScrollWheelZoom();
139:           geocoder = new GClientGeocoder();
140:         if( lastSearchPoint )
141:           googleMaps.setCenter( lastSearchPoint, initZoom, G_NORMAL_MAP );
142:       }
143:       // お店検索、ジャンル一覧を初期化する
144:       // HTTP Requestを実行し、onGenreLoadで処理
145:       var oReq = createHttpRequest();
146:       oReq.onreadystatechange = function(){
147:         if( oReq.readyState==4 ){ //readyState値は4で受信完了
148:           onGenreLoad( oReq );
149:         }
150:       }
151:       oReq.open( "GET", urlGenreMaster );
152:       oReq.send();
153:       setDockState();
154:       if( lastSearchPoint != null )
155:         findAtPoint( lastSearchPoint, lastSearchGenre );
156:     }
157:     catch( e ){}
158:   }
159:
160: // クッキーの設定
161: //
162:   function saveState(){ 解説
163:     try{
164:       ckstr = "";
165:       ckstr += 0 + "%00";
166:       if( lastSearchPoint != null ){
167:         ckstr += escape(lastSearchPoint.y) + "%00";
168:         ckstr += escape(lastSearchPoint.x) + "%00";
169:       }
170:       if( lastSearchGenre != null ){
171:         ckstr += escape(lastSearchGenre) + "%00";
172:       }
173:       document.cookie = "pepper=" + ckstr + "; expires=Tue, 31-Dec-2030 23:59:59; "
174:     }
175:     catch( e ){}
176:   }
177:
178: // HTML bodyのアンロード時
179: //
180:   function unload(){ 解説
181:     GUnload();
182:   }
183:
184: // クッキーの取得
185: //
186:   function loadState(){ 解説
187:     try{
188:       cklng = document.cookie.length;
189:       ckary = document.cookie.split("; ");
190:       ckstr = "";
191:       i = 0;
192:       while (ckary[i]){
193:         if (ckary[i].substr(0,7) == "pepper="){
194:           ckstr = ckary[i].substr(7,ckary[i].length);
195:           break;
196:         }
197:         i++;
198:       }
199:       ckary = ckstr.split("%00");
200:       if (ckary[0]) skinno  = eval(ckary[0]);
201:       if (ckary[1]) search_latitude = ckary[1];
202:       if (ckary[2]) search_longitude = ckary[2];
203:       if (ckary[3]) lastSearchGenre = ckary[3];
204:       if( search_latitude != null && search_longitude != null )
205:         lastSearchPoint = new GLatLng( search_latitude, search_longitude );
206:     }
207:     catch( e ){}
208:   }
209:
210: // ジャンル一覧のデータ取得
211: //
212:   function onGenreLoad( oReq ){ 解説
213:     try{
214:       var elmSel = document.getElementById( "genreSelect" );
215:       var oXMLDocument = oReq.responseXML;
216:       genreNodeList = oXMLDocument.selectNodes( "//Genre" );
217:       if( genreNodeList == null )
218:         return;
219:       for( var i = 0 ; i < genreNodeList.length ; i++ ){
220:         var node = genreNodeList.item( i );
221:         var ndName = node.selectSingleNode( "GenreName" );
222:         var ndCD = node.selectSingleNode( "GenreCD" );
223:         if(ndName != null && ndCD != null ){
224:           var opt = document.createElement( "OPTION" );
225:           elmSel.options.add( opt );
226:           opt.innerText = ndName.text;
227:           opt.value = ndCD.text;
228:           if( opt.value == lastSearchGenre )
229:             opt.selected = true;
230:         }
231:       }
232:     }
233:     catch( e ){}
234:   }
235:
236: // 時刻表示のための書式規格化
237: //
238:   function format(Num, Format){
239:     var s = new String(Num);
240:     var z = "";
241:     for(c = 0 ; c < Format - s.length ; c++){
242:       z = z + "0";
243:     }
244:     return (z + s);
245:   }
246:
247: // 現在時刻の表示
248: //
249:   function showCurrentTime(){
250:     var date = new Date();
251:     var y = date.getFullYear();
252:     var mo = date.getMonth();
253:     var d = date.getDate();
254:     var h = date.getHours();
255:     var m = date.getMinutes();
256:     var s = date.getSeconds();
257:       document.getElementById( "timeView" ).innerHTML = format(y,4) + "." + format(mo+1,2) + "." + format(d,2) + " " + format(h,2) + ":" + format(m,2); 解説
258:   }
259:
260: // 緯度経度による、お店の検索
261: //
262:   function findAtPoint( pt, genre ){
263:     var oReq = createHttpRequest();
264:     oReq.onreadystatechange = function(){
265:       if( oReq.readyState==4 ){ //readyState値は4で受信完了
266:         onXmlLoad( oReq, true, false );
267:       }
268:     }
269:     var url = urlGourmetSearch + "&Latitude=" + pt.y + "&Longitude=" + pt.x;
270:      if( ! isNullOrEmpty( genre ) )
271:       url = url + "&GenreCD=" + genre;
272:     oReq.open( "GET", url ); 解説
273:     oReq.send();
274:   }
275:
276: // お店検索の実行
277: //
278:   function onFindShops(){ 解説
279:     try{
280:       lastSearchPoint = googleMaps.getCenter();
281:       lastSearchGenre = document.getElementById( "genreSelect" ).value;
282:       findAtPoint( lastSearchPoint, lastSearchGenre );
283:     }
284:     catch( e ){//document.writeln( e );}
285:   }
286:
287: //住所検索のためのキーダウン処理
288: //
289:   function onKeydown(){ 解説
290:     if(event.keyCode == 13){findAddress();}
291:   }
292:
293: // 住所検索
294: //
295:   function findAddress(){
296:     try{
297:       var address = document.getElementById( "address" ).value;
298:       if( isNullOrEmpty( address ) )
299:           return;
300:       if (geocoder) {
301:         geocoder.getLatLng( 解説
302:         address,
303:
304:         function( point ) {
305:           if (!point) {
306: //           alert(address + " not found");
307:           } else {
308:             googleMaps.setCenter(point, initZoom, G_NORMAL_MAP );
309:           }
310:         });
311:       }
312:     }
313:     catch( e ){
314:       document.write("error = " + e.description + "<BR>");
315:       document.write("error # = " + e.number + "<BR>");
316:       document.writeln( e );
317:     }
318:   }
319:
320: // 地図上へのマーカーの追加
321: //
322:   function addMarker( pt, idx ){ 解説
323:     var marker = new GMarker( pt );
324:     GEvent.addListener( marker, "click", function(){
325:       setShopInfo( idx );
326:     } );
327:     googleMaps.addOverlay( marker );
328:   }
329:
330: // お店一覧のXMLデータ、読み込み処理
331: //
332:   function onXmlLoad( oReq, bSetMarker, bForceCenter ){ 解説
333:     try{
334:       var oXMLDocument = oReq.responseXML;
335:       shopNodeList = oXMLDocument.selectNodes( "//Shop" );
336:       if( shopNodeList == null )
337:         return;
338:       googleMaps.clearOverlays();
339:       for( var i = 0 ; i < shopNodeList.length ; i++ ){
340:         var node = shopNodeList.item(i);
341:         var ndLatitude = node.selectSingleNode( "Latitude" );
342:         var ndLongitude = node.selectSingleNode( "Longitude" );
343:         if( ndLatitude != null && ndLongitude != null ){
344:           var latitude = parseFloat( ndLatitude.text );    // 経度
345:           var longitude = parseFloat( ndLongitude.text );    // 緯度
346:           if( bSetMarker ){
347:             addMarker( new GPoint( longitude, latitude ), i );
348:           }
349:           if( bForceCenter ){
350:             googleMaps.setCenter( new GLatLng( latitude, longitude ) );
351:             bForceCenter = true;
352:           }
353:         }
354:       }
355:     }
356:     catch( e ){}
357:   }
358:
359: // お店情報を表示設定する
360: //
361:   function setShopInfo( idx ){ 解説
362:     if( shopNodeList == null || idx < 0 || shopNodeList.length <= idx )
363:       return;
364:     curShopNode = idx;
365:     document.getElementById("shop").style.visibility = "visible";
366:     node = shopNodeList.item( idx );
367:     var ndShopName = node.selectSingleNode( "ShopName" );
368:     var ndShopURL = node.selectSingleNode( "ShopUrl" );
369:     var ndGenreCatch = node.selectSingleNode( "GenreCatch" );
370:     var ndPictureURL;
371:     ndPictureURL = node.selectSingleNode( "PictureUrl/PcLargeImg" );
372:     if( ndShopName != null )
373:       document.getElementById( "shopName" ).innerHTML = ndShopName.text;
374:     if( ndShopURL != null )
375:       document.getElementById( "shopName" ).setAttribute( "href", ndShopURL.text );
376:     if( ndGenreCatch != null )
377:       document.getElementById( "genreCatch" ).innerHTML = ndGenreCatch.text;
378:     if( ndPictureURL != null )
379:       document.getElementById( "shopPicture" ).setAttribute( "src", ndPictureURL.text );
380:     if( ( curShopNode + 1 ) < shopNodeList.length ) 解説
381:       document.getElementById("btnNext").style.visibility = "inherit";
382:     else
383:       document.getElementById("btnNext").style.visibility = "hidden";
384:     if( curShopNode > 0 )
385:       document.getElementById("btnPrev").style.visibility = "inherit";
386:     else
387:       document.getElementById("btnPrev").style.visibility = "hidden";
388:   }
389:
390: // 前のお店情報に移動する
391: //
392:   function prevShop(){
393:     if( curShopNode > 0 ){
394:       setShopInfo( curShopNode -1 );
395:     }
396:   }
397:
398: // 次のお店情報に移動する
399: //
400:   function nextShop(){
401:     if( ( curShopNode + 1 ) < shopNodeList.length ){
402:       setShopInfo( curShopNode + 1 );
403:     }
404:   }
405:
406: // お店情報表示を閉じる
407: //
408:   function closeShopInfo(){ 解説
409:     document.getElementById("shop").style.visibility = "hidden";
410:   }
411:
412: // 表示を設定する
413: //
414:   function setDockState(){ 解説
415:       document.getElementById( "shopName" ).style.fontSize = "10px";
416:       document.getElementById( "genreCatch" ).style.fontSize = "10px";
417:       document.body.style.width = "320px";
418:       document.body.style.height = "320px";
419:       document.getElementById( "base").style.width = "320px";
420:       document.getElementById( "base").style.height = "320px";
421:       cklng = document.cookie.length;
422:       ckary = document.cookie.split("; ");
423:       ckstr = "";
424:       i = 0;
425:       while (ckary[i]){
426:         if (ckary[i].substr(0,7) == "pepper="){
427:           ckstr = ckary[i].substr(7,ckary[i].length);
428:           break;
429:         }
430:         i++;
431:       }
432:       // 背景の状態を設定する
433:       var n = eval(ckstr.substring(0,1));
434:       if( n != null ){
435:         switch( n ){
436:           case 2:
437:             document.getElementById("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=b2.png)";
438:             document.getElementById( "timeView" ).style.color = "#8B795E";
439:             break;
440:           case 3:
441:             document.getElementById("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=b3.png)";
442:             document.getElementById( "timeView" ).style.color = "#8B795E";
443:             break;
444:           case 4:
445:             document.getElementById("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=b4.png)";
446:             document.getElementById( "timeView" ).style.color = "#8B795E";
447:             break;
448:           case 5:
449:             document.getElementById("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=b5.png)";
450:             document.getElementById( "timeView" ).style.color = "#8B795E";
451:             break;
452:           default:
453:             document.getElementById("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=b1.png)";
454:             document.getElementById( "timeView" ).style.color = "#8B795E";
455:             break;
456:         }
457:       }
458:       document.getElementById( "titlelogo").style.position = "relative";
459:       document.getElementById( "address").style.position = "relative";
460:       document.getElementById( "Button1").style.position = "relative";
461:       document.getElementById( "btnPrev").style.position = "relative";
462:       document.getElementById( "btnNext").style.position = "relative";
463:       document.getElementById( "Button4").style.position = "relative";
464:       document.getElementById( "genreSelect").style.position = "relative";
465:       idIntervalTimer = setInterval( "showCurrentTime()", 1000 );
466:   }
467:   </script>
468: </head>
469:
470: <body onload="load()" onunload="unload()">
471:   <div id="base">
472:     <table width="100%" cellpadding="4px" cellspacing="4px">
473:       <tr style="height:20px;">
474:         <td>
475:           <span id="titlelogo" style="width:132px;height:22px;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=title.png);" onclick="saveState()"></span>
476:         </td>
477:         <td align="right"><b><span id="timeView"></span></b></td>
478:       </tr>
479:       <tr>
480:         <td valign="middle">
481:           <input id="address" style="width:180px;" type="text" onKeyPress="onKeydown()" />
482:         </td>
483:         <td>
484:           <span id="Button4" style="cursor:hand;width:117px;height:20px;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=button1.png);" onclick="findAddress()"></span>
485:         </td>
486:       </tr>
487:       <tr>
488:         <td colspan="2">
489:           <div id="map" style="width:304px;height:210px;" onmousedown=""></div>
490:         </td>
491:       </tr>
492:       <tr>
493:         <td valign="middle" colspan="2">
494:           <select id="genreSelect" style="cursor:hand;width:200px;"><option value="">全てのジャンル</option></select>
495:           <span id="Button1" style="cursor:hand;width:97px;height:20px;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=button2.png)" onclick="onFindShops()"></span></td>
496:         </td>
497:       </tr>
498:     </table>
499:     <div id="poweredby" style="color:#363636;font-size: 9px; width:300px;text-align:right;" >Powered by ホットペッパー.jp</div>
500:   </div>
501:   <div id="shop" align="center" style="width:283px;height:195px;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=shop.png)" >
502:     <table width="100%" cellpadding="4px" cellspacing="4px">
503:       <tr valign="top" style="height:25px">
504:         <td colspan="2" align="right">
505:           <div id="Button1" style="cursor:hand;width:21px;height:21px;background-image:url(close.png)" onclick="closeShopInfo()"></div>
506:         </td>
507:       </tr>
508:       <tr valign="top" style="height:15px;">
509:         <td colspan="2">
510:           <a id="shopName" target="_blank"></a>
511:         </td>
512:       </tr>
513:       <tr valign="top" style="height:100px;">
514:         <td style="text-align:left;width:110px;" >
515:           <img id="shopPicture" src="null" alt="写真" style="width:100px;height:100px;"/></td>
516:         <td align="left">
517:           <span id="genreCatch"></span>
518:         </td>
519:       </tr>
520:       <tr valign="bottom">
521:         <td align="left">
522:           <span id="btnPrev" style="cursor:hand;width:22px;height:22px;background-image:url(prev.png)" onclick="prevShop()"></span>
523:         </td>
524:         <td align="right">
525:           <span id="btnNext" style="cursor:hand;width:22px;height:22px;background-image:url(next.png)" onclick="nextShop()"></span>
526:         </td>
527:       </tr>
528:     </table>
529:   </div>
530: </body>
531: </html>
ウィジェットのタイトルなど、ウィジェット属性情報を設定します。 設定できるオプションを定義しています。GadgetsPepperでは、背景画像を選ぶことができるようにしています。 COM(Component Object Model)はWindowsに備わっているWindowsプログラムを部品化する仕組みですが、Yahoo! WidgetsにもCOMを呼び出する仕組みがあります。COM形式のコンポーネントを作成するにはVisualBasic などWindows用のコンパイラが必要となります。Yahoo! Widgetsから呼びだす際にはCOM.createObject()という関数を使い、COMコンポーネントそれぞれに割り当てられているProgIDを渡すようにします。 オプション設定の読み込みを行っています。オプション設定の読み込みにはPreferencesオブジェクトを使用します。ここでは、オプション設定によって設定されている背景画像の種類(値)を、COMのプロパティを通じて、画面の方に反映しています。 Windowsプログラムとのやり取りにはCOMのプロパティとイベントを利用しています。プロパティの利用は比較的容易ですが、イベントを利用するためには、COM.connectObject()という関数を用い、また、イベントに対応する関数をJavascriptで用意しする必要があります。このとき関数名の先頭部分をCOM.connectObject()の引数と一意させると連動するようになります。ここでは引数に"Com_"を渡しているため、Com_○○という名前の関数がイベントに対応しています。 HotScript.jsを読み込みます。HotScript.jsではXMLHttpRequestオブジェクトの生成、変数のnullの判定、URLエンコードなど、プログラムで繰り返し使用されるファンクションをまとめて記述しています。 HTML bodyがロードされた際に、各種初期化処理を行います。GMap2オブジェクトの生成および地図の初期表示、GClientGeocoderオブジェクトの生成、料理ジャンルの一覧の初期化、背景画像の設定、時計表示のためのインターバルタイマーの設定。ガジェットが前回終了した時の状態を復帰する処理も行います。 Cookieを設定します。第三者サイト由来のCookie情報取得に対して、ブラウザがエラーを起こすため、保存情報を連結した形式でCookieを作成しています。 HTML bodyがアンロードされた時に、アプリケーションの状態を保存します。 Cookieを取得します。連結された情報を分解し、そこから検索条件を再構築しています。 ジャンルマスターを含むXMLデータを処理します。GenreName(ジャンル名)ノード、GenreCD(ジャンルコード)ノードの値をHTMLのOPTION要素に設定し、プルダウンメニューを設定します。 ウィジェットの右上に表示される日付と時間をセットします。 緯度経度、料理ジャンルを指定して、グルメサーチAPIを呼び出します(お店を検索します)。HTTP Requestで取得したデータはonXmlLoad()で処理します。 「読込」ボタンが押されたときに呼び出されます。地図中心の緯度経度、選択されている料理ジャンルを取得し、findAtPoint()を実行します。 住所検索時のリターンキーを処理します。キーコードが13であれば、findAddress()を実行します。 ジオコーダーGClientGeocorderを用い住所を緯度経度に変換します。変換できればその位置に地図中心点を移動、変換できなかった場合はエラーメッセージを表示します。 地図にマーカーを追加します。addListenerメソッドで、マーカーがクリックされた時にsetShopInfo()が呼ばれるようにします。 グルメ検索APIの結果データを処理します。Shop(グルメ店)ノードを詳細画面表示用に保存します。また、Latitude(緯度)ノードとLongitude(経度)ノードを使い、地図にグルメ店マーカーを追加します。 onXmlLoad()内で保存したShop(グルメ店)ノードのリストから、idx番目のお店の情報を表示します。ShopName(店名)、ShopUrl(店URL)、GenreCatch(店キャッチコピー)、PcLargeImg(店画像URL)の各ノードの値を取得し、該当するHTML要素にセット。グルメ店詳細情報の表示は、div要素のvisibilityスタイルをhiddenからvisibleに変えることで行います。 idxの値により「前へ」「次へ」ボタンの表示、非表示を制御します。 表示されているグルメ店詳細情報を閉じます。グルメ店詳細情報のdiv要素をhiddenにします。 ウィジェットの表示状態を設定します。背景画像の設定もここで行っています。
Google Gadget チュートリアル Google Desktop Gadget チュートリアル Live Gadget チュートリアル Vista Sidebar Gadget チュートリアル Y! Widget チュートリアル