/* CONSTANTS */
var VEI_URL_PREFIX = ""; //"/OHI01" ;	// Prefix added to any absolute URLs in the format '/images/icon1.gif', to account for different environments (dev/live)
var VEI_AJAXURL = VEI_URL_PREFIX + "/WebServices/maproute.asmx/";
var VEI_POI_DETAIL_IDSUFFIX = "_content";
var VEI_ZOOM_MAX = 17;	// Maximum map zoom value (closest)
var VEI_ZOOM_MIN = 7;	// Minimum map zoom value (farthest)
var VEI_SHOWPOI = 0;	// Not used as of 8/14/07; true when points of interest should be displayed on the map
var VEI_CLASSID = 0;	// Not used as of 8/14/07; GMap-specific
var VEI_DEFAULT_CONTAINER = 'mainmap';	// ID of default HTML container for visual map
var VEI_DEFAULT_OVERVIEWCONTAINER = 'overviewmap'; 	// ID of default HTML container for smaller overview map
var VEI_CSSCLASS_AUTOCB = 'routetoggle';		// CSS class assigned to the DIV that contains all automatically-inserted route-toggling checkboxes
var VEI_CSSCLASS_AUTOCB_SPAN = 'toggleroute';	// CSS class for each span within 'routetoggle' DIV
var VEI_CSSCLASS_SECTIONUL = 'mapsectionmenu';	// CSS class for the list of all predefined map sections 
var VEI_TOOLSID = 'maptools';			// DOM ID of default container for map 'tools'
var VEI_DEFAULT_LONG = -81.58447; 		// Default longitude coordinate
var VEI_DEFAULT_LAT = 41.00892; 		// Default latitude coordinate
var VEI_OVERVIEW_OFFSET_LONG = 0.07;	// Adjustment from default LONG for center of overview map
var VEI_OVERVIEW_OFFSET_LAT = -0.03;	// Adjustment from default LAT for center of overview map
var VEI_DEFAULT_ZOOM = 10;				// Default map zoom level; VE zoom level ranges from 1(out) to 19(in)
var VEI_DEFAULT_ZOOM_REQUEST = 16;		// Zoom level for when querystring specifies lat/long coordinates
var VEI_POI_ZOOM_THRESHHOLD = 10;		// POI information is retrieved ONLY when current zoom level is GREATER (closer) than this value

var VEI_ROUTE_TRAIL1 = {'rid':1, 'inputid':'auto', 'visible':true, 'name':'Route 1'};	// Definition of route 1 (paved trail)
var VEI_ROUTE_TRAIL2 = {'rid':2, 'inputid':'auto', 'visible':true, 'name':'Route 2'};	// Definition of route 2 (street)
var VEI_ROUTE_TRAIL3 = {'rid':3, 'inputid':'auto', 'visible':true, 'name':'Route 3'};	// Definition of route 3 (open)
var VEI_ROUTE_TRAIL4 = {'rid':4, 'inputid':'auto', 'visible':true, 'name':'Route 4'};	// Definition of route 4 (planned)
var VEI_ROUTE_TRAIL5 = {'rid':5, 'inputid':'auto', 'visible':true, 'name':'Route 5'};	// Definition of route 5 (construction)

var VEI_ROUTE_SECTIONS = [
	{ 'summary':"Northern Leg (Cleveland to Clinton)", 'center':new VELatLong(41.107025524835285,-81.60369873046875), 'zoom':9 }, 
	{ 'summary':"Heart of Ohio (Clinton to Mt Vernon)", 'center':new VELatLong(40.58058466412763,-82.10357666015625), 'zoom':9 },
	{ 'summary':"Southern Leg (Mt Vernon to Cincinatti)", 'center':new VELatLong(39.74521015328692,-83.3367919921875), 'zoom':8 }
];
// Definitions of the rectangular areas that define the different 'Communities' along the trails (OECA-specific)
var VEI_COMMUNITIES = [
	{ 'name':"Industrial City Canalway Community", 'color':"8000FF", 'coords':[ 
		new VELatLong(41.50643274517805,-81.71574628006756), 
		new VELatLong(41.38744499917864,-81.71574628006756),
		new VELatLong(41.38744499917864,-81.60389555790216),
		new VELatLong(41.50643274517805,-81.60389555790216)
	] },
	{ 'name':"Cuyahoga Valley Canalway Community", 'color':"FF0055", 'coords':[ 
		new VELatLong(41.40177757959104,-81.65482095370733),
		new VELatLong(41.14136443227362,-81.65482095370733),
		new VELatLong(41.14136443227362,-81.48877730632717),
		new VELatLong(41.40177757959104,-81.48877730632717)
	] },
	{ 'name':"Portage Path and Summit Canalway Community", 'color':"00C89D", 'coords':[ 
		new VELatLong(41.16896625403489,-81.66364176853499),
		new VELatLong(40.91029952516806,-81.66364176853499),
		new VELatLong(40.91029952516806,-81.48374159800719),
		new VELatLong(41.16896625403489,-81.48374159800719)
	] },
	{ 'name':"Canal Villages Canalway Community", 'color':"F1BD00", 'coords':[ 
		new VELatLong(40.92929272841764,-81.644623042757),
		new VELatLong(40.62917000365967,-81.644623042757),
		new VELatLong(40.62917000365967,-81.44674499511865),
		new VELatLong(40.92929272841764,-81.44674499511865)
	] },
	{ 'name':"Lincoln Highway Canalway Community", 'color':"FF0000", 'coords':[ 
		new VELatLong(40.8718608167481,-81.66163196855025),
		new VELatLong(40.73803585345387,-81.66163196855025),
		new VELatLong(40.73803585345387,-81.30319392530949),
		new VELatLong(40.8718608167481,-81.30319392530949)
	] },
	{ 'name':"Tuscarawas Valley Canalway Community", 'color':"ABD400", 'coords':[ 
		new VELatLong(40.66956461246414,-81.50426089921143),
		new VELatLong(40.46668372609096,-81.50426089921143),
		new VELatLong(40.46668372609096,-81.35503836575867),
		new VELatLong(40.66956461246414,-81.35503836575867)
	] }
];
// AJAX class definition that contains all the consistent, basic information for 
// communication with the OECA server
function VEI_AJAX(objVeiInstance, strServiceName)
{
	this.dataType = "xml"; 
	this.type = "POST";
	this.error = function(obj_request,str_error,obj_exception) { objVeiInstance.ajaxError(obj_request,str_error,obj_exception); }; 
	this.success = function(obj_data) { objVeiInstance.ajaxReceiveMapData(obj_data); };
	this.url = VEI_AJAXURL + strServiceName;
	this.data = "";
}

// Class definition of a route object to be generated from the raw XML 
function VEI_ROUTE()
{
	this.id = -1;
	this.name = "";
	this.style = {
		'color':"#000",
		'width':4,
		'opacity':0.5
	};
	this.segments = new Array();	// Each 'segment' is an array of VELatLong coordinates
	this.layer = {};
}


/**
 * @author Michael Tipton
 * @class VEInterface
 * @requires Microsoft Virtual Earth 5.0 Map API
 * @requires jQuery library (jquery.com)
 * @param {Object} obj_init Initialization parameters
 * 		.container	{String} Id of the DOM container for the map
 * 		.routes		{Array} List of definitions of all available route overlays
 * 			.rid		{Integer} Numeric id of the route
 * 			.inputid	{String} Id of the input that indicates visibility
 * 			.input		{DOM Object} Reference to the checkbox input (acquired during init())
 * 			.visible	{Boolean} True when the route overlay is visible
 * 			.line		{Object} GPolyline definition
 * 			.points		{Array} List of all GLatLng points
 * 			.style		{Object}
 *		.autocheckboxes	{Boolean} True when the checkboxes for each of the routes should be automatically generated and inserted directly after the VEMap container; Defaults to true
 *		.overviewcontainer	{String} Id of the DOM container for the smaller overview map; None by default
 */
function VEInterface(obj_init)
{
	//##########################################################################
	// VEInterface Object PROPERTIES
	/**
	 * Save the initialization object for future reference
	 * @type Object
	 */
	this.obj_init = (typeof(obj_init)!="undefined" ? obj_init : {'container':VEI_DEFAULT_CONTAINER, 'overviewcontainer':VEI_DEFAULT_OVERVIEWCONTAINER, 'routes':[VEI_ROUTE_TRAIL1,VEI_ROUTE_TRAIL2,VEI_ROUTE_TRAIL3], 'autocheckboxes':true});
	/** 
	 * DOM Container for the visual VEMap
	 * @type DOM Object 
	 */
	this.dom_container;
	/**
	 * DOM Container for the visual overview VEMap, if applicable
	 * @type DOM Object
	 */
	this.dom_overviewContainer;
	/**
	 * True when an overview map is being used
	 * @type Boolean
	 */
	this.bln_overviewMap = false;
	/**
	 * Reference to the instance of the VEMap object
	 * @type Object
	 */
	this.obj_map;
	/**
	 * Reference to the instance of the overview VEMap object
	 * @type Object
	 */
	this.obj_overviewMap;
	/**
	 * All 'routes' that may be displayed on this map instance
	 * @type Array
	 */
	this.arr_routes = this.obj_init.routes;
	/**
	 * Associative array/object that basically lets us quickly find the index of a specific route ID within arr_routes.
	 * Very similar to the Mozilla Array.indexOf() method. After init() has executed,
	 * should be able to find the index by using obj_routeHash[routeId] to get 
	 * the numeric index of that route within arr_routes.
	 */
	this.obj_routeHash = new Object();
	/**
	 * Property to contain a reference to the asynchronous HTTP request made for 
	 * updates of either points of interest or routes. Can be used to abort an 
	 * existing request 
	 */
	this.obj_ajaxRequest;
	/**
	 * Contains a reference to the container for all automatically-generated checkboxes.
	 * @type DOM Object
	 */
	this.dom_autoCheckboxes = true;
	/**
	 * Reference to the DOM element that lists all predefined map sections (i.e., Northern Leg, etc)
	 */
	this.dom_ulMapSections = false;
	/**
	 * 
	 */
	this.bln_poi = false;
	/**
	 * List of all ShapeLayers managed within the main and overview maps
	 */
	this.obj_mapLayers = new Object();
	/**
	 * Shape on the overview map that indicates location of main map
	 */
	this.obj_ovShapeYouAreHere = false;
	/**
	 * References the select element that allows filtering of Points of Interest
	 */
	this.dom_poiFilter;
	
	//##########################################################################
	// MODEL methods
	/**
	 * Intializes the VE map object instance, gets reference to map DOM container
	 */
	this.init = function()
	{
		try
		{
			// Acquire references to DOM container and VEMap elements
			var obj_this = this;
			this.dom_container = document.getElementById(this.obj_init.container);	// Save reference to DOM object
			// Create layer properties
			this.obj_mapLayers.poi = false;
			this.obj_mapLayers.comm = false;
			try
			{
				this.obj_map = new VEMap(this.obj_init.container);
				this.obj_map.SetDashboardSize(VEDashboardSize.Small);
				this.obj_map.LoadMap();
			} catch(obj_vemapError) { alert("There was an error while loading the trail map"); }
			this.obj_map.AttachEvent("onchangeview",function() {obj_this.updateMaps()})
			
			this.loadRouteTogglers();
			this.loadPoiFilter();
			this.loadOverviewMap();	// If applicable
			if (typeof(this.obj_init.showcommunities)!="undefined" && this.obj_init.showcommunities===true)
			{
				// mrt: Commented out as of 3.14.08
//				this.loadCommunities();
			}
			
	    	this.loadCenterZoom();	// Automatically calls updateVisibleRoutes(), since it is essentially a movement of the viewing area
		} catch(obj_err) { alert("VEInterface.init()\nERROR: " + obj_err.message); }
	};
	
	//--------------------------------------------------------------------------
	/**
	 * Generates a VEMap Polyline object for each segment in the route, given the points and style of a route, and saves
	 *  that object under the appropriate route.
	 * @param {Integer} int_route Index of the route (within arr_routes) for which a Polyline should be generated
	 * @deprecated Replaced by convertXmlRouteToVELayer()
	 */
/*	 
	this.generateRoute = function(int_route)
	{
		var obj_layer = new VEShapeLayer();
		var obj_color = this.arr_routes[int_route].style.color;
		
		for (var s=0; s<this.arr_routes[int_route].segmentpoints.length; s++)
		{
			// Check that there are visible points for this segment in this viewport
			if (this.arr_routes[int_route].segmentpoints[s].length>0)
			{
				// Catch a VEShape exception per segment, not route, so that the 
				//  other segments will show up even if one has an error
				try
				{
					// If there are less than 2 points for this segment, copy the first one
					if (this.arr_routes[int_route].segmentpoints[s].length<2)
					{
						var obj_pointCopy = new VELatLong(this.arr_routes[int_route].segmentpoints[s][0].Latitude,this.arr_routes[int_route].segmentpoints[s][0].Longitude)
						this.arr_routes[int_route].segmentpoints[s].push(obj_pointCopy);
					}
					var obj_poly = new VEShape(VEShapeType.Polyline,this.arr_routes[int_route].segmentpoints[s]);
					obj_poly.SetLineColor(obj_color);
					obj_poly.SetLineWidth(this.arr_routes[int_route].style.width);
					obj_poly.HideIcon();
	 				this.arr_routes[int_route].segmentlines[s] = obj_poly;
	 				obj_layer.AddShape(obj_poly);
				} catch(obj_err) { alert("VEInterface.generateRoute()\nERROR: " + obj_err.message); }
			}
		}
		this.arr_routes[int_route].shapelayer = obj_layer;
	}
*/	
	//--------------------------------------------------------------------------
	/**
	 * Converts the hex string value of a color to an RGB object of numeric values
	 * @param {String} str_hex Hex value, WITHOUT the '#' prefix
	 * @type {Object}
	 */
	this.convertColorHexToRGB = function(str_hex)
	{
		try
		{
			var obj_rgb = { 'red':0, 'green':0, 'blue':0 };
			obj_rgb.red = parseInt(str_hex.substr(0,2),16);
			obj_rgb.green = parseInt(str_hex.substr(2,2),16);
			obj_rgb.blue = parseInt(str_hex.substr(4,2),16);
			return obj_rgb;
		} catch(obj_err) { this.Error(obj_err.message, "convertColorHexToRGB"); }
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Accepts a hex string color definitions and converts it to a valid VEColor
	 * @type VEColor
	 */
	this.convertColorHexToVEColor = function(str_hex,flt_opacity)
	{
		try
		{
			flt_opacity = (typeof(flt_opacity)=="undefined" || flt_opacity>1 ? 1 : flt_opacity);
			var obj_rgb = this.convertColorHexToRGB(str_hex);
			var obj_VEColor = new VEColor(obj_rgb.red, obj_rgb.green, obj_rgb.blue, flt_opacity);
			return obj_VEColor;
		} 
		catch(obj_err) 
		{
			this.Error(obj_err.message, "convertColorHexToVEColor"); 
			return new VEColor(0,0,0,1); 
		}
	}	
	
	//--------------------------------------------------------------------------
	/**
	 * Uses raw server XML response to create organized groups of route points ("segments")
	 * that are then used by generateRoute to create VE PolyLines 
	 */
	this.parseXmlRoutes = function(xml_routes)
	{
        // If there is at least one route
        if (xml_routes.length>0)
        {
        	// Go through all routes, parse out the lat/long coordinates, and save the points in route segments
        	for (var re=0; re<xml_routes.length; re++)
        	{                
				var obj_route = this.convertXmlRouteToVELayer(xml_routes[re]);
                
                // Save the points within the arr_routes property
				var int_rIndex = this.obj_routeHash[obj_route.id];
				this.deleteRoute(int_rIndex);	// Remove the old version of this route
				
				this.arr_routes[int_rIndex].color = xml_routes[re].getAttribute("color");
				obj_route.name = xml_routes[re].getAttribute("name");
				this.arr_routes[int_rIndex].name = (obj_route.name!="" ? obj_route.name : 'r'+re);
				
				// Draw the route overlay
				this.arr_routes[int_rIndex].shapelayer = obj_route.layer;
	            this.showRoute(int_rIndex);
        	}
        }		
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Creates an object that defines a route's segments and style, given the XML definition of the route
	 * @type {VEI_ROUTE Object}
	 */
	this.convertXmlRouteToVELayer = function(xmlRoute)
	{
		var objRoute = new VEI_ROUTE();
		
		try
		{
	        // Set Route characteristics
	        objRoute.style.color = this.convertColorHexToVEColor(xmlRoute.getAttribute("color"),xmlRoute.getAttribute("opacity"));
	        objRoute.style.width = xmlRoute.getAttribute("width");
	        objRoute.id = xmlRoute.getAttribute("id"); 
	                
	        // Save all latitude/longitude coordinates 
			var arrXmlPoints = xmlRoute.getElementsByTagName("Pt");
			var arrPoints = new Array();	// Array of all points that make up a segment
			
	        for (var pn=0; pn<arrXmlPoints.length; pn++)
	        {
				if (arrXmlPoints[pn].getAttribute("start")==1)
				{
					objRoute.segments.push(arrPoints);	// Save this segment
					arrPoints = new Array();	// Array of all points that comprise this route segment
				}
	            arrPoints.push(new VELatLong(parseFloat(arrXmlPoints[pn].getAttribute("lat")),parseFloat(arrXmlPoints[pn].getAttribute("lon"))))
	        }
	        objRoute.segments.push(arrPoints);	// Save the last segment		
		} catch(objErr) { this.Error(objErr.message,"convertRoutePointsXmlToVE"); }
		
		try
		{
			var objLayer = new VEShapeLayer();
			
			// Loop through all segments in this route
			for (var s=0; s<objRoute.segments.length; s++)
			{
				// If there are points for this segment
				if (objRoute.segments[s].length>0)
				{
					// Catch a VEShape exception per segment, not route, so that the 
					//  other segments will show up even if one has an error
					try
					{
						// If there are less than 2 points for this segment, copy the first one
						if (objRoute.segments[s].length<2)
						{
							var objPointCopy = new VELatLong(objRoute.segments[s][0].Latitude,objRoute.segments[s][0].Longitude)
							objRoute.segments[s].push(objPointCopy);
						}
						var objPoly = new VEShape(VEShapeType.Polyline,objRoute.segments[s]);
						objPoly.SetLineColor(objRoute.style.color);
						objPoly.SetLineWidth(objRoute.style.width);
						objPoly.HideIcon();
		 				objLayer.AddShape(objPoly);
					} catch(objErr) { this.Error(objErr.message,"convertXmlRouteToVELayer"); }
				}
			}
			objRoute.layer = objLayer;
		} catch(objErr) { this.Error(objErr.message,"convertXmlRouteToVELayer"); }
		
		return objRoute;
	}
	
	//--------------------------------------------------------------------------
	/**
	 * 
	 */
	this.parseXmlPoi = function(xml_poi)
	{
		try
		{
			if (xml_poi.length>0)
			{
				// Remove the existing POI layer if it exists
				if (typeof(this.obj_mapLayers.poi)=="object")
				{
					this.obj_map.DeleteShapeLayer(this.obj_mapLayers.poi);
				}			
				this.obj_mapLayers.poi = new VEShapeLayer();
				// Add a 'pushpin' shape for each POI
				for (var p=0; p<xml_poi.length; p++)
				{
					var obj_latLong = new VELatLong(parseFloat(xml_poi[p].getAttribute("lat")),parseFloat(xml_poi[p].getAttribute("lon")));
					var obj_pushpin = new VEShape(VEShapeType.Pushpin,obj_latLong);
					this.obj_mapLayers.poi.AddShape(obj_pushpin);
					var str_ShapeID = obj_pushpin.GetID();
					var int_PoiID = xml_poi[p].getAttribute("id");
					var strIcon = ( typeof($(xml_poi[p]).attr("th"))=="undefined" ? "/images/maps/icon_yellowpin.gif" : "/images/maps/icon_trailhead.gif" );
					obj_pushpin.SetCustomIcon(VEI_URL_PREFIX + strIcon);
					obj_pushpin.SetTitle("<h4 class='poi_title'>" + xml_poi[p].getAttribute("name") + "</h4>");
					// If this POI has a valid numeric ID, add link to view details
					if (!isNaN(int_PoiID))
					{
						// Create a custom DOM container inside of the InfoBox that we can reference via AJAX later
						var str_InfoBoxDetailID = str_ShapeID + VEI_POI_DETAIL_IDSUFFIX;
						obj_pushpin.SetDescription("<div id=\"" + str_InfoBoxDetailID + "\" class=\"poi_details\"><a href=\"#\" onclick=\"return obj_vei.clickPoiDetails(event," + int_PoiID + ",'" + str_InfoBoxDetailID + "')\">Details...</a></div>");
					}
				}
				this.obj_map.AddShapeLayer(this.obj_mapLayers.poi);
			}
		} catch(obj_err) { this.Error(obj_err.message,"parseXmlPoi"); }
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Loads all predefined communities as colored overlays. Uses VEI_COMMUNITIES as main data source.
	 * @type void
	 */
	this.loadCommunities = function()
	{
		try
		{
			this.obj_mapLayers.comm = new VEShapeLayer();	// New layer for main map
	
			// Create the polygon objects
			for (var c=0; c<VEI_COMMUNITIES.length; c++)
			{
				var obj_poly = new VEShape(VEShapeType.Polygon,VEI_COMMUNITIES[c].coords);
				obj_poly.SetLineWidth(2);
				obj_poly.SetFillColor(this.convertColorHexToVEColor(VEI_COMMUNITIES[c].color,.1));
				obj_poly.SetLineColor(this.convertColorHexToVEColor(VEI_COMMUNITIES[c].color,.8));
				obj_poly.SetCustomIcon(VEI_URL_PREFIX + "/images/maps/icon_bluepin.gif")
				obj_poly.SetTitle(VEI_COMMUNITIES[c].name);
				obj_poly.SetIconAnchor(VEI_COMMUNITIES[c].coords[0]);	// Assumes first coordinate is the top left
				VEI_COMMUNITIES[c].shape = obj_poly;
				this.obj_mapLayers.comm.AddShape(obj_poly);
			}

			// Add the communities shape layer to the main map
			this.obj_map.AddShapeLayer(this.obj_mapLayers.comm);

			// Only attempt to add the overview community overlay if the overview map is being used
			if (this.bln_overviewMap)
			{
				this.obj_mapLayers.ovComm = new VEShapeLayer();	// New layer for overview map
				for (var c=0; c<VEI_COMMUNITIES.length; c++)
				{
					var obj_poly = new VEShape(VEShapeType.Polygon,VEI_COMMUNITIES[c].coords);
					obj_poly.SetLineWidth(1);
					obj_poly.SetFillColor(this.convertColorHexToVEColor(VEI_COMMUNITIES[c].color,.1));
					obj_poly.SetLineColor(this.convertColorHexToVEColor(VEI_COMMUNITIES[c].color,.8));
					obj_poly.HideIcon();
					this.obj_mapLayers.ovComm.AddShape(obj_poly);
				}
				this.obj_overviewMap.AddShapeLayer(this.obj_mapLayers.ovComm);
			}
		} catch(obj_err) { this.Error(obj_err.message,"loadCommunities"); }
	}
	
	//##########################################################################
	// VIEW methods
	/**
	 * Displays the VEShapeLayer for the specified route
	 */
	this.showRoute = function(int_route)
	{
		try
		{
			if (typeof(this.arr_routes[int_route])!="undefined")
			{
				if (typeof(this.arr_routes[int_route].shapelayer)!="undefined")
				{
					this.obj_map.AddShapeLayer(this.arr_routes[int_route].shapelayer);				
				}
/*				
				// Iterate through all segments for this route
				for (var s=0; s<this.arr_routes[int_route].segmentlines.length; s++)
				{
					if (typeof(this.arr_routes[int_route].segmentlines[s])!="undefined")
					{
						this.obj_map.AddShape(this.arr_routes[int_route].segmentlines[s]);
					}
				}
*/				// Set the appropriate color for the route toggle checkmark line

 				if (typeof(this.arr_routes[int_route].input)!="undefined")
 				{
 					var dom_lbl = $(this.arr_routes[int_route].input).siblings("label").get(0);
 					dom_lbl.style.borderColor = '#' + this.arr_routes[int_route].color;
 					dom_lbl.innerHTML = this.arr_routes[int_route].name;
 				}
 

 			}
		}
		catch(obj_err)
		{
//			var obj_this = this;
//			window.setTimeout(function() { obj_this.showRoute(int_route) },500);
			alert("VEInterface.showRoute()\nERROR: " + obj_err.message); 
		}
	};
	
	//--------------------------------------------------------------------------
	/**
	 * Wrapper for the GMap2.removeOverlay method
	 */
	this.deleteRoute = function(int_route)
	{
		try
		{
			if (typeof(this.arr_routes[int_route])!="undefined")
			{
				if (typeof(this.arr_routes[int_route].shapelayer)!="undefined")
				{
					this.obj_map.DeleteShapeLayer(this.arr_routes[int_route].shapelayer);
//					this.arr_routes[int_route].shapelayer.DeleteAllShapes();
//					this.obj_map.DeleteShape(this.arr_routes[int_route].line);
				}
			}
		} catch(obj_err) { alert("VEInterface.deleteRoute()\nERROR: " + obj_err.message); }
	};
	
	//--------------------------------------------------------------------------
	/**
	 * Checks the querystring to see if a calling page is setting the default lat/long
	 */
	this.loadCenterZoom = function()
	{
		// First check to see if there are coordinates in the querystring. If not, use defaults
		var objCenter;
		var intZoom;
		try
		{
			if (window.location.search!="")
			{
				var strQuery = window.location.search;
				strQuery = strQuery.substr(1);
				var arrQsPairs = strQuery.split('&');
				var objLatLong = {};
				for (var q=0; q<arrQsPairs.length; q++)
				{
					var arrSplit = arrQsPairs[q].split('=');
					objLatLong[arrSplit[0]] = arrSplit[1];
				}
				if (typeof(objLatLong['lat'])!="undefined" && typeof(objLatLong['long'])!="undefined")
				{
					objCenter = new VELatLong(objLatLong['lat'],objLatLong['long']);
				}
				intZoom = VEI_DEFAULT_ZOOM_REQUEST;
			}
			else
			{
				objCenter = new VELatLong(VEI_DEFAULT_LAT,VEI_DEFAULT_LONG);
				intZoom = VEI_DEFAULT_ZOOM;
			}
		} catch(objErr) { objCenter = new VELatLong(VEI_DEFAULT_LAT,VEI_DEFAULT_LONG); }
		this.obj_map.SetCenterAndZoom(objCenter,intZoom);		
	};
	
	//--------------------------------------------------------------------------
	/**
	 * @deprecated
	 */
	this.goToMapSection = function(int_section)
	{
		if (typeof(int_section)!="undefined")
		{
			try
			{
				this.obj_map.SetCenterAndZoom(VEI_ROUTE_SECTIONS[int_section].center,VEI_ROUTE_SECTIONS[int_section].zoom);
			} catch(obj_err) { alert("VEInterface.goToMapSection()\ERROR: " + obj_err.message); }
		}
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Displays a list of all map sections defined in VEI_ROUTE_SECTIONS
	 * @deprecated
	 */
	this.loadMapSectionsMenu = function()
	{
		try
		{
			var dom_ul = document.createElement("ul"); 
			dom_ul.className = VEI_CSSCLASS_SECTIONUL;
			var obj_this = this;
			for (var s=0; s<VEI_ROUTE_SECTIONS.length; s++)
			{
				var dom_li = document.createElement("li");
				var dom_a = document.createElement("a");
				dom_a.setAttribute('href','#');
				dom_a.setAttribute('mapsection',s);
				dom_a.innerHTML = VEI_ROUTE_SECTIONS[s].summary;
				dom_li.innerHTML = ">";
				dom_li.appendChild(dom_a);
				$(dom_a).click(function() 
					{ 
						var int_section = this.getAttribute('mapsection');
						obj_this.goToMapSection(int_section); 
					})
				dom_ul.appendChild(dom_li);
			}
			$(this.dom_autoCheckboxes).after(dom_ul);	// Insert the UL after the list of trail surface types
			this.dom_ulMapSections = dom_ul;	// Save a reference to this UL at the object instance scope
		} catch(obj_err) { alert("VEInterface.loadMapSectionsMenu()\nERROR: " + obj_err.message); }
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Inserts a smaller side VEMap to act as an overview. Clicking on it will move the main
	 * map to the clicked location.
	 */
	this.loadOverviewMap = function()
	{
		try
		{
			if (typeof(this.obj_init.overviewcontainer)!="undefined")
			{
				this.bln_overviewMap = true;
				this.dom_overviewContainer = document.getElementById(this.obj_init.overviewcontainer);
				this.obj_overviewMap = new VEMap(this.obj_init.overviewcontainer);
				this.obj_overviewMap.LoadMap(new VELatLong(VEI_DEFAULT_LAT + VEI_OVERVIEW_OFFSET_LAT, VEI_DEFAULT_LONG + VEI_OVERVIEW_OFFSET_LONG),9,'r',false);
				this.obj_overviewMap.HideDashboard();
				
				// Add route layers
				this.loadOverviewRoutes();

				// Add 'You Are Here' indicator
				var obj_bounds = this.obj_map.GetMapView();
				this.obj_ovShapeYouAreHere = new VEShape(VEShapeType.Polygon,[
					obj_bounds.TopLeftLatLong,
					new VELatLong(obj_bounds.TopLeftLatLong.Latitude, obj_bounds.BottomRightLatLong.Longitude),
					obj_bounds.BottomRightLatLong,
					new VELatLong(obj_bounds.BottomRightLatLong.Latitude, obj_bounds.TopLeftLatLong.Longitude)
				]);
				this.obj_ovShapeYouAreHere.SetLineColor(this.convertColorHexToVEColor("000000"));
				this.obj_ovShapeYouAreHere.SetFillColor(this.convertColorHexToVEColor("FFFF44",0.3));
				this.obj_ovShapeYouAreHere.SetLineWidth(2);
				this.obj_ovShapeYouAreHere.HideIcon();
				this.obj_overviewMap.AddShape(this.obj_ovShapeYouAreHere);
				
				// Create and insert title for map
				var obj_this = this;
				var dom_overviewHeading = document.createElement('h3');
				dom_overviewHeading.innerHTML = "Navigation";
				dom_overviewHeading.className = "overviewHeading";
				$(this.dom_overviewContainer).prepend(dom_overviewHeading);
//				$(this.dom_overviewContainer).click(function(obj_event) { alert("clicked"); })
				this.obj_overviewMap.AttachEvent("onclick",function(obj_event) { obj_this.clickOverviewMap(obj_event); });
			}
		} catch(obj_err) { this.Error(obj_err.message,"loadOverviewMap"); }		
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Updates the visual 'you are here' indicator within the overview map to reflect where we are within the big picture.
	 * Occurs after any movement or zoom event.
	 */
	this.updateOverviewYouAreHere = function()
	{
		try
		{
			var obj_bounds = this.obj_map.GetMapView();
			this.obj_ovShapeYouAreHere.SetPoints([
				obj_bounds.TopLeftLatLong,
				new VELatLong(obj_bounds.TopLeftLatLong.Latitude, obj_bounds.BottomRightLatLong.Longitude),
				obj_bounds.BottomRightLatLong,
				new VELatLong(obj_bounds.BottomRightLatLong.Latitude, obj_bounds.TopLeftLatLong.Longitude)
			]);
		} catch(obj_err) { this.Error(obj_err.message,"updateOverviewYouAreHere"); }
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Connects route-visibility-toggling checkboxes to event handlers
	 */
	this.loadRouteTogglers = function()
	{
		try
		{
			var obj_this = this;	// Alias to current VEI instance, for use in assignment of event handlers
/*			
			// Insert the container for the route-visibility-toggling checkboxes, if applicable
			if (typeof(this.obj_init.autocheckboxes)!="undefined")
			{
				if (this.obj_init.autocheckboxes==true)
				{
					this.dom_autoCheckboxes = document.createElement("div");
					this.dom_autoCheckboxes.className = VEI_CSSCLASS_AUTOCB;
					$(this.dom_container).after(this.dom_autoCheckboxes);
				}
			}
*/
			var dom_tools = document.getElementById(VEI_TOOLSID);
					
	    	// Connect all routes to their visibility control inputs, if applicable
	    	for (var r=0; r<this.arr_routes.length; r++)
	    	{
	
	    		// There is no input connected to this route overlay's visibility
	    		if (this.arr_routes[r].inputid=='')
	    		{
	    			this.arr_routes[r].input = false;
	    		}
				else if (this.arr_routes[r].inputid=='auto')	// Automatically insert the checkbox
				{
					var dom_span = document.createElement("span");
					dom_span.className = VEI_CSSCLASS_AUTOCB_SPAN;
					var dom_cb = document.createElement("input");
					dom_cb.setAttribute('type','checkbox');
					dom_cb.setAttribute('id','cbRoute'+r);
					dom_cb.setAttribute('route',r);
					this.arr_routes[r].input = dom_cb;
					var dom_label = document.createElement("label");
					dom_label.setAttribute('for','cbRoute'+r);
					dom_label.innerHTML = 'Route ' + r;
					dom_span.appendChild(dom_cb);
					dom_span.appendChild(dom_label);
					dom_tools.appendChild(dom_span);
					dom_cb.setAttribute('checked',true);
				}
	    		else	// The id of the checkbox is provided
	    		{
		    		this.arr_routes[r].input = document.getElementById('cb' + this.arr_routes[r].inputid);
	    		}
	
	    		if (this.arr_routes[r].input!==false) 
	    		{
		    		$(this.arr_routes[r].input).click( function(obj_event) {obj_this.clickRouteVisibility(obj_event)} );
	    		}
 
	    		// Create entry in obj_routeHash for this route
	    		this.obj_routeHash[this.arr_routes[r].rid] = r;
	    	}
		} catch(obj_err) { this.Error(obj_err.message,"loadRouteTogglers"); }
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Attaches event handlers to select box that allows filtering of POI by category/classification
	 */
	this.loadPoiFilter = function()
	{
		try
		{
			if (typeof(this.obj_init.poifilterid)!="undefined")
			{
				this.dom_poiFilter = document.getElementById(this.obj_init.poifilterid)
				if (this.dom_poiFilter)
				{
					var obj_this = this;
					// attach event handler(s)
					$(this.dom_poiFilter).change(function() {obj_this.changePoiFilter(this.value);})
				}
			}
		} catch(obj_err) { this.Error(obj_err.message,"loadPoiFilter"); }
	}	
	
	//--------------------------------------------------------------------------
	/**
	 * Sends AJAX request for overview map's routes
	 */
	this.loadOverviewRoutes = function()
	{
		try
		{
			// Get overview map lat/long bounds
			var obj_mapView = this.obj_overviewMap.GetMapView();
			var obj_bottomRight = obj_mapView.BottomRightLatLong;
			var obj_topLeft = obj_mapView.TopLeftLatLong;
			var arr_routeIds = new Array();
			for (var r=0; r<this.arr_routes.length; r++)
			{
				arr_routeIds.push(this.arr_routes[r].rid);				
			}
			// jQuery AJAX configuration
			var obj_this = this; // Alias used in assignment of event handlers
			// Definition of the asynchronous request object for retrieving updated route coordinates
			var objAjax = new VEI_AJAX(this,"RetrieveMapData");
			objAjax.success = function(obj_data) { obj_this.ajaxReceiveOverviewRoutes(obj_data); };
			objAjax.data = "RouteID=" + arr_routeIds.join("&RouteID=") + "&ZoomLevel=128" 
				+ "&MaxLong=" + obj_bottomRight.Longitude + "&MaxLat=" + obj_topLeft.Latitude
				+ "&MinLong=" + obj_topLeft.Longitude + "&MinLat=" + obj_bottomRight.Latitude
				+ "&GetPOI=0&POICategoryID=0";
			$.ajax(objAjax);
				
		} catch(obj_err) { this.Error(obj_err.message,"loadOverviewRoutes"); }
	}
	
	//##########################################################################
	// CONTROLLER methods
	/**
	 * Refreshes all map data via AJAX. Called after map is moved or zoom 
	 * is changed.
	 * @type void
	 */
	this.updateMaps = function()
	{
		try
		{
			if (this.bln_overviewMap)
			{
				this.updateOverviewYouAreHere();
			}
			
			// Determine all routes that are visible
			var arr_visibleRoutes = new Array(); 
			// Create an array that has all visible route IDs
			for (var r=0; r<this.arr_routes.length; r++)
			{
				if (this.arr_routes[r].visible===true) arr_visibleRoutes.push(this.arr_routes[r].rid);
			}
			
			// Only proceed with server calls if there is at least one visible route
			if (arr_visibleRoutes.length>0)
			{
				if (typeof(this.obj_ajaxRequest)!="undefined")
				{
					// If there is already a map update request in process, abort it
					if (this.obj_ajaxRequest.readyState!=0 && this.obj_ajaxRequest.readyState!=4)
					{
						this.obj_ajaxRequest.abort();
					}
				}
				
				// jQuery AJAX configuration
//				var obj_this = this; // Alias used in assignment of event handlers
				// Create request object for retrieving updated route coordinates
				var obj_ajax = new VEI_AJAX(this, "RetrieveMapData");
						        
		        // Map Bounds
		        var obj_mapView = this.obj_map.GetMapView();
		        var int_zoomLevel = this.obj_map.GetZoomLevel();
		        
		        /*
		         * (mrt) int_sampleMod is crucial for the efficient filtering of route 
		         *  points. As the user zooms OUT, more points are removed.
		         *  In an effort to make the filtering occur within the database query
		         *  as much as possible, I determined that we could essentially take 
		         *  every nth point, where n = 2 ^ (VEI_ZOOM_MAX - int_zoomLevel - 1)
		         *  The value of n is used within the [stored procedure] SELECT query to MOD the
		         *  individual unique point IDs against. The end result is that, for example,
		         *  at the zoom level of 16 (the second closest you can get), the value of 
		         *  n = 2 ^ (17 - 16 - 1) = 2 ^ 0 = 1
		         *  Then, in the WHERE clause of the query, we could say 'WHERE RouteDetailId%n=0',
		         *  which would become 'WHERE RouteDetailId%1=0', which would select every record
		         *  and do no filtering. For a zoom value of 7 (the farthest),
		         *  n = 2 ^ (17 - 7 - 1) = 2 ^ 9 = 512, so only every 512th point is used.
		         *  The goal behind the filtering is to only allow roughly 50-70 
		         *  points to be transferred from the server to the client. 
		         *  The only significant exception to these calculations is zoom level 17,
		         *  which automatically gets set to 'no filtering' when all is said and done.
		         */
		        var int_power = (VEI_ZOOM_MAX-int_zoomLevel-1>=0 ? VEI_ZOOM_MAX-int_zoomLevel-1 : 0);
		        var int_sampleMod = Math.pow(2,int_power);
		        
		        // Points of interest will only be loaded when the user is zoomed in enough
		        var int_getPoi = (int_zoomLevel>VEI_POI_ZOOM_THRESHHOLD ? 1 : 0);
		        var int_poiCategory = (isNaN($(this.dom_poiFilter).val()) ? -1 : new Number($(this.dom_poiFilter).val()));
		        var obj_bottomRight = obj_mapView.BottomRightLatLong;
		        var obj_topLeft = obj_mapView.TopLeftLatLong;
				obj_ajax.data = "RouteID=" + arr_visibleRoutes.join("&RouteID=") + "&ZoomLevel=" + int_sampleMod 
					+ "&MaxLong=" + obj_bottomRight.Longitude + "&MaxLat=" + obj_topLeft.Latitude
					+ "&MinLong=" + obj_topLeft.Longitude + "&MinLat=" + obj_bottomRight.Latitude
					+ "&GetPOI=" + int_getPoi + "&POICategoryID=" + int_poiCategory;
				this.obj_ajaxRequest = $.ajax(obj_ajax);
			}
	   	} catch(obj_err) { this.Error(obj_err.message,"updateVisibleRoutes"); }
	};
	
	//--------------------------------------------------------------------------
	/**
	 * Handler for click event of the checkbox input connected to the route
	 * @param {Event} obj_event Route affected by the changed visibility selection
	 */
	this.clickRouteVisibility = function(obj_event)
	{
		try
		{
			var dom_trigger;
			if (obj_event.target)
			{
				dom_trigger = obj_event.target;
			}
			else
			{
				dom_trigger = obj_event.srcElement;
			}

			var int_route = dom_trigger.getAttribute('route');
			var bln_selected = Boolean(this.arr_routes[int_route].input.checked);
			if (typeof(this.arr_routes[int_route].shapelayer)!="undefined")
			{
				if (bln_selected)
				{
					this.arr_routes[int_route].shapelayer.Show();
				}
				else
				{
					this.arr_routes[int_route].shapelayer.Hide();
				}
				this.checkRouteVisibility();
			}
		} catch(obj_err) { alert("VEInterface.clickRouteVisibility()\nERROR: " + obj_err.message); }
	};
	
	//--------------------------------------------------------------------------
	/**
	 * Loops through all defined routes and their appropriate input elements to determine
	 * if the route should be visible or not
	 */
	this.checkRouteVisibility = function()
	{
		try
		{
			for (var v=0; v<this.arr_routes.length; v++)
			{
				// If there is no valid reference to a visibility checkbox, assume the route is always visible
				if (this.arr_routes[v].input===false)
				{
					this.arr_routes[v].visible = true;
				}
				else
				{
					this.arr_routes[v].visible = (Boolean(this.arr_routes[v].input.checked)==true ? true : false);
				}
			}
		} catch(obj_err) { alert("VEInterface.checkRouteVisibility()\nERROR: " + obj_err); }
	};
	
	//--------------------------------------------------------------------------
	/**
	 * Handler for onclick event of the side overview map
	 */
	this.clickOverviewMap = function(objEvent)
	{
		var objLatLong = this.obj_overviewMap.PixelToLatLong(new VEPixel(objEvent.mapX,objEvent.mapY));
		this.obj_map.SetCenter(objLatLong);
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Handler for onclick event of the 'Add to Itinerary' link in the details popup
	 * for poins of interest
	 */
	this.clickAddToItinerary = function(objEvent,strAlias,intPoiID)
	{
		try
		{
			if (objEvent.preventDefault) objEvent.preventDefault();
			var objThis = this;
			var objAjax = new VEI_AJAX(this,"AddToItinerary");
			objAjax.data = "AliasPath=" + strAlias + "&AttractionID=" + intPoiID;
			var domTarget = (objEvent.srcElement ? objEvent.srcElement : objEvent.target);
			objAjax.success = function(objData) { objThis.ajaxAddedToItinerary(objData,domTarget); };
			$.ajax(objAjax);
			return false;
		} catch(objErr) { this.Error(objErr.message,"clickAddToItinerary"); }
	}	

	//--------------------------------------------------------------------------
	/**
	 * Handler for onclick event of the 'Details' link in a point-of-interest popup box
	 */
	this.clickPoiDetails = function(obj_Event,int_PoiID,str_ShapeID)
	{
		try
		{
			if (obj_Event.preventDefault) obj_Event.preventDefault();	// Prevent FF browser from navigating to '#' 
			var obj_this = this;
			var objAjax = new VEI_AJAX(this,"RetrievePoiDetails");
			objAjax.data = "PoiID=" + int_PoiID;
			objAjax.success = function(obj_data) { obj_this.ajaxReceivePoiDetails(obj_data,str_ShapeID,int_PoiID); };
			$.ajax(objAjax);	
			return false;
		} catch(obj_err) {this.Error( obj_err.message,"ajaxReceiveMapData"); }	
	}

	//--------------------------------------------------------------------------
	/**
	 * Callback for successful AJAX request for map data update
	 * @param {Object} obj_xml Successful XML server response
	 */
	this.ajaxReceiveMapData = function(obj_xml)
	{
		try
		{
            // If the data is a valid XML document
            if (obj_xml.documentElement)
            {
            	try
            	{
	            	// Parse and display all returned routes
	            	var xml_AllRoutes = obj_xml.documentElement.getElementsByTagName("Routes")[0];            	
	                xml_AllRoutes = xml_AllRoutes.getElementsByTagName("Route");
					this.parseXmlRoutes(xml_AllRoutes);
				} catch(obj_rErr) { this.Error(obj_rErr.message, "ajaxReceiveMapData"); }
				
				// Only refresh the POI layer if zoomed in enough
				if (this.obj_map.GetZoomLevel() > VEI_POI_ZOOM_THRESHHOLD)
				{
					try
					{
		                // Parse and display all returned points of interest
		                var xml_AllPoi = obj_xml.documentElement.getElementsByTagName("PointsOfInterest")[0];
		                xml_AllPoi = xml_AllPoi.getElementsByTagName("Poi");
						this.parseXmlPoi(xml_AllPoi);
					} catch(obj_pErr) { this.Error(obj_pErr.message, "ajaxReceiveMapData"); }
				}
				else
				{
					if (typeof(this.obj_mapLayers.poi)=="object")
					{
						this.obj_map.DeleteShapeLayer(this.obj_mapLayers.poi)
						this.obj_mapLayers.poi = undefined;
					}
				}
            }
		} catch(obj_err) { this.Error(obj_err.message,"ajaxReceiveMapData"); }
	}

	//--------------------------------------------------------------------------
	/**
	 * 
	 */
	this.ajaxReceiveOverviewRoutes = function(objXml)
	{
		try
		{
            // If the data is a valid XML document
            if (objXml.documentElement)
            {           		
            	// Parse and display all returned routes
            	var xmlAllRoutes = objXml.documentElement.getElementsByTagName("Routes")[0];            	
                xmlAllRoutes = xmlAllRoutes.getElementsByTagName("Route");
				for (var r=0; r<xmlAllRoutes.length; r++)
				{
					var objORoute = this.convertXmlRouteToVELayer(xmlAllRoutes[r])
					this.obj_overviewMap.AddShapeLayer(objORoute.layer);
				}
            }			
		} catch(objErr) { this.Error(objErr.message,"ajaxReceiveOverviewRoutes"); }
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Callback for successful retrieval of point-of-interest details.
	 * @param {Object} obj_xml XML Document result from AJAX call that contains POI details
	 * @param {String} str_ShapeID Unique ID of the shape whose description will be replaced by the details in the XML doc
	 * @type void
	 */
	this.ajaxReceivePoiDetails = function(objXml, strShapeDetailID, intPoiID)
	{	
		try
		{
            // If the data is a valid XML document
            if (objXml.documentElement)
            {
            	var objThis = this;
				var domPoiDetail = objXml.getElementsByTagName("PoiDetail")[0];
				if (domPoiDetail)
				{
					var strDetail = new String();
					var domDetail = document.getElementById(strShapeDetailID);
					domDetail.innerHTML = "";
					
					
					var strAlias = $("Alias",domPoiDetail).text();
					// If an alias URL is provided, then create anchor	 
					if (typeof(strAlias)!="undefined")
					{
						var domAlias = document.createElement("a");
						domAlias.setAttribute("href",VEI_URL_PREFIX + strAlias + ".aspx");
						domAlias.setAttribute("class","detaillink");
						domAlias.innerHTML = "&nbsp;";
						domDetail.appendChild(domAlias);
					}
					
					var strAddItinerary = "<a href=\"#\" class=\"additinerary\" onclick=\"return obj_vei.clickAddToItinerary(event,'" + strAlias + "'," + intPoiID + ")\">&nbsp;</a>";
					domDetail.innerHTML += strAddItinerary;
					
					var strAddress = $("Address",domPoiDetail).text();
					strDetail = strAddress + "<br/>";

					var strDesc = $("Desc",domPoiDetail).text();
					if (typeof(strDesc)!="undefined")
					{
						strDetail += "<br/>" + strDesc + "<br/>";
					}
					
					domDetail.innerHTML += strDetail;
					
					var strImg = $("Img",domPoiDetail).attr("src");
					if (typeof(strImg)!="undefined")
					{
						var domImg = document.createElement("img");
						domImg.setAttribute("src",VEI_URL_PREFIX+strImg);
						domDetail.appendChild(domImg);
					}

					// Insert icon images, if applicable
					var arrIcons = $("Icons > Icon",objXml).get();
					if (arrIcons.length>0)
					{
						var domIconHolder = document.createElement("div");
						domIconHolder.setAttribute("class","iconholder");
						for (var i=0; i<arrIcons.length; i++)
						{
							var domIcon = document.createElement("img");
							domIcon.setAttribute("src",VEI_URL_PREFIX+arrIcons[i].getAttribute("url"));
							domIcon.setAttribute("alt",arrIcons[i].getAttribute("alt"));
							domIconHolder.appendChild(domIcon);
						}
						domDetail.appendChild(domIconHolder);
					}
				}
            }	
		} catch(obj_err) { this.Error(obj_err.message,"ajaxReceivePoiDetails"); }
	}	
	
	//--------------------------------------------------------------------------
	/**
	 * Callback handler for successful AJAX request to add an attraction to personal itinerary
	 * @param {Object} objData Server XML response
	 * @param {DOM Object} domEventTarget Link that was clicked to trigger the AJAX call. Used to give visual indication that the attraction was added to itinerary
	 */
	this.ajaxAddedToItinerary = function(objData,domEventTarget)
	{
		try
		{
			var strSuccess = $("boolean",objData).text();	// Retrieve the value of the 
			var domConfirmation = document.createElement("div");
			domConfirmation.className = "attractionadded";
			if (strSuccess=="true")
			{
				domConfirmation.innerHTML = "This item has been added to your Favorites";
			}
			else 
			{
				domConfirmation.innerHTML = "This item is already in your Favorites list";
			}
			$(domEventTarget).before(domConfirmation);
			$(domEventTarget).remove();
		} catch(obj_err) { this.Error(obj_err.message,"ajaxAddedToItinerary"); }
	}	
		
	 //--------------------------------------------------------------------------
	/**
	 * Handler for onchange event of point-of-interest select box.
	 * @param {Integer} intPoiCategory
	 */
	this.changePoiFilter = function(intPoiCategory)
	{
		this.updateMaps();
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Handler for all AJAX errors 
	 * @param {Object} obj_request
	 * @param {String} str_error
	 * @param {Object} obj_exception
	 */
	this.ajaxError = function(obj_request,str_error,obj_exception)
	{
		if (typeof(obj_exception)!="undefined")
		{
			alert("VEInterface General AJAX ERROR: " + str_error + ' -- ' + obj_exception.message);
		}
	}
	
	//--------------------------------------------------------------------------
	/**
	 * Generalized error event handler 
	 */
	this.Error = function(str_message,str_funcName)
	{
		str_funcName = (typeof(str_funcName)!="undefined" ? "."+str_funcName+"()" : "");
		alert("VEInterface" + str_funcName + "\nERROR: " + str_message);
	}
	
}
