// Javascript source code
// --------------------------------------------
// REQUIRED: 
// var Icons: Array(Object, Object, ...); 
//		Object Properties: 
//			(index = database id), image, shadow, iconSize, shadowSize, iconAnchor, infoWindowAnchor, 
// 			printImage, mozPrintImage, printShadow, transparent, imageMap, maxHeight, 
// 			dragCrossImage, dragCrossSize, dragCrossAnchor 
// var placedIcons: Array(icon, new GLatLng(40.4079, -111.7662), ...);
//    	icon: database id and the Icons index
// var placedRoutes: Array(color, points, color, points, ...);
//		color: String;  color of the route ('red' - datasource.Color.Name field)
//    	points: Array(point, ...); points in the route 
//    		point: GLatLng(1.2, -543.883772334); a point in the route
//
// IMPORTANT:
// It is important to note the different axises that are used.
// - Latitude and Longitude are written (lat, lng)
//     - latitude progresses from -90 to 90 increasing from bottom to top
//         with the equator being 0
//     - longitude progresses from -180 to 180 increasing from left to right
//         with 0 being about mid europe
//
// - Pixel coordinates are are written (x,y)
//     - x increases from left to right and the left side of the map being 0
//     - y increases from top to bottom and the top of the map being 0
//
// - Angles are measured counter-clockwise from west(left side)
//       +90 (-270)
//   +---------+
//   | Q1 | Q2 |
// 0 +----+----+ +/-180
//   | Q4 | Q3 |
//   +---------+
//      +270 (-90)
//
// - Quadrants follow angles counter-clockwise
//      0 < Q1 <= 90
//     90 < Q2 <= 180
//    180 < Q3 <= 270
//    270 < Q4 <= 360
//
// - Icons are numbered following angles
//             5
//         4   |   6
//      3      |      7
//    2        |        8
//   1 --------+-------- 9
//    16       |       10
//      15     |     11
//         14  |  12
//             13
// --------------------------------------------

try {

	//alert('1: waitingForAJAX=' + waitingForAJAX);
	var waitingForAJAX = ( ! waitingForAJAX) ? false : waitingForAJAX ;
	var isRefreshing = false;
	//alert('2: waitingForAJAX=' + waitingForAJAX);
	
	// ------- Global Variables -------
	var debugLevel = 3;
	var log = new Log(LOG_ALL);
	log.funcStackPush('GMapLocal.js');
	
	var ICON_LOCATION = 'Styles/MapIcons/image';	// url from context root where the icon size folders are found
	var MapIconTableName = 'MapIconTable';			// base name of the MapIcon edit table
	var RoutePointTableName = 'RoutePointTable';	// base name of the RoutePoint edit table
	
	/** (miTBL and rpTBL)
	 * The DOM id is used to get handles to objects.  These objects hold the static elements 1 and 3.
	 * Something like ('MapIconTableRepeater_ctl' + '05' + '_MapIconTable' + 'NameField')
	 * This id is made up of 4 parts:
	 * 1) a prefix
	 * 2) a row id
	 * 3) the table name
	 * 4) the element name
	 */
	var miTBL = { pre: MapIconTableName    + 'Repeater_ctl', post: '_' + MapIconTableName };
	var rpTBL = { pre: RoutePointTableName + 'Repeater_ctl', post: '_' + RoutePointTableName };
	
	var changingRowNum = null;						// used when moving an icon
	var startMarker = null							// used when moving an icon
	var map = null;									// the base map
	
	var mapCenter = null;							// the map center point (new GLatLng(40.405662,-111.762553))
	var defaultZoomLevel = 16;								// the default zoom level


		
	// ------- Main Functions -------
	/**
	 * Establish the center of the Map and reload or, create the map and add the initial elements.
	 */
	function initialize() {
		try {
			log.funcStackPush('initialize()');

			logRequiredVariables();
	
			var urlAlertLevel = getURLVar("alertLevel");
			if(urlAlertLevel) log.alertLevel = urlAlertLevel;
		
			if(getURLPage() == 'map_update') {
				log.info('GDraggableObject.setDraggableCursor(\'crosshair\')');
				GDraggableObject.setDraggableCursor('crosshair');
			}
		    
		    if (GBrowserIsCompatible())
		    {
				log.major('GBrowserIsCompatible() = true');
				var lat = document.getElementById("MapRecordLatitude");
				var lng = document.getElementById("MapRecordLongitude");
		
				// Thow error because of bad  values
				if(lat == null || lng == null)
				{
					log.error('There is a problem with this Map in the database.  Latitude and Longitude cannot be null');
				}
		
				// Set the Map Center and reload
				else if(lat.value == 0 || lng.value == 0)
				{
					log.major('Setting Map Center');
					log.trace3('One of the fields was 0');
					var address = document.getElementById("MapRecordAddress");
					var city = document.getElementById("MapRecordCity");
					//var state = document.getElementById("MapRecordState");
					var zip = document.getElementById("MapRecordZip");
					var fullAddress = trim(address.value);
					fullAddress += ' ' + trim(city.value);
					//fullAddress += ' ' + trim(state.value);
					fullAddress += ' ' + trim(zip.value);
		
						geocoder = new GClientGeocoder();
						geocoder.getLatLng(fullAddress, GeocoderCallbackFunction);
			
					if(trim(fullAddress) != '') {
						alert('Centering the Map on\n'+fullAddress); // This alert is functionally required because the GeocoderCallbackFunction has not set mapCenter yet.  Please find a better way.
						
	
					}
				}
		
				// Create the Map and add the initial elements
				else
				{
					log.major('Drawing the Map');
		
					// Map
					log.minor('initialize canvas');
					var canvas = initCanvas();
					map = new GMap2(canvas);
					//log.minor('Initialize Map from Cookies');
					
					initMapFromCookies(lat, lng);		
					
				    map.addControl(new GSmallMapControl());
				    map.addControl(new GScaleControl());
				    map.addControl(new GMapTypeControl());
		
					// Bind Map Events
					log.minor('Bind Map Events');
				    GEvent.addListener(map, "zoomend", mapZoomendFunction);
					if(getURLPage() == 'map_update') {
					    GEvent.addListener(map, "click", mapClickFunction);
					    GEvent.addListener(map, "addoverlay", mapAddOverlayFunction);
					}
		
					// Add placedRoutes and Icons
					log.minor('Draw placedRoutes');
					drawPlacedRoutes(map); // add predefined routes (lines)
					log.minor('Draw Icons');
					drawPlacedIcons(map); // add predefinted icons
		
					// color the selected row as selected
					var iconID = getURLVar('IconID');
					if(iconID != '') {
						log.minor('Initialize Icons');
						initIcons(iconID);
					}
		
					var mapRouteID = getURLVar('MapRouteID');
					if(mapRouteID != '') {
						log.minor('Initialize Map Route');
						initRoutes(mapRouteID);
					}
				}
		    } else {
				log.warn('GBrowserIsCompatible() = false');
			}
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in initialize(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: function initialize()
	
	/**
	 * saves the map state
	 */
	function logRequiredVariables() {
		try {
			log.funcStackPush('logRequiredVariables()');
	
			// Log Icons
			var tmpStr = '\nIcons:\n'
			for(var a=0; a<Icons.length; a++) {
				if(Icons[a] != undefined) {
					var i = Icons[a];
					tmpStr += a + ' ' 
						+ 'name:' + i.name 
						+ '; image:' + i.image 
						+ '; shadow:' + i.shadow 
						+ '; iconSize:' + i.iconSize 
						+ '; iconAnchor:' + i.iconAnchor 
						+ '; zoomLevel:' + i.zoomLevel
						+ '\n';
				}
			}
			log.info(tmpStr);
			
			logPlacedVariables();
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in logRequiredVariables(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	function logPlacedVariables() {
		try {
			log.funcStackPush('logPlacedVariables()');
	
			// Log placedIcons
			var tmpStr = ''
		
			tmpStr += '\nplacedIcons:\n'
			for(var a=0; a<placedIcons.length; a++) {
				if(placedIcons[a] != undefined) {
					var i = placedIcons[a];
					tmpStr += a + ' ' 
						+ i
						+ '\n';
				}
			}
		
			// Log placedRoutes
			tmpStr += '\nplacedRoutes:\n'
			for(var a=0; a<placedRoutes.length; a++) {
				if(placedRoutes[a] != undefined) {
					var i = placedRoutes[a];
					tmpStr += a + ' ' 
						+ i
						+ '\n';
				}
			}
			log.info(tmpStr);
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in logPlacedVariables(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * saves the map state
	 */
	function uninitialize() {
		try {
			log.funcStackPush('uninitialize()');
	
			// prepare expiration date for the cookie
			var exp = new Date();
			var nowPlusOneWeek = exp.getTime() + (7*24*60*60*1000);
			exp.setTime(nowPlusOneWeek);
		
			if(map != null) {
		
				// get the zoom
				var zm = map.getZoom();
				document.cookie = 'mapZoomLevel=' + zm + '; expires=' + exp.toGMTString();
		
				// get the center
				var mapX = map.getCenter().lat();
				var mapY = map.getCenter().lng();
				document.cookie = 'mapCenter=' + mapX + ',' + mapY + '; expires=' + exp.toGMTString();
		
				// get the map type
				var tp = map.getCurrentMapType();
				document.cookie = 'mapType=' + tp.getName() + '; expires=' + exp.toGMTString();
		
			    log.minor('unloading .: setting cookie:'+document.cookie+'; '+'Zoom Level: '+zm);
			}
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in uninitialize(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * set the canvas size
	 */
	function initCanvas() {
		try {
			log.funcStackPush('initCanvas()');
	
			var canvas = document.getElementById("map_canvas");
			if(canvas == null) {
				alert('There is no map_canvas on this page');
				return;
			}
			var iconTable = document.getElementById("map_icons");
			var lineTable = document.getElementById("map_lines");
			var printSize = getURLVar('printSize');
			if (printSize == '' || printSize == null) {
				printSize = '8.5x11'
			}
		
			if(getURLPage() == 'map_update') { // MapBulder, MapViewer
				canvas.style.width = '675px';
				canvas.style.height = '575px';
				if(iconTable != null) {
					iconTable.style.width = '675px';
				}
				if(lineTable != null) {
					lineTable.style.width = '675px';
				}
			} else {
				if(printSize == '11x17') {
					canvas.style.width = '850px';
					canvas.style.height = '850px';
					if(iconTable != null) {
						iconTable.style.width = '850px';
					}
					if(lineTable != null) {
						lineTable.style.width = '850px';
					}
				} else {
					canvas.style.width = '675px';
					canvas.style.height = '575px';
					if(iconTable != null) {
						iconTable.style.width = '675px';
					}
					if(lineTable != null) {
						lineTable.style.width = '675px';
					}
				}
			}
		
			return canvas;
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in initCanvas(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 *
	 */
	function initMapFromCookies(lat, lng) {
		try {
			log.funcStackPush('initMapFromCookies()');
	
			/*-*/ if(debugLevel >= 7) { alert('cookie: \''+document.cookie+'\''); }
		
			// var map is a global variable
			var mapType			= getCookieVar('mapType');
			var mapCenter		= getCookieVar('mapCenter');
			var mapZoomLevel	= parseInt(getCookieVar('mapZoomLevel'));
		
			if(mapType != null && mapType != '') {
				switch (mapType) {
					default:			map.setMapType(G_NORMAL_MAP); 		break;
					case "Satellite":	map.setMapType(G_SATELLITE_MAP);	break;
					case "Hybrid": 		map.setMapType(G_HYBRID_MAP); 		break;
				}
			}
		
			// Map Center
			// var mapCenter // global
			if(mapCenter != null && mapCenter != '') {
				var mapCenterArr = mapCenter.split(',');					
				if(parseENFloat(mapCenterArr[0]) == lat && parseENFloat(mapCenterArr[1]) == lng)
				{
				     mapCenter = new GLatLng(parseENFloat(mapCenterArr[0]), parseENFloat(mapCenterArr[1]));
				}
				else
				{
				     mapCenter = new GLatLng(parseENFloat(lat.value), parseENFloat(lng.value));
				}
			}
			else {
				mapCenter = new GLatLng(parseENFloat(lat.value), parseENFloat(lng.value));
			}
			map.setCenter(mapCenter); // map center point
		
			// Map Zoom Level
			if(mapZoomLevel == null || mapZoomLevel == '' || isNaN(mapZoomLevel)) {
				mapZoomLevel = defaultZoomLevel;
			}
		    map.setZoom(mapZoomLevel); // map center point
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in initMapFromCookies(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	
	
	
	
	// ------- Support Functions -------
	function trim(stringToTrim) {
		try {
			log.funcStackPush('trim()');
	
			if(typeof stringToTrim != 'string') return;
			return stringToTrim.replace(/^\s+|\s+$/g,"");
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in trim(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	function ltrim(stringToTrim) {
		try {
			log.funcStackPush('ltrim()');
	
			if(typeof stringToTrim != 'string') return;
			return stringToTrim.replace(/^\s+/,"");
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in ltrim(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	function rtrim(stringToTrim) {
		try {
			log.funcStackPush('rtrim()');
	
			if(typeof stringToTrim != 'string') return;
			return stringToTrim.replace(/\s+$/,"");
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in rtrim(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	function isArray(obj) {
		try {
			log.funcStackPush('isArray()');
	
			if(obj == null) return;
			if (obj.constructor.toString().indexOf("Array") == -1)
				return false;
			else
			return true;
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in isArray(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	function printObject(obj) {
		try {
			log.funcStackPush('printObject()');
	
			if(obj == null) return;
			var str = obj + '\n';
			for(var a in obj) {
				str += '  ' + a + '=' + eval("obj."+a) + '\n';
			}
			alert(str);
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in printObject(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * gets the value of the key handed in from the document.cookie
	 */
	function getCookieVar(varKey) {
		try {
			log.funcStackPush('getCookieVar()');
	
			var pairs = document.cookie.split('; ');
			for(var i=0; i<pairs.length; i++)
			{
				var curVar = pairs[i].split('=');
				if(curVar[0] == varKey)
				{
		//			alert(typeof curVar[0] + ' \'' + curVar[0] + '\' == ' + typeof varKey + ' \'' + varKey + '\'');
					return curVar[1];
				} else {
		//			alert(typeof curVar[0] + ' \'' + curVar[0] + '\' != ' + typeof varKey + ' \'' + varKey + '\'');
				}
			}
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in getCookieVar(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * 
	 */
	function parseENFloat(param) {
		try {
			log.funcStackPush('parseENFloat()');
	
			var retVal = 0;
			var str = param.toString();
			if(str.indexOf(',')) {
				var newStr = str.replace(',', '.');
				var newFloat = parseFloat(newStr);
				log.minor(param+' parsed to '+newFloat);
				if(newFloat > 360) {
					log.warn('value larger than 360 .: using 0');
				} else {
					retVal = newFloat;
				}
			} else {
				retVal = str;
			}
		
			log.funcStackPop();
			return retVal;
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in parseENFloat(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Returns the window.location.href without the variable passed in.
	 */
	function removeURLVar(urlVarName, inURL) {
		try {
			log.funcStackPush('removeURLVar()');
	
			/*-*/ if(debugLevel >= 6) { alert('Begin removeURLVar("' + urlVarName + '")'); }
		
			inURL = (inURL) ? inURL : document.location ;

			//divide the URL in half at the '?'
			var urlHalves = String(inURL).split('?');
			/*-*/ if(debugLevel >= 9) { alert('urlHalves: ' + urlHalves); }
			var urlVarValue = urlHalves[0];
			/*-*/ if(debugLevel >= 9) { alert('urlVarValue: ' + urlVarValue); }
			if(urlHalves[1]){
				urlVarValue += '?';
				/*-*/ if(debugLevel >= 10) { alert('1'); }
				//load all the name/value pairs into an array
				var urlVars = urlHalves[1].split('&');
				/*-*/ if(debugLevel >= 10) { alert('2'); }
				//loop over the list, and find the specified url variable
				for(i=0; i<=(urlVars.length); i++){
					/*-*/ if(debugLevel >= 10) { alert('3'); }
					if(urlVars[i]){
						/*-*/ if(debugLevel >= 10) { alert('4'); }
						//load the name/value pair into an array
						var urlVarPair = urlVars[i].split('=');
						/*-*/ if(debugLevel >= 9) { alert('urlVarPair[0]: ' + urlVarPair[0] + '\n' + 'urlVarPair[1]: ' + urlVarPair[1] + '\n' + 'urlVarName: ' + urlVarName); }
						if (urlVarPair[0] && urlVarPair[0] != urlVarName) {
							/*-*/ if(debugLevel >= 9) { alert('add it'); }
							// This will only occure when the var is not the passed in var
							urlVarValue += urlVarPair[0] + '=' + urlVarPair[1] + '&';
							/*-*/ if(debugLevel >= 10) { alert('7'); }
						}
					}
				}
			}
			/*-*/ if(debugLevel >= 9) { alert('urlVarValue: ' + urlVarValue); }
			/*-*/ if(debugLevel >= 6) { alert('End removeURLVar("' + urlVarName + '")'); }
			return urlVarValue;   
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in removeURLVar(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: getURLVar(urlVarName)
	
	/**
	 * Returns the value of the URL variable passed in.
	 */
	function getURLVar(urlVarName) {
		try {
			log.funcStackPush('getURLVar()');
	
			log.all('searching for ' + urlVarName);
		
			//divide the URL in half at the '?'
			var urlHalves = String(document.location).split('?');
			var urlVarValue = '';
			if(urlHalves[1]){
				//load all the name/value pairs into an array
				var urlVars = urlHalves[1].split('&');
				//loop over the list, and find the specified url variable
				for(i=0; i<=(urlVars.length); i++){
					if(urlVars[i]){
						//load the name/value pair into an array
						var urlVarPair = urlVars[i].split('=');
						if (urlVarPair[0] && urlVarPair[0] == urlVarName) {
							//I found a variable that matches, load it's value into the return variable
							log.all('Found (' + urlVarPair[0] + '=' + urlVarPair[1] + ')');
							urlVarValue = urlVarPair[1];
							break;
						}
					}
				}
			}
			
			log.funcStackPop();
			return urlVarValue;   
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in getURLVar(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: getURLVar(urlVarName)
	
	/**
	 * Returns the value of the URL variable passed in.
	 */
	function getURLPage() {
		try {
			log.funcStackPush('getURLPage()');
	
			var absoluteURL = String(document.location).split('?')[0];
			var pathArray = absoluteURL.split('/')
			var file = pathArray[pathArray.length-1];
			var pageName = file.split('.')[0];

			log.funcStackPop();
			return pageName.toLowerCase();   
	
	    } catch (e) {
	    	alert('Unexpected error in getURLPage(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: getURLVar(urlVarName)
	
	/**
	 * Draw lines from the global array 'placedRoutes' on the map
	 */
	function drawPlacedRoutes(map) {
		try {
			// placedRoutes is defined globally by the master page
			if(typeof placedRoutes == 'undefined') {
				log.info('placedRoutes is undefined');
				return;
			}
		
			// note: each route is defined by two rows in placedRoutes
			for(var route=0; route<placedRoutes.length; route+=2)
			{
				if(typeof placedRoutes[route+1] != 'undefined') {
				
					// establish line type (icon line or color line)
					var isIconLine = false;
					switch (placedRoutes[route][0]) {
						case 'Safest Walking Route' :
						case 'Safest Biking Route' :
						case 'Damaged/No Sidewalk' :
						case 'Reduced Speed Zone' :
						case 'Other':
			                isIconLine = true;
							log.all('this is an icon line');
							break;
					}
		
					drawLines(route, placedRoutes[route+1], isIconLine);
				}
			}
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in drawPlacedRoutes(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: function drawPlacedRoutes()
	
	/**
	 * Add an icon line for each line/point after the first
	 */
	function drawLines(route, points, isIconLine) {
		try {
			log.funcStackPush('drawLines()');
	
		    log.major('route: ' + placedRoutes[route][0]);
		
		    // for each point/line (the last point will be ignored) draw a line (overlay/GMarker)
		    for(var point=0; point<points.length-1; point++)
		    {
		        if(points[point+1]) {
			        log.all(points[point] + ' to ' + points[point+1]);
		            if(isIconLine)
		                drawIconLine(placedRoutes[route], points[point], points[point+1]);
		            else
		                drawColorLine(placedRoutes[route], points[point], points[point+1]);
		        }
		    }
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in drawLines(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: function drawLines(route, points, isIconLine)
	
	/**
	 * Add a series of icons
	 */
	function drawIconLine(route, p1, p2) {
		try {
			log.funcStackPush('drawIconLine()');
	
		    log.info('route: ' + route);
		    log.info('p1: ' + p1);
		    log.info('p2: ' + p2);
		    
			if(isNaN(p1.lat()) || isNaN(p1.lng()) || isNaN(p2.lat()) || isNaN(p2.lng())) {
				log.error('error with points: ' + p1 + ' - ' + p2);
				return;
			}
		
			// add a colored line under the icons
		//	map.addOverlay(new GPolyline([p1, p2], "#FFFFFF", 8, .4));
			
			// find selected route's icon
			var jsIcon = Icons[route[4]];
			log.all('jsIcon: ' + jsIcon);
			
			// build icon image url
			var dotIndex = jsIcon.image.lastIndexOf('.');
			var jsIconImage = jsIcon.image.substring(0, dotIndex);
			jsIconImage += getIconNumber(p1, p2);
			jsIconImage += '.png';
			log.all('jsIconImage: ' + jsIconImage);
			
			// build Icon
			var icon1 = new GIcon(jsIcon, jsIconImage);
			    icon1.padding = new GSize(0,0);
			log.major('Icon image: ' + icon1.image);
			
			var mapZoom = map.getZoom();
			var projection = map.getCurrentMapType().getProjection();
			var point1 = projection.fromLatLngToPixel(p1, mapZoom);
			var point2 = projection.fromLatLngToPixel(p2, mapZoom);
			log.all('point1: ' + point1);
			log.all('point2: ' + point2);
			
			var numXIcons = (point2.x - point1.x) / (icon1.iconSize.width + icon1.padding.width);
			var numYIcons = (point2.y - point1.y) / (icon1.iconSize.height + icon1.padding.height);
			var numIcons = (Math.abs(numXIcons) > Math.abs(numYIcons)) ? Math.abs(numXIcons) : Math.abs(numYIcons) ;
			numIcons = Math.ceil(numIcons);
		    log.all('numXIcons: ' + numXIcons);
			log.all('numYIcons: ' + numYIcons);
			log.all('numIcons: ' + numIcons);
			var curIcon = 1;
			
			var dX = null;
			var dY = null;
			if(Math.abs(numXIcons) > Math.abs(numYIcons)) {
				dX = (icon1.iconSize.width + icon1.padding.width);
				dY = (point2.y - point1.y) / numIcons;
				if(numXIcons < 0) {
					dX *= -1;
				}
			} else {
				dX = (point2.x - point1.x) / numIcons;
				dY = (icon1.iconSize.height + icon1.padding.height)
				if(numYIcons < 0) {
					dY *= -1;
				}
			}
			log.all('dX: ' + dX);
			log.all('dY: ' + dY);
			
			var curX = point1.x;
			var curY = point1.y;
			log.all('curX: ' + curX);
			log.all('curY: ' + curY);
			
			// add each icon on the line
			do {
			    var curPoint = new GPoint(curX, curY);
			    var latlng = projection.fromPixelToLatLng(curPoint, mapZoom);
		
			    log.major('icon1: ' + icon1);
			    log.major('icon1.image: ' + icon1.image);
			    var marker = new GMarker(latlng, icon1);					
			    map.addOverlay(marker);
		
			    curIcon++;
			
			    curX += dX;
			    curY += dY;
			    log.all('Variables: '
			       + '; ' + 'dX: ' + dX 
			       + '; ' + 'dY: ' + dY 
			       + '; ' + 'curX: ' + curX 
			       + '; ' + 'curY: ' + curY 
			       + '; ' + 'numXIcons: ' + numXIcons 
			       + '; ' + 'numYIcons: ' + numYIcons 
			       + '; ' + 'numIcons: ' + numIcons);
		
			// these are other algorithms	       
			//} while(curX < (point2.x - (icon1.iconSize.width + icon1.padding.width)) || curY < (point2.y - (icon1.iconSize.width + icon1.padding.width)))
		    //} while(curX < (point2.x) || curY < (point2.y))
		    } while(curIcon <= numIcons)

			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in drawIconLine(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: function drawIconLine(points)
	
	/**
	 * Add a colored line
	 */
	function drawColorLine(route, p1, p2) {
		try {
			log.funcStackPush('drawColorLine()');
	
		    log.all('color route');
		
		    var lineWidth = getLineWidth(map.getZoom());
		    log.all('lineWidth: ' + lineWidth);
			var effectiveLineWidth = (placedRoutes[route][1]) ? (lineWidth * 2) : lineWidth ;
			log.all('effectiveLineWidth: ' + effectiveLineWidth);
			var p1 = new GLatLng(parseENFloat(p1.lat()), parseENFloat(p1.lng()));
			var p2 = new GLatLng(parseENFloat(p2.lat()), parseENFloat(p2.lng()));
			var polyline = new GPolyline([p1, p2], placedRoutes[route][0], effectiveLineWidth);
			log.all('polyline: ' + polyline + '\n' + p1 + '\n' + p2 + '\n' + placedRoutes[route][0] + '\n' + effectiveLineWidth);
			map.addOverlay(polyline);
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in drawColorLine(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: function drawColorLine(points)
	
	/**
	 * Find the Icon number for the angle and quadrant
	 */
	function getIconNumber(p1, p2) {
		try {
			log.funcStackPush('getIconNumber()');
	
			// delta (change in) latitude and longitude
			var dLat = (parseENFloat(p2.lat()) - parseENFloat(p1.lat()));
			var dLng = (parseENFloat(p2.lng()) - parseENFloat(p1.lng()));
			log.all('dLat: ' + dLat);
			log.all('dLng: ' + dLng);
			
			var iconSlope = (dLat / dLng);
			log.all('iconSlope: ' + iconSlope);
			var radIconAngle = Math.atan(iconSlope);                 // -PI/2 radians to PI/2 radians
			log.all('radIconAngle: ' + radIconAngle);
			var degIconAngle = (radIconAngle * 360) / (2 * Math.PI); // -90 degrees to 90 degrees
			log.all('degIconAngle: ' + degIconAngle);
		    var absIconAngle = Math.abs(degIconAngle);               // 0 degrees to 90 degrees
			log.all('angle: ' + absIconAngle);
			
			// set vector quadrant (see note at top)
			var quadrant = 0;
			if(dLat >= 0)
			    if(dLng >= 0) quadrant = 2;
			    else          quadrant = 1;
			else
			    if(dLng >= 0) quadrant = 3;
			    else          quadrant = 4;
		    log.all('quadrant: ' + quadrant);
		
		    var iconNumber = 0;
		    switch (quadrant) {
				case 1:
		            log.all('case 1');
				         if(absIconAngle < 11.25) iconNumber = 1;
				    else if(absIconAngle < 33.75) iconNumber = 2;
				    else if(absIconAngle < 56.25) iconNumber = 3;
				    else if(absIconAngle < 78.75) iconNumber = 4;
				    else                          iconNumber = 5;
				    break;
		
				case 2: 
		            log.all('case 2');
		                 if(absIconAngle < 11.25) iconNumber = 9;
		            else if(absIconAngle < 33.75) iconNumber = 8;
		            else if(absIconAngle < 56.25) iconNumber = 7;
		            else if(absIconAngle < 78.75) iconNumber = 6;
		            else                          iconNumber = 5;
		            break;
		
				case 3: 
		            log.all('case 3');
		                 if(absIconAngle < 11.25) iconNumber = 9;
		            else if(absIconAngle < 33.75) iconNumber = 10;
		            else if(absIconAngle < 56.25) iconNumber = 11;
		            else if(absIconAngle < 78.75) iconNumber = 12;
		            else                          iconNumber = 13;
		            break;
		
				case 4: 
		            log.all('case 4');
		                 if(absIconAngle < 11.25) iconNumber = 1;
		            else if(absIconAngle < 33.75) iconNumber = 16;
		            else if(absIconAngle < 56.25) iconNumber = 15;
		            else if(absIconAngle < 78.75) iconNumber = 14;
		            else                          iconNumber = 13;
		            break;
		    }
	
		    return iconNumber;
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in getIconNumber(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: function getIconNumber(absIconAngle, quadrant)
	
	/**
	 * Add icons from the global array 'Icons' as an overlay to the map
	 */
	function drawPlacedIcons(map) {
		try {
			log.funcStackPush('drawPlacedIcons()');
	
			// placedIcons is defined globally by the master page
			if(typeof placedIcons == 'undefined') {
				log.info('placedIcons is undefined');
				return;
			}
		
			
			// Set icon properties based on the zoom level
			updateJSIcons(map);
		
			// add images to the map
			log.major('add icons to the map');
			log.minor('Number of icons: ' + ( (placedIcons.length-1) / 2) );
			for(var indx=0; indx<=placedIcons.length; indx+=2)
			{
				log.all('loop index ' + indx);
				if(typeof placedIcons[indx] != 'undefined' && placedIcons[indx] != 0) {
					var icon = Icons[placedIcons[indx]];
					log.all('icon.image: '+icon.image);
					var latLng = placedIcons[indx+1];
					log.all('icon.location: '+latLng);
					var marker = null;
					
					// don't allow interaction unless on the Map_update page
					if(getURLPage() == 'map_update') {
						marker = new GMarker(latLng, {draggable: true, icon: icon});
						GEvent.addListener(marker, "click", iconClickFunction);
						GEvent.addListener(marker, "dragstart", iconDragstartFunction);
						GEvent.addListener(marker, "dragend", iconDragendFunction);
					} else {
						marker = new GMarker(latLng, {draggable: false, icon: icon});
					}
					log.all('add icon to map');
		    		map.addOverlay(marker);
				}
			}
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in drawPlacedIcons(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: fundtion drawPlacedIcons()
	
	/**
	 * Calculate line width based on the zoom level of the map
	 */
	function getLineWidth(zoom) {
		try {
			log.funcStackPush('getLineWidth()');
	
			var lineWidth = 0;
			switch (zoom)
			{
				case 14: lineWidth =  3; break;
				case 15: lineWidth =  5; break;
				case 16: lineWidth =  7; break;
				case 17: lineWidth = 11; break;
				case 18: lineWidth = 17; break;
			} // End: switch (zoom)
		
			return lineWidth;
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in getLineWidth(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: function getLineWidth(zoom)
	
	/**
	 * Set properties of the Icons array based on the zoom level
	 */
	function updateJSIcons(map) {
		try {
			log.funcStackPush('updateJSIcons()');
	
			var zoom = map.getZoom();
		
			// update the icon definitions
			log.minor('Icons: '+Icons+'\n'+'Icons.length: '+Icons.length);
			for(var icon=0; icon<99; icon++)
			{
				if(Icons[icon] == null) {
					continue;
				} else {
					updateJSIconImage(Icons[icon], zoom);
					updateJSIconSize(Icons[icon], zoom);
				}
			} // End: for(var icon in Icons)
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in updateJSIcons(): ' + e + '\n' + log.funcStackPrint());
	    }
	} // End: function updateJSIcons()
	
	/**
	 * Adjust image attributes of the icon
	 */
	function updateJSIconImage(icon, zoom) {
		try {
			log.funcStackPush('updateJSIconImage()');
	
			log.minor('icon: '+icon+'\n'+'icon: '+icon+'\n'+'icon.image: '+icon.image);
			if(icon.image != 'G_DEFAULT_ICON')
			{
				// set image property
				var slash = icon.image.lastIndexOf('/');
				if(slash != -1)
				{
					icon.image = icon.image.substr(slash+1);
				}
				// ICON_LOCATION defined globally by this script
				icon.image = ICON_LOCATION+'/'+zoom+'/'+icon.image;
			}
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in updateJSIcons(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Adjust some size attributes of the icon
	 */
	function updateJSIconSize(icon, zoom) {
		try {
			log.funcStackPush('updateJSIconSize()');
	
	
			// set iconSize and iconAnchor properties
			var baseSize;
			switch (icon.name) {

				case 'Safest Walking Route':
					switch (zoom) {
						default: baseSize = 14; break;
					}
					break;
				case 'Safest Biking Route':
					switch (zoom) {
						default: baseSize = 8; break;
					}
					break;
				case 'Other':
					switch (zoom) {
						default: baseSize = 10; break;
					}
					break;case 'Damaged/No Sidewalk':
				case 'Reduced Speed Zone':
					switch (zoom) {
						default: baseSize = 6; break;
					}
					break;
					
				case 'School':
				switch (zoom) {
						case 13: baseSize = 12; break;
						case 14: baseSize = 16; break;
						case 15: baseSize = 24; break;
						case 16: baseSize = 32; break;
						case 17: baseSize = 48; break;
						case 18: baseSize = 64; break;
						case 19: baseSize = 64; break;
						case 20: baseSize = 64; break;
						default: baseSize = 32; break;
					}
					break;
				case 'Student Drop-off/Pick-up':	
				case 'Crossing Guard':
					switch (zoom) {
						case 13: baseSize = 8; break;
						case 14: baseSize = 12; break;
						case 15: baseSize = 16; break;
						case 16: baseSize = 24; break;
						case 17: baseSize = 32; break;
						case 18: baseSize = 48; break;
						case 19: baseSize = 64; break;
						case 20: baseSize = 64; break;
						default: baseSize = 24; break;
					}
					break;
				case 'Crosswalk-Left':
				case 'Crosswalk-Right':
					switch (zoom) {
						case 13: baseSize = 7; break;
						case 14: baseSize = 11; break;
						case 15: baseSize = 15; break;
						case 16: baseSize = 21; break;
						case 17: baseSize = 27; break;
						case 18: baseSize = 35; break;
						case 19: baseSize = 51; break;
						case 20: baseSize = 67; break;
						default: baseSize = 19; break;
					}
					break;	
				default:
					switch (zoom) {
						case 13: baseSize = 4; break;
						case 14: baseSize = 8; break;
						case 15: baseSize = 12; break;
						case 16: baseSize = 16; break;
						case 17: baseSize = 24; break;
						case 18: baseSize = 32; break;
						case 19: baseSize = 48; break;
						case 20: baseSize = 64; break;
						default: baseSize = 16; break;
					}
					break;
	
			} // End: Switch (icon.name)
	
			icon.iconSize   = new GSize(baseSize, baseSize);
			icon.iconAnchor = new GPoint(baseSize/2, baseSize/2);
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in updateJSIcons(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Get the row number with the latitude and longitude of 'latLng'
	 */
	function findRowByLatLng(latLng) {
		try {
			log.funcStackPush('findRowByLatLng()');
	
			var rowNum = 1;
			var searchStr = miTBL['pre'] + '01' + miTBL['post'] + 'Latitude';
			log.all('searchStr='+searchStr);
			var latFld = document.getElementById(searchStr);
			log.all('latFld.id='+latFld.id);
			while(latFld != null) {
		
				if((parseFloat(latFld.value)).toFixed(12) == (parseFloat(latLng.lat())).toFixed(12))
				{
					// Latitude matches .: check Longitude
					var rowLongitude = (rowNum<10) 
						? miTBL['pre'] + '0' + rowNum + miTBL['post'] + 'Longitude'
						: miTBL['pre']       + rowNum + miTBL['post'] + 'Longitude';
					var lngFld = document.getElementById(rowLongitude);
					log.all('lngFld.id=' +lngFld.id);
					
					if((parseFloat(lngFld.value)).toFixed(12) == (parseFloat(latLng.lng())).toFixed(12))
					{
						// Longitude matches .: update the Latitude and Longitude
						log.minor('Latitude and Longitude match row ' + rowNum);
		
						log.funcStackPop();
		
						return rowNum;
					}
				}
		
				rowNum++;
				var rowLatitude = (rowNum<10) 
					? miTBL['pre'] + '0' + rowNum + miTBL['post'] + 'Latitude'
					: miTBL['pre']       + rowNum + miTBL['post'] + 'Latitude';
				latFld = document.getElementById(rowLatitude);
				if(latFld != null)
				{
				log.all('latFld.value=' + latFld.value 
												+ '\n' + 'latLng.lat(): ' + latLng.lat()
												+ '\n' + 'latLng.lng(): ' + latLng.lng()
											);
				}
			}
			log.warn('Latitude and Longitude NOT matched');
		
			return -1;
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in findRowByLatLng(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Set the delete checkbox of the row indicated by the global
	 * variable 'changingRowNum'.
	 */
	function updateRowDelete() {
		try {
			log.funcStackPush('updateRowDelete()');
	
			var rowDelete = (changingRowNum<10) 
				? miTBL['pre'] + '0' + changingRowNum + miTBL['post'] + 'CheckBox_Delete'
				: miTBL['pre']       + changingRowNum + miTBL['post'] + 'CheckBox_Delete';
			log.minor('rowDelete=' + rowDelete);
			var delFld = document.getElementById(rowDelete);
			log.minor('delFld=' + delFld);
			log.minor('delFld.checked=' + delFld.checked);
		
			delFld.checked = true;
		
			log.funcStackPop();
		
			return true;
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in updateRowDelete(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Update the latitude and longitude of the row indicated by the global
	 * variable 'changingRowNum'.  Set them to the values in 'marker'.
	 */
	function updateRowLatLng(marker) {
		try {
			log.funcStackPush('updateRowLatLng()');
	
			var rowLatitude = (changingRowNum<10) 
				? miTBL['pre'] + '0' + changingRowNum + miTBL['post'] + 'Latitude'
				: miTBL['pre']       + changingRowNum + miTBL['post'] + 'Latitude';
			latFld = document.getElementById(rowLatitude);
		
			var rowLongitude = (changingRowNum<10) 
				? miTBL['pre'] + '0' + changingRowNum + miTBL['post'] + 'Longitude'
				: miTBL['pre']       + changingRowNum + miTBL['post'] + 'Longitude';
			lngFld = document.getElementById(rowLongitude);
		
			log.minor('row was: ('+latFld.value+','+lngFld.value+')');
		
			latFld.value = marker.lat();
			lngFld.value = marker.lng();
		
			log.minor('row is: ('+latFld.value+','+lngFld.value+')');
		
			log.funcStackPop();
		
			return true;
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in updateRowLatLng(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Find the submit button that has 'submitPart' as part of its id,
	 * and a execute its click() method.
	 */
	function submitForm(submitPart, frm) {
		try {
			log.funcStackPush('submitForm()');
	
			log.minor('submitPart: ' + submitPart);
			frm = (frm) ? frm : document.forms[0];
			log.minor('form: id=' + frm.id + '; name=' + frm.name);
			log.minor('form elements: ' + frm.elements.length);
			for(var objName in frm.elements)
			{
				try {
		//			log.minor('objName: ' + objName); // too much data
					var objID = frm.elements[objName].id;
					if(objID != null && objID.indexOf(submitPart) >= 0)
					{
						log.minor('objID=' + objID + '; ' + objName);
						log.minor('element: id=' + frm.elements[objName].id + '; name=' + frm.elements[objName].name);
		//alert('3: waitingForAJAX=' + waitingForAJAX);
						frm.elements[objName].click();
		//alert('4: waitingForAJAX=' + waitingForAJAX);
		//				alert('setting waiting for AJAX');
						if(BrowserDetect.browser == 'Explorer') {
							waitingForAJAX = true;
		//					log.minor('This is Explorer .: reloading');
		//					document.location.href = document.location.href;
						}
		
						log.funcStackPop();
						return;
					}
				} catch (e) {
		//			log.minor('ignoring because ' + e.name); // too much data
				}
			}
			log.warn('no submit found');
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in submitForm(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * find the last row in the table represented by the parameter
	 */
	function findLastRow(param) {
		try {
			log.funcStackPush('findLastRow()');
	
			var newRow = 1;
			var tmpStr = param['pre'] + '01' + param['post'] + 'Latitude'
			log.minor('tmpStr=' + tmpStr);
			var selOb = document.getElementById(tmpStr);
			log.minor('selOb=' + selOb);
			while(selOb != null) {
				newRow++;
				log.minor('selOb.id=' + selOb.id);
				var rowID = (newRow<10) 
					? param['pre'] + '0' + newRow + param['post'] + 'Latitude'
					: param['pre']       + newRow + param['post'] + 'Latitude';
				var selOb = document.getElementById(rowID);
			}
			newRow--;
			log.major('returning ' + newRow);
		
			log.funcStackPop();
		
			return newRow;
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in findLastRow(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Fill the empty row in the editable grid with information from the
	 * mode selected and the point clicked.
	 */
	function addIcon(point) {
		try {
			log.funcStackPush('addIcon()');
/*	
*/
			// find the last (empty) row in the 'MapIconTable' table
			var newRow = findLastRow(miTBL);
			log.minor('newRow: ' + newRow);
			var baseStr  = miTBL['pre'] + ((newRow<10)     ? '0' + newRow     : newRow)     + miTBL['post'];
			var baseNext = miTBL['pre'] + (((newRow+1)<10) ? '0' + (newRow+1) : (newRow+1)) + miTBL['post'];
/*
			var basePrev = miTBL['pre'] + (((newRow-1)<10) ? '0' + (newRow-1) : (newRow-1)) + miTBL['post'];
			log.minor('bases:\n' + 'basePrev: ' + basePrev + '\n' + 'baseStr: ' + baseStr + '\n' + 'baseNext: ' + baseNext);
		
			// Set the icon menu
*/
			var idStr = baseStr + 'IconID';
			log.minor(idStr);
			var newIconMenu = document.getElementById(idStr);
			log.minor('icon menu: ' + newIconMenu.value + ' ' + newIconMenu.options[newIconMenu.selectedIndex].text);
			newIconMenu.selectedIndex = getSelectedIndex(newIconMenu);
			log.minor('icon menu: ' + newIconMenu.value + ' ' + newIconMenu.options[newIconMenu.selectedIndex].text);
		
/*
			// set the Latitude
			idStr = baseStr + 'Latitude';
			var lat = document.getElementById(idStr);
			lat.value = point.y;
		
			// set the Longitude
			idStr = baseStr+ 'Longitude';
			var lng = document.getElementById(idStr);
			lng.value = point.x;
		
			// set the MapID from the MasterMapID field
			idStr = baseStr + 'MapID';
			var newMapID = document.getElementById(idStr);
		//	var newRowStr = ((newRow+1)<10) ? '0'+(newRow+1) : (newRow+1) ;
			idStr = baseNext + 'MasterMapID';
			var mapID = document.getElementById(idStr);
			newMapID.value = mapID.value;
*/

			// Show the icon on the map
			var idStr = baseStr + 'IconID';
			var newIconMenu = document.getElementById(idStr);
			var iconID = newIconMenu.options[newIconMenu.selectedIndex].value;
			var icon = Icons[iconID];
			log.minor('icon.image:'+icon.image);
			var marker = new GMarker(point, {draggable: true, icon: icon})
			GEvent.addListener(marker, "click", iconClickFunction);
			GEvent.addListener(marker, "dragstart", iconDragstartFunction);
			GEvent.addListener(marker, "dragend", iconDragendFunction);
			map.addOverlay(marker);
			log.minor('End: addIcon(point)');
		
/* I don't think this is doing anything useful
			Icons[(Icons.length-1)] = drawModeItem;
			Icons[Icons.length] = point;
			Icons[Icons.length] = 0;
*/		
			// click the submit button
//			submitForm('MapIconTableSubmit');

			// add the icon to the map in the database
			idStr = baseNext + 'MasterMapID';
			var newMapID = document.getElementById(idStr).value;
			ajax('InsertIcon.aspx?IconId=' + iconID + '&MapID=' + newMapID + '&Latitude=' + point.y + '&Longitude=' + point.x);
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in addIcon(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Get the selectedIndex of the object array.
	 */
	function getSelectedIndex(newIconMenu) {
		try {
			log.funcStackPush('getSelectedIndex()');
	
			/*-*/ if(debugLevel >= 6) { alert('Begin: getSelectedIndex('+newIconMenu+')'); }
			for(var opt=0; opt<newIconMenu.length; opt++)
			{
				/*-*/ if(debugLevel >= 7) { alert('drawModeItem: ' + drawModeItem); }
				if(newIconMenu.options[opt].value == drawModeItem)
				{
					/*-*/ if(debugLevel >= 6) { alert('End: getSelectedIndex(newIconMenu) - item found\n  returning ' + opt); }
					return opt;
				}
			}
			/*-*/ if(debugLevel >= 6) { alert('End: getSelectedIndex(newIconMenu) - no item found'); }
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in getSelectedIndex(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Add a new point/line to the current route.
	 */
	function addLine(point) {
		try {
			log.funcStackPush('addLine()');
	
/*
			// Add point to the GridTable (database)
			var newRow = findLastRow(rpTBL);
			log.all('newRow: ' + newRow);
			var basePrev = rpTBL['pre'] + (((newRow-1)<10) ? '0' + (newRow-1) : (newRow-1)) + rpTBL['post'];
			var baseStr  = rpTBL['pre'] + ((newRow<10)     ? '0' + newRow     : newRow)     + rpTBL['post'];
			var baseNext = rpTBL['pre'] + (((newRow+1)<10) ? '0' + (newRow+1) : (newRow+1)) + rpTBL['post'];
			log.minor('basePrev: ' + basePrev);
			log.minor(' baseStr: ' + baseStr);
			log.minor('baseNext: ' + baseNext);
		
			// set the Latitude
			idStr = baseStr + 'Latitude';
			var lat = document.getElementById(idStr);
			lat.value = point.y;
			log.all('Latitude: ' + 'value=' + lat.value + '; ' + idStr);
		
			// set the Longitude
			idStr = baseStr + 'Longitude';
			var lng = document.getElementById(idStr);
			lng.value = point.x;
			log.all('Longitude: value=' + lng.value + '; ' + idStr);
		
			// Set the Order
			var idStr = baseStr + 'Order';
			var ordr = document.getElementById(idStr);
			var idStr = basePrev + 'Order';
			var ordrPrev = document.getElementById(idStr);
			ordr.value = parseInt((ordrPrev == null) ? 0 : ordrPrev.value) + 1;
			log.all('Order: ' + 'value=' + ordr.value + '; ' + baseStr);
		
			// set the MapID from the MasterMapID field
			idStr = baseStr + 'MapRouteID';
			var newRoutePointID = document.getElementById(idStr);
			newRoutePointID.value = drawModeItem;
			log.all('ID: ' + 'value=' + newRoutePointID.value + '; ' + idStr);
*/
		
			var lastPoint
			var color;
		
			// find the last point
			var curRoute
			var curRoutePts;
			for (var i=0; i<placedRoutes.length; i+=2)
			{
				j = parseInt(i)+1;
				log.all('Current Route: ' + placedRoutes[i] + '; ' + placedRoutes[j]);
				if(placedRoutes[i][2] == drawModeItem && isArray(placedRoutes[j])) 
				{
					// this is the route being edited
					curRoute = placedRoutes[i]; // the meta data
					curRoutePts = placedRoutes[j]; // the points
					log.minor('Selected Route: ' + placedRoutes[i] + '; ' + placedRoutes[j]);
					lastPoint = curRoutePts[(curRoutePts.length-1)];
					color = placedRoutes[i][0];
					curRoutePts[curRoutePts.length] = point; // add the point to the javascript array
					break;
				}
			}
		
			logPlacedVariables();
			if(lastPoint == null)
			{
				log.minor('lastPoint == null');
		
				// this is the first point in the route .:
				// add the route with its initial point
		//		placedRoutes[placedRoutes.length] = new Array(drawModeColor,false,drawModeItem);
		//		placedRoutes[placedRoutes.length] = new Array(point);
		//		alert('Beginning New Safe Route');
		
			} else {
				// show the line on the map
				log.minor('lastPoint != null');
				log.minor('curRoute: ' + curRoute + '; lastPoint:' + lastPoint + '; point:' + point);
		        drawIconLine(curRoute, lastPoint, point);
			}
		
			logPlacedVariables();
		
			// click the submit button
//			submitForm('RoutePointTableSubmit'); // (ajax update)
			ajax('InsertRoutePoint.aspx?MapRouteID=' + drawModeItem + '&Latitude=' + point.y + '&Longitude=' + point.x);
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in addLine(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	
	
	// ------- Event Functions -------
	/**
	 * Add an object to a GMap.
	 */
	var mapClickFunction = function(marker, point) {
		try {
			log.funcStackPush('mapClickFunction()');
	
			if(isRefreshing) {
				alert('Adding point.  Please wait.');
				
			} else if (!marker) {
				log.minor('NOT a marker');
		
				switch (drawMode) {
					case 'IconTable':
						log.minor('using IconTable');
						addIcon(point);
						break;
		
					case 'RouteTable':
						log.minor('using RouteTable');
						addLine(point);
						break;
						
					default:
						log.warn('no drawMode');
				}
		    } // End: else (!marker)
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in mapClickFunction(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Redraw the objects when a GMap has changed zoom.
	 */
	var mapZoomendFunction = function(marker, point) {
		try {
			log.funcStackPush('mapZoomendFunction()');
	
			zoomLevel = map.getZoom(); // global variable
			
			//Force refresh on zoom to make sure all icons load.
			if(getURLPage() == 'map_update') {
				window.location.reload( false );
			}
			
			if(getURLPage() == 'map_view') {
				map.clearOverlays();
				drawPlacedIcons(map);
				drawPlacedRoutes(map);
			}
			
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in mapZoomendFunction(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Delete an icon.
	 */
	var iconClickFunction = function(marker, point) {
		try {
			log.funcStackPush('iconClickFunction()');
	
			if(confirm('Do you really want to remove this icon?')) {
				log.major('Removing Icon');
/*
				changingRowNum = findRowByLatLng(marker); // global variable
				log.minor('changingRowNum=' + changingRowNum);
				updateRowDelete(); // click the delete checkbox
				log.minor('row updated');
				submitForm('MapIconTableSubmit');
				log.minor('form submitted');
*/
				var newRow = findLastRow(miTBL);
				log.minor('newRow: ' + newRow);
				var baseNext = miTBL['pre'] + (((newRow+1)<10) ? '0' + (newRow+1) : (newRow+1)) + miTBL['post'];
				idStr = baseNext + 'MasterMapID';
				var newMapID = document.getElementById(idStr).value;
				ajax('DeleteIcon.aspx?MapID=' + newMapID + '&Latitude=' + marker.y + '&Longitude=' + marker.x);
		
				// remove the icon from the screen
				log.major('refreshing the page');
				log.funcStackPop();
				window.location.href = window.location.href;
			}
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in iconClickFunction(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Delete an icon.
	 */
	var mapAddOverlayFunction = function(marker, point) {
		try {
			log.funcStackPush('mapAddOverlayFunction()');
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in mapAddOverlayFunction(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Set the global variable 'startMarker' to the icon being dragged.
	 */
	var iconDragstartFunction = function(marker, point) {
		try {
			log.funcStackPush('iconDragstartFunction()');
	
			startMarker = marker; // global variable
			/*if(startMarker < 0)
				alert('Row not found');*/
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in iconDragstartFunction(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Update the information of the dragged icon.
	 */
	var iconDragendFunction = function(marker, point) {
		try {
			log.funcStackPush('iconDragendFunction()');
	
			/* updateRowLatLng(marker);
			submitForm('MapIconTableSubmit');*/
			
			var newRow = findLastRow(miTBL);
			log.minor('newRow: ' + newRow);
			var baseNext = miTBL['pre'] + (((newRow+1)<10) ? '0' + (newRow+1) : (newRow+1)) + miTBL['post'];
			idStr = baseNext + 'MasterMapID';
			var newMapID = document.getElementById(idStr).value;
			ajax('UpdateIcon.aspx?MapID=' + newMapID + '&Latitude=' + startMarker.y + '&Longitude=' + startMarker.x + '&newLatitude=' + marker.y  + '&newLongitude=' + marker.x );
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in iconDragendFunction(): ' + e + '\n' + log.funcStackPrint());
	    }
	}
	
	/**
	 * Sets the global variable mapCenter to the point found or alerts the user to the failure.
	 */
	var GeocoderCallbackFunction = function(point) {
		try {
			log.funcStackPush('GeocoderCallbackFunction()');
	
			if (!point) {
				alert('To access your map, you must first create your School profile.'
						+'\n'+'Please see "Edit Profile" section to do this.');
				return;
			} else {
				log.all('Point: '+point);
				mapCenter = point;
				log.all('mapCenter: '+mapCenter);
			}
		
			var lat = document.getElementById("MapRecordLatitude");
			var lng = document.getElementById("MapRecordLongitude");
		
			lat.value = mapCenter.lat();
			lng.value = mapCenter.lng();
		
			submitForm("MapRecordSubmit");
	
			log.funcStackPop();
	    } catch (e) {
	    	alert('Unexpected error in GeocoderCallbackFunction(): ' + e + '\n' + log.funcStackPrint());
	    }
	}


	try {
		var BrowserDetect = {
			init: function () {
				this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
				this.version = this.searchVersion(navigator.userAgent)
					|| this.searchVersion(navigator.appVersion)
					|| "an unknown version";
				this.OS = this.searchString(this.dataOS) || "an unknown OS";
			},
			searchString: function (data) {
				for (var i=0;i<data.length;i++)	{
					var dataString = data[i].string;
					var dataProp = data[i].prop;
					this.versionSearchString = data[i].versionSearch || data[i].identity;
					if (dataString) {
						if (dataString.indexOf(data[i].subString) != -1)
							return data[i].identity;
					}
					else if (dataProp)
						return data[i].identity;
				}
			},
			searchVersion: function (dataString) {
				var index = dataString.indexOf(this.versionSearchString);
				if (index == -1) return;
				return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
			},
			dataBrowser: [
				{
					string: navigator.userAgent,
					subString: "Chrome",
					identity: "Chrome"
				},
				{ 	string: navigator.userAgent,
					subString: "OmniWeb",
					versionSearch: "OmniWeb/",
					identity: "OmniWeb"
				},
				{
					string: navigator.vendor,
					subString: "Apple",
					identity: "Safari",
					versionSearch: "Version"
				},
				{
					prop: window.opera,
					identity: "Opera"
				},
				{
					string: navigator.vendor,
					subString: "iCab",
					identity: "iCab"
				},
				{
					string: navigator.vendor,
					subString: "KDE",
					identity: "Konqueror"
				},
				{
					string: navigator.userAgent,
					subString: "Firefox",
					identity: "Firefox"
				},
				{
					string: navigator.vendor,
					subString: "Camino",
					identity: "Camino"
				},
				{		// for newer Netscapes (6+)
					string: navigator.userAgent,
					subString: "Netscape",
					identity: "Netscape"
				},
				{
					string: navigator.userAgent,
					subString: "MSIE",
					identity: "Explorer",
					versionSearch: "MSIE"
				},
				{
					string: navigator.userAgent,
					subString: "Gecko",
					identity: "Mozilla",
					versionSearch: "rv"
				},
				{ 		// for older Netscapes (4-)
					string: navigator.userAgent,
					subString: "Mozilla",
					identity: "Netscape",
					versionSearch: "Mozilla"
				}
			],
			dataOS : [
				{
					string: navigator.platform,
					subString: "Win",
					identity: "Windows"
				},
				{
					string: navigator.platform,
					subString: "Mac",
					identity: "Mac"
				},
				{
					   string: navigator.userAgent,
					   subString: "iPhone",
					   identity: "iPhone/iPod"
			    },
				{
					string: navigator.platform,
					subString: "Linux",
					identity: "Linux"
				}
			]
		
		};
	
		BrowserDetect.init();
	
		log.funcStackPop();
	} catch (e) {
		alert('Unexpected error initializing BrowserDetect: ' + e + '\n' + log.funcStackPrint());
	}


	try {
	
		function ajax(url) {
		
		    var xmlHttp = getXMLHttpRequest();
		    if(xmlHttp) {
		        xmlHttp.onreadystatechange = function() {
		            if(xmlHttp.readyState == 4) {
//		                alert(xmlHttp.responseText);
		            }
		        }
		    }
		
		    xmlHttp.open("GET",url,true);
		    xmlHttp.send(null);
		}
		
		function getXMLHttpRequest() {
		    var xmlHttp;
		    try {
		        // Firefox, Opera 8.0+, Safari
		        xmlHttp = new XMLHttpRequest();
		    } catch (e) {
		        // Internet Explorer
		        try {
		            xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
		        } catch (e) {
		            try {
		                xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
		            } catch (e) {
						alert("Your browser does not support AJAX!");
						return false;
		            }
		        }
		    }
		    return xmlHttp;
		}	

	} catch (e) {
		alert('Unexpected error initializing Ajax: ' + e + '\n' + log.funcStackPrint());
	}

} catch (e) {
	alert('Unexpected error initializing GMapLocal.js: ' + e + '\n' + log.funcStackPrint());
}
