Flickr Badge

Sunday, December 19, 2010

The Scavenger app from my Chennai Geeks presentation

This weekend I presented at the Chennai Geeks meet on using HTML5 to build mobile applications. As a part of the talk, I showed a sample scavenger application that was built using HTML5.

The application defined a list of scavenger locations, and used the Geolocation API to check you in when you were close to one of the defined locations. The app used Local Storage to save the list of previously visited locations and the timestamp when you last checked in. Finally we used some CSS3 Media Queries to show some additional information when the app was accessed through a desktop browser.

To run the application, just copay and save the code below and open the html file in Firefox 3.6, Google Chrome, iPhone or Android browser.

Here is the source code for the application:
<script type="text/javascript" src=""></script>
<script type="text/javascript" src="json2.js"></script>
header {font-size: 11px;}
h1 {font-family: Verdana,Helvetica,sans-serif; font-size:25px;}
.error, .notice, .success {padding:.8em;margin-bottom:1em;border:2px solid #ddd;}
.error {background:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;}
.notice {background:#FFF6BF;color:#514721;border-color:#FFD324;}
.success {background:#E6EFC2;color:#264409;border-color:#C6D880;}
.location {background: #eeeeee; border-radius: 15px; -moz-border-radius: 15px; -webkit-border-radius: 15px; margin: 5px 0; padding: 10px;}
.visited {background:#ccff99; font-weight: bold;}
.location-distance {display:block; color: #555555; font-weight:normal; font-size: 0.8em;}
.location-checkin {display:block; font-weight:normal; font-size: 0.9em;}

/* Dont show distance and timestamp on small screens */
@media only screen and (max-width: 480px) {
.location-distance {display:none;}
.location-checkin {display:none;}

<script type="text/javascript">

Number.prototype.toRad = function() {
return this * Math.PI / 180;

// List of locations. Format: ["location name", lat, long]
["Thoughtworks", 13.01245, 80.201451],
["Ashoka Pillar", 13.035028, 80.212512],
["Kathipara", 13.007359, 80.20394]

// Haversine formula to calculate distance (in km) between two lat/long coordinates
function distance(lat1, lon1, lat2, lon2) {
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d;

function check_in(index) {
jQuery("#status-bar").html("<div class='success'>Checked in to " + LOCATIONS[index][0] + "</div>");
var check_ins_str = localStorage["check_ins"];
if (!check_ins_str) { check_ins_str = "{}"; }
var check_ins = JSON.parse(check_ins_str);
var location = LOCATIONS[index];
check_ins[location[0]] = new Date();
localStorage["check_ins"] = JSON.stringify(check_ins);

function show_distance(index, d) {
var container = jQuery("#locations-container").find("#location-"+index);
container.append("<span class='location-distance'>" + Math.floor(d*100)/100 + " km away</span>");

function success(pos) {
var distance_list = [];
for (var i=0; i<LOCATIONS.length; i++) {
var d = distance(pos.coords.latitude, pos.coords.longitude, LOCATIONS[i][1], LOCATIONS[i][2]);
show_distance(i, d);
distance_list.push([i, d]);
distance_list.sort(function(a,b) { return a[1] > b[1]; });
if (distance_list[0][1] < 2) {
} else {
jQuery("#status-bar").html("<div class='notice'>No checkins</div>");

function error() {
jQuery("#status-bar").html("<div class='error'>Unable to get position</div>");

function locate_user() {

function render_previous_check_in(index, check_in_date) {
var container = jQuery("#location-" + index);
container.append("<span class='location-checkin'>Last checked in on " + check_in_date + "</span>");

function show_checkins() {
var check_ins_str = localStorage["check_ins"];
if (!check_ins_str) return;
var check_ins = JSON.parse(check_ins_str);
for (var i=0; i<LOCATIONS.length; i++) {
var previous_check_in = check_ins[LOCATIONS[i][0]];
if (previous_check_in) {
render_previous_check_in(i, previous_check_in);

function show_locations() {
var container = jQuery("#locations-container");
for (var i=0; i<LOCATIONS.length; i++) {
container.append("<div class='location' id='location-" + i + "'><span class='location-name'>" + LOCATIONS[i][0] + "</span></div>");

jQuery().ready(function() {
A HTML5 mobile app demo by <a href="">Siddhi</a>
<div id="status-bar"><div class="notice">Checking your location...</div></div>
<div id="locations-container"></div>

No comments: