//
// Copyright (c) 2005, Android Technologies, Inc.
//
// FILE: upcoming_maps_2.js
//
// NOTE: See upcoming-maps-2.php for the global variable
//			declarations and initialization code.

var lastSearch = "";

var gBusyAnimationFrame = 1;

var gAnimationInterval = null;

// ======================= METRO data cache "CLASS"

// This class helps us manage the POI marker lists for 
//  neighboring metros that have the events simultaneously
//  displayed on the map.
function _metroData(metroID, poiMarkerList, minLat, minLon, maxLat, maxLon)
{
	this._metroID 			= metroID;
	this._poiMarkerList		= poiMarkerList;
	this._minLat			= minLat;
	this._minLon			= minLon;
	this._maxLat			= maxLat;
	this._maxLon			= maxLon;
} // function _metroData()

// ======================= END: METRO data cache "CLASS"

/*
// ======================= Upcoming.org METRO "CLASS"

// This class helps us manage the POI marker lists for 
//  neighboring metros that have the events simultaneously
//  displayed on the map.
function _metroData(metroID, metroName, metroPostalCode, poiMarkerList)
{
	this._metroID 			= metroID;
	this._metroName			= metroName;
	this._metroPostalCode	= metroPostalCode;
	this._poiMarkerList		= poiMarkerList;
} // function _metroData()

// ======================= END: Upcoming.org METRO "CLASS"
*/

// ======================= Yahoo Map "CLASS"

// Constructor for the yahooMap class.  This class encapsulates
//  the elements and events needed to maintain a Yahoo map
//  and it's member POI markers.
function yahooMap(mapDivContainerName, poiMarkerList, minLat, minLon, maxLat, maxLon)
{
	// Find the center of the bounded region and use that value as
	//  the center point for the Map object constructor.
	var centerLat = minLat + ((maxLat - minLat) / 2);
	var centerLon = minLon + ((maxLon - minLon) / 2);
	
	var centerLatLon = new LatLon(centerLat, centerLon);
	
	this._map 			= new Map(mapDivContainerName, "androidtech", centerLatLon, 3);
	
	// Map events and map data member events.
	this._onInitialize	= yahooMap_onInitialize;
	this._onMove		= yahooMap_onMove;
	this._onZoom		= yahooMap_onZoom;
	this._onSetBounds	= yahooMap_onSetBounds;
	this._onGeocodeError= yahooMap_onGeocodeError;
	this._onOverlayInit = yahooMap_onOverlayInit;
	
	this._trafficOverlay= null;
	this._localSearchOverlay
						= null;
	
	this._panTool		= new PanTool();
	this._navigatorTool	= null;
	this._onInitializePanTool
						= yahooMap_onInitializePanTool;
						
	this._latLonRect 	= new LatLonRect(minLat, minLon, maxLat, maxLon);

	// List of our 	
	this._poiMarkerList	= poiMarkerList;
	
	// Add the event listeners.
	this._map.addEventListener(Map.EVENT_MOVE, this._onMove, this);
	this._map.addEventListener(Map.EVENT_ZOOM, this._onZoom, this);
	this._map.addEventListener(Map.EVENT_MAP_GEOCODE_ERROR, this._onGeocodeError, this);
	
	this._map.addEventListener(Map.EVENT_INITIALIZE, this._onInitialize, this);
	this._panTool.addEventListener(Tool.EVENT_INITIALIZE, this._onInitializePanTool, this );
}

// ----------------------- EVENT HANDLERS AND STUBS.
function yahooMap_onOverlayInit()
{
} // function yahooMap_onOverlayInit()

function yahooMap_onGeocodeError( oEvent )
{
	var suggestedAddresses = oEvent.suggestedAddresses;

	document.setCenterByAddress.mapAddressList.options.length = 0;
	if( suggestedAddresses != null )
	{
		for( var i = 0; i < suggestedAddresses.length; i++ )
		{
			var address = suggestedAddresses[i];
			var s = address.toString();
			document.setCenterByAddress.mapAddressList.options[ document.setCenterByAddress.mapAddressList.options.length ] = 
				new Option( s,s );
		}
		document.setCenterByAddress.mapAddressList.selectedIndex = document.setCenterByAddress.mapAddressList.options.length - 1;
	}
}

function yahooMap_onMove( oMoveEvent )
{
}

function yahooMap_onZoom( oZoomEvent )
{
}

function yahooMap_onSetBounds()
{
}

/*
function yahooMap_onGeocodeError()
{
}
*/

// This event is called when the map is ready to
//  initialize the pan tool.
function yahooMap_onInitializePanTool()
{
	this._map.setActiveTool( this._panTool );
}

// This event is called when the map initializes.
function yahooMap_onInitialize()
{
	var c = this._map.getCenter();
	var z = this._map.getZoomLevel();
	
	if (this._poiMarkerList)
	{
		for (var k in this._poiMarkerList)
		{
			// Make the add call for the object.
			this._poiMarkerList[k]._add(this._map);
		} // for()
	} // if (this._poiMarkerList)
	
	// Add a navigator.
	this._navigatorTool = new NavigatorWidget();
	this._map.addWidget( this._navigatorTool );
			
	this._localSearchOverlay = new LocalSearchOverlay();
	// Note: There is only one set of events and HTML form elements
	//  for Local search.  That's why onSearch is not a instance method
	//  of the Yahoo map class.
	this._localSearchOverlay.addEventListener(LocalSearchOverlay.EVENT_SEARCH_SUCCESS, onSearch, this);
	this._map.addOverlay(this._localSearchOverlay);
	
	// Set the this._map viewport to encompass all the points of interest
	//  we have generated from our Upcoming.org query.
	this._map.addTool( this._panTool );
	
	/*
alert(
	"Setting bounds to (" + 
	this._latLonRect.minLat + 
	"," +
	this._latLonRect.minLon + 
	"," +
	this._latLonRect.maxLat + 
	"," +
	this._latLonRect.maxLon +
	")");
	*/
	
	this._map.setBounds(this._latLonRect);

	clearFilterLists();
}
// ======================= END: Yahoo Map "CLASS"

// ======================= _mapMarkerData "CLASS"

// This class stores all the data associated with the marker
//  that is needed for operations related to event details.
function _mapMarkerData(eventID, venueID, eventDetailsHtml, venueDetailsHtml)
{
	this._eventID		= eventID;
	this._venueID		= venueID;
	this._eventDetailsHtml= eventDetailsHtml;
	this._venueDetailsHtml= venueDetailsHtml;
} // function _mapMarkerData()

// ======================= END: _mapMarkerData "CLASS"

// ======================= Marker "CLASS"
// This class encapsulates the functionality needed to represent
//  and create a Yahoo map point of interest marker.
function _mapMarkerPOI(
				mapAddress, 
				poiIndex, 
				poiTitle, 
				poiDescription, 
				poiMarkerColor, 
				poiStrokeColor,
				poiObj)
{
	// ================ Instance properties.
	this._mapAddress		= mapAddress;
	this._poiIndex			= poiIndex;
	this._poiTitle			= poiTitle;
	this._poiDescription	= poiDescription;
	this._poiMarkerColor	= poiMarkerColor;
	this._poiStrokeColor	= poiStrokeColor;
	
	// User defined object to associate with this POI marker.
	this._poiObj			= poiObj;
	
	// Events
	this._onMarkerInit		= _mapMarkerPOI_onMarkerInit;
	
	// Methods
	this._add				= _mapMarkerPOI_add;
	
	// Place to store the CustomPOIMarker object that will
	//  be created from this object, during the
	//  add() call.
	this._marker			= null;
	
	// Store a reference to the map that owns us.
	// NOTE: This is filled in when the marker add()
	//	call is made, NOT during construction.
	this._ownerMap			= null;

} // function _mapMarkerPOI()

function _mapMarkerPOI_onMarkerInit(e)
{
} // function _mapMarkerPOI_onMarkerInit()

function cleanString(theStr)
{
	var regPat = /[^A-Z,a-z,\s, ,0-9,\,,\.,&,;,:,<,>,",',\/,=,(,),\*,%,\$,@,\[,\],\{,\},\+,\-,_,!,’,`]/g;
	
	var retStr = theStr.replace(regPat, "");
	
	return retStr;
}

// This function adds the marker using the
//  the current values for the class data members.
function _mapMarkerPOI_add(ownerMap)
{
	var sType = 
		Marker.TYPE_POI;
	
	// Currently Yahoo maps doesn't like "<br>" elements in the
	//  marker title.  Replace them with spaces.
	var theTitle =
		this._poiTitle.replace(/<br>/ig, " ");
		
	// Clean it.
	theTitle = cleanString(theTitle);
		
	// Limit the length of the title.
	maxTitleLen = 47;
	
	if (theTitle.length > maxTitleLen)
		theTitle = theTitle.substr(0, 50) + "...";
		
	// Clean the description of funny characters.
	var theDescription =
		cleanString(this._poiDescription);
		
	this._marker = 
		new CustomPOIMarker( 
					this._poiIndex,
			  		theTitle,
					theDescription,
					this._poiMarkerColor,
					this._poiStrokeColor);
					
	/*
	this._marker =
		new CustomSWFMarker("./swf-marker.swf");
		
	// marker.callCustomMethod("setUrl", "http://www.macromedia.com/");		
	*/
	
	
	// Store a reference to the map that owns us.
	this._ownerMap = ownerMap;
					
	this._ownerMap.addEventListener( Marker.EVENT_INTIALIZE, this._onMarkerInit, window );

	// The location field string will contain strings defined by 
	//  a "record type".  Currently there are two record types:
	//		'address(<address string>)'
	//		'latlon(<lat, lon>)'
	//
	var address, lat, lon;
	var bAddressParsed = false;
	
	// Use regular expressions to determine the location
	var addressRegExp 	= /address\((.*)\)/;
	var latlonRegExp	= /latlon\((.*?),(.*?)\)/;
	
	// Regular address type?
	var matches = this._mapAddress.match(addressRegExp);
	
	if (matches)
	{
		// Matches should have a length of 2.
		if (matches.length == 2)
		{
			address = matches[1];
			this._ownerMap.addMarkerByAddress( this._marker, address );
			bAddressParsed = true;
		} // if (matches.length == 2)
	} // if (matches)

	if (!bAddressParsed)
	{			
		// Latitude/Longitude pair?
		var matches = this._mapAddress.match(latlonRegExp);
		
		if (matches)
		{
			// Matches should have a length of 3.
			if (matches.length == 3)
			{
				var lat = matches[1];
				var lon = matches[2];
				var theLatLon = new LatLon(lat, lon);
				this._ownerMap.addMarkerByLatLon( this._marker, theLatLon );
				bAddressParsed = true;
			} // if (matches.length == 2)
		} // if (matches)
	} // if (!bAddressParsed)

} // function _mapMarkerPOI_add()

// Marker class functions.

// ======================= END: Marker "CLASS"

// handler for search complete
function onSearch() 
{
	// we only reset the filters when the user submits a new search
	// term via peformSearch().  When we just submit new filters,
	// we get new and differents sets of filters back from the
	// LocalSearchResults, might be a bug?
	if (!resetFilters) 
	{
		stopBusyAnimation();

		return;
	} // if (!resetFilters) 
	
	resetFilters = false;

	// sort the filters into different select lists for category and rating
	clearFilterLists();
	filters = gCurrentYahooMap._localSearchOverlay.localSearchResults.searchFilters.concat();
	for (i = 0; i < filters.length; i++) {
		var list = null;
		switch (filters[i].type) {
		case LocalSearchFilter.TYPE_CATEGORY:
			list = document.searchfilters.categoryfilters;
			break;
		case LocalSearchFilter.TYPE_RATING:
			list = document.searchfilters.ratingfilters;
			break;
		}
		if (list != null) {
			list.options[list.options.length] = new Option(filters[i].name, filters[i].key);
		}
	}
	
	stopBusyAnimation();
}

function onSearchSuccess( event )
{			
	updateRefineLists();
}

function updateRefineLists()
{
	var id = document.overlayAPI.overlayList.options[document.overlayAPI.overlayList.selectedIndex].value;

	var overlay = Overlay.getOverlayByJavaScriptId( id );
	var results = overlay.localSearchResults;
	
	document.overlayAPI.categoryFilterList.options.length = 0;
	document.overlayAPI.categoryFilterList.options[ document.overlayAPI.categoryFilterList.options.length ] = 
				new Option("-- No Filter --","");
	document.overlayAPI.categoryFilterList.selectedIndex = 0;
	document.overlayAPI.ratingFilterList.options.length = 0;						
	document.overlayAPI.ratingFilterList.options[ document.overlayAPI.ratingFilterList.options.length ] = 
				new Option("-- No Filter --","");
	document.overlayAPI.ratingFilterList.selectedIndex = 0;						
				
	for( var i = 0; i < results.searchFilters.length; i++ )
	{
		var filter = results.searchFilters[i];
		
		if( filter.type == LocalSearchFilter.TYPE_CATEGORY )
		{
			document.overlayAPI.categoryFilterList.options[ document.overlayAPI.categoryFilterList.options.length ] = 
				new Option(filter.name+" ("+filter.count+")", filter.key);
		}
		else if( filter.type == LocalSearchFilter.TYPE_RATING )	
		{
			document.overlayAPI.ratingFilterList.options[ document.overlayAPI.ratingFilterList.options.length ] = 
				new Option(filter.name+" ("+filter.count+")", filter.key);						
		}
	}
}

function onLocalSearch( overlayId )
{
	var overlay = Overlay.getOverlayByJavaScriptId( overlayId );
	overlay.search( document.overlayAPI.searchTerms.value,
					gCurrentYahooMap._map.getCenter(),
					document.overlayAPI.searchBeginIndex.value,
					document.overlayAPI.searchResults.value );
}

function onRefineSearch( overlayId )
{
	var overlay = Overlay.getOverlayByJavaScriptId( overlayId );
	var categoryKey = document.overlayAPI.categoryFilterList.options[ document.overlayAPI.categoryFilterList.selectedIndex ].value;
	var ratingKey = document.overlayAPI.ratingFilterList.options[ document.overlayAPI.ratingFilterList.selectedIndex ].value;;
	
	overlay.search( document.overlayAPI.searchTerms.value,
					document.overlayAPI.searchBeginIndex.value,
					document.overlayAPI.searchResults.value,
					null,
					categoryKey,
					ratingKey );			
}		

function onLocalClear( overlayId )
{
	var overlay = Overlay.getOverlayByJavaScriptId( overlayId );
	overlay.clear();
}

function toggleVisibility( elementId )
{
	var display = document.getElementById( elementId ).style.display;

	if( display == 'none' || display == '' )
		document.getElementById( elementId ).style.display = 'block';
	else
		document.getElementById( elementId ).style.display = 'none';
}

// ============================================================


function performSearch() 
{
	startBusyAnimation();
	lastSearch = document.search.terms.value;
	resetFilters = true;
	gCurrentYahooMap._localSearchOverlay.search(lastSearch, gCurrentYahooMap._map.getCenter(), 0, 100);
}

// rests the filter select lists to empty and wipes cached filter keys
function clearFilterLists() 
{
	categoryFilterKey = undefined;
	document.searchfilters.categoryfilters.options.length = 0;
	document.searchfilters.categoryfilters.options[0] = new Option("no filter", "");
	document.searchfilters.categoryfilters.selectedIndex = 0;

	ratingFilterKey = undefined;
	document.searchfilters.ratingfilters.options.length = 0;
	document.searchfilters.ratingfilters.options[0] = new Option("no filter", "");
	document.searchfilters.ratingfilters.selectedIndex = 0;
}

// update category filter and do new search with new filters
function onCategoryFilter() 
{
	if (lastSearch == null) return;
	startBusyAnimation();
	categoryFilterKey = getFilterKey(document.searchfilters.categoryfilters);
	gCurrentYahooMap._localSearchOverlay.search(lastSearch, gCurrentYahooMap._map.getCenter(), 0, 100, null, categoryFilterKey, ratingFilterKey);
}

// update rating filter and do new search with new filters
function onRatingFilter() 
{
	if (lastSearch == null) return;
	startBusyAnimation();
	ratingFilterKey = getFilterKey(document.searchfilters.ratingfilters);
	gCurrentYahooMap._localSearchOverlay.search(lastSearch, gCurrentYahooMap._map.getCenter(), 0, 100, null, categoryFilterKey, ratingFilterKey);
}

// grabs value from the selected index in the given select list
function getFilterKey(list) 
{
	return list[list.selectedIndex].value;
}

// Using Javascript to create DIV's because Mozilla doesn't handle the STYLE attribute well
// This code is using constants for the DIV ID's when it should
//  be dynamic or table driven.  Demo pressure is the reason.
function createOneDiv(elementName, divHeight, divWidth, visibleYN)
{
	var divElement = document.createElement("DIV");
	divElement.setAttribute("name", elementName);
	divElement.setAttribute("id", elementName);
	
	var s = divElement.style;
	var sDivHeight = divHeight + "PX";
	s.height = sDivHeight;
	var sDivWidth = divWidth + "PX";
	s.width = sDivWidth;
	
	if (visibleYN)
		s.display = "block";
	else
		s.display = "none";
		
	return divElement;
} // function createOneDiv(

function createOneIFrame(elementName, iframeHeight, iframeWidth, visibleYN)
{
	var iframeElement = document.createElement("IFRAME");
	iframeElement.setAttribute("name", elementName);
	iframeElement.setAttribute("id", elementName);
	
	var s = iframeElement.style;
	var siframeHeight = iframeHeight + "PX";
	iframeElement.height = siframeHeight;
	var siframeWidth = iframeWidth + "PX";
	iframeElement.width = siframeWidth;
	
	if (visibleYN)
		s.display = "block";
	else
		s.display = "none";
		
	return iframeElement;
} // function createOneiframe(

// Create the needed DIVs and IFRAMEs to support the application.
function createDivElements(docWindow, parentElementID)
{
	var parentElement = document.getElementById(parentElementID);
	
	if (!parentElement)
	{
		alert("(createDivElements) Unable to find the parent element, ID = " + parentElementID);
		return;
	}

	/*	
	define('ATI_DIV_MAIN_ID', "atiDivMain");
	define('ATI_DIV_RESOURCE_ID', "atiDivResource");
	define('ATI_DIV_EVENT_ID', "atiDivEvent");
	define('ATI_DIV_VENUE_ID', "atiDivVenue");
	define('ATI_DIV_EXTRA_STUFF_ID', "atiDivExtraStuff");
	*/
	
	// =============== DIV: MAIN YAHOO MAP ======================
	var divHeight = 694;
	var divWidth = 600;
	
	var divElement = createOneDiv("atiDivMain", divHeight, divWidth, true);
	parentElement.appendChild(divElement);	
	
	// =============== DIV: LOCAL SEARCH RESOURCES MAP ======================
	divElement = createOneDiv("atiDivResource", divHeight, divWidth, false);
	parentElement.appendChild(divElement);	
	
	// =============== DIV: EVENT DETAILS ======================
	divElement = createOneDiv("atiDivEvent", divHeight, divWidth, false);
	// divElement.setAttribute("innerHTML", "<p>Please click on a map marker to select an event.</p>");
	parentElement.appendChild(divElement);	

	/*	
	// Add an IFRAME for displaying the details.
	iframeElement = createOneIFrame("atiIFrameEventDetails", divHeight, divWidth, true);
	divElement.appendChild(iframeElement);	
	*/
	
	// =============== DIV: VENUE DETAILS ======================
	divElement = createOneDiv("atiDivVenue", divHeight, divWidth, false);
	// divElement.setAttribute("innerHTML", "<p>Please click on a map marker to select an event.</p>");
	parentElement.appendChild(divElement);	
	
	/*
	// Add an IFRAME for displaying the details.
	iframeElement = createOneIFrame("atiIFrameVenueDetails", divHeight, divWidth, true);
	divElement.appendChild(iframeElement);	
	*/
	
	// =============== IFRAME: KLUDGE TO UPDATE DETAIL DIVS WHEN 'Details' link
	//							is clicked in a event description.
	iframeElement = document.createElement("IFRAME");
	iframeElement.style.visibility='hidden';
	iframeElement.style.width = "600PX";
	iframeElement.style.height = "600PX";
	iframeElement.name=iframeElement.id='iframeDetailsDirector';
	document.body.appendChild(iframeElement);
}

function eventDetails(poiMarkerNdx)
{
	// alert("poiMarker title = " + gPoiMarkerList[poiMarkerNdx]._poiTitle);
	// var ifrEvent = document.getElementById("atiIFrameEventDetails");
	
	var poiMarker = gPoiMarkerList[poiMarkerNdx];

	// Write the event details HTML directly into the div.
	var eventDetailsDiv = 
		document.getElementById("atiDivEvent");
	if (!eventDetailsDiv)
	{
		alert ("(eventDetails) Unable to find the event details area.");
		return;
	}

	var newHtml = poiMarker._poiObj._eventDetailsHtml;
	
	// Restore apostrophes.
	var newHtml = newHtml.replace(/&apos;/ig, "'");
	
	eventDetailsDiv.innerHTML = newHtml;
	changeSubPage(false, document, "atiDivEvent");
	
	// Fill in the venue details DIV too.
	venueDetails(poiMarkerNdx);
}

function venueDetails(poiMarkerNdx)
{
	var poiMarker = gPoiMarkerList[poiMarkerNdx];

	// Write the venue details HTML directly into the div.
	var venueDetailsDiv = 
		document.getElementById("atiDivVenue");
		
	if (!venueDetailsDiv)
	{
		alert ("(venueDetails) Unable to find the venue details area.");
		return;
	}
	
	var newHtml = poiMarker._poiObj._venueDetailsHtml;
	
	// Restore apostrophes.
	var newHtml = newHtml.replace(/&apos;/ig, "'");
	
	venueDetailsDiv.innerHTML = newHtml;
	// changeSubPage(false, document, "atiDivVenue");
}

// Show the traffic overlay.
function showTrafficOverlay()
{
	
	if (!gMainMap._trafficOverlay)
	{
		// First use, create the traffic overlay.
		// Add a traffic overlay.
		gMainMap._trafficOverlay = new TrafficOverlay();	
		
		gMainMap._trafficOverlay.addEventListener( Overlay.EVENT_INITIALIZE, gMainMap._onOverlayInit, gMainMap );
		gMainMap._map.addOverlay( gMainMap._trafficOverlay );
		// gMainMap._trafficOverlay.hide();
	} // if (!gMainMap._trafficOverlay)
	

	if (gMainMap._trafficOverlay)
		if (gMainMap._trafficOverlay.isInitialized())
			if (!gMainMap._trafficOverlay.isVisible())
			{
				gMainMap._trafficOverlay.show();
				
				/*				
				// Change the link to "show traffic" mode.
				var trafficLink = document.getElementById("linkTraffic");
				
				if (!trafficLink)
				{
					alert("(showTrafficOverlay) Unable to find the traffic link.");
					return false;
				} // if (!trafficLink)

				if (window.netscape)
				{
					trafficLink.href = "javascript:hideTrafficOverlay();";
					trafficLink.innerHTML = "<i>Hide Traffic</i>";
				}
				else
				{
					trafficLink.setAttribute("href", "javascript:hideTrafficOverlay();");
					trafficLink.setAttribute("innerHTML", "<i>Hide Traffic</i>");
					
					trafficLink.outerHTML = "<a href='javascript:hideTrafficOverlay();' ><i>Hide Traffic</i></a>";
				} // if (window.netscape)
				*/
			} // if (gMainMap._trafficOverlay.isVisible())
}
		
	
// Hide the traffic overlay.
function hideTrafficOverlay()
{
	if (gMainMap._trafficOverlay)
		if (gMainMap._trafficOverlay.isInitialized())
			if (gMainMap._trafficOverlay.isVisible())
			{
				gMainMap._trafficOverlay.hide();
				
				/*				
				// Change the link to "show traffic" mode.
				var trafficLink = document.getElementById("linkTraffic");
				
				if (!trafficLink)
				{
					alert("(hideTrafficOverlay) Unable to find the traffic link.");
					return false;
				} // if (!trafficLink)

				trafficLink.href = "javascript:showTrafficOverlay();";
				trafficLink.innerHTML = "<i>Show Traffic</i>";
				*/
			} // if (gMainMap._trafficOverlay.isVisible())
}
		
// Execute a local search with the given term/phrase
function doLocalSearch(theQuery)
{

	var searchForm = document.getElementById("search");
	if (!searchForm)
	{
		alert("(doLocalSearch) Unable to find the search form.");
		return false;
	} // if (!searchForm)
	
	var searchTermsBox = document.getElementById("terms");
	
	if (!searchTermsBox)
	{
		alert("(doLocalSearch) Unable to find the search terms entry box.");
		return false;
	} // if (!searchTermsBox)
	
	theQuery2 = AllTrim(theQuery);
	
	if (theQuery2.length < 3)
	{
		alert("Please enter a valid search query.");
		return false;
	} // if (theQuery2.length < 3)
	
	searchTermsBox.value = theQuery2;
	
	// Now execute the search form.
	performSearch();
	
} // function doLocalSearch()
		
// Clear the local search overlay.
function clearSearch()
{
	if (gMainMap._localSearchOverlay)
		gMainMap._localSearchOverlay.clear();
		
	stopBusyAnimation();
} // function clearSearch()

// This function will update the map based on the
//  current contents of the global POI marker list
//  data object and the global boundary rectangle
//  variables.
// NOTE: Only call this function if the metro changes.  It
//	ALWAYS clears all the markers and redraws the entire map.
function updateMap()
{
	// Find the center of the bounded region and use that value as
	//  the center point for the Map object constructor.
	var centerLat = gMinLat + ((gMaxLat - gMinLat) / 2);
	var centerLon = gMinLon + ((gMaxLon - gMinLon) / 2);
	
	var centerLatLon = new LatLon(centerLat, centerLon);
	
	// Remove all existing markers.
	gMainMap._map.removeAllMarkers();
	
	// Clear local search.
	clearSearch();
	
	// Hide the traffic overlay.
	hideTrafficOverlay()
	
	// Update the map's lat/lon rect.
	gMainMap._latLonRect = new LatLonRect(gMinLat, gMinLon, gMaxLat, gMaxLon);
	
	// Set the viewport.	
	gMainMap._map.setBounds(gMainMap._latLonRect);
	
	// Set the map's center point.
	// gMainMap._map.setCenterByLatLon(centerLatLon, 0);
	
	// Update the map's POI marker list.
	gMainMap._poiMarkerList	= gPoiMarkerList;

	if (gMainMap._poiMarkerList)
	{
		for (var k in gMainMap._poiMarkerList)
		{
			// Make the add call for the object.
			gMainMap._poiMarkerList[k]._add(gMainMap._map);
		} // for()
	} // if (gMainMap._poiMarkerList)
	
	stopBusyAnimation();
} // function updateMap()

// Given a _metroData object, set up the necessary
//  data structures to service the metro represented
//  by that _metroData object, and update the map
//  to reflect the new settings.
function setMetro(metroData)
{
	// _metroData(metroID, poiMarkerList, minLat, minLon, maxLat, maxLon)
	// Set the POI marker list bounding rectangle variables.
	gMinLat	= metroData._minLat;
	gMaxLat	= metroData._maxLat;
	gMinLon	= metroData._minLon;
	gMaxLon	= metroData._maxLon;

	// Set the global POI marker list variable.
	gPoiMarkerList = null;
	gPoiMarkerList = metroData._poiMarkerList;
	
	// Set the current metro ID.
	gCurrentMetroID = metroData._metroID;
	
	// Update the map.
	updateMap();
} // function setMetro()
		
// This function checks the cache for a metro's 
//  POI marker list and related data.  If the
//  cache has no data, itrequests a metro's event list from
//  the server using AJAX, centers the map around
//  the points, and displays them on screen.
function selectMetro(metroID)
{
	// First see if we already have the data in the cache.
	if (gPoiMarkerListCache[metroID])
	{
		// Use the cached data.
		setMetro(gPoiMarkerListCache[metroID]);
	} // if (gPoiMarkerListCache[metroID])
	else
	{
		// No cached data.  Request it and wait for the server return document.
		// var mydata = 'sacktest=' + "hello";
		gAjaxPOIMarkerList = new sack('./get-metro-events.php?metro_id=' + metroID);
		gAjaxPOIMarkerList.method = "GET";
		// gAjaxPOIMarkerList.element = divName;
		// gAjaxPOIMarkerList.rungAjaxPOIMarkerList(mydata);
		gAjaxPOIMarkerList.onCompletion = ajaxReceivePOIMarkerList;
		gAjaxPOIMarkerList.runAJAX();
	} // else - if (gPoiMarkerListCache[metroID])
} // function selectMetro(metroID)

// This function is called by the "sack" AJAX control
//  when the xmlHttpRequest() response is received.
// The "sack" AJAX object passes a reference to itself to
//  this function.
function ajaxReceivePOIMarkerList(sackObj)
{

	var jsPOIMarkerList = null;
	
	var bSuccess = false;
		
	var returnDoc = sackObj.response;

	// Replace the linefeeds from the Javascript code.  Even
	//  with the multiline "/m" flag, Javascript's regular
	//  expression parser doesn't seem to span line breaks.
	returnDoc = returnDoc.replace(/\n/g, "&LF;");

	if (returnDoc.length > 0)
	{
		// Check for the empty result message first.
		// var regExp = /<emptyMarkerList>(.*?)<\/emptyMarkerList>/;
	
		try
		{	
			var matches = extractXMLTag("emptyMarkerList", returnDoc);
			
			if (matches)
			{
				// There were no events for the selected metro.  Let the caller know.
				// Restore the linefeeds.
				var msg = matches[0];
				
				msg = msg.replace(/&LF;/g, "\n");
				
				alert(msg);
				return;
			} // if (matches)
			
			// Clear out the current contents of the global POI
			//  marker list array.
			gPoiMarkerList = new Array();
			
			// Process all the POI marker elements first.
			matches = extractXMLTag("poiMarker", returnDoc);
			
			if (matches)
			{
				for (var i = 0; i < matches.length; i++)
				{
					/*
					// Exec each poi marker Javascript snippet.
					try
					{
						eval(matches[i]);
					}
					catch(exception)
					{
						alert("Error: " + exception + "\nevaluating line: \n" + matches[i]);
						return;
					}
					*/
					
					// Process each poiMarker XML record.
					var theText = matches[i];
					
					// VARIABLE: locationField 
					var locationField = extractOneXMLTag("locationField", theText);
					
					// VARIABLE: ndx
					var ndx = extractOneXMLTag("ndx", theText);
					
					// VARIABLE: title
					var theTitle = extractOneXMLTag("title", theText);
					
					// VARIABLE: description
					var description = extractOneXMLTag("description", theText);
					
					// VARIABLE: markerBodyColor
					var markerBodyColor = extractOneXMLTag("markerBodyColor", theText);
					
					// VARIABLE: markerStrokeColor
					var markerStrokeColor = extractOneXMLTag("markerStrokeColor", theText);
					
					// VARIABLE: markerObjXML (nested element)
					var markerObjXML = extractOneXMLTag("mapMarkerData", theText);
					
					// Process the nested element, markerObjXML.
					var mapMarkerData = null;
					
					if (markerObjXML.length > 0)
					{
						// VARIABLE: eventID.
						var eventID = extractOneXMLTag("eventID", theText);
						
						// VARIABLE: venueID.
						var venueID = extractOneXMLTag("venueID", theText);
						
						// VARIABLE: eventDetailsHtml.
						var eventDetailsHtml = extractOneXMLTag("eventDetailsHtml", theText);
						
						// VARIABLE: venueDetailsHtml.
						var venueDetailsHtml = extractOneXMLTag("venueDetailsHtml", theText);
						
						mapMarkerData = new _mapMarkerData(eventID, venueID, eventDetailsHtml, venueDetailsHtml);
					} // if (markerObjXML != "")
					
					gPoiMarkerList[i] = 
						new _mapMarkerPOI(
									locationField,
									ndx,
									theTitle,
									description,
									markerBodyColor,
									markerStrokeColor,
									mapMarkerData
										);
				} // for()
			} // if (matches)
			
			// Now do the same for the lat/long boundary values.
			// VARIABLE: gMinLat
			matches = extractXMLTag("gMinLat", returnDoc);
			
			if (matches)
				gMinLat = parseFloat(matches[0]);
			
			// VARIABLE: gMinLon
			matches = extractXMLTag("gMinLon", returnDoc);
			
			if (matches)
				gMinLon = parseFloat(matches[0]);
			
			// VARIABLE: gMaxLat
			matches = extractXMLTag("gMaxLat", returnDoc);
			
			if (matches)
				gMaxLat = parseFloat(matches[0]);
			
			// VARIABLE: gMaxLon
			matches = extractXMLTag("gMaxLon", returnDoc);
			
			if (matches)
				gMaxLon = parseFloat(matches[0]);
			
			// VARIABLE: current metro ID.
			matches = extractXMLTag("currentMetroID", returnDoc);
			
			if (matches)
				gCurrentMetroID = matches[1];
		
			// Update the map.
			updateMap();
			
			bSuccess = true;
				
			/*	
			matches = returnDoc.match(regExp);
			
			if (matches)
			{
				if (matches.length == 2)
				{
					// There were no events for the selected metro.  Let the caller know.
					var msg = matches[1];
					
					// Restore the linefeeds.
					msg = msg.replace(/&LF;/g, "\n");
					
					alert(msg);
					return;
				} // if (matches.length == 2)
			} // if (matches)
					
			// Pull out the Javascript code that we need to execute.
			regExp = /<poiMarkerList>(.*?)<\/poiMarkerList>/;
			
			matches = returnDoc.match(regExp);
		
			var bSuccess = false;
				
			if (matches)
			{
				if (matches.length == 2)
				{
					var jsCode = matches[1];
					
					// Restore the linefeeds.
					jsCode = jsCode.replace(/&LF;/g, "\n");
					
					// Evaluate the code to setup the new map data.
					eval(jsCode);
					
					// The global variables should now be set to the
					//  proper values.  Store them in the cache.
					gPoiMarkerListCache[gCurrentMetroID] = 
						new _metroData(gCurrentMetroID, gPoiMarkerList, gMinLat, gMinLon, gMaxLat, gMaxLon);
						
					// Update the map.
					updateMap();
					
					bSuccess = true;
				} // if (matches.length == 2)
			} // if (matches)
			*/
		}
		catch (exception)
		{
			alert(exception);
			return;
		}
	} // if (strlen(returnDoc) < 0)
	
	if (!bSuccess)
	{
		alert("The metro events list server returned an error when requesting data for the new metro.");
		return;
	} // else - if (matches)
} // function ajaxReceivePOIMarkerList()
	
// This function is called form the main page's "body onload" event.
function appStartUp()
{
	startBusyAnimation();
	
	// Highlight the first notebook tab, "atiDivMain".
	highlightSubPageNotebookTab(1, 5); 
	
	var initMsg = "<center><h3>Please select an event from the map first.</h3></center>";	
	
	// Initialization text for event details div.
	var eventDetailsDiv = 
		document.getElementById("atiDivEvent");
		
	if (!eventDetailsDiv)
	{
		alert ("(eventDetails) Unable to find the event details area.");
		return;
	}
	
	eventDetailsDiv.innerHTML = initMsg;
	
	// Initialization text for venue details div.
	var venueDetailsDiv = 
		document.getElementById("atiDivVenue");
		
	if (!venueDetailsDiv)
	{
		alert ("(venueDetails) Unable to find the venue details area.");
		return;
	}
	
	venueDetailsDiv.innerHTML = initMsg;
	
	// Start by showing the San Francisco metro area.
	selectMetro(45);
} // function appStartUp()
		
// This function extracts all instances of an XML tag contents from a string.  
// RETURNS:
//	null - if the XML tag could not be found
//  an array of matches, even if there is only one match.
function extractXMLTag(tag, theText)
{
	// When using the RegExp constructor to create a regular expression,
	//  do not include the "/" bookends and all backslashes have to
	//  be escaped.  So "\w" has to be "\\w".
	var regPat = "<" + tag + ">(.*?)<\/" + tag + ">";
	
	var regExp = new RegExp(regPat, "g");
	
	var matchCount = 0;
	
	var matches = new Array();
	var bLoop = true;
	var result = null;
	
	while ( (result = regExp.exec(theText)) != null)
	{
		matches[matchCount] = result[1];
		matchCount++;
		if (!bLoop)
			break;
	} // while()
	
	if (matchCount < 1)
		return false; // No matches found.
	return matches;
} // function extractXMLTag()
		
// This function extracts ONE instances of an XML tag contents from a string.  
// RETURNS:
//	{empty string} - if the XML tag could not be found
//  an array of matches, even if there is only one match.
function extractOneXMLTag(tag, theText)
{
	var matches = extractXMLTag(tag, theText);
	
	if (matches)
		return matches[0];
	return "";  
}		
	
function doBusyAnimation()
{
	var imgBusy = document.getElementById("imgBusy");
	
	if (imgBusy)
	{
		if (gBusyAnimationFrame > 2)
			gBusyAnimationFrame = 1; // Loop.
			
		var imgName = "./busy-" + gBusyAnimationFrame + ".gif";
		
		imgBusy.src = imgName;
			
		gBusyAnimationFrame++;
	} // if (imgBusy);
}	
	
function startBusyAnimation()
{
	gBusyAnimationFrame = 1;
	
	var imgBusy = document.getElementById("imgBusy");
	
	if (imgBusy)
	{
		gAnimationInterval = setInterval("doBusyAnimation()", 500);
	} // if (imgBusy)
}		

function stopBusyAnimation()
{
	var imgBusy = document.getElementById("imgBusy");
	
	if (imgBusy)
	{
		clearInterval(gAnimationInterval);
		
		imgBusy.src = "./ready.gif";
	} // if (imgBusy)
}		
		

