1: ?<?xml version="1.0" encoding="UTF-8" ?>
2: <Module>
3: <ModulePrefs title="Gadgets Pepper" directory_title="" description="" screenshot="" title_url="" author="" author_email="" author_link="" author_location="" author_affiliation="None" scrolling="false" width="210" height="217" />
4: <UserPref name="backcolor"> 設定できるオプションを定義しています。GadgetsPepperでは、背景画像を選ぶことができるようにしています。
5: display_name="背景画像の変更"
6: datatype="enum"
7: default_value="1">
8: <EnumValue value="1" display_value="モザイク模様(赤)"/>
9: <EnumValue value="2" display_value="モザイク模様(緑)"/>
10: <EnumValue value="3" display_value="空と雲の風景"/>
11: <EnumValue value="4" display_value="花柄模様"/>
12: <EnumValue value="5" display_value="バブル模様(パープル)"/>
13: </UserPref>
14: <Content type="html">
15: <
28: var urlGourmetSearch = "http://api.hotpepper.jp/GourmetSearch/V110/?key=guest";
29: var urlGenreMaster = "http://api.hotpepper.jp/Genre/V110/?key=guest";
30: var site_url = "http://www.tkrb.jp/gadgets/pepper/gd/";
31: var googleMaps = null;
32: var smallMapCtrl = null;
33: var shopNodeList = null;
34: var curShopNode = 0;
35: var lastSearchPoint = new GLatLng( 35.681074, 139.767079 );
36: var lastSearchGenre = null;
37: var initZoom = 13;
38: var idIntervalTimer = null;
39:
40: // 画面ロード時の処理
41: //
42: function load(){
43: try{
44: loadState();
45: if( GBrowserIsCompatible() ){
46: googleMaps = new GMap2( _gel( "map" ) ); 
47: smallMapCtrl = new GSmallMapControl();
48: googleMaps.enableScrollWheelZoom();
49: if( lastSearchPoint )
50: googleMaps.setCenter( lastSearchPoint, initZoom, G_NORMAL_MAP );
51: }
52: _IG_FetchXmlContent(urlGenreMaster, function (response) { 
53: if (response == null || typeof(response) != "object" || response.firstChild == null) {
54: return;
55: }else{
56: onGenreLoad( response );
57: }
58: });
59: setDockState();
60: if( lastSearchPoint != null )
61: findAtPoint( lastSearchPoint, lastSearchGenre );
62: }
63: catch( e ){}
64: }
65:
66: // ジャンル一覧を取得する処理
67: //
68: function onGenreLoad( response ){
69: try{
70: var elmSel = _gel( "genreSelect" );
71: genreNodeList = response.getElementsByTagName( "Genre" );
72: if( genreNodeList == null )
73: return;
74: for( var i = 0 ; i < genreNodeList.length ; i++ ){
75: var node = genreNodeList.item( i );
76: var ndName = node.selectSingleNode( "GenreName" );
77: var ndCD = node.selectSingleNode( "GenreCD" );
78: if( ndName != null && ndCD != null ){
79: var opt = document.createElement( "OPTION" );
80: elmSel.options.add( opt ); 
81: opt.innerText = ndName.text;
82: opt.value = ndCD.text;
83: if( opt.value == lastSearchGenre )
84: opt.selected = true;
85: }
86: }
87: }
88: catch( e ){}
89: }
90:
91: // クッキーに状態を保存する処理
92: //
93: function saveState(){
94: try{
95: if( lastSearchPoint != null ){
96: setCookie( "search_latitude", lastSearchPoint.y );
97: setCookie( "search_longitude", lastSearchPoint.x );
98: }
99: if( lastSearchGenre != null ){
100: setCookie( "search_genre", lastSearchGenre );
101: }
102: }
103: catch( e ){}
104: }
105:
106: // 画面がアンロード時の処理
107: //
108: function unload(){
109: saveState();
110: GUnload();
111: }
112:
113: // クッキーから状態をロードする処理
114: //
115: function loadState(){
116: try{
117: var search_latitude = getCookie( "search_latitude" );
118: var search_longitude = getCookie( "search_longitude" );
119: lastSearchGenre = getCookie( "search_genre" );
120: if( ! isNullOrEmpty( search_latitude ) && ! isNullOrEmpty( search_longitude ) )
121: lastSearchPoint = new GLatLng( parseFloat( search_latitude ), parseFloat( search_longitude ) );
122: }
123: catch( e ){}
124: }
125:
126: // クッキーに値を設定する関数
127: //
128: function setCookie(fieldId,value){
129: var expires = new Date();
130: expires.setTime(expires.getTime() + (1*24*60*60*1000));
131: document.cookie=fieldId+"="+escape(value)+"; expires="+expires.toGMTString();
132: }
133:
134: // クッキーから値を取得する関数
135: //
136: function getCookie(fieldId){
137: var tmp=(document.cookie+";").match(fieldId+"=([^;]*);");
138: if(tmp!=null){
139: return unescape(tmp[1]);
140: }else{
141: return null;
142: }
143: }
144:
145: // 数字を0詰めする関数
146: //
147: function format(Num, Format){
148: var s = new String(Num);
149: var z = "";
150: for(c = 0 ; c < Format - s.length ; c++){
151: z = z + "0";
152: }
153: return (z + s);
154: }
155:
156: // 現在時刻を表示する処理
157: //
158: function showCurrentTime(){
159: var date = new Date();
160: var y = date.getFullYear();
161: var mo = date.getMonth();
162: var d = date.getDate();
163: var h = date.getHours();
164: var m = date.getMinutes();
165: var s = date.getSeconds();
166: _gel( "timeView" ).innerHTML = format(y,4) + "." + format(mo+1,2) + "." + format(d,2) + " " + format(h,2) + ":" + format(m,2);
167: }
168:
169: // お店を検索する処理
170: //
171: function findAtPoint( pt, genre ){
172: var url = urlGourmetSearch + "&Latitude=" + pt.y + "&Longitude=" + pt.x;
173: if( ! isNullOrEmpty( genre ) )
174: url = url + "&GenreCD=" + genre;
175: _IG_FetchXmlContent(url, function (response) {
176: if (response == null || typeof(response) != "object" || response.firstChild == null) {
177: return;
178: }else{
179: onXmlLoad( response , true, false);
180: }
181: });
182: }
183:
184: // お店情報読み込みボタンを押した時の処理
185: //
186: function onFindShops(){
187: try{
188: lastSearchPoint = googleMaps.getCenter();
189: lastSearchGenre = _gel( "genreSelect" ).value;
190: findAtPoint( lastSearchPoint, lastSearchGenre );
191: }
192: catch( e ){}
193: }
194:
195: // 住所検索のテキストボックス上でキーを押した時の処理
196: //
197: function onKeydown(){
198: if(event.keyCode == 13){findAddress();}
199: }
200:
201: // 住所検索を行うときの処理
202: //
203: function findAddress(){
204: try{
205: var address = _gel( "address" ).value;
206: if( isNullOrEmpty( address ) )
207: return;
208: var url = urlGourmetSearch + "&ShopAddress=" + encodeURL( address );
209: _IG_FetchXmlContent(url, function (response) {
210: if (response == null || typeof(response) != "object" || response.firstChild == null) {
211: return;
212: }else{
213: onXmlLoad( response , false, true);
214: }
215: });
216: }
217: catch( e ){}
218: }
219:
220: // 地図にマーカーを追加する処理
221: //
222: function addMarker( pt, idx ){
223: var marker = new GMarker( pt );
224: GEvent.addListener( marker, "click", function(){
225: setShopInfo( idx );
226: } );
227: googleMaps.addOverlay( marker );
228: }
229:
230: // ホットペッパーAPIの検索結果にもとづいてXMLを解析する処理
231: //
232: function onXmlLoad( response, bSetMarker, bForceCenter ){
233: try{
234: shopNodeList = response.getElementsByTagName( "Shop" );
235: if( shopNodeList == null )
236: return;
237: googleMaps.clearOverlays();
238: for( var i = 0 ; i < shopNodeList.length ; i++ ){
239: var node = shopNodeList.item(i);
240: var ndLatitude = node.selectSingleNode( "Latitude" );
241: var ndLongitude = node.selectSingleNode( "Longitude" );
242: if( ndLatitude != null && ndLongitude != null ){
243: var latitude = parseFloat( ndLatitude.text ); // 経度
244: var longitude = parseFloat( ndLongitude.text ); // 緯度
245: if( bSetMarker ){
246: addMarker( new GPoint( longitude, latitude ), i );
247: }
248: if( bForceCenter ){
249: googleMaps.setCenter( new GLatLng( latitude, longitude ) );
250: bForceCenter = true;
251: }
252: }
253: }
254: }
255: catch( e ){}
256: }
257:
258: // お店情報を表示する処理
259: //
260: function setShopInfo( idx ){
261: if( shopNodeList == null || idx < 0 || shopNodeList.length <= idx )
262: return;
263: curShopNode = idx;
264: _gel("shop").style.visibility = "visible";
265: _gel( "find" ).style.visibility = "hidden";
266: _gel( "genre" ).style.visibility = "hidden";
267: node = shopNodeList.item( idx );
268: var ndShopName = node.selectSingleNode( "ShopName" );
269: var ndShopURL = node.selectSingleNode( "ShopUrl" );
270: var ndGenreCatch = node.selectSingleNode( "GenreCatch" );
271: var ndPictureURL;
272: ndPictureURL = node.selectSingleNode( "PictureUrl/PcSmallImg" );
273: if( ndShopName != null )
274: _gel( "shopName" ).innerHTML = ndShopName.text;
275: if( ndShopURL != null )
276: _gel( "shopName" ).setAttribute( "href", ndShopURL.text );
277: if( ndGenreCatch != null )
278: _gel( "genreCatch" ).innerHTML = ndGenreCatch.text;
279: if( ndPictureURL != null )
280: _gel( "shopPicture" ).setAttribute( "src", ndPictureURL.text );
281: if( ( curShopNode + 1 ) < shopNodeList.length )
282: _gel("btnNext").style.visibility = "inherit";
283: else
284: _gel("btnNext").style.visibility = "hidden";
285: if( curShopNode > 0 )
286: _gel("btnPrev").style.visibility = "inherit";
287: else
288: _gel("btnPrev").style.visibility = "hidden";
289: }
290:
291: // 前のお店情報に移動する
292: //
293: function prevShop(){
294: if( curShopNode > 0 ){
295: setShopInfo( curShopNode -1 );
296: }
297: }
298:
299: // 次のお店情報に移動する
300: //
301: function nextShop(){
302: if( ( curShopNode + 1 ) < shopNodeList.length ){
303: setShopInfo( curShopNode + 1 );
304: }
305: }
306:
307: // お店情報表示を閉じる
308: //
309: function closeShopInfo(){
310: _gel("shop").style.visibility = "hidden";
311: _gel( "find" ).style.visibility = "visible";
312: _gel( "genre" ).style.visibility = "visible";
313: }
314:
315: // 画面の状態を設定する処理
316: //
317: function setDockState(){
318: if (document.body.clientWidth < 210 ) {
319: googleMaps.removeControl( smallMapCtrl );
320: _gel( "base" ).style.left = "0px";
321: _gel( "find" ).style.left = "0px";
322: _gel( "genre" ).style.left = "0px";
323: }else{
324: googleMaps.addControl( smallMapCtrl );
325: _gel( "base" ).style.left = "80px";
326: _gel( "find" ).style.left = "80px";
327: _gel( "genre" ).style.left = "80px";
328: }
329: _gel( "shopName" ).style.fontSize = "10px";
330: _gel( "genreCatch" ).style.fontSize = "10px";
331: _gel( "titlelogo" ).style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "titlelogo.png)";
332: _gel( "findaddress" ).style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "findaddress.png)";
333: _gel( "findshop" ).style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "findshop.png)";
334: _gel( "close" ).style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "close.png)";
335: _gel( "btnPrev" ).style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "prev.png)";
336: _gel( "btnNext" ).style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "next.png)";
337: var n = prefs.getInt("backcolor");
338: if( n != null ){
339: switch( n ){
340: case 2: 
341: _gel("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "b2.png)";
342: _gel( "timeView" ).style.color = "#CCCC66";
343: break;
344: case 3:
345: _gel("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "b3.png)";
346: _gel( "timeView" ).style.color = "#BFCBD4";
347: break;
348: case 4:
349: _gel("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "b4.png)";
350: _gel( "timeView" ).style.color = "#8B795E";
351: break;
352: case 5:
353: _gel("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "b5.png)";
354: _gel( "timeView" ).style.color = "#F2D7E7";
355: break;
356: default:
357: _gel("base").style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + site_url + "b1.png)";
358: _gel( "timeView" ).style.color = "#8B795E";
359: break;
360: }
361: }
362: _gel( "address").style.position = "relative";
363: _gel( "findaddress").style.position = "relative";
364: _gel( "findshop").style.position = "relative";
365: _gel( "close").style.position = "relative";
366: _gel( "btnPrev").style.position = "relative";
367: _gel( "btnNext").style.position = "relative";
368: _gel( "genreSelect").style.position = "relative";
369: idIntervalTimer = setInterval( "showCurrentTime()", 1000 );
370: }
371: </script>
372:
373: </head>
374: <body onload="load()" onunload="unload()" onresize="setDockState()">
375: <div align="center">
376: <div style="color:#363636;font-size: 7px; text-align:right;" >Powered by ホットペッパー.jp</div>
377: <div id="map" style="width:210px;height:210px;"></div>
378: <div id="items" align="left">
379: <div id="base" style="text-align:center;"><div id="titlelogo"></div><b><div style="font-size: 9px;text-align:right;" id="timeView"></div></b></div>
380: <div id="find">
381: <input id="address" style="font-size: 10px; width:80px;" type="text" onKeyDown="onKeydown()"/>
382: <span id="findaddress" style="cursor:hand; width:41px; height:20px;" onclick="findAddress()"></span>
383: </div>
384: <div id="genre">
385: <select id="genreSelect" style="font-size:10px;"><option value="">全てのジャンル</option></select>
386: <span id="findshop" style="cursor:hand; width:41px; height:20px;" onclick="onFindShops()"></span>
387: </div>
388: </div>
389: <div id="shop">
390: <table width="100%" cellpadding="0px" cellspacing="0px">
391: <tr>
392: <td align="right" valign="top" margin="0px" padding="0px">
393: <span id="close" style="font-size:9px; cursor:hand;width:11px;height:11px;" onclick="closeShopInfo()"></span>
394: </td>
395: </tr>
396: <tr>
397: <td>
398: <p style="width:120px; height:30px; overflow: hidden; text-align:left; ">
399: <a id="shopName" target="_blank" alt=""></a>
400: </p>
401: </td>
402: </tr>
403: </table>
404: <table width="100%" cellpadding="2px" cellspacing="2px">
405: <tr valign="top" style="height:80px">
406: <td style="text-align:center; width:60px; height:60px;" >
407: <img id="shopPicture" src="null" alt="写真"/></td>
408: <td align="left">
409: <span id="genreCatch" style="overflow: hidden;"></span>
410: </td>
411: </tr>
412: <tr style="height:12px;">
413: <td align="left">
414: <span id="btnPrev" style="font-size:9px;cursor:hand;width:11px;height:11px;" onclick="prevShop()"></span>
415: </td>
416: <td align="right">
417: <span id="btnNext" style="font-size:9px;cursor:hand;width:11px;height:11px;" onclick="nextShop()"></span>
418: </td>
419: </tr>
420: </table>
421: </div>
422: </div>
423: </body>
424: </html>
425: ]]>
426: </Content>
427: </Module>
ガジェットのタイトルなど、ガジェット属性情報を設定します。
設定できるオプションを定義しています。GadgetsPepperでは、背景画像を選ぶことができるようにしています。
HotScript.jsを読み込みます。HotScript.jsではXMLHttpRequestオブジェクトの生成、変数のnullの判定、URLエンコードなど、プログラムで繰り返し使用されるファンクションをまとめて記述しています。
オプション設定を読み込むための準備。_IG_Prefs()を呼ぶことによって、ユーザー設定のオブジェクトにアクセスすることができるようになります。
Google Maps APIで地図の表示、コントロールボタンの追加、中心点の移動を行います。
ホットペッパーAPIを呼び出し、ジャンル一覧を取得します。
ドキュメント要素genreSelectを変数に設定します。iGoogleでは、document.getElementById()の変わりに_gel()という専用の関数を使用します。Google Desktop専用のSDKを用いた開発では、≶edit>タグや≶label>タグといった独自タグで画面を構成できます。値の取得や代入には、innerTextを使います。
例: label1.innerText = "abc"/span>
ホットペッパーAPIで取得したジャンル一覧をプルダウンメニューにセットします。
最後に検索された場所の緯度経度、料理ジャンルをCookieに保存します。
ガジェットの右上に表示される日付と時間をセットします。
iGoogleで、ホットペッパーAPIのようなWebサービスからXMLデータを取得する場合、固有の関数_IG_FetchXmlContent()を使います。通常、閲覧しているドメイン外のURLを直接参照しようとするとブラウザでセキュリティーエラーが出ますが、この関数を使用することでそれを回避できます。この関数がプロキシとして動作してくれるようです。Google Desktop専用のSDKを用いた開発では、普通にXMLHttpRequestオブジェクトを使用することができます。 例: req.open ("GET",url, false); req.send();
ジャンル選択プルダウンリストの現在の選択値を取得します。ここでも_gel()を用います。
マーカーにイベントリスナーを追加します。マーカーをクリックした時に、呼ばれる関数をここで設定します。
グルメ店の緯度経度を用い、地図上にグルメ店の位置を表すマーカーを表示します。
グルメ店の詳細情報を表示するため、詳細情報レイヤ<div id="shopquot;gt;のvisibilityスタイルをvisibleにします。この際、詳細情報レイヤのpositionを絶対位置指定(absolute)にし、ちょうど地図の上に重ねて表示することで、地図画面と詳細画面が切り変わったように見せています。 Google Desktop専用のSDKを用いた開発では、スタイルシートではなくタグにvisible設定の機能が備わっており、それを使用することができます。 例: Item1.visible = true;
"
visibilityスタイルをhiddenにすることで、グルメ店の詳細情報レイヤを閉じます。
IE7、FireFoxで扱える透過PNGがIE6ではサポートされません。そこで、Filter機能を使い、IE6でも透過PNGが表示できるようにしています。通常、ブラウザ上で展開するiGoogleで動作させる場合には、FireFox等を考慮して透過PNGをサポートしているブラウザのための処理を別途、記述しますが、今回はデスクトップ上での展開となるGoogleDesktopで動作させることが前提であるため、FireFox上で動作させるための処理を割愛しています。
オプション設定を取得します。GadgetsPepperの場合、背景画像の番号を取得し、その値によって処理を振り分けています。
ガジェット右上に表示される日時を再表示する間隔を設定します。
|