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