GPS機能とTwitterを連携させて指定エリア周辺のつぶやきを表示する
TwitterのSearch APIを利用して「池袋駅から半径3km」のつぶやきを自動で連続取得・表示します。
用意するファイル
- index.html
- style.css
- js/modernizr.js
- js/geo.js
ファイルの内容
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="initial-scale=1.0, user-scalable=no"> <title>GPS機能とTwitterの連携</title> <link rel="stylesheet" href="style.css"> <style> body { background: #75B98C; } header { height: 40px; background:#508368; display: block; } h1 { color: #fff; font-size: 1.2em; font-family:Verdana, Geneva, sans-serif; font-weight: bold; text-align: center; padding: 6px 0 0 10px; } /* ツイッターモジュール */ #twitterModule { width: 300px; height: 300px; overflow: hidden; border: 10px solid #75B98C; position: absolute; left: 0; top: 40px; background: #fff; } #twitterModule .title { position: relative; padding:0 2px 6px 6px; background: #75B98C; color: #F2F2F2; font-size: 18px; } /* ツイートブロック */ #area-tweets p{ padding: 1em; font-size:14px; line-height:1.2em; } #area-tweets p a{ color: #3774ed; text-decoration:none; } #area-tweets p a.reply { color: #666; } #area-tweets .user, #area-tweets .date { padding-right: 1em; font-style: normal; font-weight:noral; color: #999; text-decoration: none; } /* 住所入力エリア */ #twitterModule .area-input{ position: absolute; bottom: 0; width: 100%; padding-top: 4px; background: #75B98C; } #twitterModule .area-input input { padding: 5px 12px 2px; width: 275px; height: 16px; font-size: 12px; border: 0; border-radius: 12px; -moz-border-radius: 12px; -webkit-border-radius:12px; } #area-tweets .address { padding: 1em; color: #279572; font-weight: bold; } </style> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script src="http://maps.google.com/maps/api/js?sensor=false"></script> <script src="js/modernizr.js"></script> <script src="js/geo.js"></script> </head> <body> <div id="container"> <header> <h1>Twitter×Geocode</h1> </header> <div id="wrapper"> <div id="twitterModule"> <p class="title">Twitter Module</p> <div id="area-tweets"> </div> <form class="area-input"><input type="text" value=""></form> </div> </div> <footer></footer> </div> </body> </html>
style.css
article, aside, canvas,details,figcaption,figure,footer,header,hgroup,menu,nav, section, summary{display:block;} html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr, address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl, dt,dd,ol,ul,li,fieldset,form,label, legend,table,caption,tbody,tfoot,thead,tr,th,td, article,aside,canvas,details,figcaption,figure,footer,header,hgroup,menu,nav,section,summary,time,mark,audio,video{margin:0; padding:0; border:0; outline:0; font-size:100%; font-style:normal; font-weight:normal; vertical-align:baseline; background:tranparent;} body{font:16px/1.5em sans-serif;} li{list-style:none;} img{vertical-align:bottom;} :focus{outline:0;}
js/geo.js
(function($){ /*設定*/ var geo={ twitter:"http://search.twitter.com/search.json", lat:35.724442, //緯度 lng:139.715447, //経度 rad:3, //半径(km) addr:"", //表示されている住所・施設名 timerGetTweet: 0, //getTweet関数のループを止めるためのタイマーID格納用 google: new google.maps.Geocoder() //このgeo.googleを通じてMaps APIを利用します。 }; /* 実行 */ $(function(){ getTweet(); slideTweet(); $("#area-tweets").hover(function(){ $(this).find("p").first().stop(); }, function() { slideTweet(); }); //form要素のsubmit時 $("#twitterModule .area-input").submit(function(){ getAddress(); //sumitiイベントのデフォルト動作(action属性への遷移。この場合は同じページの再読込み)をキャンセルし、リロードしないようにします。 return false; }); // input要素のblur時 $("#twitterModule input").blur(function() { getAddress(); }); }); /* 入力 */ function getAddress(){ // var value = $("#twitterModule input").attr("value"); var value = $("#twitterModule input").val(); //表示内容と入力内容の間に変更がなかった場合は何もしません。 if (geo.addr==value){return false; } //入力内容を表示内容として保存します。 geo.addr=value; //getGeocode関数を実行します。 getGeocode(); } /* 取得位置情報 */ function getGeocode(){ if(!geo.google){ return false; } geo.google.geocode({"address": geo.addr},function(results, status) { if(status==google.maps.GeocoderStatus.OK){ geo.lat=results[0].geometry.location.lat(); geo.lng=results[0].geometry.location.lng(); } //すでに登録されたツイートを削除するためresetTweetを実行します。 resetTweet(); //getTweetは30秒ごとに実行するようになっていますので、タイマーIDを使って、いったんそのタイマーを中断します。 clearTimeout(geo.timerGetTweet); //新たにgetTweetを実行します。 getTweet(); }); } /* 消去 */ function resetTweet() { var mod = $("#twitterModule"), //ツイートエリアの高さを取得します。 //モジュールの高さ−タイトルエリアの高さ-インプットエリアの高さ areaHeight=mod.height()-mod.children(".title").height()-mod.chirdren(".area-input").height(); h=0, //ツイートブロックの高さ集計用 index=0; //削除基点となるツイートブロックのインデックス番号用 //eachメソッドでツイートブロック(p)の高さを順に加算して、ツイートエリアの高さを越えた時点で、そのインデックス番号を取得して終了する。 $("#area-tweets p").each(function(i) { h += $(this).height(); if(areaHeight < h) { index = i; return false; } }); // nextAllメソッドとremoveメソッドでインデックス番号以降のツイートブロックを削除する $("#area-tweets p").eq(index).nextAll().remove(); } /* 取得(ツイート) */ function getTweet(){ var area = $("#area-tweets"), // Twitter Search APIのURLにパラメータを付加する。 url=geo.twitter + "?geocode=" + geo.lat+ "," + geo.lng+ "," + geo.rad +"km"; $.getJSON(url + "&callback=?", function(data){ var results = data.results, p=$("<p>", {"class" : "address"});//住所表示用 //住所を表示するブロックを追加する。 p.text(geo.addr + "周辺").appendTo(area); for(var i=results.length; i--;){ var p=$("<p>"), user = $("<a>", {"class" : "user", target: "_blank"}), date = $("<span>", {"class" : "date"}), datetime=new Date(results[i].created_at); user.text(results[i].from_user).attr("href", "http://twitter.com/" + results[i].from_user); date.text( datetime.getFullYear()+ "/" + (datetime.getMonth()+1) + "/" + datetime.getDate() + " " + datetime.toLocaleTimeString() ); results[i].text = results[i].text.replace(/(https?:\/\/[\-\/a-z0-9_~.#?&=%]+)/ig,"<a target='_blank' href='$1'>$1</a>"); results[i].text=results[i].text.replace(/@([a-z0-9_]+)/ig, "<a class='reply' target='_blank' href='http://twitter.com/$1'>@$1</a>"); p.append(user, date, "<br>", results[i].text).appendTo(area); } //30秒後に再実行させます。 //ループ中断用にタイマーIDを格納します。 geo.timerGetTweet = setTimeout(getTweet, 30000); }); } /* 表示 */ function slideTweet(){ var p = $("#area-tweets > p"), h=0; if(!p.length){ setTimeout(slideTweet, 500); return false; } h=p.first().innerHeight(); p.first().animate({ marginTop:-h },{ duration:(h+Math.round(p.first().css("marginTop").slice(0,-2)))*50, easing:"linear", complete:function(){ $(this).remove(); slideTweet(); } }); } })(jQuery);
js/moderneizr.js
// Modernizr v1.7 www.modernizr.com window.Modernizr=function(a,b,c){function G(){e.input=function(a){for(var b=0,c=a.length;b<c;b++)t[a[b]]=!!(a[b]in l);return t}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),e.inputtypes=function(a){for(var d=0,e,f,h,i=a.length;d<i;d++)l.setAttribute("type",f=a[d]),e=l.type!=="text",e&&(l.value=m,l.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(f)&&l.style.WebkitAppearance!==c?(g.appendChild(l),h=b.defaultView,e=h.getComputedStyle&&h.getComputedStyle(l,null).WebkitAppearance!=="textfield"&&l.offsetHeight!==0,g.removeChild(l)):/^(search|tel)$/.test(f)||(/^(url|email)$/.test(f)?e=l.checkValidity&&l.checkValidity()===!1:/^color$/.test(f)?(g.appendChild(l),g.offsetWidth,e=l.value!=m,g.removeChild(l)):e=l.value!=m)),s[a[d]]=!!e;return s}("search tel url email datetime date month week time datetime-local number range color".split(" "))}function F(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1),d=(a+" "+p.join(c+" ")+c).split(" ");return!!E(d,b)}function E(a,b){for(var d in a)if(k[a[d]]!==c&&(!b||b(a[d],j)))return!0}function D(a,b){return(""+a).indexOf(b)!==-1}function C(a,b){return typeof a===b}function B(a,b){return A(o.join(a+";")+(b||""))}function A(a){k.cssText=a}var d="1.7",e={},f=!0,g=b.documentElement,h=b.head||b.getElementsByTagName("head")[0],i="modernizr",j=b.createElement(i),k=j.style,l=b.createElement("input"),m=":)",n=Object.prototype.toString,o=" -webkit- -moz- -o- -ms- -khtml- ".split(" "),p="Webkit Moz O ms Khtml".split(" "),q={svg:"http://www.w3.org/2000/svg"},r={},s={},t={},u=[],v,w=function(a){var c=b.createElement("style"),d=b.createElement("div"),e;c.textContent=a+"{#modernizr{height:3px}}",h.appendChild(c),d.id="modernizr",g.appendChild(d),e=d.offsetHeight===3,c.parentNode.removeChild(c),d.parentNode.removeChild(d);return!!e},x=function(){function d(d,e){e=e||b.createElement(a[d]||"div");var f=(d="on"+d)in e;f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=C(e[d],"function"),C(e[d],c)||(e[d]=c),e.removeAttribute(d))),e=null;return f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),y=({}).hasOwnProperty,z;C(y,c)||C(y.call,c)?z=function(a,b){return b in a&&C(a.constructor.prototype[b],c)}:z=function(a,b){return y.call(a,b)},r.flexbox=function(){function c(a,b,c,d){a.style.cssText=o.join(b+":"+c+";")+(d||"")}function a(a,b,c,d){b+=":",a.style.cssText=(b+o.join(c+";"+b)).slice(0,-b.length)+(d||"")}var d=b.createElement("div"),e=b.createElement("div");a(d,"display","box","width:42px;padding:0;"),c(e,"box-flex","1","width:10px;"),d.appendChild(e),g.appendChild(d);var f=e.offsetWidth===42;d.removeChild(e),g.removeChild(d);return f},r.canvas=function(){var a=b.createElement("canvas");return a.getContext&&a.getContext("2d")},r.canvastext=function(){return e.canvas&&C(b.createElement("canvas").getContext("2d").fillText,"function")},r.webgl=function(){return!!a.WebGLRenderingContext},r.touch=function(){return"ontouchstart"in a||w("@media ("+o.join("touch-enabled),(")+"modernizr)")},r.geolocation=function(){return!!navigator.geolocation},r.postmessage=function(){return!!a.postMessage},r.websqldatabase=function(){var b=!!a.openDatabase;return b},r.indexedDB=function(){for(var b=-1,c=p.length;++b<c;){var d=p[b].toLowerCase();if(a[d+"_indexedDB"]||a[d+"IndexedDB"])return!0}return!1},r.hashchange=function(){return x("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},r.history=function(){return !!(a.history&&history.pushState)},r.draganddrop=function(){return x("dragstart")&&x("drop")},r.websockets=function(){return"WebSocket"in a},r.rgba=function(){A("background-color:rgba(150,255,150,.5)");return D(k.backgroundColor,"rgba")},r.hsla=function(){A("background-color:hsla(120,40%,100%,.5)");return D(k.backgroundColor,"rgba")||D(k.backgroundColor,"hsla")},r.multiplebgs=function(){A("background:url(//:),url(//:),red url(//:)");return(new RegExp("(url\\s*\\(.*?){3}")).test(k.background)},r.backgroundsize=function(){return F("backgroundSize")},r.borderimage=function(){return F("borderImage")},r.borderradius=function(){return F("borderRadius","",function(a){return D(a,"orderRadius")})},r.boxshadow=function(){return F("boxShadow")},r.textshadow=function(){return b.createElement("div").style.textShadow===""},r.opacity=function(){B("opacity:.55");return/^0.55$/.test(k.opacity)},r.cssanimations=function(){return F("animationName")},r.csscolumns=function(){return F("columnCount")},r.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";A((a+o.join(b+a)+o.join(c+a)).slice(0,-a.length));return D(k.backgroundImage,"gradient")},r.cssreflections=function(){return F("boxReflect")},r.csstransforms=function(){return!!E(["transformProperty","WebkitTransform","MozTransform","OTransform","msTransform"])},r.csstransforms3d=function(){var a=!!E(["perspectiveProperty","WebkitPerspective","MozPerspective","OPerspective","msPerspective"]);a&&"webkitPerspective"in g.style&&(a=w("@media ("+o.join("transform-3d),(")+"modernizr)"));return a},r.csstransitions=function(){return F("transitionProperty")},r.fontface=function(){var a,c,d=h||g,e=b.createElement("style"),f=b.implementation||{hasFeature:function(){return!1}};e.type="text/css",d.insertBefore(e,d.firstChild),a=e.sheet||e.styleSheet;var i=f.hasFeature("CSS2","")?function(b){if(!a||!b)return!1;var c=!1;try{a.insertRule(b,0),c=/src/i.test(a.cssRules[0].cssText),a.deleteRule(a.cssRules.length-1)}catch(d){}return c}:function(b){if(!a||!b)return!1;a.cssText=b;return a.cssText.length!==0&&/src/i.test(a.cssText)&&a.cssText.replace(/\r+|\n+/g,"").indexOf(b.split(" ")[0])===0};c=i('@font-face { font-family: "font"; src: url(data:,); }'),d.removeChild(e);return c},r.video=function(){var a=b.createElement("video"),c=!!a.canPlayType;if(c){c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"');var d='video/mp4; codecs="avc1.42E01E';c.h264=a.canPlayType(d+'"')||a.canPlayType(d+', mp4a.40.2"'),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"')}return c},r.audio=function(){var a=b.createElement("audio"),c=!!a.canPlayType;c&&(c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"'),c.mp3=a.canPlayType("audio/mpeg;"),c.wav=a.canPlayType('audio/wav; codecs="1"'),c.m4a=a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;"));return c},r.localstorage=function(){try{return!!localStorage.getItem}catch(a){return!1}},r.sessionstorage=function(){try{return!!sessionStorage.getItem}catch(a){return!1}},r.webWorkers=function(){return!!a.Worker},r.applicationcache=function(){return!!a.applicationCache},r.svg=function(){return!!b.createElementNS&&!!b.createElementNS(q.svg,"svg").createSVGRect},r.inlinesvg=function(){var a=b.createElement("div");a.innerHTML="<svg/>";return(a.firstChild&&a.firstChild.namespaceURI)==q.svg},r.smil=function(){return!!b.createElementNS&&/SVG/.test(n.call(b.createElementNS(q.svg,"animate")))},r.svgclippaths=function(){return!!b.createElementNS&&/SVG/.test(n.call(b.createElementNS(q.svg,"clipPath")))};for(var H in r)z(r,H)&&(v=H.toLowerCase(),e[v]=r[H](),u.push((e[v]?"":"no-")+v));e.input||G(),e.crosswindowmessaging=e.postmessage,e.historymanagement=e.history,e.addTest=function(a,b){a=a.toLowerCase();if(!e[a]){b=!!b(),g.className+=" "+(b?"":"no-")+a,e[a]=b;return e}},A(""),j=l=null,f&&a.attachEvent&&function(){var a=b.createElement("div");a.innerHTML="<elem></elem>";return a.childNodes.length!==1}()&&function(a,b){function p(a,b){var c=-1,d=a.length,e,f=[];while(++c<d)e=a[c],(b=e.media||b)!="screen"&&f.push(p(e.imports,b),e.cssText);return f.join("")}function o(a){var b=-1;while(++b<e)a.createElement(d[b])}var c="abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",d=c.split("|"),e=d.length,f=new RegExp("(^|\\s)("+c+")","gi"),g=new RegExp("<(/*)("+c+")","gi"),h=new RegExp("(^|[^\\n]*?\\s)("+c+")([^\\n]*)({[\\n\\w\\W]*?})","gi"),i=b.createDocumentFragment(),j=b.documentElement,k=j.firstChild,l=b.createElement("body"),m=b.createElement("style"),n;o(b),o(i),k.insertBefore(m,k.firstChild),m.media="print",a.attachEvent("onbeforeprint",function(){var a=-1,c=p(b.styleSheets,"all"),k=[],o;n=n||b.body;while((o=h.exec(c))!=null)k.push((o[1]+o[2]+o[3]).replace(f,"$1.iepp_$2")+o[4]);m.styleSheet.cssText=k.join("\n");while(++a<e){var q=b.getElementsByTagName(d[a]),r=q.length,s=-1;while(++s<r)q[s].className.indexOf("iepp_")<0&&(q[s].className+=" iepp_"+d[a])}i.appendChild(n),j.appendChild(l),l.className=n.className,l.innerHTML=n.innerHTML.replace(g,"<$1font")}),a.attachEvent("onafterprint",function(){l.innerHTML="",j.removeChild(l),j.appendChild(n),m.styleSheet.cssText=""})}(a,b),e._enableHTML5=f,e._version=d,g.className=g.className.replace(/\bno-js\b/,"")+" js "+u.join(" ");return e}(this,this.document)
今回苦労した点
打ち間違いにより、動作しない・・。疲れきった目と脳ではなかなか間違い箇所を発見できません。そんな時に強い助けとなるフリーソフト「WinMerge」を、今回使ってみました。
あてにならない自分の目を凝らしてDreamweaverやテキストエディターをしらみつぶしに見ていても全く発見できない間違いも、相当見つけやすくなります。
2つのドキュメントの差分を検出するソフト「WinMerge」