
	// page instance variables 
	var locations = [];
	var gmap ;
	var gdirections; 


	/**
	 * function to display a div using the effects library 
	 * @param divid - id of the div to be displayed
	 */	
	function showdiv(divid) { 
		obj = document.getElementById(divid);
 		new Effect.Appear(obj);
	} 
	
	/**
	 * function to fade and hide a div using the effects library 
	 * @param divid - id of the div to be hidden
	 */		
	function fadediv(divid) { 
		obj = document.getElementById(divid);
		new Effect.Fade(obj); 
	} 

	/**
	 * function to check if a argument is null or empty 
	 *
	 * @param arg - string value
	 * @returns true if the argument is empty or null
	 */		
	function isEmpty(arg) {
		if ( arg==null  || arg == '' ) {
		    return true;
	      }
		return false;
	}

	/**
	 * function to insert the html contents of a div
	 * @param divId - id of the div 
	 * @param html - contents to be inserted in the div 
	 */		
	function setDivHtml(divId, html){
		div = document.getElementById(divId);
		if (div!=null) { 
			div.innerHTML = html; 
		} 
	}
	 

	/**
	 * compares two locations based on a alpha comparison of the name of each location.
	 *
	 * @param  locationA - a Location object to compare against second location based on the name attribute
	 * @param  locationB - a Location object to compare against first location based on the name attribute
	 * @returns 	-1 if name value of locationA is less than that of locationB
	 *	  	         0 if name value of locationA is equal to that of locationB
	 *			     1 if name value of locationA is greater than that of locationB
	*/
	function compareLocationsByName(locationA, locationB) {
		var x = locationA.name;
		var y = locationB.name;
	    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
	}	 
	

	/**
	 * compares two locations based the distance attribute of each location 
	 *
	 * @param  locationA - a Location object to compare against second location based on the distance attribute
	 * @param  locationB - a Location object to compare against first location based on the distance attribute
	 * @returns 	-1 if locationA is closer than locationB based on the distance attribute
	 *	  	         0 if locationA is the same distance as locationB
	 *			     1 if locationA is farther than locationB based on the distance attribute
	 */
	function compareLocationsByDistance(locationA, locationB) {
		var x = locationA.distance;
		var y = locationB.distance;
	    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
	}	


 	/**
	 * function handles loading an  array of data from an xml file 
	 * @param fileName- String containing the xml filename 
	 */
	function loadLocationsFromXML(fileName)  { 
		GDownloadUrl(fileName , function(data) { 
				var xml = GXml.parse(data);
				var markers = xml.documentElement.getElementsByTagName("location");
				for (var i = 0; i < markers.length; i++) {
					locations[i] = new Object();
					locations[i].code = markers[i].getAttribute("branchcode");
					locations[i].lat = markers[i].getAttribute("lat");
					locations[i].lng = markers[i].getAttribute("lng");
					locations[i].name =  markers[i].getAttribute("name");		
					locations[i].address =  markers[i].getAttribute("address");
					locations[i].city = markers[i].getAttribute("city");
					locations[i].state = markers[i].getAttribute("state");
					locations[i].zip = markers[i].getAttribute("zip");
					locations[i].link = markers[i].getAttribute("link");	 
					locations[i].phone = markers[i].getAttribute("phone");	
					locations[i].distance=0;
				}
						
				if (locations==null) {
		  	 		alert("Unable to populate data objects  from xml file");
		  	 	}
		  	 	
		  	 	// populate display with locations 
				populatePositionsOnMap(locations);
				populateLocationHtmlRows(locations, false,true);
							
				
			}
		)

	 	

	}
	

	
 	/**
	 * function finds and returns a requested location based the code attribute 
	 * @param code - value used to identify a matching location
	 * @returns location  
	 */	
	function getLocationByCode(code){
		for (var i = 0; i < locations.length; i++) {
			if (locations[i].code==code) {
				return locations[i];
			}
		}
	} 

 	/**
	 * function to sort the locations array based distance attribute 
	 */	
	function sortLocationsByDistance()
	{
		locations.sort(compareLocationsByDistance);
	}	
 
 	/**
	 * function returns the html contents to display in the information bubble
	 * for a given location. 
	 * @param location - a populated location object
	 */	
   	function getLocationHtmlBubble(location)
   	{		
		return   '<a  href="' + 
					location.link 		+ '">' + 
			   		location.name   	+ '</a></br>' +
			   		location.address 	+ '</br>' + 
			   		location.city 		+  ', ' + 
			   		location.state 		+ ' ' + 
			   		location.zip 		+ '</br>'  +
					location.phone 		+ '</br>'  ;  
	}

	/**
	 * function returns the formatted html row for a given location 
	 * @param location - a populated location object
	 * @param displayMapToLink - boolean flag that controls if a 'Map It' should be included in the row 
	 * @returns - html 
	 */
   	function getLocationHtmlRow(location, displayMapToLink )
    {	
    	if (location==null){
    	   alert('Invalid location detected');
    	}
    	 
		var html  =  '<tr ><td></td><td ><a   style="color:black; letter-spacing: 0pt; text-decoration: underline;font-size: 9px;font-family: Tahoma, Arial, Sans-serif;" >'
					+  location.name   + '</a><td></tr>' + '<tr><td >&nbsp;</td><td ><font color="#585858">'  
			   		+  location.phone + '</br>'
					+  location.address + '</br>'  
			  		+  location.city +  ' ' 
			  		+  location.state + ' ' 
					+  location.zip + '</font></td></tr>' ;
		
		if (displayMapToLink) {
				html = html + '<tr><td></td><td align=right>' 
							+ '<font color="#CC3300">' 
							+ '<span  onclick="' + "mapDirectionsToLocation('" + location.code + "'); return false;" + '" >map it&nbsp;</span>' 
							+ '</font><td></tr>' ;
		}	   
		 
		html = html +  '<tr><td colspan=3 height="1px" style="border-bottom-style: solid; border-bottom-width: 1;">&nbsp;</td></tr>'  ;  
	 
		return html;		   
			   
	}
	
 	/**
	 * function sets the distance to each location in the locations array 
	 * based on the geocoded starting position.
	 * @param fromPoint - geocoded start position
	 */	
	function populateLocationDistance(fromPoint)
	{ 
		for (var i = 0; i < locations.length; i++) {
			distance = locations[i].point.distanceFrom(fromPoint);
    	    locations[i].distance = distance;
		}
	}	
	
	/**
	 * function displays the list of locations sorted by the distance from a geocoded start point 
	 * @param  startpoint - gpoint start position (lat/long)
	 */
	function displayLocationsByDistance(startpoint) {
			populateLocationDistance(startpoint);
   			sortLocationsByDistance();	
   			populateLocationHtmlRows(locations,true,false);	 
	}  


 	/**
	 * function populates the list of all locations  
	 * @param args[] - array containing the loations to display 
	 * @param displayMapToLink - boolean flag determines if a 'Map It' should be included in the row 
	 * @param displayInfoBubble - boolean flag determines if a informational bubble is available 
	 */
    function populateLocationHtmlRows(args, displayMapToLink, displayInfoBubble)
    { 
		html = "<table class='locationslist' style='cursor:pointer;cursor:hand;' " +
			   " border=0 width='100%' cellspacing=3 cellpading=3  >"; 
			  
		
		for (var i = 0; i < args.length; i++) {
			if (  (args[i].state==null) || (args[i].state=='') ||(args[i].state=='MX') ) {
				continue;
			} else if (displayInfoBubble) {
				 html = html + '<span onmouseover="javascript: showInfoBubble( ' + i + ');  " >'
						     +  getLocationHtmlRow( args[i],  displayMapToLink )
						     + '</span>';
			} else {
				 html = html + getLocationHtmlRow( args[i],  displayMapToLink );
			}	      
		}
		
		html = html + "</table>";
		setDivHtml(DIV_LOCATIONLIST, html);

	}     

	/**
	 * displays the starting location entry form
	 */	
	function showStartEntryForm()  {
		fadediv(DIV_DIRECTIONSLINK);
		showdiv(DIV_STARTADDRESS);
		setTimeout('setStartAddressFocus()',1250 ) ;
	}
	
	/**
	 * hides the starting location entry form
	 */		
	function hideStartEntryForm() {
		fadediv(DIV_STARTADDRESS);
		showdiv(DIV_DIRECTIONSLINK);
		resetForm();
	}

	/**
	 * function to return the user entered starting address.  
	 * @returns value entered the start address form 
	 */	
	function getStartAddress() {
		obj = document.getElementById(STARTADDRESS_FIELDNAME);
		if (obj!=null) { 
			return obj.value;
		}	
	}
	
	/**
	 * function to set the value in the start address 
	 * @param - value to be set in the start address field
	 */		
	function setStartAddress(arg) {
		obj = document.getElementById(STARTADDRESS_FIELDNAME);
		if (obj!=null) {
			obj.value=arg;
		}
	}

	/**
	 * sets the start field active 
	 */	
	function setStartAddressFocus()  {
		obj = document.getElementById(STARTADDRESS_FIELDNAME);
		if (obj!=null) {
			obj.focus();
		}
	}

	/**
	 * function to set a default value in the starting adress 
	 * form field.  This text contains an example or hint of what 
	 * the user should be entering in the field. 
	 */	
	function setStartAddressDefaults() {
		if ( isEmpty(getStartAddress()) )  {
			setStartAddress(STARTADDRESS_DEFAULTTEXT);
		} 
	}
 
 	/**
	 * function to clear the default value (if exists) in the start field 
	 * without clearing any user entered text. 
	 */	
	function clearStartAddressDefaults(){ 		
		fromAddress = getStartAddress();
		if (fromAddress != STARTADDRESS_DEFAULTTEXT  )  {
			return false; 	
		}  
		setStartAddress('');	
		return; 
	}
	
 
 	/**
	 * function to reset the form and any existing routing dirctions 
	 */	
	function resetForm() {
		form = document.forms[STARTADDRESS_FORMNAME];
		if (gmap!=null && form!=null) {  
			form.reset();
			setDivHtml(DIV_ROUTE,"");
			gmap.clearOverlays();
			gmap.setCenter(new GLatLng(38.2, -95.8), 3);
			populatePositionsOnMap(locations);			
			populateLocationHtmlRows(locations, false,true);
		}
	} 
 
 	/**
	 * function handles initialization and setup of the page when first loaded 
	 */
    function initialize() {
		if (GBrowserIsCompatible()) {
			createGoogleMap();
			loadLocationsFromXML(DATA_FILE);
		} else {
			alert('This browser does not support Google Maps');
		}
			
		// default with the entry form open	
		setTimeout('showStartEntryForm()',450); 
    }	
	


	function validateStartAddressValid()
	{ 
		startObj = document.getElementById(STARTADDRESS_FIELDNAME);
		startObj.focus();
		startAddress = getStartAddress(); 
		if ( isEmpty(startAddress) || startAddress==STARTADDRESS_DEFAULTTEXT ) {
			alert("Enter starting address"); 
			return false;
		}  
		return true;

	}

 
 	/**
	 * function envoked by the user when requesting the directions to 
	 * the closest location
	 */	
 	function showDirections()  {
		if ( ! validateStartAddressValid() ) {
			return false;
		}
		
		geocodeMapDirections(startAddress);
		 
	}
	
 	/**
	 * function to create a popup informational bubble associated with a marker on the map. 
	 * @param pin - a marker position on the map which appears as a red icon
	 * @param html - the information (html) to appear in the bubble
	 */		
	function createInfoBubble(pin, html) 
	{
		GEvent.addListener(pin, "click", function(){pin.openInfoWindowHtml(html);} )
	} 

	
 	/**
	 * function handles envoking the display of the information bubble for a location 
	 * @param index - index position in the locations array
	 */	
	function showInfoBubble(index)
	{  
	     html = getLocationHtmlBubble(locations[index]);
         locations[index].gmarker.openInfoWindowHtml(html); 
	}	
	
	/**
	 * function to handle the creation and initialiation of a GMap2 object  
	 */
	function createGoogleMap() 
	{
 		gmap = new GMap2(document.getElementById(DIV_MAP));
 		gdirections = new GDirections(gmap, document.getElementById(DIV_ROUTE));
        gmap.setCenter(new GLatLng(38.2, -95.8), 3) ;
        gmap.addControl(new GSmallMapControl());
        gmap.addControl(new GMapTypeControl());
  		gmap.enableContinuousZoom();
        gmap.enableDoubleClickZoom();	
		gmap.hideControls();
		GEvent.addListener(gmap, "mouseover", function(){gmap.showControls();});
		GEvent.addListener(gmap, "mouseout", function(){gmap.hideControls();});
		GEvent.addListener(gdirections, "error", handleRoutingError);
	} 

 	/**
	 * function handles mapping directions for a from/to address 
	 * @param fromAddress- String containing the starting address 
	 */
	function mapDirections(fromAddress, toAddress) 
	{
		if (gdirections==null) {
			gdirections = new GDirections(gmap, document.getElementById(DIV_ROUTE));
			GEvent.addListener(gdirections, "error", handleRoutingError ); 
		}
		
		setDivHtml(DIV_ROUTE,"");
		gdirections.clear(); 
		showdiv(DIV_ROUTE);  
		gdirections.load("from:  " + fromAddress + " to:  " + toAddress);
		
		
	}  
	
 	/**
	 * function handles mapping directions for a from/to address 
	 * @param fromAddress- String containing the starting address 
	 */
	function populatePositionsOnMap(args) 
	{
		for (var i = 0; i < args.length; i++) {
			var point = new GLatLng(args[i].lat,args[i].lng);
			var gmarker = new GMarker(point,{icon:tiny, title:args[i].name});	
			args[i].gmarker = gmarker ;	
			args[i].point=point;  
			gmap.addOverlay(gmarker);	
			htmlInfo = getLocationHtmlBubble(args[i]);
			createInfoBubble(gmarker , htmlInfo ) ; 				 		
	 	}
	}  	

	/**
	 * function displays routing directions to the closest location based on the start address.  
     * @param startaddress - string containing the user entered starting address
     *
	 * Steps: 
	 *           (1) Starting address is geocoded into lat/long position (asynchronous call)
	 *           (2) Sorts and redisplays the list of locations 
	 *           (3) Maps directions to the first location of the sorted locations array
	 * 
	 */
	function geocodeMapDirections(startaddress)
	{ 
		gmap.closeInfoWindow();	
		geocoder = new GClientGeocoder(); 
		geocoder.getLatLng(startaddress ,
      		function(point) {
				if (point==null) { 
				handleGeocodingError(geocoder, startaddress);
				return false;
		    }
   			displayLocationsByDistance(point); 
			
		    toAddress = locations[0].lat + ' '  + locations[0].lng;
		    mapDirections(startaddress, toAddress ) ;	
		   }
  		);
  		
  		return true;
	}

 	/**
	 * function handles mapping directions to a given location based on the location code
	 * @param code- String containing a code that uniquely identifies the location 
	 */
	function mapDirectionsToLocation(code){

		if ( ! validateStartAddressValid() ) {
			return false;
		}

		loc = getLocationByCode(code);
		if (loc!=null){ 
			startAddress = getStartAddress();
			toAddress = loc.lat + ' '  + loc.lng;
			mapDirections(startAddress, toAddress);
		}
	}	


 	/**
	 * function handles display of error messages when unable to geocode (lat/long) an address 
	 * @param Geocoder - Google geocoder object
	 * @param address - address which generated the error
	 */		
	function handleGeocodingError(geocoder, address)
	{
		if (geocoder==null || geocoder.getCache()==null) {
			alert('Geocoding error:  Unable to route directions');
		}
		
		var result=geocoder.getCache().get(address);
		if (result!=null && result.Status!=null) 
			displayErrorMessage(result.Status.code);
		else 
			alert('Unable to directions for address:  ' + address); 
	
	}

 	/**
	 * function handles display of error messages when unable to route directions for an address 
	 */	
	function handleRoutingError() 
	{
	
		if (gdirections==null || gdirections.getStatus()==null ){ 
		   alert('Error:  Unable to route address due to null gdirections object');
		   return;
		} 
		displayErrorMessage(gdirections.getStatus().code);
	 } 	 


 	/**
	 * function handles display of error messages by code
	 */		
	function displayErrorMessage(code) {
		if (code==null || ERRORMESSAGES[code]==null) { 
			alert('Invalid address detected');
		} else {
			alert(ERRORMESSAGES[code]); 
		}
	}






