//
// map_functions.js
//
// Downloaded from http://www.googlemapsbook.com/
// 
// Modified for the Shamash Kosher Restaurant Database Project / http://www.shamash.org/kosher
// by Mark Ferneau, ferneau at yahoo.com, 2007-2008
//
// NOTES:
//  - Don't try to use the MarkerManager with zoom levels > 17
// TODO: 
//  - Remove double-query on zoom
//  - Add sort by feature for establishments
//  - Add proximity search (to address)
//  - Add traffic overlay
//  - Add ability to print (PDF) a particular list of restaurants
//    with addresses, names, labels on page, etc.
//    (How to grab the map picture??)
//  - Make it clear the filters are clickable
//  - Show labels at certain zoom levels automatically
//  - Determine "better" starting location & zoom level 
//

// GMAP API related items
var map = null;
var markerManager = null;
var geocoder = null;

// GMAP Addressing variables
var directions = null;
var directionsPanel = null;
var directionsStartInput = null;
var startAddr = null;
var endAddr = null;

// Arrays of markers from most to least-specific
var countryMarkers = [];
var metroareaMarkers = [];
var cityMarkers = [];
var restaurantMarkers = [];
var currentMarkers = [];

// Store Search Info
var savedSearchText = "";
var savedSearchSkip = 0;
// Default icons
var geoIcon = "images/omarker.png";
var nongeoIcon = "images/bkmarker.png"
var multiIcon = "images/pmarker.png";
var meatIcon = "images/rmarker.png";
var dairyIcon = "images/bmarker.png";
var pareveIcon = "images/gmarker.png";

// Filter items
var type = 0; // [0 - multi, 1 - meat, 2 - dairy, 3 - pareve/veggie]
var cuisineFilterValue = 0; // [0 - none]

// currently displayed tooltip marker
var curTooltipMarker = null;
var deselectCurrent = function() {};

function Directions( restaurantAddress ) {

  if ( directions == null ) {
    directionsPanel = document.getElementById("route-info");
    directionsStartInput = document.getElementById("startaddress");  
    changeBodyClass( 'noroutebar', 'route-right');
    directions = new GDirections(map, directionsPanel);
  }
  
  var startAddress = directionsStartInput.value; 
  if ( startAddress.length == 0 ) {
    //alert ("Enter a starting address first");
    ;
  } else {
    computeDirections( startAddress, restaurantAddress );
  }
}

function computeDirections( from, to ) {
  var totalAddress = from + " to " + to;
  directions.load( totalAddress );
  startAddr = from;
  endAddr = to;
  //GLog.write( totalAddress );
}

function clearDirections() {
  if ( directions != null ) {
    directions.clear();
    startAddr = null;
    endAddr = null;
  }
}

function reverseDirections() {
  if ( directions != null ) {
    var newStart = endAddr;
    var newEnd = startAddr;
    clearDirections();
    computeDirections( newStart, newEnd );
  }
}

//create the ToolTip overlay object
function ToolTip(marker,html,width) {
  this.html_ = html;
  this.width_ = (width ? width + 'px' : 'auto');
  this.marker_ = marker;
}

ToolTip.prototype = new GOverlay();

ToolTip.prototype.initialize = function(map) {
  var div = document.createElement("div");
  div.style.display = 'none';
  map.getPane(G_MAP_FLOAT_PANE).appendChild(div);
    
  this.map_ = map;
  this.container_ = div;
}

ToolTip.prototype.remove = function() {
  this.container_.parentNode.removeChild(this.container_);
}

ToolTip.prototype.copy = function() {
  return new ToolTip(this.html_);
}

ToolTip.prototype.redraw = function(force) {
  if (!force) return;
    
  var pixelLocation = this.map_.fromLatLngToDivPixel(this.marker_.getPoint());
  
  this.container_.innerHTML = this.html_;
  this.container_.style.position = 'absolute';
  this.container_.style.left = pixelLocation.x + "px";
  this.container_.style.top = pixelLocation.y + "px";
  this.container_.style.width = this.width_;
  this.container_.style.font = 'bold 10px/10px verdana, arial, sans';
  this.container_.style.border = '1px solid black';
  this.container_.style.background = 'white';
  this.container_.style.padding = '4px';
  
  //one line to desired width
  this.container_.style.whiteSpace = 'nowrap';
  if(this.width_ != 'auto') this.container_.style.overflow = 'hidden';
  this.container_.style.display = 'block';
}

GMarker.prototype.ToolTipInstance = null;

GMarker.prototype.openToolTip = function(content) {
  //don't show the tool tip if there is acustom info window
  if(this.ToolTipInstance == null) {
    this.ToolTipInstance = new ToolTip(this,content)
      map.addOverlay(this.ToolTipInstance);
  }
}

GMarker.prototype.closeToolTip = function() {
  if (this.ToolTipInstance != null) {
    map.removeOverlay(this.ToolTipInstance);
    this.ToolTipInstance = null;
  }
}

function createMarker(name, latlng, address, type, iconImage, recnum, c1, c2, c3, c4, geocoded, link) {
  if (iconImage!='') {
    var icon = new GIcon();
    icon.image = iconImage;
    icon.iconSize = new GSize(20, 34);
    icon.iconAnchor = new GPoint(14, 25);
    icon.infoWindowAnchor = new GPoint(14, 14);
    var marker = new GMarker(latlng, { icon:icon, title:name });
  } else {
    var marker = new GMarker(latlng, { title:name });
  }
    
  // Show InfoWindow when clicked
  if ( recnum != null ) {
    GEvent.addListener(marker, 'click', function() {
  	  showRestaurantDetails( recnum );
    });
  } else if ( geocoded == false ) {
    GEvent.addListener(marker, 'click', function() {
      showShamashPage( marker, link );
      map.setCenter( marker.getPoint(), map.getZoom() );
    });  
  } else {
    // Zoom in when double-clicked
    GEvent.addListener(marker, 'dblclick', function() {
      map.setCenter( marker.getPoint(), map.getZoom() + 1 );
    });
  }

  // If the marker has an address, allow for directions
  if ( address ) {
    GEvent.addListener(marker, 'dblclick', function() {
      Directions( address );
    });
  }
    
  // Hook in tooltip show/hide
  GEvent.addListener(marker,'mouseover',function() {
    var markerName = name;
   	marker.openToolTip(markerName);
    curTooltipMarker = marker;
  });
  GEvent.addListener(marker,'mouseout',function() {
    curTooltipMarker = null;
    marker.closeToolTip();
  });

  var myMarker = {};
  myMarker.marker = marker;
  myMarker.name = name;
  myMarker.type = type;
  myMarker.recnum = recnum;
  myMarker.geocoded = geocoded;
  myMarker.link = link;
  if ( recnum != null ) {
    myMarker.c1 = c1;
    myMarker.c2 = c2;
    myMarker.c3 = c3;
    myMarker.c4 = c4;
  }
  return myMarker;
}

function AddItemToResultsList( marker, name, latlng, recnum, geocoded, link ) {
  // Add info to text list
  var listItem = document.createElement('li');
  var listItemLink = listItem.appendChild(document.createElement('a'));
  listItemLink.href = "#";

  listItemLink.innerHTML = name;    
/*  if ( html && ( html.length > 0 )) {
    listItemLink.innerHTML= '<span>' + html + '</span>';
  } else {
    listItemLink.innerHTML= name;
  }
*/

  var focusPoint = function() {
    if ( recnum != null )
      showRestaurantDetails( recnum );
    if ( geocoded == false ) {
      showShamashPage( marker, link );
    }
    map.panTo(latlng);
    return false;
  };

  var zoomToPoint = function() {
    var curZoom = map.getZoom();
    if ( curZoom < 12 )
  	  newZoom = curZoom + 2;
    else 
  	  newZoom = 17;
	  
	  if ( newZoom > 17 )
	    newZoom = 17;
	    
    map.setCenter( marker.getPoint(), newZoom );
	  return false;
  }

  if ( recnum == null && geocoded == true ) {
    listItemLink.onclick = zoomToPoint;
  } else {
    listItemLink.onclick = focusPoint;
  }
  document.getElementById('results-list').appendChild(listItem);
}

function addHeadingToList( heading ) {
  var listItem = document.createElement('li');
  listItem.innerHTML = heading;    
  document.getElementById('results-list').appendChild(listItem);
}

function updateMarkers() {

  // Must rebuild list each time
  clearList('results-list');
  
  // Turn off any open tooltips
  if ( curTooltipMarker ) {
    curTooltipMarker.closeToolTip();
    curTooltipMarker = null;
  }

  var curzoom = map.getZoom();
    
  switch ( curzoom ) {
    case 0:
    case 1:
    case 2:
        disableFilters();
        updateRegions(countryMarkers, 1, 5);
        level = "Global"; break;
    case 3: 
    case 4:
    case 5:
        disableFilters();
        updateRegions(countryMarkers, 1, 5);
        level = "Continental"; break;
    case 6:
    case 7:
        disableFilters();
        updateRegions(metroareaMarkers, 6, 9 );
        level = "National"; break;
    case 8:
    case 9:
        disableFilters();
        updateRegions(metroareaMarkers, 6, 9);
        level = "Regional"; break;
    case 10:
        disableFilters();
        updateRegions(cityMarkers, 10, 12);
        level = "State / Province"; break;
    case 11:
    case 12:
        disableFilters();
        updateRegions(cityMarkers, 10, 12);
        level = "City"; break;
    case 13:
    case 14:
    case 15:
        updateRestaurants( 13, 17);
        enableFilters();
        level = "Neighborhood"; break;
    case 16:
    case 17:
    case 18:
    case 19:
        enableFilters();
        updateRestaurants( 13, 17);
        level = "Street"; break;
    default:
      level = ""; break;
  }
  
  // Display current level
  if ( document.all ) {
    document.getElementById('levelofdetail').innerText = 'Map Level: ' + level;
  } else {
    document.getElementById('levelofdetail').textContent = 'Map Level: ' + level;
  }
}

function openPage( title, link ) {
  window.open( link, title, "directories=no,height=800,width=800,locations=no,menubar=no,status=no,toolbar=no,scrollbars=yes" );  
}

function URLEncode (clearString) {
  var output = '';
  var x = 0;
  clearString = clearString.toString();
  var regex = /(^[a-zA-Z0-9_.]*)/;
  while (x < clearString.length) {
    var match = regex.exec(clearString.substr(x));
    if (match != null && match.length > 1 && match[1] != '') {
    	output += match[1];
      x += match[1].length;
    } else {
      if (clearString[x] == ' ')
        output += '+';
      else {
        var charCode = clearString.charCodeAt(x);
        var hexVal = charCode.toString(16);
        output += '%' + ( hexVal.length < 2 ? '0' : '' ) + hexVal.toUpperCase();
      }
      x++;
    }
  }
  return output;
}

function URLDecode( encoded ) {
   // Replace + with ' '
   // Replace %xx with equivalent character
   // Put [ERROR] in output if %xx is invalid.
   var HEXCHARS = "0123456789ABCDEFabcdef"; 
   var plaintext = "";
   var i = 0;
   while (i < encoded.length) {
       var ch = encoded.charAt(i);
	   if (ch == "+") {
	       plaintext += " ";
		   i++;
	   } else if (ch == "%") {
			if (i < (encoded.length-2) 
					&& HEXCHARS.indexOf(encoded.charAt(i+1)) != -1 
					&& HEXCHARS.indexOf(encoded.charAt(i+2)) != -1 ) {
				plaintext += unescape( encoded.substr(i,3) );
				i += 3;
			} else {
				plaintext += "%[ERROR]";
				i++;
			}
		} else {
		   plaintext += ch;
		   i++;
		}
	} // while
   return plaintext;
}

function showShamashPage( marker, link ) {

  var newlink = "http://www.shamash.org/kosher/search.php" + URLDecode(link);
  //var newlink = "http://www.shamash.org/kosher/search.php?Comm=three&Country=COSTA+RICA";
  var title = "Shamash Search Text Results";
  var html = 'At the current time, the kosher establishments in this area<br>are not geographically referenced.  Please click here to <br><a href="#" onclick="openPage( title, \'' + newlink + '\')"> Search Shamash Database (Text)</a>';

  marker.openInfoWindowHtml( html );
}

function showRestaurantDetails( recnum ) {

  myMarker = restaurantMarkers[recnum];
  
  if ( myMarker.html != null ) {
    myMarker.marker.openInfoWindowHtml(myMarker.html);
  } else {
    var request = GXmlHttp.create();
    var getVars =  "?rc=" + recnum;
      
    //tell the request where to retrieve data from.
    request.open('GET', 'gmapdetail.php' + getVars, true);
    request.onreadystatechange = function() {
      if (request.readyState == 4) {
        
   	    //GLog.write( 'Length of response ' + request.responseText.length );
    	  var xmlResp = request.responseText;
        //GLog.write (xmlResp);	
        if ( xmlResp ) {
          myMarker.marker.openInfoWindowHtml(xmlResp);
          myMarker.html = xmlResp;      
        }  	    
    	} 
    } //request.onreadystategchange function
    
    //tell the request what to do when the state changes.
    request.send(null);
  }
}

function requestRestaurants( minZoom, maxZoom, curZoom, minlon, maxlon, minlat, maxlat ) {

 var request = GXmlHttp.create();
  var getVars =  "?lngMax=" + maxlon + "&lngMin=" + minlon + "&latMax=" + maxlat + "&latMin=" + minlat + "&zoomLevel=" + curZoom;
  //GLog.writeUrl('gmapproxtest_new.php' + getVars);
    
  //tell the request where to retrieve data from.
  request.open('GET', 'gmapproxtest_new.php' + getVars, true);
  request.onreadystatechange = function() {
    if (request.readyState == 4) {
      
 	    //GLog.write( 'Length of response ' + request.responseText.length );
  	  var xmlResp = request.responseText;	
  	  var xmlDoc = request.responseXML;
 	  
  	    
  	  if ( xmlDoc && xmlDoc.documentElement ) {

  	    // Process Markers
  	    var markers = xmlDoc.documentElement.getElementsByTagName("marker");
  	    
  	    // Empty "current" restaurants array 
        currentMarkers.length = 0;
        
 	      //GLog.write( "# Markers = " + markers.length );
  	    for (var i = 0; i < markers.length; i++) {
      		var lng = markers[i].getAttribute("lng");
      		var lat = markers[i].getAttribute("lat");
      		var name = markers[i].getAttribute("name");
      		var filtertype = markers[i].getAttribute("type");
      		var address = markers[i].getAttribute("address");
          var recno = markers[i].getAttribute("recno");
      		var recnum = parseInt(recno,10);
      		var c1 = markers[i].getAttribute("c1");
      		var c2 = markers[i].getAttribute("c2");
      		var c3 = markers[i].getAttribute("c3");
      		var c4 = markers[i].getAttribute("c4");

          // Add this restaurant to the currently in view list
          // This does two things -> (1) it gives us the list of items in view
          // (2) it maintains the alphabetical order for the listing on the 
          // results bar
          currentMarkers[currentMarkers.length] = recnum;
          
          // set Icon
          var icon;
          switch ( filtertype ) {
            case '0': icon = multiIcon; break;
            case '1': icon = meatIcon; break;
            case '2': icon = dairyIcon; break;
            case '3': icon = pareveIcon; break;
          }
          
          
          // If we have do not have this restaurant already, create it 
    		  if ( restaurantMarkers[recnum] == null ) {
        		//check for lng and lat so MSIE does not error
        		//on parseFloat of a null value
        		if(lng && lat) {
        		  var latlng = new GLatLng(parseFloat(lat),parseFloat(lng));
              var marker = createMarker(name, latlng, address, filtertype, icon, recnum, c1, c2, c3, c4, true, null);
        		  restaurantMarkers[recnum] = marker;
              //GLog.write( "added " + name + " to array, rec# " + recnum + ", size = " + restaurantMarkers.length);
              marker.marker.hide();
              markerManager.addMarker( marker.marker, minZoom );
        		}
          }
    	  } //for
        setTimeout('applyFilter()',100);

     	} else {
     	  //alert( "xmlDoc or documentElement is null ");
      } //if

      // Set loading state to ready
      changeBodyClass('loading' , 'standby' );
  	} 
  } //request.onreadystategchange function
  
  //tell the request what to do when the state changes.
  request.send(null);
}

function updateRestaurants( minZoom, maxZoom ) {

  // Set loading state to Loading
  changeBodyClass( 'standby', 'loading' );

  // Get the current lat/lon extents for the map
  var bounds = map.getBounds();
  var lat1 = bounds.getSouthWest().lat().toFixed(6);
  var lat2 = bounds.getNorthEast().lat().toFixed(6);
  var lon1 = bounds.getSouthWest().lng().toFixed(6);
  var lon2 = bounds.getNorthEast().lng().toFixed(6);
    
  var minlat = Math.min( lat1, lat2 );
  var maxlat = Math.max( lat1, lat2 );
  var minlon = Math.min( lon1, lon2 );
  var maxlon = Math.max( lon2, lon1 );
  
  requestRestaurants( minZoom, maxZoom, map.getZoom(), minlon, maxlon, minlat, maxlat );
  
}

function updateRegions( markerlist, minZoom, maxZoom ) {

  if ( markerlist.length == 0 ) {
  
    // Set loading state to Loading
    changeBodyClass( 'standby', 'loading' );

    var request = GXmlHttp.create();
    var getVars =  "?zoomLevel=" + map.getZoom();

    //tell the request where to retrieve data from.
    request.open('GET', 'gmapproxtest_new.php' + getVars, true);
    //GLog.writeUrl('gmapproxtest_new.php' + getVars);
    request.onreadystatechange = function() {
      if (request.readyState == 4) {
      
    	  var xmlResp = request.responseText;	
    	  var xmlDoc = request.responseXML;
    	    
    	  if ( xmlDoc && xmlDoc.documentElement ) {
    	    // Process Markers
    	    var markers = xmlDoc.documentElement.getElementsByTagName("marker");
    	    for (var i = 0; i < markers.length; i++) {
        		var lng = markers[i].getAttribute("lng");
        		var lat = markers[i].getAttribute("lat");
        		var name = markers[i].getAttribute("name");
        		var rlat = markers[i].getAttribute("rlat");
            var link = markers[i].getAttribute("link");
            
            var coded = true;
            var icon = geoIcon;
            if ( ( rlat == null ) || (( rlat != null ) && ( rlat == 0.0 )) ) {
              name = name + " [Mapping Not Available]";
              coded = false;
              icon = nongeoIcon;  
            }
            
        		//check for lng and lat so MSIE does not error
        		//on parseFloat of a null value
        		if(lng && lat) {
        		  var latlng = new GLatLng(parseFloat(lat),parseFloat(lng));
              var marker = createMarker(name, latlng, null, 0, icon, null, 0, 0, 0, 0, coded, link);
              markerlist.push(marker);
        		  markerManager.addMarker( marker.marker, minZoom, maxZoom );
        		}
      	  } //for

          // Clear the list
   	      clearList('results-list');
          refreshMarkerResultsList(markerlist);
          
          // Set loading state to ready
          changeBodyClass('loading' , 'standby' );
        } //if
      } //request.onreadystategchange function
    }
    //tell the request what to do when the state changes.
    request.send(null);

  } else { 
    // Refresh the results list
    refreshMarkerResultsList(markerlist);
  }
}

function refreshMarkerResultsList( markerlist ) {

  // Get the current lat/lon extents for the map
  var bounds = map.getBounds();

  for ( var i=0; i < markerlist.length; i++ ) {
    var testMarker = markerlist[i].marker;
    var testLL = testMarker.getLatLng();
    if ( bounds.containsLatLng(testLL)) {
//      AddItemToResultsList( testMarker, markerlist[i].name, testLL, markerlist[i].html );
      AddItemToResultsList( testMarker, markerlist[i].name, testLL, null, markerlist[i].geocoded, markerlist[i].link );
    }
  }
}

function clearList ( list ) {

  // Clear the list
  var rList = document.getElementById(list);
  while ( rList.hasChildNodes() == true ) {
    rList.removeChild(rList.childNodes[0]);
  }
}

function storeMarker(){
  var lng = document.getElementById("longitude").value;
  var lat = document.getElementById("latitude").value;
    
  var getVars =  "?found=" + document.getElementById("found").value
	  + "&left=" + document.getElementById("left").value
	  + "&lng=" + lng
	  + "&lat=" + lat ;
    
  var request = GXmlHttp.create();
    
  //open the request to storeMakres.php on your server
  request.open('GET', 'storeMarker.php' + getVars, true);
  request.onreadystatechange = function() {
    if (request.readyState == 4) {
      //the request in complete
    	    
    	var xmlDoc = request.responseXML;
    	    
    	//retrieve the root document element (response)
    	var responseNode = xmlDoc.documentElement;
    	    
    	//retrieve the type attribute of the node
    	var type = responseNode.getAttribute("type");
    	    
    	//retrieve the content of the responseNode
    	var content = responseNode.firstChild.nodeValue;
    	    
    	//check to see if it was an error or success
    	if(type!='success') {
        alert(content);
    	} else {
        //Create a new marker and add it's info window.
        var latlng = new GLatLng(parseFloat(lat),parseFloat(lng));
        		
        var marker = createMarker(latlng, content, "");
        		
        map.addOverlay(marker);
        map.closeInfoWindow();
    	}
    }
  }
  
  request.send(null);
  return false;
}

// This routine returns the height of the inner window for different browsers
function windowHeight() {
  // Standard browsers (Mozilla, Safari, etc.)
  if ( document.all ) {
    if (document.body.offsetHeight) {
    	return document.body.offsetHeight;
    }
  } else {
    if (self.innerHeight) {
    	return self.innerHeight;
    }
  }
  
    
  // IE 6
  if (document.documentElement && document.documentElement.clientHeight)
	  return document.documentElement.clientHeight;
    
  // IE 5
  if (document.body)
  	return document.body.clientHeight;
    
  // Just in case.
  return 0;
}

// This routine is called when the window is resized to move all the components around
function handleResize() {
  var height = windowHeight() - document.getElementById('toolbar').offsetHeight - 30;
  document.getElementById('map').style.height = (height-16) + 'px';
  document.getElementById('sidebar').style.height = (height-14) + 'px';
  document.getElementById('route').style.height = (height-16) + 'px';
  document.getElementById('results').style.height = (height -  document.getElementById('searchPane').offsetHeight - 1 - 15) + 'px';
  if ( map != null ) {
    map.checkResize();
  }
}

// This routine handles changing the state from loading to standby
function changeBodyClass(from, to) {
  document.body.className = document.body.className.replace(from, to);
  return false;
}

function filterItem( marker, recnum ) {

  // Return value:
  // -1 - Not in view or not displayed
  // 0 - In view but filtered out by cuisine
  // 1 - Matches filter
  var matched  = -1;
  
  // Skip if the marker is null (i.e. if this is an empty array value)
  if ( marker != null ) {
  
    // The marker member variable is the GMarker which is needed either way
    var myMarker = marker.marker;
  
    // Process based on filter value
    if ( type == 0 || marker.type == type ) {
      var myMarkerLL = myMarker.getLatLng();
      var bounds = map.getBounds();
      if ( bounds.containsLatLng(myMarkerLL)) {

        if ( cuisineFilterValue == 0 || marker.c1 == cuisineFilterValue ||
             marker.c2 == cuisineFilterValue ||
             marker.c3 == cuisineFilterValue || 
             marker.c4 == cuisineFilterValue ) {

          AddItemToResultsList( myMarker, marker.name, myMarkerLL, recnum, true, null );
          //myMarker.setImage(null);
          //GLog.write( "+ " + marker.name + " type: " + marker.type );
          switch ( marker.type ) {
            case '0': myMarker.setImage( "images/pmarker.png"); break;
            case '1': myMarker.setImage( "images/rmarker.png"); break;
            case '2': myMarker.setImage( "images/bmarker.png"); break;
            case '3': myMarker.setImage( "images/gmarker.png"); break;
          }
          
          matched = 1;
          
        } else {
          switch ( marker.type ) {
            case '0': myMarker.setImage( "images/pmarkerlt.png"); break;
            case '1': myMarker.setImage( "images/rmarkerlt.png"); break;
            case '2': myMarker.setImage( "images/bmarkerlt.png"); break;
            case '3': myMarker.setImage( "images/gmarkerlt.png"); break;
          }
          matched = 0;
        }
      }
      // Turn "on" matching items even if not in view to show when panning
      myMarker.show();
    } else {
      // Turn off displaying the marker 
      myMarker.hide();
      //GLog.write( "- " + marker.name + " type: " + marker.type );
    }
  }
  
  return matched;
}


function applyFilter() {
              
  // Clear the list as we need to rebuild
  clearList('results-list');
  
  // If no markers are in the list, quit early
  if ( currentMarkers.length == 0 ) 
    return;

  // If a cuisine filter is on AND at least one restaurant is in view
  // remind the user that it is on
  var cs = document.getElementById('cuisineSelectID');
  if ( cs.selectedIndex != 0 ) {
    var name = cs.options[cs.selectedIndex].text;
    addHeadingToList( "<b> Matching: " + name + "</b>" );
  }
  
  // Keep a list of unmatched items
  var unmatched = [];
  var matched = false;
  
  // Run through and filter each item
  var l=currentMarkers.length;
  var i=-1;
  while ( ++i !== l) {
    var recnum = currentMarkers[i];
    var match = filterItem( restaurantMarkers[recnum], recnum );
    
    if ( match == 0 ) {
      unmatched[unmatched.length] = recnum;
    } else if ( match == 1 ) {
      matched = true;
    }
  }

  // If there is at least one marker which could be displayed
  // and there weren't any matches to the filter, indicate so
  if ( ! matched )  {
    addHeadingToList( "None" );
  }

  // If a filter is enabled AND at least one item didn't match, list them
  if (( cs.selectedIndex != 0 ) && ( unmatched.length > 0 )) {
    addHeadingToList( "<b> Additional (Not Matched) </b>" );
    
    // Iterate on items which didn't match
    var uCount = -1;
    while ( ++uCount !== unmatched.length ) {
      var item = unmatched[uCount];
      var marker = restaurantMarkers[item].marker;
      AddItemToResultsList( marker, restaurantMarkers[item].name, marker.getLatLng(), item, true );
    }
  }
}

function filter(filterType) {

  // Ignore if type requested is already active
  if ( type != filterType ) {
    type = filterType;
    
    // Only update screen if showing restaurants
    if ( map.getZoom() >= 13 ) { 
      applyFilter();
    }
  }
  
  // Set the "selected" filter to be underlined & bold, first clear all
  document.getElementById('filterAll').style.fontWeight = '';
  document.getElementById('filterMeat').style.fontWeight = '';
  document.getElementById('filterDairy').style.fontWeight= '';
  document.getElementById('filterPareve').style.fontWeight= '';
  document.getElementById('filterAll').style.textDecoration = '';
  document.getElementById('filterMeat').style.textDecoration = '';
  document.getElementById('filterDairy').style.textDecoration = '';
  document.getElementById('filterPareve').style.textDecoration = '';
  switch (filterType) {
    case 1 : document.getElementById('filterMeat').style.fontWeight = 'bold';
             document.getElementById('filterMeat').style.textDecoration = 'underline';
               break;
    case 2 : document.getElementById('filterDairy').style.fontWeight = 'bold';
             document.getElementById('filterDairy').style.textDecoration = 'underline';
               break;
    case 3 : document.getElementById('filterPareve').style.fontWeight = 'bold';
             document.getElementById('filterPareve').style.textDecoration = 'underline';
               break;
    case 0 :
    default:   document.getElementById('filterAll').style.fontWeight = 'bold';
              document.getElementById('filterAll').style.textDecoration = 'underline';
               break;
  }
}

function cuisineFilter() {
  var cs = document.getElementById('cuisineSelectID');
  var value = cs.options[cs.selectedIndex].value;
  
  if ( cuisineFilterValue != value ) {
    cuisineFilterValue = value;
    applyFilter();
  }
}

function enableFilters() {
  document.getElementById('filter').style.visibility='visible';
  document.getElementById('cuisines').style.visibility='visible';
}

function disableFilters() {
  document.getElementById('filter').style.visibility = 'hidden';
  document.getElementById('cuisines').style.visibility='hidden';
  
  // Set this filter to "off"
  cuisineFilterValue = 0;
  document.getElementById('cuisineSelectID').selectedIndex = 0;
}

function gotoAddress( searchText ) {
 
  // only initialize the geocoder once
  if (!geocoder) geocoder = new GClientGeocoder();
  
  // Clear list
  clearList('findResults'); 
    
/*  geocoder.getLatLng(searchText,
    function (response) {

      if (!response) {
        alert('Error geocoding address: ' + searchText );
      } else {
        map.setCenter( response, 13 );
        document.getElementById('searchLocation').value = ''; // clear the search box
        var listItem = document.createElement('li');
        listItem.innerHTML = "<b><i>" + searchText + "</i></b>";    
        document.getElementById('findResults').appendChild(listItem);
      }
    }
  );
*/

  geocoder.getLocations(searchText,
    function (response) {

      var found;
      if (!response || response.Status.code != 200) {
        found = false;
      } else {
        found = true;
        place = response.Placemark[0];
        point = new GLatLng(place.Point.coordinates[1],
                            place.Point.coordinates[0]);
        
        var newZoom = zoomLevel( place );
        map.setCenter( point, newZoom );
        document.getElementById('searchLocation').value = ''; // clear the search box
        var listItem = document.createElement('li');
        listItem.innerHTML = "<b><i>&nbsp;" + place.address + "</i></b>";    
        document.getElementById('findResults').appendChild(listItem);
      }
      
      // if Geocoding didn't work, try a restaurant query - now by name only
      if ( ! found ) {
        var listItem = document.createElement('li');
        listItem.innerHTML = "<b><i>&nbsp;" + searchText + " Not Found</i></b>";    
        document.getElementById('findResults').appendChild(listItem);
      }
    }
  );
        
  return false;
}


function zoomLevel( placemark ) {
  var accuracy = placemark.AddressDetails.Accuracy;

  /*
  0 	 Unknown location. (Since 2.59)
  1 	Country level accuracy. (Since 2.59)
  2 	Region (state, province, prefecture, etc.) level accuracy. (Since 2.59)
  3 	Sub-region (county, municipality, etc.) level accuracy. (Since 2.59)
  4 	Town (city, village) level accuracy. (Since 2.59)
  5 	Post code (zip code) level accuracy. (Since 2.59)
  6 	Street level accuracy. (Since 2.59)
  7 	Intersection level accuracy. (Since 2.59)
  8 	Address level accuracy. (Since 2.59)
  */
  switch ( accuracy ) {
    case 0: //   0 	 Unknown location. (Since 2.59)
    default:
      newZoomLevel = 4;
      break;
    case 1: //   1 	Country level accuracy. (Since 2.59)
      newZoomLevel = 6;
      break;
    case 2: //   2 	Region (state, province, prefecture, etc.) level accuracy. (Since 2.59)
      newZoomLevel = 8;
      break;
    case 3: //   3 	Sub-region (county, municipality, etc.) level accuracy. (Since 2.59)
      newZoomLevel = 9;
      break;
    case 4: //   4 	Town (city, village) level accuracy. (Since 2.59)
      newZoomLevel = 10;
      break;
    case 5: //   5 	Post code (zip code) level accuracy. (Since 2.59)
      newZoomLevel = 13;
      break;
    case 6: //   6 	Street level accuracy. (Since 2.59)
      newZoomLevel = 15;
      break;
    case 7: //   7 	Intersection level accuracy. (Since 2.59)
    case 8: //   8 	Address level accuracy. (Since 2.59)
      newZoomLevel = 16;
      break;
  }
  return newZoomLevel;
}

function findKey(eventData) {
  if(window.event) { // IE
    keynum = eventData.keyCode;
  } else if(eventData.which) { // Netscape/Firefox/Opera
    keynum = eventData.which;
  }

  if ( keynum == 13 ) {
    find();
  }
}

function gotoLL( lat, lng ) {
      	var	latlng = new GLatLng(parseFloat(lat),parseFloat(lng));
        map.setCenter( latlng, 16 );
}

// Call from UI
function gotoEstablishmentNext( ) {
  savedSearchSkip += 5;
  gotoEstablishmentSearch( savedSearchText, savedSearchSkip );
}

function gotoEstablishmentPrev( ) {
  savedSearchSkip -= 5;
  if ( savedSearchSkip < 0 ) savedSearchSkip = 0;
  gotoEstablishmentSearch( savedSearchText, savedSearchSkip );
}

// Call from UI
function gotoEstablishment( searchText ) {
  savedSearchText = searchText;
  savedSearchSkip = 0;
  gotoEstablishmentSearch( searchText, 0 );
}

function gotoEstablishmentSearch( searchText, skip ) {
  //console.trace();
  var request = GXmlHttp.create();
  var getVars =  "?nm=" + searchText + "&skip=" + savedSearchSkip;
  
  // Clear list
  clearList('findResults'); 
    
  //tell the request where to retrieve data from.
  request.open('GET', 'gmapq.php' + getVars, true);
  request.onreadystatechange = function() {
    if (request.readyState == 4) {
      
      //GLog.write( 'Length of response ' + request.responseText.length );
  	  var xmlResp = request.responseText;
  	  var xmlDoc = request.responseXML;
      //GLog.write (xmlResp);	
  
  	  if ( xmlDoc && xmlDoc.documentElement ) {
  
  	    // Process Markers
  	    var places = xmlDoc.documentElement.getElementsByTagName("place");

        // Clear the search box
        document.getElementById('searchLocation').value = ''; 
  	    var listItem;
        var latlng;
        var gotolat;
        var gotolng;

        // Handle Prev - Next, Next, Prev options for end of list
        if ( skip > 0 && places.length >= 5 ) {
          listItem = document.createElement('li');
          listItem.innerHTML = "<a href='#' onclick='gotoEstablishmentPrev()'>Prev Five</a> - <a href='#' onclick='gotoEstablishmentNext()'>Next Five</a><br>";
          document.getElementById('findResults').appendChild(listItem);
        } else if ( places.length >= 5 ) {
          listItem = document.createElement('li');
          listItem.innerHTML = "<a href='#' onclick='gotoEstablishmentNext()'>Next Five</a><br>";
          document.getElementById('findResults').appendChild(listItem);
        } else if ( skip > 0 && places.length < 5 ) {
          listItem = document.createElement('li');
          listItem.innerHTML = "<a href='#' onclick='gotoEstablishmentPrev()'>Prev Five</a><br>";
          document.getElementById('findResults').appendChild(listItem);
        }
          	    
        //GLog.write( "# places = " + places .length );
        for ( var iCount = 0; iCount < places.length; iCount++ ) {
      		var lng = places[iCount].getAttribute("lng");
      		var lat = places[iCount].getAttribute("lat");
      		var loc = places[iCount].getAttribute("loc");
      		var name = places[iCount].getAttribute("name");
      		
          // Handle both geocoded & non-geocoded versions
          listItem = document.createElement('li');
          if ( lat != "" ) {
            latlng = new GLatLng(parseFloat(lat),parseFloat(lng));
            listItem.innerHTML = "<a href='#' onclick='gotoLL( " + lat + ", " + lng + ")'><b><i>" + name + "</i></b> (" + loc + ")</a><br>";
            if ( iCount == 0 ) {
              gotolat = lat;
              gotolng = lng;//map.setCenter( latlng, 17 );
            }
          } else {
            var newlink = "http://www.shamash.org/kosher/search.php?Comm=three&Name="+URLEncode(name);
            var title = "Shamash Results";
            listItem.innerHTML = html = '&nbsp;&nbsp;<img src="images/bkmarkerwh.png" height="10" width="8"><a href="#" onclick="openPage( title, \'' + newlink + '\')"><b><i>' + name + '</i></b> (' + loc + ')</a><br>';
          }
          
          // Add the entry to the list    
          document.getElementById('findResults').appendChild(listItem);
        }
        
        if ( places.length == 0 && skip == 0 ) {
          listItem = document.createElement('li');
          listItem.innerHTML = "<b><i>&nbsp;" + searchText + "&nbsp;Not Found</i></b>";    
          document.getElementById('findResults').appendChild(listItem);
        } else if ( places.length == 0 && skip != 0 ) {
          listItem = document.createElement('li');
          listItem.innerHTML = "<b><i>&nbsp;No more matches - " + searchText + "</i></b>";    
          document.getElementById('findResults').appendChild(listItem);
        }
  	  }
    }   
  } //request.onreadystategchange function
  
  //tell the request what to do when the state changes.
  request.send(null);
}

function find() {
  var placeSearch = document.getElementById("placeSearch").checked;
  var findText = document.getElementById("searchLocation").value; 
  if ( findText.length > 0 ) {
    if ( placeSearch ) {
      gotoAddress( findText );
    } else {
      gotoEstablishment( findText );
    }
  }
}


function createCallbacks() {

  // Set up hooks for route panel controls
  document.getElementById('button-directions-show').onclick = function() {
    return changeBodyClass('noroutebar', 'route-right' )};     
  document.getElementById('button-directions-hide').onclick = function() {
    return changeBodyClass('route-right', 'noroutebar' )};     
  document.getElementById('clear-directions').onclick = function() {
    return clearDirections()};
  document.getElementById('reverse-directions').onclick = function() {
    return reverseDirections()};

  // Set up hook for search
  document.getElementById('findButton').onclick = function() {
    return find() };
      
  // Set up hooks for filter controls & initial state
  document.getElementById('filterAll').style.fontWeight = 'bold';
  document.getElementById('filterAll').style.textDecoration = 'underline';
  
  document.getElementById('filterAll').onclick = function() {
    return filter(0) };
  document.getElementById('filterMeat').onclick = function() {
    return filter(1) };
  document.getElementById('filterDairy').onclick = function() {
    return filter(2) };
  document.getElementById('filterPareve').onclick = function() {
    return filter(3) };
  
  document.getElementById('cuisineSelectID').onchange = function() {
    return cuisineFilter() };
    
}

// The routine is called only once when the URL is first called
function init() {
    
  // Set up the sizes for all elements - first time only, other times are handled
  handleResize();
    
  // Create the main map and add all controls
  map = new GMap2(document.getElementById("map"));
  map.addControl(new GLargeMapControl());
  map.addControl(new GMapTypeControl());
  map.setCenter(new GLatLng(startLat, startLon), startZoom);
  map.enableDoubleClickZoom();
  map.enableContinuousZoom();
    
  // Create Marker Manager to manage all the markers
  markerManager = new GMarkerManager(map);
    
  // This causes restaurants to be added to the map when the map moves
  GEvent.addListener(map, "moveend", function() { updateMarkers() });

  // Initialize callbacks
  createCallbacks();
    
  // start off with "working" turned off
  changeBodyClass( 'standby', 'loading' );
  changeBodyClass( 'geocoder-idle', 'geocoder-busy' );
  changeBodyClass( 'route-right', 'noroutebar');

  // DO THIS AGAIN AFTER SETTING BODY CLASS
  handleResize();
    
  // Process the first batch of markers
  updateMarkers();
  
  //GLog.write ( startLat + " " + startLon + " " + startZoom );	
}

// Equivalent of "main" code below
window.onresize = handleResize;
window.onload = init;
window.onunload = GUnload;
