changeset 0:c978fac38f18

Origination.
author Atul Varma <varmaa@toolness.com>
date Fri, 08 Feb 2008 20:33:37 +0000
parents
children 0a09525111a6
files index.html locationInfo.xml map.css map.js
diffstat 4 files changed, 514 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/index.html	Fri Feb 08 20:33:37 2008 +0000
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
+    <title>Atul's Map</title>
+    <base target="_blank" />
+    <link rel="stylesheet" type="text/css" href="map.css" />
+    <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAzBIC_wxmje-aKLT3RzZx7BQqYoqieBGZ_GPQY-hbWWrY23D7qxTw2ek84-RtvWTLn318N3vg6dKmhg"
+      type="text/javascript"></script>
+    <script src="map.js" type="text/javascript"></script>
+  </head>
+  <body onload="load()" onunload="GUnload()">
+    <div id="quasimode"></div>
+    <div id="map"></div>
+    <div id="selection_info"></div>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/locationInfo.xml	Fri Feb 08 20:33:37 2008 +0000
@@ -0,0 +1,41 @@
+<locationInfo>
+  <loc name="The Neo-Futurarium"
+       address="5153 N. Ashland, Chicago, IL">
+    <![CDATA[ Home to the Neo-Futurists, a dramatic group that is
+    worth checking out at least once&mdash;if not over and over.
+    Their signature act, <a
+    href="http://www.neofuturists.org/index.php?option=com_content&task=view&id=20&Itemid=45">Too
+    Much Light Makes the Baby Go Blind</a>, is the longest-running
+    show in Chicago and changes frequently.<p>See <a
+    href="http://www.neofuturists.org">neofuturists.org</a> for more
+    information.]]>
+  </loc>
+  <loc name="Hopleaf"
+       address="5148 N. Clark, Chicago, IL">
+    <![CDATA[ An excellent bar that specializes in a wide variety of
+    Belgian beers.  Easily Atul's favorite bar in the Chicagoland
+    area.  Find out more at <a href="http://www.hopleaf.com">hopleaf.com</a>.]]>
+  </loc>
+  <loc name="Landmark Century Centre Cinema"
+       address="2828 N. Clark, Chicago, IL"
+       lat="41.933632" long="-87.645689">
+    <![CDATA[ An excellent independent theater that is used to show a
+    number of entries in the annual Chicago Film Festival.  Take a
+    look at the <a
+    href="http://www.landmarktheatres.com/Market/Chicago/Chicago_Frameset.htm">showtimes</a>.
+    ]]>
+  </loc>
+  <loc name="Roscoe Hall"
+       address="1925 W. Newport, Chicago, IL">
+    <![CDATA[ Where John, J.P., Erik, Atul, and/or Isaac used to
+    live.]]>
+  </loc>
+  <loc name="Mozilla, Building K"
+       address="1981 Landings Dr., Mountain View, CA">
+    <![CDATA[ Mozilla Headquarters! ]]>
+  </loc>
+  <loc name="Mozilla, Toronto Office"
+       address="20 Richmond St East, Toronto, Ontario, Canada">
+    <![CDATA[ Mozilla's Toronto office.]]>
+  </loc>
+</locationInfo>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/map.css	Fri Feb 08 20:33:37 2008 +0000
@@ -0,0 +1,65 @@
+a:link {
+color:#990000;
+text-decoration:none;
+}
+
+a:visited {
+color:#990000;
+text-decoration:none;
+}
+
+a:hover {
+color:#CC3300;
+text-decoration:underline;
+}
+
+a:active {
+color:#990000;
+text-decoration:none;
+}
+
+body {
+    font-family: lucida grande,lucida sans unicode,arial,verdana,sans serif;
+    font-size: 8pt;
+    line-height: 1.4em;
+}
+
+.locationHeader {
+    font-weight: bold;
+}
+
+.searchHighlight {
+    background-color: yellow;
+}
+
+.searchSuccessful {
+    font-weight: bold;
+}
+
+.searchUnsuccessful {
+    font-weight: bold;
+    color: red;
+}
+
+#map {
+    width: 600px;
+    height: 600px;
+    top: 10;
+    left: 10;
+    border: 1px solid black;
+    position: absolute;
+}
+
+#selection_info {
+    width: 200px;
+    top: 10;
+    left: 620px;
+    position: absolute;
+}
+
+#quasimode {
+    width: 640px;
+    top: 620px;
+    left: 10;
+    position: absolute;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/map.js	Fri Feb 08 20:33:37 2008 +0000
@@ -0,0 +1,390 @@
+/* General */
+const SITE_HOSTNAME = "labs.toolness.com"
+
+function load()
+{
+    if (GBrowserIsCompatible())
+    {
+        Location.prototype.initMap(
+            document.getElementById("map"),
+            document.getElementById("selection_info")
+            );
+        GDownloadUrl( "locationInfo.xml", parseLocationInfo );
+    }
+
+    theQuasimode = new Quasimode(
+        SHIFT_KEYCODE,
+        new QuasimodeDelegate( document.getElementById("quasimode") )
+        );
+}
+
+/* Location */
+const MARKER_SELECTED_IMG = "http://labs.google.com/ridefinder/images/mm_20_red.png";
+const MARKER_UNSELECTED_IMG = "http://labs.google.com/ridefinder/images/mm_20_gray.png";
+const MARKER_SHADOW_IMG = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
+
+function Location( info )
+{
+    for ( key in info )
+        this[key] = info[key];
+
+    if ( !Location.prototype.icon )
+    {
+        var icon = new GIcon();
+
+        icon.image = MARKER_UNSELECTED_IMG;
+        icon.shadow = MARKER_SHADOW_IMG;
+        icon.iconSize = new GSize(12, 20);
+        icon.shadowSize = new GSize(22, 20);
+        icon.iconAnchor = new GPoint(6, 20);
+        icon.infoWindowAnchor = new GPoint(5, 1);
+
+        Location.prototype.icon = icon;
+    }
+
+    var i = this.locations.length;
+
+    var point = new GLatLng( this.lat, this.long );
+
+    var markerOptions = { icon: Location.prototype.icon,
+                          title: this.name };
+
+    this.marker = new GMarker(point, markerOptions);
+
+    this.globalName = "Location.prototype.locations[" + i + "]";
+
+    this.map.addOverlay( this.marker );
+
+    GEvent.bind( this.marker, "click", this, this.select );
+
+    this.locations.push( this );
+
+    if ( !Location.prototype.selection )
+        this.select();
+}
+
+Location.prototype.select = function()
+{
+    if ( Location.prototype.selection )
+        Location.prototype.selection.unselect();
+    this.marker.setImage( MARKER_SELECTED_IMG );
+    this.map.panTo( new GLatLng(this.lat, this.long) );
+
+    var html = ( "<div class=\"locationHeader\" onclick=\"" +
+                 this.globalName +
+                 ".select()\">" + this.name + "</div>" +
+                 "<p>" + this.address + "</p>" +
+                 "<p>" + this.description + "</p>" );
+
+    Location.prototype.selectionInfoElement.innerHTML = html;
+
+    Location.prototype.selection = this;
+}
+
+Location.prototype.unselect = function()
+{
+    this.marker.setImage( MARKER_UNSELECTED_IMG );
+    Location.prototype.selection = null;
+}
+
+Location.prototype.initMap = function( mapElement, selectionInfoElement )
+{
+    var map = new GMap2( mapElement );
+
+    Location.prototype.selectionInfoElement = selectionInfoElement;
+    Location.prototype.map = map;
+    Location.prototype.locations = [];
+
+    map.setCenter(new GLatLng(0, 0), 13);
+
+    map.enableScrollWheelZoom();
+    map.enableContinuousZoom();
+    map.disableInfoWindow();
+}
+
+Location.prototype.icon = null;
+Location.prototype.selection = null;
+Location.prototype.map = null;
+Location.prototype.selectionInfoElement = null;
+Location.prototype.locations = null;
+
+function findLatLong( info )
+{
+    var cachedLocs = null;
+
+    var key = "mapCache_" + info.address;
+
+    if ( window.globalStorage != undefined )
+        cachedLocs = window.globalStorage[SITE_HOSTNAME];
+    else
+        cachedLocs = {};
+
+    var makeLocationFromCache = function()
+        {
+            var latAndLong = new String(cachedLocs[key]).split( "," );
+
+            info.lat = parseFloat( latAndLong[0] );
+            info.long = parseFloat( latAndLong[1] );
+
+            new Location( info );
+        }
+
+    if ( cachedLocs[key] == undefined )
+    {
+        var geocoder = new GClientGeocoder();
+        var geocoderCallback = function( point )
+            {
+                if ( !point )
+                {
+                    //console.log( "Can't get geocoding info for " +
+                    //             info.address );
+                } else {
+                    //console.log( "Fetched geocoding info for " +
+                    //             info.address );
+                    var pointArray = [ point.lat(), point.lng() ];
+                    cachedLocs[key] = pointArray.join( "," );
+                    makeLocationFromCache();
+                }
+            }
+
+        geocoder.getLatLng( info.address, geocoderCallback );
+    } else
+        makeLocationFromCache();
+}
+
+function parseLocationInfo( data )
+{
+    var xml = GXml.parse( data );
+    var locations = xml.documentElement.getElementsByTagName( "loc" );
+
+    for ( var i = 0; i < locations.length; i++ )
+    {
+        var loc = locations[i];
+
+        var info = { name: loc.getAttribute( "name" ),
+                     address: loc.getAttribute( "address" ),
+                     description: loc.textContent };
+
+        if ( loc.hasAttribute("lat") )
+        {
+            info.lat = parseFloat( loc.getAttribute("lat") );
+            info.long = parseFloat( loc.getAttribute("long") );
+
+            new Location( info );
+        } else
+            findLatLong( info );
+    }
+}
+
+/* Quasimode Delegate */
+
+QUASIMODE_HELP_MSG = ( "Press and hold down the shift key to " +
+                       "search for a location." );
+
+QUASIMODE_HELP_MSG2 = ( "Continue to hold the shift key and " + 
+                        "start typing the name of the location " +
+                        "you want to go to." );
+
+QUASIMODE_SEARCH_MSG = "Now searching for: "
+
+function QuasimodeDelegate( displayElement )
+{
+    this.displayElement = displayElement;
+    this.displayElement.innerHTML = QUASIMODE_HELP_MSG;
+}
+
+QuasimodeDelegate.prototype.onQuasimodeBegin = function()
+{
+    this.originalSelection = Location.prototype.selection;
+    this.displayElement.innerHTML = QUASIMODE_HELP_MSG2;
+}
+
+QuasimodeDelegate.prototype.onQuasimodeEnd = function()
+{
+    this.displayElement.innerHTML = QUASIMODE_HELP_MSG;
+    this.originalSelection = null;
+    this.resetSelectionInfoHighlighting();
+}
+
+QuasimodeDelegate.prototype.resetSelectionInfoHighlighting = function()
+{
+    if ( !this.pristineSelInfoElement )
+        return;
+
+    this.pristineSelInfoElement.innerHTML = this.pristineSelInfo;
+    this.pristineSelInfoElement = null;
+    this.pristineSelInfo = null;
+}
+
+QuasimodeDelegate.prototype.onSearch = function( searchString )
+{
+    var locs = Location.prototype.locations;
+    var bestMatchLoc = null;
+    var bestMatchIndex = Infinity;
+    var spanClass = "searchUnsuccessful"
+
+    if ( searchString.length > 0 )
+    {
+        for ( var i = 0; i < locs.length; i++ )
+        {
+            var searchText = ( locs[i].name + locs[i].address +
+                               locs[i].description );
+
+            searchText = searchText.toLowerCase();
+
+            var matchIndex = searchText.search( searchString );
+
+            if ( matchIndex != -1 && matchIndex < bestMatchIndex )
+            {
+                bestMatchIndex = matchIndex;
+                bestMatchLoc = locs[i];
+            }
+        }
+    }
+
+    this.resetSelectionInfoHighlighting();
+
+    if ( bestMatchLoc )
+    {
+        bestMatchLoc.select();
+
+        this.pristineSelInfoElement = bestMatchLoc.selectionInfoElement;
+        this.pristineSelInfo = this.pristineSelInfoElement.innerHTML;
+        this.pristineSelInfoElement.innerHTML = doHighlight(
+            this.pristineSelInfo,
+            searchString,
+            "<span class=\"searchHighlight\">",
+            "</span>"
+            );
+        spanClass = "searchSuccessful";
+    } else
+        this.originalSelection.select();
+
+    this.displayElement.innerHTML = ( QUASIMODE_SEARCH_MSG +
+                                      "<span class=\"" + spanClass + "\">" +
+                                      searchString +
+                                      "</span>" );
+}
+
+/* Quasimode */
+
+var theQuasimode = null;
+
+SHIFT_KEYCODE = 16;
+BACKSPACE_KEYCODE = 8;
+
+function Quasimode( quasimodeKeycode, delegate )
+{
+    this.quasimodeKeycode = quasimodeKeycode;
+    this.searchString = "";
+    this.inQuasimode = false;
+    this.delegate = delegate;
+
+    self = this;
+
+    window.addEventListener( "keydown",
+                             function(evt) { self.onKeyDown(evt) },
+                             false );
+    window.addEventListener( "keyup",
+                             function(evt) { self.onKeyUp(evt) },
+                             false );
+    window.addEventListener( "keypress",
+                             function(evt) { self.onKeypress(evt) },
+                             false );
+}
+
+Quasimode.prototype.onKeypress = function( evt )
+{
+    if ( this.inQuasimode )
+    {
+        //console.log( evt );
+        /* Originally I used evt.isChar here, but according to bug
+         * 312552 it always returns false. It appears to be fixed in
+         * Firefox 3, but not in earlier versions. */
+        if ( evt.charCode )
+        {
+            var newChar = String.fromCharCode( evt.charCode );
+            this.searchString += newChar.toLowerCase();
+            //console.log( "char" );
+        } else {
+            //console.log( "not a char" );
+            if ( evt.keyCode == BACKSPACE_KEYCODE &&
+                 this.searchString.length > 0 )
+                this.searchString = this.searchString.slice(
+                    0,
+                    this.searchString.length - 1
+                    )
+        }
+        this.delegate.onSearch( this.searchString );
+    }
+}
+
+Quasimode.prototype.onKeyDown = function( evt )
+{
+    if ( (!this.inQuasimode) && evt.keyCode == this.quasimodeKeycode )
+    {
+        this.inQuasimode = true;
+        this.searchString = "";
+        this.delegate.onQuasimodeBegin();
+    }
+}
+
+Quasimode.prototype.onKeyUp = function( evt )
+{
+    if ( this.inQuasimode && evt.keyCode == this.quasimodeKeycode )
+    {
+        this.inQuasimode = false;
+        this.searchString = "";
+        this.delegate.onQuasimodeEnd();
+    }
+}
+
+/* Text highlighting code, taken from:
+ * http://www.nsftools.com/misc/SearchAndHighlight.htm */
+
+/*
+ * This is the function that actually highlights a text string by
+ * adding HTML tags before and after all occurrences of the search
+ * term. You can pass your own tags if you'd like, or if the
+ * highlightStartTag or highlightEndTag parameters are omitted or
+ * are empty strings then the default <font> tags will be used.
+ */
+function doHighlight(bodyText, searchTerm, highlightStartTag, highlightEndTag) 
+{
+  // the highlightStartTag and highlightEndTag parameters are optional
+  if ((!highlightStartTag) || (!highlightEndTag)) {
+    highlightStartTag = "<font style='color:blue; background-color:yellow;'>";
+    highlightEndTag = "</font>";
+  }
+  
+  // find all occurences of the search term in the given text,
+  // and add some "highlight" tags to them (we're not using a
+  // regular expression search, because we want to filter out
+  // matches that occur within HTML tags and script blocks, so
+  // we have to do a little extra validation)
+  var newText = "";
+  var i = -1;
+  var lcSearchTerm = searchTerm.toLowerCase();
+  var lcBodyText = bodyText.toLowerCase();
+    
+  while (bodyText.length > 0) {
+    i = lcBodyText.indexOf(lcSearchTerm, i+1);
+    if (i < 0) {
+      newText += bodyText;
+      bodyText = "";
+    } else {
+      // skip anything inside an HTML tag
+      if (bodyText.lastIndexOf(">", i) >= bodyText.lastIndexOf("<", i)) {
+        // skip anything inside a <script> block
+        if (lcBodyText.lastIndexOf("/script>", i) >= lcBodyText.lastIndexOf("<script", i)) {
+          newText += bodyText.substring(0, i) + highlightStartTag + bodyText.substr(i, searchTerm.length) + highlightEndTag;
+          bodyText = bodyText.substr(i + searchTerm.length);
+          lcBodyText = bodyText.toLowerCase();
+          i = -1;
+        }
+      }
+    }
+  }
+  
+  return newText;
+}