/*
 * Compiled Fri May  4 15:13:36 CDT 2007 with the GeoMOOSE compiler.
 */

var CSV_DELIMITER = ',';
var CSV_ENCLOSURE = '"';

function ParseCSV(str) {
	var rows = new Array();
	var length = str.length;


	var quoted = false;

	var row = 0, col = 0;
	rows[0] = new Array();
	for(var i = 0; i < length; i++) {
		var ch = str.substring(i,i+1);
		var nextCh = str.substring(i+1,i+2);
		if(!rows[row][col]) {
			rows[row][col] = '';
		}
		if(ch.match(/\n/) && !quoted) {
			col = 0;
			row++;
			rows[row] = new Array();
		} else if(ch == CSV_ENCLOSURE && !quoted) {
			quoted = true;
		} else if(ch == CSV_ENCLOSURE && quoted && nextCh == CSV_DELIMITER) {
			// Do nothing
			quoted = false;
		} else if(ch == CSV_DELIMITER && !quoted) {
			col++;
			quoted = false;
		} else if(quoted && ch == CSV_ENCLOSURE && nextCh == CSV_ENCLOSURE) {
			rows[row][col]+=CSV_ENCLOSURE;
			i++;
		} else {
			rows[row][col]+=ch;
		}
	}
	return rows;
}
/*
 * Processes MAP_COORDINATE_SYSTEM and loads a javascript file to deal with it.
 */

var CoordinateSystemFiles = new Array();
CoordinateSystemFiles['UTM'] = 'projections/utm.js';
CoordinateSystemFiles['MNCOUNTY'] = 'projections/mncounty.js';

function loadCoordinateSystem() {
	var CoordSys = new String(MAP_COORDINATE_SYSTEM).split('-');
	var theHead = document.getElementsByTagName('head')[0];
	var scriptTag =document.createElement('script');
	scriptTag.src = CoordinateSystemFiles[CoordSys[0]];
	theHead.appendChild(scriptTag);
}

loadCoordinateSystem();
function showMenu(parent,submenu) {
	submenu.style.visibility = 'visible';
	submenu.style.left = parent.offsetLeft + 'px';
	if(document.all) {
		var trueTop = 0;
		var sumNode = parent;
		while(sumNode.offsetParent) {
			trueTop += sumNode.offsetTop;
			sumNode = sumNode.offsetParent;
		}
		submenu.style.top = trueTop + parent.offsetHeight + 'px';
	} else {
		submenu.style.top = parent.offsetTop + parent.offsetHeight + 'px';
	}
}

function menuIsVisible(submenu) {
	if(submenu.style.visibility == 'visible') return true;
	return false;
}

function showSubMenu(parent, submenu) {
	submenu.style.visibility = 'visible';
	submenu.style.left = parent.offsetLeft + parent.offsetWidth + 'px';
	submenu.style.top = parent.offsetTop + 'px';
}

function onoverShowSubMenu() {
	showSubMenu(this, this.getElementsByTagName('ul')[0]);
}

function hideMenu(submenu) {
	if(!submenu) { submenu = this.submenu; }
	if(submenu) {
		submenu.style.visibility = 'hidden';
	}
}

function toggleCheckMark() {
	var checkMark = this.checkMark;
	if(checkMark.style.width == '0px') {
		checkMark.style.width = 'auto';
	} else {
		checkMark.style.width = '0px';
	}
}

function hideDoubleParent() {
	hideMenu(this.parentNode.parentNode);
}

function hideFirstUL() {
	hideMenu(this.getElementsByTagName('ul')[0]);
}

var MENUBAR_INSTANCE = 0;
function Menubar() {
	var Tree;
	var Parent;
	var MenuId = 'Menu'+MENUBAR_INSTANCE;
	MENUBAR_INSTANCE++;

	this.createBranch = function(xml) {
		var items = xml.getElementsByTagName('item');	
		var branch = document.createElement('ul');
		branch.className = 'submenu';
		for(var i = 0; i < items.length; i++) {
			if(items[i].parentNode == xml) {
				var item = document.createElement('li');
				var itemLink = document.createElement('a');

				branch.appendChild(item);

				if(items[i].getAttribute('href')) {
					itemLink.href = items[i].getAttribute('href');
				} else {
					itemLink.href = '#';
				}

				if(items[i].getAttribute('target')) {
					itemLink.target = items[i].getAttribute('target');
				}

				if(items[i].getAttribute('id')) {
					itemLink.id = items[i].getAttribute('id');
				}
				itemLink.onclick = hideDoubleParent;
				itemLink.appendChild(document.createTextNode(items[i].getAttribute('title')));
				item.appendChild(itemLink);
				itemLink.style.marginLeft = '22px';
				if(items[i].getAttribute('toggle') && items[i].getAttribute('toggle').match(/true/i)) {
					/*var checkBox = document.createElement('input');
					checkBox.type = 'checkbox';
					checkBox.disabled = true;
					checkBox.style.position = 'absolute';
					checkBox.style.right = '2px';
					itemLink.appendChild(checkBox);
					if(items[i].getAttribute('default') && items[i].getAttribute('default').match(/true/i)) {
						checkBox.defaultChecked = true;
					} else {
						checkBox.defaultChecked = false;
					}
					itemLink.onclick = function () {
						checkBox.checked = !checkBox.checked;
					}*/
					var checkMark = document.createElement('img');
					checkMark.src = MENU_TOGGLE_ICON;
					checkMark.style.verticalAlign = 'middle';
					checkMark.style.position = 'absolute';
					checkMark.style.left = '2px';
					//checkMark.style.marginTop = '4px';
					if(items[i].getAttribute('default') && items[i].getAttribute('default').match(/true/i)) {
						checkMark.style.width = 'auto';;
					} else {
						checkMark.style.width = '0px';
					}
					itemLink.checkMark = checkMark;
					itemLink.onclick = toggleCheckMark;
					itemLink.appendChild(checkMark);
				}

				
				var submenus = items[i].getElementsByTagName('item');
				for(var sb = 0; sb <  submenus.length; sb++) {
					var subBranch = this.createBranch(items[i]);
					item.appendChild(subBranch);
					
/*					item.onmouseover = function() {
						showSubMenu(this, this.getElementsByTagName('ul')[0]);
					}
					item.onmouseout = function() {
						hideMenu(this.getElementsByTagName('ul')[0]);
					}*/
					item.onmouseover = onoverShowSubMenu;
					item.onmouseout = hideFirstUL;
				}
			
				if(document.all) {
					//menuElements[menuElements.length] = item;
				}
			}
		}
		return branch;

	}

	//var menuElements = new Array();

	var MySelf = this;

	function rootHideAll(event) {
		if(window.event) { event = window.event; }
		var allMenus = this.getElementsByTagName('ul');
		for(var m = 0; m < allMenus.length; m++) {
			hideMenu(allMenus[m]);
		}

	}

	this.populateMenuBar = function(xml) {
		var root = document.createElement('ul');
		root.className = 'menubar';
		root.hideAll = rootHideAll;
		root.setAttribute('id',MenuId);

		var menus = xml.getElementsByTagName('menu');
		for(var m = 0; m < menus.length; m++) {
			var menu = document.createElement('li');
			root.appendChild(menu);
			menu.className = 'menu';

			var menuLink = document.createElement('a');
			menu.appendChild(menuLink);
			menuLink.appendChild(document.createTextNode(menus[m].getAttribute('title')));

			if(menus[m].getAttribute('href')) {
				menuLink.href = menus[m].getAttribute('href');
			} else {
				menuLink.href = '#';
			}
			
			if(menus[m].getAttribute('target')) {
				menuLink.target = menus[m].getAttribute('target');
			}

			if(menus[m].getAttribute('id')) {
				menuLink.id = menus[m].getAttribute('id');
			}

			var branch = this.createBranch(menus[m]);
			menu.appendChild(branch);
			branch = null;
			function focusFunction(event) {
				if(window.event) { event = window.event; }
				if(this.getElementsByTagName('ul')[0]) {
					var subItems = this.getElementsByTagName('ul')[0].getElementsByTagName('li');
					if(subItems.length > 0) {
						showMenu(this, this.getElementsByTagName('ul')[0]);
					}
				}
			}

			menu.onfocus = focusFunction;
			menu.onblur = hideFirstUL; 
			
			if(document.all) {
				function f() {
					this.parentNode.hideAll();
					this.onfocus();
				}
				menu.onclick = f;
				//menuElements[menuElements.length] = menu;
			}
			menu = null;


		}

		if(document.all) {
			document.attachEvent('onclick',IEonBlurSim);
		}
		if(Parent) {
			document.getElementById(Parent).appendChild(root);
		}
		root = null;
	}
	
	function IEonBlurSim(event) {
		if(window.event) { event=  window.event; }
		var menuItem = event.srcElement.parentNode;
		var menuElements = document.getElementById(Parent).getElementsByTagName('li');
		for(var i = 0; i < menuElements.length; i++) {
			if(menuItem != menuElements[i]) {
				if(menuElements[i].onblur) {
					menuElements[i].onblur();
				}
			}
		}
	}
	
	this.setParent = function(p) {
		Parent = p;
		if(Tree) {
			document.getElementById(Parent).appendChild(Tree);
		}
	}
}
/*
City of Saint Paul Open Source License

LICENSE

Copyright © 2006 City of Saint Paul, Minnesota. 

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following terms and conditions:

1.The above copyright notice and this permission notice shall be included in all copies of this Software or works derived from this Software.
2.Whenever a Recipient distributes or redistributes the Software, a copy of this agreement must be included with each copy of the Software
3.If Recipient distributes or redistributes the Software in any form other than source code, Recipient must also make the source code freely available, and must provide with each copy of the Software information on how to obtain the source code in a reasonable manner on or through a medium customarily used for software exchange
4.A Recipient may not make any representation in the Software or in any promotional, advertising or other material that may be construed as an endorsement by the City of Saint Paul of any product or service provided by Recipient, or that may seek to obtain commercial advantage by the fact of the City of Saint Paul’s participation in this Agreement.

DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION

No Warranty:  The Software is provided “as is” without any warranty of any kind, either expressed, implied, or statutory, including, but not limited to, any warranty that the Software will conform to specifications, any implied warranties of merchantability, fitness for a particular purpose, or freedom from infringement, any warranty that the Software will be error free, or any warranty that documentation, if provided, will conform to the Software. This agreement does not, in any manner, constitute an endorsement by the City of Saint Paul or any prior recipient of any results, resulting designs, hardware, software products or any other applications resulting from use of the Software. Further, the City of Saint Paul disclaims all warranties and liabilities regarding third-party software, if present in the original Software, and distributes it “as is.”

Waiver and Indemnity:  Recipient agrees to waive any and all claims against the City of Saint Paul, its contractors and subcontractors, as well as any prior recipient.  If recipient's use of the Software results in any liabilities, demands, damages, expenses or losses arising from such use, including any damages from products based on, or resulting from, recipient's use of the Software, recipient shall indemnify and hold harmless the City of Saint Paul, it’s contractors and subcontractors, as well as any prior recipient, to the extent permitted by law. Recipient's sole remedy for any such matter shall be the immediate, unilateral termination of this agreement.

SUPPORT STATEMENT 

The Software is freely provided by the City of Saint Paul as a public service.  The recipient is herby notified that the provision and distribution of the Software should in no way be construed to mean that the City of Saint Paul is obligated to provide any level of support for the Software, now or in the future.  Support in this context shall be construed to mean providing coordination, technical assistance, responding to questions, releasing bug fixes, providing future releases, or otherwise obligating the City of Saint Paul to commit resources to maintain this Software or assist in its implementation by any person or entity.  The City of Saint Paul may, at it’s sole option, and without notice or obligation to the recipients, provide varying levels of support for the Software or terminate the agreement.  Provision of such support shall not alter the condition that the Software is provided without support as stated above.
*/

/*
 * Moose Debugging Layer
 */

var DebuggerWindow = null;

var DebuggerQueuedMessages = new Array();

function startDebugger(func) {
	DebuggerWindow = window.open('libs/Debugger.html', 'Debugger', 'width=640,height=480,toolbar=0,resizable=1,status=0,menubar=0,location=0,scrollbars=0');
	//DebuggerWindow = window.open();

	DebuggerWindow._onload = onload;
	DebuggerWindow.onload = function() {
		this._onload();
		func();
	}

	window._alert = window.alert;
	window.alert = function(msg) {
		logDebugMessage('Alert',true,msg);
		window._alert(msg);
	}
	window._confirm = window.confirm;
	window.confirm = function(msg) {
		var result = window._confirm(msg);
		logDebugMessage('Confirm',false,msg+' '+result);
		return result;
	}
}


function clearDebuggerLog() {
	var log = DebuggerWindow.document.getElementById('log');
	var p = log.parentNode;
	p.removeChild(log);

	var winBody = DebuggerWindow.document.getElementsByTagName('body')[0];
	var logger = DebuggerWindow.document.createElement('div');
	logger.id = 'log';
	winBody.appendChild(logger);


}

function logDebugMessage(unit, severe, msg, link) {
	if(DebuggerWindow == null || DebuggerWindow.closed) return false;

	//var time = new Date();
	//unit = time.getHours()+':'+time.getMinutes()+':'+time.getSeconds()+'.'+time.getMilliseconds()+' '+unit;
	var doc = DebuggerWindow.document;
	var message;
	var message = doc.createElement('a');
	message.innerHTML = unit+': '+msg;
	if(severe) {
		message.style.fontWeight = 'bold';
	}

	if(link) {
		message.href = link;
		message.target = '_blank';
	}
	message.appendChild(doc.createElement('br'));
	if(DebuggerWindow.ready) {
		var debugBody = doc.getElementById('log');
		if(DebuggerQueuedMessages.length > 0) {
			for(var i = 0; i < DebuggerQueuedMessages.length; i++) {
//				debugBody.appendChild(DebuggerQueuedMessages[i]);
				debugBody.insertBefore(DebuggerQueuedMessages[i], debugBody.firstChild);
			}
			DebuggerQueuedMessages = new Array();
		}
		debugBody.insertBefore(message, debugBody.firstChild);
	} else {
		DebuggerQueuedMessages[DebuggerQueuedMessages.length] = message;
	}
}

function stopDebugger() {
	DebuggerWindow.close();
	DebuggerWindow = null;
}

/*
City of Saint Paul Open Source License

LICENSE

Copyright © 2006 City of Saint Paul, Minnesota. 

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following terms and conditions:

1.The above copyright notice and this permission notice shall be included in all copies of this Software or works derived from this Software.
2.Whenever a Recipient distributes or redistributes the Software, a copy of this agreement must be included with each copy of the Software
3.If Recipient distributes or redistributes the Software in any form other than source code, Recipient must also make the source code freely available, and must provide with each copy of the Software information on how to obtain the source code in a reasonable manner on or through a medium customarily used for software exchange
4.A Recipient may not make any representation in the Software or in any promotional, advertising or other material that may be construed as an endorsement by the City of Saint Paul of any product or service provided by Recipient, or that may seek to obtain commercial advantage by the fact of the City of Saint Paul’s participation in this Agreement.

DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION

No Warranty:  The Software is provided “as is” without any warranty of any kind, either expressed, implied, or statutory, including, but not limited to, any warranty that the Software will conform to specifications, any implied warranties of merchantability, fitness for a particular purpose, or freedom from infringement, any warranty that the Software will be error free, or any warranty that documentation, if provided, will conform to the Software. This agreement does not, in any manner, constitute an endorsement by the City of Saint Paul or any prior recipient of any results, resulting designs, hardware, software products or any other applications resulting from use of the Software. Further, the City of Saint Paul disclaims all warranties and liabilities regarding third-party software, if present in the original Software, and distributes it “as is.”

Waiver and Indemnity:  Recipient agrees to waive any and all claims against the City of Saint Paul, its contractors and subcontractors, as well as any prior recipient.  If recipient's use of the Software results in any liabilities, demands, damages, expenses or losses arising from such use, including any damages from products based on, or resulting from, recipient's use of the Software, recipient shall indemnify and hold harmless the City of Saint Paul, it’s contractors and subcontractors, as well as any prior recipient, to the extent permitted by law. Recipient's sole remedy for any such matter shall be the immediate, unilateral termination of this agreement.

SUPPORT STATEMENT 

The Software is freely provided by the City of Saint Paul as a public service.  The recipient is herby notified that the provision and distribution of the Software should in no way be construed to mean that the City of Saint Paul is obligated to provide any level of support for the Software, now or in the future.  Support in this context shall be construed to mean providing coordination, technical assistance, responding to questions, releasing bug fixes, providing future releases, or otherwise obligating the City of Saint Paul to commit resources to maintain this Software or assist in its implementation by any person or entity.  The City of Saint Paul may, at it’s sole option, and without notice or obligation to the recipients, provide varying levels of support for the Software or terminate the agreement.  Provision of such support shall not alter the condition that the Software is provided without support as stated above.
*/

/*
 * Useful Javascript Functions
 */

function trim(str) {
	while(str.substring(0,1).match(/\s/)) {
		str = str.substring(1, str.length);
	}
	while(str.substring(str.length-1,1).match(/\s/)) {
		str = str.substring(0, str.length-1);
	}
	str = str.replace(/\x0A/,'');
	return str;
}
function doNadda() { return false; }


function clearSelection () {
	if (document.selection)
	document.selection.empty();
	else if (window.getSelection)
	window.getSelection().removeAllRanges();
}

function roundUp(n) {
	var noDecimals = Math.round(n);
	if(n - noDecimals > 0) {
		noDecimals++;
	}
	return noDecimals;
}

/*
 * Truncates a number to a specific number of digits
 */
function round(number,X) {
	X = (!X ? 2 : X);

	var n = new String(number);
	var loc = n.indexOf('.');
	var whole = n.substring(0,loc);
	var decimal = n.substring(loc+1, loc+1+X);
	return (whole+'.'+decimal);
}

function getKeyCode(event) {
	var KeyString = '';
	if(event.ctrlKey) {
		KeyString = KeyString + 'CTRL+';
	}
	if(event.shiftKey) {
		KeyString = KeyString + 'SHIFT+';
	}
	var key;
	if(event.which) {
		key = event.which;
	}
	if(event.keyCode) {
		key = event.keyCode;
	}
	return KeyString = KeyString + String.fromCharCode(key);
}


function stripCommas(v) {
	v = new String(v);
	while(v.match(/\,/)) {
		v = v.replace(',','');
	}
	return v;
}

function commifyNumber(number) {
	var numberOrig = new String(number);
	var numberArr = new String(number).split('.');
	number = numberArr[0];
 number = '' + number;
 if (number.length > 3) {
  var mod = number.length % 3;
  var output = (mod > 0 ? (number.substring(0,mod)) : '');
  for (i=0 ; i < Math.floor(number.length / 3); i++) {
   if ((mod == 0) && (i == 0))
    output += number.substring(mod+ 3 * i, mod + 3 * i + 3);
   else
    output+= ',' + number.substring(mod + 3 * i, mod + 3 * i + 3);
  }
 } else {
   output=number;
 }
 if(numberOrig.match('.') && numberArr[1]) {
 	output = output + '.' + numberArr[1];
 }
 return output;
}


/*
 * Ground Unit Translations
 */
var UNITS_STRINGS = new Array();
UNITS_STRINGS['M'] = 'Meters';
UNITS_STRINGS['IN'] = 'Inches';
UNITS_STRINGS['MI'] = 'Miles';
UNITS_STRINGS['FT'] = 'Feet';

var UNITS_SQ_STRINGS = new Array();
UNITS_SQ_STRINGS['SQIN'] = 'Square Inches';
UNITS_SQ_STRINGS['SQFT'] = 'Square Feet';
UNITS_SQ_STRINGS['SQMI'] = 'Square Miles';
UNITS_SQ_STRINGS['SQM'] = 'Square Meters';
UNITS_SQ_STRINGS['SQACRE'] = 'Acres';

var UNIT_CONVERSION = new Array();

UNIT_CONVERSION['IN'] = new Array();
UNIT_CONVERSION['IN']['FT'] = 0.083333333; // f(x) where x = inches and f(x) = feet
UNIT_CONVERSION['IN']['MI'] = 0.000015783;
UNIT_CONVERSION['IN']['M'] = 0.0254;
UNIT_CONVERSION['IN']['IN'] = 1;

UNIT_CONVERSION['FT'] = new Array();
UNIT_CONVERSION['FT']['IN'] = 12;
UNIT_CONVERSION['FT']['MI'] = 0.000189394;
UNIT_CONVERSION['FT']['M'] = 0.3048;
UNIT_CONVERSION['FT']['FT'] = 1;

UNIT_CONVERSION['MI'] = new Array();
UNIT_CONVERSION['MI']['IN'] = 63360;
UNIT_CONVERSION['MI']['FT'] = 5280;
UNIT_CONVERSION['MI']['M'] = 1609.344;
UNIT_CONVERSION['MI']['MI'] = 1;

UNIT_CONVERSION['M'] = new Array();
UNIT_CONVERSION['M']['FT'] = 3.280839895;
UNIT_CONVERSION['M']['MI'] = 0.000621371;
UNIT_CONVERSION['M']['IN'] = 39.37007874;
UNIT_CONVERSION['M']['M'] = 1;

UNIT_CONVERSION['SQIN'] = new Array();
UNIT_CONVERSION['SQIN']['SQIN'] = 1;
UNIT_CONVERSION['SQIN']['SQFT'] = 0.006944;
UNIT_CONVERSION['SQIN']['SQMI'] = .000000000249;
UNIT_CONVERSION['SQIN']['SQM'] = 0.000645;
UNIT_CONVERSION['SQIN']['SQACRE'] = 0.000000159422;

UNIT_CONVERSION['SQFT'] = new Array();
UNIT_CONVERSION['SQFT']['SQIN'] = 144;
UNIT_CONVERSION['SQFT']['SQFT'] = 1;
UNIT_CONVERSION['SQFT']['SQMI'] = .0000000358;
UNIT_CONVERSION['SQFT']['SQM'] = 0.092903;
UNIT_CONVERSION['SQFT']['SQACRE'] = 0.000022957;

UNIT_CONVERSION['SQMI'] = new Array();
UNIT_CONVERSION['SQMI']['SQIN'] = 4014489600;
UNIT_CONVERSION['SQMI']['SQFT'] = 27878288.486594;
UNIT_CONVERSION['SQMI']['SQMI'] = 1;
UNIT_CONVERSION['SQMI']['SQM'] = 2589988.110336;
UNIT_CONVERSION['SQMI']['SQACRE'] = 640;

UNIT_CONVERSION['SQM'] = new Array();
UNIT_CONVERSION['SQM']['SQIN'] = 1550.0031;
UNIT_CONVERSION['SQM']['SQFT'] = 10.763867;
UNIT_CONVERSION['SQM']['SQMI'] = .0000003861;
UNIT_CONVERSION['SQM']['SQM'] = 1;
UNIT_CONVERSION['SQM']['SQACRE'] = 0.000247105;

UNIT_CONVERSION['SQACRE'] = new Array();
UNIT_CONVERSION['SQACRE']['SQIN'] = 6272614.883;
UNIT_CONVERSION['SQACRE']['SQFT'] =  43559.825;
UNIT_CONVERSION['SQACRE']['SQMI'] = 0.0015625;
UNIT_CONVERSION['SQACRE']['SQM'] = 4046.856;
UNIT_CONVERSION['SQACRE']['SQACRE'] = 1;

/*
 * Conversion Dropdown population 
 */
function populateUnitConversionSelection(mType, obj) {
	var selectedArray;
	if(mType == 'area') {
		selectedArray = UNITS_SQ_STRINGS;
	} else {
		selectedArray = UNITS_STRINGS;
	}
	var options = obj.getElementsByTagName('option');
	for(var i = options.length - 1; i >= 0; i--) {
		obj.removeChild(options[i]);
	}
	
	for(var unit in selectedArray) {
		var opt = document.createElement('option');
		opt.value = unit;
		if(unit == MAP_GROUND_UNITS || unit == 'SQ'+MAP_GROUND_UNITS) {
			opt.setAttribute('selected', 'true');
			opt.selected = true;
		}
		opt.appendChild(document.createTextNode(selectedArray[unit]));
		obj.appendChild(opt);
		opt = null;
	}
	obj = null;
}

/*
 * Scale conversions that use the UNIT_CONVERSION table.
 */
function pixelScaleToInchScale(scMap, v) {
	var w = scMap.getMapSize().x;
	return ((v*w)/(w-1))*MAP_PIXELS_PER_INCH*UNIT_CONVERSION[MAP_GROUND_UNITS]['IN'];
}

function inchScaleToPixelScale(scMap, v) {
	var w = scMap.getMapSize().x;
	return ((v*(w-1))/w)*(1/MAP_PIXELS_PER_INCH)*(UNIT_CONVERSION['IN'][MAP_GROUND_UNITS])+.000001;
}


function LegendObj(id, legend) {
	this.id = id;
	this.legend = legend;
}


function getEventTarget(e) {
	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ.nodeType == 3) // defeat Safari bug
		targ = targ.parentNode;
	return targ;
}


function objectClientPosition(obj) {
	var position = new Point(obj.offsetLeft, obj.offsetTop);
	var parent = obj.parentNode;

	while(parent && parent.tagName == 'DIV') {
		if(parent.offsetLeft) {
			position.x += parent.offsetLeft;
		}
		if(parent.offsetTop) {
			position.y += parent.offsetTop;
		}
		parent = parent.parentNode;
	}
	return position;
}

function preventDefaultEvent(event) {
	if(window.event) { event = window.event; }
	event.returnValue = false;
	if(event.preventDefault) {
		event.preventDefault();
	}
	return false;
}

/* 
 * attachEvent()
 * Use Gecko style eventname's
 */

function attachEvent(obj, eventname, workingFn) {
	if(!obj) { return false; }
	if(document.all) {
		obj.attachEvent('on'+eventname, workingFn);
	} else {
		var retValue = true;
		if(eventname == 'mousewheel') { eventname = 'DOMMouseScroll'; retValue = false; }
		obj.addEventListener(eventname, workingFn, retValue);
	}
	obj = null;
}

function removeEvent(obj, eventname, workingFn) {
	if(!obj) { return false; }
	if (document.all) {
		obj.detachEvent("on"+eventname, workingFn);
	} else {
		obj.removeEventListener(eventname, workingFn, true);
	}
	obj = null;
}

function getBrowser() {
	return new String(navigator.appName).toLowerCase();
}

function getBrowserVersion() {
	return parseInt(navigator.appVersion);
}

/* Used to create layer controls */
function createIcon(EventManager, src,eventTrigger,mapTitle) {
	var newIcon = document.createElement('img');
	newIcon.setAttribute('src', src);
	newIcon.style.verticalAlign = 'middle';
	newIcon.EventManager = EventManager;
	newIcon.eventTrigger= eventTrigger;
	newIcon.mapTitle = mapTitle;
	newIcon.onclick = triggerSelfEvent;
	return newIcon;
}
	
/*
City of Saint Paul Open Source License

LICENSE

Copyright © 2006 City of Saint Paul, Minnesota. 

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following terms and conditions:

1.The above copyright notice and this permission notice shall be included in all copies of this Software or works derived from this Software.
2.Whenever a Recipient distributes or redistributes the Software, a copy of this agreement must be included with each copy of the Software
3.If Recipient distributes or redistributes the Software in any form other than source code, Recipient must also make the source code freely available, and must provide with each copy of the Software information on how to obtain the source code in a reasonable manner on or through a medium customarily used for software exchange
4.A Recipient may not make any representation in the Software or in any promotional, advertising or other material that may be construed as an endorsement by the City of Saint Paul of any product or service provided by Recipient, or that may seek to obtain commercial advantage by the fact of the City of Saint Paul’s participation in this Agreement.

DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION

No Warranty:  The Software is provided “as is” without any warranty of any kind, either expressed, implied, or statutory, including, but not limited to, any warranty that the Software will conform to specifications, any implied warranties of merchantability, fitness for a particular purpose, or freedom from infringement, any warranty that the Software will be error free, or any warranty that documentation, if provided, will conform to the Software. This agreement does not, in any manner, constitute an endorsement by the City of Saint Paul or any prior recipient of any results, resulting designs, hardware, software products or any other applications resulting from use of the Software. Further, the City of Saint Paul disclaims all warranties and liabilities regarding third-party software, if present in the original Software, and distributes it “as is.”

Waiver and Indemnity:  Recipient agrees to waive any and all claims against the City of Saint Paul, its contractors and subcontractors, as well as any prior recipient.  If recipient's use of the Software results in any liabilities, demands, damages, expenses or losses arising from such use, including any damages from products based on, or resulting from, recipient's use of the Software, recipient shall indemnify and hold harmless the City of Saint Paul, it’s contractors and subcontractors, as well as any prior recipient, to the extent permitted by law. Recipient's sole remedy for any such matter shall be the immediate, unilateral termination of this agreement.

SUPPORT STATEMENT 

The Software is freely provided by the City of Saint Paul as a public service.  The recipient is herby notified that the provision and distribution of the Software should in no way be construed to mean that the City of Saint Paul is obligated to provide any level of support for the Software, now or in the future.  Support in this context shall be construed to mean providing coordination, technical assistance, responding to questions, releasing bug fixes, providing future releases, or otherwise obligating the City of Saint Paul to commit resources to maintain this Software or assist in its implementation by any person or entity.  The City of Saint Paul may, at it’s sole option, and without notice or obligation to the recipients, provide varying levels of support for the Software or terminate the agreement.  Provision of such support shall not alter the condition that the Software is provided without support as stated above.
*/

function Point(x, y) {
	this.x = x;
	this.y = y;

	this.type = 'point';
	
	this.fromString = function(str,delim) {
		var arr = str.split(delim);
		this.x = parseFloat(arr[0]);
		this.y = parseFloat(arr[1]);
	}

	this.toString = function(delim) {
		return this.x+delim+this.y;
	}

	this.equals = function(otherPoint) {
		return(this.x == otherPoint.x && this.y == otherPoint.y);
	}

	this.scale = function(s) {
		this.x = this.x * s;
		this.y = this.y * s;
	}

	this.clean = function() {
		this.x = parseFloat(this.x);
		this.y = parseFloat(this.y);
	}

	this.buffer = function(rad) {
		var newCirc = new Circle();
		newCirc.Center = new Point(this.x, this.y);
		newCirc.Radius = rad;
		return newCirc;
	}


	this.minX = function() {
		return x;
	}
	this.maxX = function() {
		return x;
	}

	this.minY = function() {
		return y;
	}

	this.maxY = function() {
		return y;
	}
}

function LineSegment(PointA, PointB) {
	this.pointA = PointA;
	this.pointB = PointB;

	this.type = 'line';
	
	this.slope = function() {
		return ((this.pointA.y - this.pointB.y) / (this.pointA.x - this.pointB.x));
	}

	this.eval = function(x) {
		if((x - this.pointA.x) == 0) { return this.pointA.y; }
		if((x - this.pointB.x) == 0) { return this.pointB.y; }
		return (this.slope() * (x - this.pointA.x) + this.pointA.y);
	}

	this.evalA = function () {
		return this.eval(this.pointA.x);
	}

	this.evalB = function () {
		return this.eval(this.pointB.x);
	}

	function returnGreater(A, B) {
		if(A > B) return A;
		return B;
	}

	function returnLesser(A, B) {
		if(A < B) return A;
		return B;
	}

	this.normalize = function() {
		if(this.pointA.x > this.pointB.x) {
			var t = this.pointA.x;
			this.pointA.x = this.pointB.x;
			this.pointB.x = t;
		}
		if(this.pointA.y > this.pointB.y) {
			var t = this.pointA.y;
			this.pointA.y = this.pointB.y;
			this.pointB.y = t;
		}

	}

	this.getLength = function() {
		if((this.pointA.x - this.pointB.x) == 0) { return this.maxY() - this.minY(); }
		if((this.pointA.y - this.pointB.y) == 0) { return this.maxX() - this.minX(); }
		return (Math.sqrt(Math.pow(this.getWidth(),2) + Math.pow(this.getHeight(),2)));
	}

	this.minX = function () {
		return returnLesser(this.pointA.x, this.pointB.x);
	}

	this.maxX = function () {
		return returnGreater(this.pointA.x, this.pointB.x);		
	}

	this.minY = function () {
		return returnLesser(this.pointA.y, this.pointB.y);
	}

	this.maxY = function () {
		return returnGreater(this.pointA.y, this.pointB.y);		
	}


	this.getWidth = function() {
		return this.maxX() - this.minX();
	}

	this.getHeight = function() {
		return this.maxY() - this.minY();
	}

	this.shift = function(x,y) {
		this.pointA.x = parseInt(this.pointA.x);
		this.pointA.y = parseInt(this.pointA.y);
		this.pointB.x = parseInt(this.pointB.x);
		this.pointB.y = parseInt(this.pointB.y);
		this.pointA.x += x;
		this.pointB.x += x;
		this.pointA.y += y;
		this.pointB.y += y;
	}

	this.scale = function(x, y) {
		this.pointA.x = this.pointA.x * x;
		this.pointA.y = this.pointA.y * y;
		this.pointB.x = this.pointB.x * x;
		this.pointB.y = this.pointB.y * y;
	}

	this.getMidPoint = function() {
		var oldMidX = (this.pointA.x + this.pointB.x) / 2;
		var oldMidY = this.eval(oldMidX);
		return new Point(oldMidX, oldMidY);
	}

	this.shiftMidPoint = function(newMidPoint) {
		var oldMidPoint = this.getMidPoint();
		this.shift(oldMidPoint.x-newMidPoint.x, oldMidPoint.y-newMidPoint.y);
	}

	this.getScaledPoint = function(fromLine, fromPoint) {
		var scaledPoint = new Point();
		scaledPoint.x = fromLine.getWidth() / (fromPoint.x - fromLine.minX());
		scaledPoint.x = (this.getWidth() / scaledPoint.x) + this.minX();
		scaledPoint.y = fromLine.getHeight() / (fromPoint.y - fromLine.minY());
		scaledPoint.y = (this.getHeight() / scaledPoint.y) + this.minY();
		return scaledPoint;
	}

	this.scaleAboutMidPoint = function(scale) {
		var oldMidPoint = this.getMidPoint();	
		this.scale(scale,scale);
		var newMidPoint = this.getMidPoint();
		this.shift((oldMidPoint.x-newMidPoint.x), oldMidPoint.y-newMidPoint.y);
	}

	this.toString = function(delim) {
		var retString = this.pointA.x+delim+this.pointA.y+delim+this.pointB.x+delim+this.pointB.y;
		return retString;
	}


	this.fromString = function(str, delim) {
		var coords = str.split(delim);
		for(var i = 0; i < coords.length; i++) {
			coords[i] = parseFloat(coords[i]);
		}
		this.pointA = new Point(coords[0], coords[1]);
		this.pointB = new Point(coords[2], coords[3]);
	}

	this.intersectEndPoint = function() {
	}
	
/*
this.intersects = function(testLine) {
		var minX = this.minX();
		var maxX = this.maxX();

		var testMinX = testLine.minX();
		var testMaxX = testLine.maxX();

		var minY = this.minY();
		var maxY = this.maxY();
		var testMinY = testLine.minY();
		var testMaxY = testLine.maxY();

		// This saves some time if the lines don't share a domain
		if(testMinX > maxX || testMaxX < minX) {
			return false;
		}

		// If the line is striaght up and down, it needs to be handled
		// In a special fashion, checking both the X and Y domain
		// using the end points.
		if((this.pointA.x - this.pointB.x) == 0) {
			var testLineY = testLine.eval(this.pointA.x);
			return (testLineY >= minY && testLineY <= maxY);
		}
		if(testMinX - testMaxX == 0) {
			return(testMinX >= minX && testMinX <= maxX);
		}

		// Do some domain normalization, narrows the test domain
		// to the smaller line segment.
		minX = returnGreater(testMinX, minX);
		maxX = returnLesser(testMaxX, maxX);


		return (
			(this.eval(minX) < testLine.eval(minX)) && (testLine.eval(maxX) <= this.eval(maxX))
			||
			(this.eval(minX) > testLine.eval(minX)) && (testLine.eval(maxX) >= this.eval(maxX))
		);

	}
*/

	this.intersects = function(testLine) {
		var X1 =  this.pointA.x;
		var Y1 =  this.pointA.y;
		var X2 =  this.pointB.x;
		var Y2 =  this.pointB.y;

		var X3 = testLine.pointA.x;
		var Y3 = testLine.pointA.y;
		var X4 = testLine.pointB.x;
		var Y4 = testLine.pointB.y;

		var dDenom = (((X2 - X1) * (Y4 - Y3)) - ((Y2 - Y1) * (X4 - X3)));

		if(dDenom == 0) {
			return false; // Line Segments are Parallel
		}
		var dSnum = (((Y1 - Y3) * (X4 - X3)) - ((X1 - X3) * (Y4 - Y3)));
		var dTnum = (((Y1 - Y3) * (X2 - X1)) - ((X1 - X3) * (Y2 - Y1)));

		var S = dSnum / dDenom;
		var T = dTnum / dDenom;
		var testS = (0 <= S && S <= 1);
		var testT = (0 <= T && T <= 1);

		return(testS && testT)
	}

	this.buffer = function(rad) {
		var slope = this.slope();
		var inverse_slope = -1 / slope;
		var x_shift = 0, y_shift = 0;

		if(!isFinite(slope)) {
			x_shift = rad;
		} else if(slope == 0) {
			y_shift = rad;
		} else {
			var theta = Math.atan(inverse_slope);
			x_shift = Math.round(Math.cos(theta) * rad);
			y_shift = Math.round(Math.sin(theta) * rad);
		}

		var newPoints = new Array();
		newPoints[0] = new Point(this.pointA.x + x_shift, this.pointA.y + y_shift);
		newPoints[1] = new Point(this.pointB.x + x_shift, this.pointB.y + y_shift);
		newPoints[2] = new Point(this.pointB.x - x_shift, this.pointB.y - y_shift);
		newPoints[3] = new Point(this.pointA.x - x_shift, this.pointA.y - y_shift);
		var pointsString = '';
		for(var i = 0; i < newPoints.length; i++) {
			pointsString += newPoints[i].toString(',')+' ';
		}

		var newLines = new Array();
		newLines[0] = new LineSegment(newPoints[0], newPoints[1]);
		newLines[1] = new LineSegment(newPoints[1], newPoints[2]);
		newLines[2] = new LineSegment(newPoints[2], newPoints[3]);
		newLines[3] = new LineSegment(newPoints[3], newPoints[0]);
		
		//logDebugMessage('Buffered Segment', true, pointsString);
		var newPoly = new Polygon();
		for(var i = 0; i < newLines.length; i++) {
			newPoly.addLine(newLines[i]);
		}
		//logDebugMessage('Buffered Segment', true, 'Lines: '+newPoly.toString(','));
		return newPoly;
	}
}


function Polygon() {
	this.Lines = new Array();

	this.origin = new Point(0,0);

	this.type = 'polygon';
	
	var MinX = 1000000, MaxX = -1, MinY = 1000000, MaxY = -1;

	this.addLine = function(line) {
		if(line.minX() < MinX) { MinX = line.minX(); }
		if(line.maxX() > MaxX) { MaxX = line.maxX(); }
		if(line.minY() < MinY) { MinY = line.minY(); }
		if(line.maxY() > MaxY) { MaxY = line.maxY(); }

		this.Lines[this.Lines.length] = line;
	}

	this.minX = function() {
		return MinX;
	}

	this.maxX = function() {
		return MaxX;
	}

	this.minY = function() {
		return MinY;
	}

	this.maxY = function() {
		return MaxY;
	}

	this.clearLines = function () {
		this.Lines = new Array();
	}

	this.countLines = function() {
		return this.Lines.length;
	}

	this.countIntersections = function(testLine) {
		var c = 0;
		for(var i = 0; i < this.Lines.length; i++) {
			if(this.Lines[i].intersects(testLine)) {
				c++;
			}
		}
		return c;
	}

	this.isMemberPoint = function(pointA) {
		if(pointA.x < MinX || pointA.x > MaxX) { return false; }
		if(pointA.y < MinY || pointA.y > MaxY) { return false; }
		var intersections = this.countIntersections(new LineSegment(new Point(0,pointA.y), pointA));
		var intersections2 = this.countIntersections(new LineSegment(new Point(0,0), pointA));
//		return((intersections % 2) == 1 || (intersections2 % 2) == 1);
//		return (intersections % 2 == 1);

		return((intersections % 2) == 1 && (intersections2 % 2) == 1);
	}

	this.intersectsPoly = function(poly) {
		var nIntersections = 0;
		var allLines = poly.Lines;
		for(var i = 0; i < allLines.length; i++) {
			nIntersections+=this.countIntersections(allLines[i]);
		}
		return nIntersections;
	}
	
	this.isMember = function(shape) {
		if(shape.type.match(/point/i)) {
			return this.isMemberPoint(shape);
		} else if(shape.type.match(/polygon/i)) {
			var points = shape.getPoints();
			for(var p = 0; p < points.length; p++) {
				if(this.isMemberPoint(points[p])) {
					return true;
				}
			}
			if(this.intersectsPoly(shape) > 0) {
				return true;
			} else {
				//logDebugMessage('Poly Test', true, 'Not Intersect');
			}
			return false;
		} else if(shape.type.match(/line/i)) {
			return(this.countIntersections(shape) > 0);
		}
	}

	
	this.buildFromPoints = function(pointsArray) {
		this.Lines = new Array();
		var lenLimit = (pointsArray.length % 2) + 1;
		for(var i = 0; i < pointsArray.length - 1; i++) {
			var newLineSeg = new LineSegment(pointsArray[i], pointsArray[i+1]);
			this.addLine(newLineSeg);
		}
		// Now Close the polygon
		var newLineSeg = new LineSegment(pointsArray[pointsArray.length-1], pointsArray[0]);
		this.addLine(newLineSeg);
	}

	this.toString = function(delim) {
		var myStr = '';
		for(var i = 0; i < this.Lines.length; i++) {
			myStr += this.Lines[i].pointA.toString(delim) + delim;
		}
		myStr += this.Lines[this.Lines.length-1].pointB.toString(delim);
		return myStr;
	}

	this.area = function() {
		var myPoints = this.getPoints();
		myPoints[myPoints.length ] = myPoints[0];
		var sum = 0;
		for(var i = 0; i < myPoints.length - 1; i++) {
			myPoints[i].clean(); myPoints[i+1].clean();
			sum += (myPoints[i].x*myPoints[i+1].y - myPoints[i+1].x*myPoints[i].y);
			//sum += ((myPoints[i].x - myPoints[i+1].x) * (myPoints[i].y + myPoints[i+1].y)) / 2;
		}
		sum *= .5;
		return sum;
	}

	this.centroid = function() {
		var myPoints = this.getPoints();
		myPoints[myPoints.length ] = myPoints[0];
		var area = 1 / (6 * this.area());
		var x = 0;
		var y = 0;
		for(var i = 0; i < myPoints.length -1; i++) {
			myPoints[i].clean(); myPoints[i+1].clean();
			var f = (myPoints[i].x*myPoints[i+1].y - myPoints[i+1].x*myPoints[i].y);
			x += ((myPoints[i].x + myPoints[i+1].x)*f);
			y += ((myPoints[i].y + myPoints[i+1].y)*f);
		}
		return new Point(x*area,y*area);
	}

	this.getPoints = function() {
		var retArr = new Array();
		for(var i = 0; i < this.Lines.length; i++) {
			retArr[retArr.length] = this.Lines[i].pointA;
		}
		return retArr;
	}

	this.buffer = function(rad) {
		var center = this.centroid();
		var bufferedPoints = new Array();
		var myPoints = this.getPoints();
		myPoints[myPoints.length] = myPoints[0];

		var elimBuffers = new Array();

		elimBuffers[0] = this;

		for(var i = 0; i < myPoints.length -1; i++) {
			var line = new LineSegment(myPoints[i], myPoints[i+1]);
			elimBuffers[elimBuffers.length] = line.buffer(rad);
			var circ = new Circle();
			circ.Center = myPoints[i];
			circ.Radius = rad;
			elimBuffers[elimBuffers.length] = circ;
		}
		return elimBuffers;
	}
}


function Circle(cCenter, cRadius) {
	this.Center = cCenter;
	this.Radius = cRadius;

	this.type = 'circle';

	this.isMemberPoint = function(P) {
		var line = new LineSegment(this.Center, P);
		var len = line.getLength();
		return(len < this.Radius);
	}

	function square(x) {
		return (x*x);
	}


	this.minX = function() {
		return (this.Center.x - this.Radius);
	}

	this.maxX = function() {
		return(this.Center.x + this.Radius);
	}

	this.minY = function() {
		return (this.Center.y - this.Radius);
	}

	this.maxY = function() {
		return (this.Center.y + this.Radius);
	}

	this.isMember = function(Poly) {
		if(Poly.type.match(/point/i)) {
			return this.isMemberPoint(Poly);
		} else if(Poly.type.match(/circle/i)) {
			var testLine = new LineSegment(this.Center, Poly.Center);
			var totalLength = this.Radius + Poly.Radius;
			return(testLine.getLength() <= totalLength);
		} else if(Poly.type.match(/line/i)) {
			var x1 = Poly.pointA.x;
			var y1 = Poly.pointA.y;
			var x2 = Poly.pointB.x;
			var y2 = Poly.pointB.y;

			var x3 = this.Center.x;
			var y3 = this.Center.y;

			var r = parseFloat(this.Radius);
			var diffX = x2-x1;
			var diffY = y2-y1;
			var diffXSquared = square(diffX);
			var diffYSquared = square(diffY);
			var a = diffXSquared + diffYSquared;
			var b = 2* (diffX*(x1 - x3) + diffY*(y1 - y3));
			var c = square(x3) + square(y3) +  square(x1) + square(y1)  - 2* ( x3*x1 + y3*y1 ) - square(r);

			var det = b * b - 4 * a * c;


			if(det <= 0) { return false; }

			b = (x3 - x1)*(x2 - x1) + (y3 - y1)*(y2 - y1);
			//b = (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1);
			var u= b/a;

			//logDebugMessage('Determinate', false, u);
			return(0 <= u && u <= 1);
		} else if(Poly.type.match(/poly/i)) {
			var points = Poly.getPoints();
			var member = false;
			for(var i = 0; i < points.length && !member; i++) {
				if(this.isMemberPoint(points[i])) {
					return true;
				}
			}

			var theLines = Poly.Lines;
			for(var i = 0; i < theLines.length && !member; i++) {
				if(this.isMember(theLines[i])) {
					return true;
				}
			}
			return false;
		}
		return false;
	}

	this.toString = function(delim) {
		var PointA = new Point(this.Center.x - this.Radius, this.Center.y - this.Radius);
		var PointB = new Point(this.Center.x + this.Radius, this.Center.y + this.Radius);
		return(PointA.toString(delim)+delim+PointB.toString(delim));
	}

	this.buffer = function(rad) {
		var newCirc = new Circle();
		newCirc.Center = this.Center;
		newCirc.Radius = this.Radius + rad;
		return newCirc;
	}
}


/* 
 * This determines the window size.
 * I wrote this because there is differences between standard and quirks mode.
 */

function getWindowSize() {
  var myWidth = 0, myHeight = 0;
  if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    myWidth = window.innerWidth;
    myHeight = window.innerHeight;
  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    myWidth = document.documentElement.clientWidth;
    myHeight = document.documentElement.clientHeight;
  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    myWidth = document.body.clientWidth;
    myHeight = document.body.clientHeight;
  }

	var winSize = new Point(myWidth, myHeight);
	return winSize;
}
/*
City of Saint Paul Open Source License

LICENSE

Copyright © 2006 City of Saint Paul, Minnesota. 

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following terms and conditions:

1.The above copyright notice and this permission notice shall be included in all copies of this Software or works derived from this Software.
2.Whenever a Recipient distributes or redistributes the Software, a copy of this agreement must be included with each copy of the Software
3.If Recipient distributes or redistributes the Software in any form other than source code, Recipient must also make the source code freely available, and must provide with each copy of the Software information on how to obtain the source code in a reasonable manner on or through a medium customarily used for software exchange
4.A Recipient may not make any representation in the Software or in any promotional, advertising or other material that may be construed as an endorsement by the City of Saint Paul of any product or service provided by Recipient, or that may seek to obtain commercial advantage by the fact of the City of Saint Paul’s participation in this Agreement.

DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION

No Warranty:  The Software is provided “as is” without any warranty of any kind, either expressed, implied, or statutory, including, but not limited to, any warranty that the Software will conform to specifications, any implied warranties of merchantability, fitness for a particular purpose, or freedom from infringement, any warranty that the Software will be error free, or any warranty that documentation, if provided, will conform to the Software. This agreement does not, in any manner, constitute an endorsement by the City of Saint Paul or any prior recipient of any results, resulting designs, hardware, software products or any other applications resulting from use of the Software. Further, the City of Saint Paul disclaims all warranties and liabilities regarding third-party software, if present in the original Software, and distributes it “as is.”

Waiver and Indemnity:  Recipient agrees to waive any and all claims against the City of Saint Paul, its contractors and subcontractors, as well as any prior recipient.  If recipient's use of the Software results in any liabilities, demands, damages, expenses or losses arising from such use, including any damages from products based on, or resulting from, recipient's use of the Software, recipient shall indemnify and hold harmless the City of Saint Paul, it’s contractors and subcontractors, as well as any prior recipient, to the extent permitted by law. Recipient's sole remedy for any such matter shall be the immediate, unilateral termination of this agreement.

SUPPORT STATEMENT 

The Software is freely provided by the City of Saint Paul as a public service.  The recipient is herby notified that the provision and distribution of the Software should in no way be construed to mean that the City of Saint Paul is obligated to provide any level of support for the Software, now or in the future.  Support in this context shall be construed to mean providing coordination, technical assistance, responding to questions, releasing bug fixes, providing future releases, or otherwise obligating the City of Saint Paul to commit resources to maintain this Software or assist in its implementation by any person or entity.  The City of Saint Paul may, at it’s sole option, and without notice or obligation to the recipients, provide varying levels of support for the Software or terminate the agreement.  Provision of such support shall not alter the condition that the Software is provided without support as stated above.
*/

/*
 * I wrote this function as a generic function
 * to change the Opacity of different objects
 * It works in both Mozilla and IE.
 */

function setOpacity(elm, opaqueness) {
	/* If the opaqueness is 100% ,remove the filter  instead of setting it to 100 */
	if(opaqueness == '100' || opaqueness == '1') {
		if(typeof elm.style.filter == "string") {
			elm.style.filter = '';
		} else {
			elm.style.opacity = '';
		}

	} else {
		if(typeof elm.style.filter == "string") {
			elm.style.filter = "alpha(opacity="+opaqueness+")"; // IE
		} else {
			elm.style.opacity = '0.' + opaqueness; // The Standard
		}
	}
	elm = null;
}

function getOpacity(elm) {
	var styleString = '';
	if(typeof elm.style.filter == "string") { // IE!
		var str = elm.style.filter;
		str = str.replace('alpha(opacity=','');
		str = str.replace(')','');
		styleString = str;
	} else {
		var str = elm.style.opacity;
		var f = parseFloat(str);
		f = f * 100;
		styleString = f;
	}

	if(!styleString) {
		styleString = "100";
	}
	elm = null;

	return parseFloat(styleString);
}


function setOpacityById(id, opacity) {
	var check = document.getElementById(id);
	if(!check) { return false; }
	setOpacity(check, opacity);
}

function fadeObject(id, opacStart, opacEnd, millisec,remove) {
    var speed = Math.round(millisec / 100);
    var timer = 0;

    //determine the direction for the blending, if start and end are the same nothing happens
    if(opacStart > opacEnd) {
        for(i = opacStart; i >= opacEnd; i--) {
            setTimeout('setOpacityById("' + id + '",' + i + ')',(timer * speed));
            timer++;
        }
    } else if(opacStart < opacEnd) {
        for(i = opacStart; i <= opacEnd; i++)
            {
            setTimeout('setOpacityById("' + id + '",' + i + ')',(timer * speed));
            timer++;
        }
    }
    if(remove) {
    	timer++;
	setTimeout('removeObject("'+id+'")', (timer*speed));
    }
} 

function removeObject(id) {
	var obj = document.getElementById(id);
	if(obj) {
		var p = obj.parentNode;
		if(p) {
			p.removeChild(obj);
		}
		p = null;
	}
	obj = null;
}
/*
City of Saint Paul Open Source License

LICENSE

Copyright © 2006 City of Saint Paul, Minnesota. 

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following terms and conditions:

1.The above copyright notice and this permission notice shall be included in all copies of this Software or works derived from this Software.
2.Whenever a Recipient distributes or redistributes the Software, a copy of this agreement must be included with each copy of the Software
3.If Recipient distributes or redistributes the Software in any form other than source code, Recipient must also make the source code freely available, and must provide with each copy of the Software information on how to obtain the source code in a reasonable manner on or through a medium customarily used for software exchange
4.A Recipient may not make any representation in the Software or in any promotional, advertising or other material that may be construed as an endorsement by the City of Saint Paul of any product or service provided by Recipient, or that may seek to obtain commercial advantage by the fact of the City of Saint Paul’s participation in this Agreement.

DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION

No Warranty:  The Software is provided “as is” without any warranty of any kind, either expressed, implied, or statutory, including, but not limited to, any warranty that the Software will conform to specifications, any implied warranties of merchantability, fitness for a particular purpose, or freedom from infringement, any warranty that the Software will be error free, or any warranty that documentation, if provided, will conform to the Software. This agreement does not, in any manner, constitute an endorsement by the City of Saint Paul or any prior recipient of any results, resulting designs, hardware, software products or any other applications resulting from use of the Software. Further, the City of Saint Paul disclaims all warranties and liabilities regarding third-party software, if present in the original Software, and distributes it “as is.”

Waiver and Indemnity:  Recipient agrees to waive any and all claims against the City of Saint Paul, its contractors and subcontractors, as well as any prior recipient.  If recipient's use of the Software results in any liabilities, demands, damages, expenses or losses arising from such use, including any damages from products based on, or resulting from, recipient's use of the Software, recipient shall indemnify and hold harmless the City of Saint Paul, it’s contractors and subcontractors, as well as any prior recipient, to the extent permitted by law. Recipient's sole remedy for any such matter shall be the immediate, unilateral termination of this agreement.

SUPPORT STATEMENT 

The Software is freely provided by the City of Saint Paul as a public service.  The recipient is herby notified that the provision and distribution of the Software should in no way be construed to mean that the City of Saint Paul is obligated to provide any level of support for the Software, now or in the future.  Support in this context shall be construed to mean providing coordination, technical assistance, responding to questions, releasing bug fixes, providing future releases, or otherwise obligating the City of Saint Paul to commit resources to maintain this Software or assist in its implementation by any person or entity.  The City of Saint Paul may, at it’s sole option, and without notice or obligation to the recipients, provide varying levels of support for the Software or terminate the agreement.  Provision of such support shall not alter the condition that the Software is provided without support as stated above.
*/
function Draggable(objSrc, trigger, dirX, dirY, pixelOverride) {

	this.onDrop = function(trigger,obj) { }

	this.whileDrag = function(trigger,obj) {  return false; }

	var cursorPosition;
	var firstXY;
	
	var DragObj = this;

	function startDrag(event) {
		if(window.event) { event = window.event; event.which = event.button; }
		if(event.which > 1) {
			return false;
		}

//		var obj = getEventTarget(event).parentNode;
		var obj = document.getElementById(objSrc);
		StartPoint = new Point(obj.offsetLeft, obj.offsetTop);
		cursorPosition = new Point(event.clientX - StartPoint.x, event.clientY - StartPoint.y);
		firstXY = new Point(event.clientX, event.clientY);
		if(document.all) {	
			document.attachEvent("onmousemove", whileDragging);
			document.attachEvent("onmouseup",   endDrag);
			window.event.cancelBubble = true;
			window.event.returnValue = false;
		} else {
			document.addEventListener("mousemove",whileDragging,true);
			document.addEventListener("mouseup",endDrag,true);
		}
		if(event.preventDefault) { event.preventDefault(); }
		return false;
	}


	function whileDragging(event) {
		if(window.event) { event = window.event; }
//		var obj = getEventTarget(event).parentNode;
		var obj = document.getElementById(objSrc);
		if(cursorPosition != null) {
			if(dirX) { obj.style.left = (event.clientX - cursorPosition.x)+'px'; }
			if(dirY) { obj.style.top = (event.clientY - cursorPosition.y)+'px'; }
			obj.style.bottom = 'auto';
			obj.style.right = 'auto';
		}
		DragObj.whileDrag(trigger,obj);

		if(event.preventDefault) { event.preventDefault(); }
		return false;
	}

	function endDrag(event) {
		if(window.event) { event = window.event; }
//		var obj = getEventTarget(event).parentNode;
		var obj = document.getElementById(objSrc);
		if (document.all) {
			document.detachEvent("onmousemove", whileDragging);
			document.detachEvent("onmouseup",   endDrag);
		} else {
			document.removeEventListener("mousemove", whileDragging,   true);
			document.removeEventListener("mouseup",   endDrag, true);
		}

		cursorPosition = null;
		if(obj.offsetTop < 0) { obj.style.top = '0px'; }

		var winSize = getWindowSize(); // From WindowSize.js
		var parent = obj.parentNode;
		winSize.x = parent.offsetWidth;
		if(parent.offsetHeight) {
			winSize.y = parent.offsetHeight;
		}
		/** Align the Leftness of the Draggable Object **/
		var newLeft = obj.offsetLeft;
		if(obj.offsetLeft < 0) { newLeft = 0; }
		if(newLeft+obj.offsetWidth > winSize.x) { newLeft = (winSize.x - obj.offsetWidth); }
		/* By Aligning to Percentages the Boxes will keep the same relative position 
			no matter the window size.
			Setting the pixel over-ride prevents Draggable from approximating a percentage.
		*/
		if(!pixelOverride) {
			if(newLeft > winSize.y/2) {
				newLeft += obj.offsetWidth;
				obj.style.left = 'auto';
				obj.style.right = ((winSize.x - newLeft) / winSize.x) * 100 + '%';
			} else {
				obj.style.right = 'auto';
				obj.style.left = ((newLeft / winSize.x) * 100)+'%';
			}
		
			/** Align the Topness of the Draggable Object **/
			var newTop = obj.offsetTop;
			if(obj.offsetTop < 0 || !obj.offsetTop) { newTop = 0; }
			else if((obj.offsetTop + obj.offsetHeight) > winSize.y) { newTop = (winSize.y - obj.offsetHeight); }

			if(newTop > winSize.y/2) {
				newTop += obj.offsetHeight;
				obj.style.top = 'auto';
				obj.style.bottom = ((winSize.y - newTop) / winSize.y) * 100 + '%';
			} else {
				obj.style.bottom = 'auto';
				obj.style.top = ((newTop / winSize.y) * 100)+'%';
				if(newTop==0) { obj.style.top = '0%'; }
			}
		}
		DragObj.onDrop(trigger,obj);

	
		if(event.preventDefault) { event.preventDefault(); }
		return false;
	}
	
	trigger._onmousedown = trigger.onmousedown;
	function mousedownFn(event) {
		if(window.event) { event = window.event; }
		startDrag(event);
		if(this._onmousedown) {
			this._onmousedown(event);
		}
	}

	trigger.onmousedown = mousedownFn;
	trigger = null;
}

/*
 * Creates an XML HTTP Object
 */

function getHTTPObject() {
	var xmlhttp;

	try {
		xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
	} catch(e) {
		try {
		xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		} catch(E) {
			try {
			xmlhttp = new XMLHttpRequest();
			} catch(ee) {
			return false;
			}
		}
	}
	return xmlhttp;

}

/*
City of Saint Paul Open Source License

LICENSE

Copyright © 2006 City of Saint Paul, Minnesota. 

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following terms and conditions:

1.The above copyright notice and this permission notice shall be included in all copies of this Software or works derived from this Software.
2.Whenever a Recipient distributes or redistributes the Software, a copy of this agreement must be included with each copy of the Software
3.If Recipient distributes or redistributes the Software in any form other than source code, Recipient must also make the source code freely available, and must provide with each copy of the Software information on how to obtain the source code in a reasonable manner on or through a medium customarily used for software exchange
4.A Recipient may not make any representation in the Software or in any promotional, advertising or other material that may be construed as an endorsement by the City of Saint Paul of any product or service provided by Recipient, or that may seek to obtain commercial advantage by the fact of the City of Saint Paul’s participation in this Agreement.

DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION

No Warranty:  The Software is provided “as is” without any warranty of any kind, either expressed, implied, or statutory, including, but not limited to, any warranty that the Software will conform to specifications, any implied warranties of merchantability, fitness for a particular purpose, or freedom from infringement, any warranty that the Software will be error free, or any warranty that documentation, if provided, will conform to the Software. This agreement does not, in any manner, constitute an endorsement by the City of Saint Paul or any prior recipient of any results, resulting designs, hardware, software products or any other applications resulting from use of the Software. Further, the City of Saint Paul disclaims all warranties and liabilities regarding third-party software, if present in the original Software, and distributes it “as is.”

Waiver and Indemnity:  Recipient agrees to waive any and all claims against the City of Saint Paul, its contractors and subcontractors, as well as any prior recipient.  If recipient's use of the Software results in any liabilities, demands, damages, expenses or losses arising from such use, including any damages from products based on, or resulting from, recipient's use of the Software, recipient shall indemnify and hold harmless the City of Saint Paul, it’s contractors and subcontractors, as well as any prior recipient, to the extent permitted by law. Recipient's sole remedy for any such matter shall be the immediate, unilateral termination of this agreement.

SUPPORT STATEMENT 

The Software is freely provided by the City of Saint Paul as a public service.  The recipient is herby notified that the provision and distribution of the Software should in no way be construed to mean that the City of Saint Paul is obligated to provide any level of support for the Software, now or in the future.  Support in this context shall be construed to mean providing coordination, technical assistance, responding to questions, releasing bug fixes, providing future releases, or otherwise obligating the City of Saint Paul to commit resources to maintain this Software or assist in its implementation by any person or entity.  The City of Saint Paul may, at it’s sole option, and without notice or obligation to the recipients, provide varying levels of support for the Software or terminate the agreement.  Provision of such support shall not alter the condition that the Software is provided without support as stated above.
*/

/*
 * Processes URL Arguements
 * This is really pretty simple.  It takes in a parameter name
 * parses the URL for it, then returns the value of the contents
 * It lets Javascript do some of the things once required by a CGI-script
 */

function CGI () {
	this.param = function(theArgName) {
		var sArgs = location.search.slice(1).split('&'); // Split up the parameters from the URL
		var r = '';
		for(var i = 0; i < sArgs.length; i++) {		// Loop through each parameter
			if (sArgs[i].slice(0,sArgs[i].indexOf('=')) == theArgName) {	// once the new one is found get the answer(s)
				r = sArgs[i].slice(sArgs[i].indexOf('=')+1);
				break;
			}
		}
		return (r.length > 0 ? unescape(r).split(',') : null) // split up the answers based on commas and return
	}

	this.getCookie = function(c_name) {
		if (document.cookie.length>0) {
			c_start=document.cookie.indexOf(c_name + "=");
			if (c_start!=-1) { 
				c_start=c_start + c_name.length+1;
				c_end=document.cookie.indexOf(";",c_start);
				
				if (c_end==-1) c_end=document.cookie.length
					return unescape(document.cookie.substring(c_start,c_end));
			} 
	        }
		return null;
	}

	this.setCookie = function(c_name,value,expiredays) {
		var exdate=new Date();
		exdate.setDate(exdate.getDate()+expiredays);
		document.cookie=c_name+ "=" +escape(value)+((expiredays==null) ? "" : ";expires="+exdate);
	}
}
/*
City of Saint Paul Open Source License

LICENSE

Copyright © 2006 City of Saint Paul, Minnesota. 

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following terms and conditions:

1.The above copyright notice and this permission notice shall be included in all copies of this Software or works derived from this Software.
2.Whenever a Recipient distributes or redistributes the Software, a copy of this agreement must be included with each copy of the Software
3.If Recipient distributes or redistributes the Software in any form other than source code, Recipient must also make the source code freely available, and must provide with each copy of the Software information on how to obtain the source code in a reasonable manner on or through a medium customarily used for software exchange
4.A Recipient may not make any representation in the Software or in any promotional, advertising or other material that may be construed as an endorsement by the City of Saint Paul of any product or service provided by Recipient, or that may seek to obtain commercial advantage by the fact of the City of Saint Paul’s participation in this Agreement.

DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION

No Warranty:  The Software is provided “as is” without any warranty of any kind, either expressed, implied, or statutory, including, but not limited to, any warranty that the Software will conform to specifications, any implied warranties of merchantability, fitness for a particular purpose, or freedom from infringement, any warranty that the Software will be error free, or any warranty that documentation, if provided, will conform to the Software. This agreement does not, in any manner, constitute an endorsement by the City of Saint Paul or any prior recipient of any results, resulting designs, hardware, software products or any other applications resulting from use of the Software. Further, the City of Saint Paul disclaims all warranties and liabilities regarding third-party software, if present in the original Software, and distributes it “as is.”

Waiver and Indemnity:  Recipient agrees to waive any and all claims against the City of Saint Paul, its contractors and subcontractors, as well as any prior recipient.  If recipient's use of the Software results in any liabilities, demands, damages, expenses or losses arising from such use, including any damages from products based on, or resulting from, recipient's use of the Software, recipient shall indemnify and hold harmless the City of Saint Paul, it’s contractors and subcontractors, as well as any prior recipient, to the extent permitted by law. Recipient's sole remedy for any such matter shall be the immediate, unilateral termination of this agreement.

SUPPORT STATEMENT 

The Software is freely provided by the City of Saint Paul as a public service.  The recipient is herby notified that the provision and distribution of the Software should in no way be construed to mean that the City of Saint Paul is obligated to provide any level of support for the Software, now or in the future.  Support in this context shall be construed to mean providing coordination, technical assistance, responding to questions, releasing bug fixes, providing future releases, or otherwise obligating the City of Saint Paul to commit resources to maintain this Software or assist in its implementation by any person or entity.  The City of Saint Paul may, at it’s sole option, and without notice or obligation to the recipients, provide varying levels of support for the Software or terminate the agreement.  Provision of such support shall not alter the condition that the Software is provided without support as stated above.
*/

/*
 * Collection of Functions for dealing with Views in the Main Application
 */

function boundingBoxStringToLineSegment(bbox,delim) {
	var line = new LineSegment();
	line.fromString(bbox, delim);
	return line;
}

function boundingBoxToPointScale(bboxLine, mapDimensions) {
	var diffX = bboxLine.getWidth(); // Width in Ground Units
	var diffY = bboxLine.getHeight(); // Height of Map in Ground Units

	// The center point of the extents
	var centerPoint = new Point(bboxLine.minX() + diffX / 2, bboxLine.minY() + diffY / 2);

	// Scale in Ground Units per Pixel
	var scaleX = diffX / mapDimensions.x;
	var scaleY = diffY / mapDimensions.y;
	// Pick the "larger" scale
	var selectedScale = Math.max(scaleX, scaleY);
	// Return it in a useful fashion for other functions
	var retArr = new Array();
	retArr[0] = centerPoint; retArr[1] = selectedScale;
	return retArr;
}

function pointScaleStringToPointScale(psString, delim) {
	var psArr = psString.split(delim);
	for(var i = 0; i < psArr.length; i++) {
		psArr[i] = parseFloat(psArr[i]);
	}
	var centerPoint = new Point(psArr[0], psArr[1]);
	var scale = psArr[2];
	var retArr = new Array();
	retArr[0] = centerPoint; retArr[1] = scale;
	return retArr;
}

function convertViewString(viewString, viewSize) {
	viewString = viewString.replace(' ','');
	var viewSplit = viewString.split(',');
	var psArr = new Array();
	if(viewSplit.length == 3) {
		psArr = pointScaleStringToPointScale(viewString,',');
	} else if(viewSplit.length == 4) {
		psArr = boundingBoxToPointScale(boundingBoxStringToLineSegment(viewString, ','), viewSize);
	}
	return new Circle(psArr[0], psArr[1]);
}

function getViewStringFromXML(xmlView) {
	var viewString = xmlView.firstChild.nodeValue;
	return convertViewString(viewString);

}

function getViewStringFromMapbook(mapBook, title) {
	var xmlObj = getObjectByTitle(mapBook, 'view', title);
	return getViewStringFromXML(xmlObj);
}


/*
function loadBoundingBox(bbox) {
	var pointScaleArr =  boundingBoxToPointScale(boundingBoxStringToLineSegment(bbox, ','), mapSize);
	changePointScale(pointScaleArr[0],pointScaleArr[1]);
	logDebugMessage('Load Bounding Box', true, mapCenterPoint.toString(',')+' '+mapScale);
}

function loadPointScale(psString) {
	var pointScaleArr =  pointScaleStringToPointScale(psString,',');
	changePointScale(pointScaleArr[0],pointScaleArr[1]);
	logDebugMessage('Load Point Scale', true, mapCenterPoint.toString(',')+' '+mapScale);
}

function loadViewString(viewString) {
	viewString = viewString.replace(' ','');
	var viewSplit = viewString.split(',');
	if(viewSplit.length == 3) {
		//logDebugMessage('Load View', false, 'Load Point/Scale Type');
		loadPointScale(viewString);
	} else if(viewSplit.length == 4) {
		//logDebugMessage('Load View', false, 'Load BBOX Type');
		loadBoundingBox(viewString);
	}
}

function loadView(viewName) {
	var view = getObjectByTitle(xmlDoc, 'view', viewName);
	loadViewXML(view);
}

function loadViewXML(view) {
	var viewString = view.firstChild.nodeValue;
	loadViewString(viewString);

}
*/
/*
City of Saint Paul Open Source License

LICENSE

Copyright © 2006 City of Saint Paul, Minnesota. 

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following terms and conditions:

1.The above copyright notice and this permission notice shall be included in all copies of this Software or works derived from this Software.
2.Whenever a Recipient distributes or redistributes the Software, a copy of this agreement must be included with each copy of the Software
3.If Recipient distributes or redistributes the Software in any form other than source code, Recipient must also make the source code freely available, and must provide with each copy of the Software information on how to obtain the source code in a reasonable manner on or through a medium customarily used for software exchange
4.A Recipient may not make any representation in the Software or in any promotional, advertising or other material that may be construed as an endorsement by the City of Saint Paul of any product or service provided by Recipient, or that may seek to obtain commercial advantage by the fact of the City of Saint Paul’s participation in this Agreement.

DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION

No Warranty:  The Software is provided “as is” without any warranty of any kind, either expressed, implied, or statutory, including, but not limited to, any warranty that the Software will conform to specifications, any implied warranties of merchantability, fitness for a particular purpose, or freedom from infringement, any warranty that the Software will be error free, or any warranty that documentation, if provided, will conform to the Software. This agreement does not, in any manner, constitute an endorsement by the City of Saint Paul or any prior recipient of any results, resulting designs, hardware, software products or any other applications resulting from use of the Software. Further, the City of Saint Paul disclaims all warranties and liabilities regarding third-party software, if present in the original Software, and distributes it “as is.”

Waiver and Indemnity:  Recipient agrees to waive any and all claims against the City of Saint Paul, its contractors and subcontractors, as well as any prior recipient.  If recipient's use of the Software results in any liabilities, demands, damages, expenses or losses arising from such use, including any damages from products based on, or resulting from, recipient's use of the Software, recipient shall indemnify and hold harmless the City of Saint Paul, it’s contractors and subcontractors, as well as any prior recipient, to the extent permitted by law. Recipient's sole remedy for any such matter shall be the immediate, unilateral termination of this agreement.

SUPPORT STATEMENT 

The Software is freely provided by the City of Saint Paul as a public service.  The recipient is herby notified that the provision and distribution of the Software should in no way be construed to mean that the City of Saint Paul is obligated to provide any level of support for the Software, now or in the future.  Support in this context shall be construed to mean providing coordination, technical assistance, responding to questions, releasing bug fixes, providing future releases, or otherwise obligating the City of Saint Paul to commit resources to maintain this Software or assist in its implementation by any person or entity.  The City of Saint Paul may, at it’s sole option, and without notice or obligation to the recipients, provide varying levels of support for the Software or terminate the agreement.  Provision of such support shall not alter the condition that the Software is provided without support as stated above.
*/

/*
 * Default Layers have a little more dimension in GISmo3 than they have in previous version
 * This file is here because G3 will also use a stored cookie in order to save default information.
 */

var DEFAULT_VIEW;

function loadDefaultsFromMapBook(xmlObj, sourceObj, EventManager, objMap) {
	var allMaps = xmlObj.getElementsByTagName('map');
	var allServices = xmlObj.getElementsByTagName('service');
	var allViews = xmlObj.getElementsByTagName('view');

	var interfaceElements = xmlObj.getElementsByTagName('interface');
	for(var i = 0; i < interfaceElements.length; i++) {
		var params = interfaceElements[i].getElementsByTagName('param');
		for(var p = 0; p < params.length; p++) {
			var name = params[p].getAttribute('name');
			var value = params[p].getAttribute('value');
			var type = params[p].getAttribute('type');
			var evalString;
			if(type && !type.match(/string/i)) {
				evalString = name+"="+value+";";
			} else {
				evalString = name+"='"+value+"';";
			}
			eval(evalString);
			logDebugMessage('Override Variable', true, evalString);
		}
	}
			

	var foundView = false;
	if(DEFAULT_VIEW) {
		DEFAULT_VIEW = false;
		foundView = true;
	} else {
		for(var v = 0 ; v < allViews.length; v++) {
			var def = new String(allViews[v].getAttribute('default'));
			if(def.match(/true/i)) {
				var name = allViews[v].getAttribute('title');
				//loadViewXML(allViews[v]);
				if(!foundView) {
					var circView = convertViewString(allViews[v].firstChild.nodeValue, objMap.getMapSize());
					EventManager.changePointScale(circView.Center,circView.Radius);
					logDebugMessage('Defaults', true, 'Loaded Default View');
				}
				EventManager.triggerEvent('addview', allViews[v]);
				foundView = true;
			}
		}
	}
	var selection = xmlObj.getElementsByTagName('selection');
	for(var s = 0; s < selection.length; s++) {
	//	DrawingArea[selection[s].getAttribute('title')] = selection[s].getAttribute('id');
		EventManager.triggerEvent('addselection', selection[s]);
	}

	var layersToAdd = new Array();
	for(var m = 0 ; m < allMaps.length; m++) {
		var name = allMaps[m].getAttribute('title');
		var def = new String(allMaps[m].getAttribute('default'));
		var found = false;
			if(def.match(/true/i) || found) {
			layersToAdd[name] = allMaps[m];
		}
	}

	for(var l in layersToAdd) {
		EventManager.triggerEvent('addlayer', layersToAdd[l]);
	}

	var servicesToAdd = new Array();
	for(var s = 0; s < allServices.length; s++) {
		var def = new String(allServices[s].getAttribute('default'));
		var sel = new String(allServices[s].getAttribute('selected'));
		if(def.match(/true/i)) {
			var title = allServices[s].getAttribute('title');
			servicesToAdd[title] = allServices[s];
		}
	}
	for(var s in servicesToAdd) {
		EventManager.triggerEvent('addservice', servicesToAdd[s]);
		if(servicesToAdd[s].getAttribute('selected') && servicesToAdd[s].getAttribute('selected').match(/true/i)) {
			EventManager.triggerEvent('selecttool', s);
		}
	}

}

function loadDefaults(xmlObj, EventManager, objMap) {
/*	if(checkState()) {
		loadDefaultsFromMapBook(getState(), xmlObj);
	} else {*/
		loadDefaultsFromMapBook(xmlObj, xmlObj,EventManager,objMap);
/*	}*/
}

function getMapbook(mapBook, EventManager, objMap) {
	var http = getHTTPObject();
	http.open("GET", mapBook);
	http.onreadystatechange = function () { 
		if(http.readyState == 4) {
			xmlDoc = http.responseXML;
			var allMaps = xmlDoc.getElementsByTagName('map');
			for(var m = 0; m < allMaps.length; m++) {
				allMaps[m].setAttribute('zindex', m);
			}

			EventManager.onLoadedMapbook(xmlDoc, objMap);
			return xmlDoc;
		}
	};
	http.send(null);
}

function getObjectByTitle(xmlDoc, objType, objTitle) {
	var objs = xmlDoc.getElementsByTagName(objType);
	for(var i = 0; i < objs.length; i++) {
		if(objs[i].getAttribute('title') && objs[i].getAttribute('title') == objTitle) {
			return objs[i];
		}
	}
	return null;
}


function getDefaultMapbook(EventManager,oMap) {
	var util = new CGI();

	var viewFromURL = util.param('view');
	var mapbookFromURL = util.param('mapbook');
	if(!viewFromURL) { viewFromURL = util.param('zoomto'); }

	if(viewFromURL) {
		var circView = convertViewString(new String(viewFromURL), oMap.getMapSize());
		EventManager.changePointScale(circView.Center,circView.Radius);
		DEFAULT_VIEW = true;
	}

	var MapBook = MAPBOOK_FILE;
	if(mapbookFromURL) {
		MapBook = mapbookFromURL;
	}


	EventManager.addEventListener('loadedmapbook', 'URLLayers', 
		function (xmlDoc) {
			EventManager.removeEventListener('loadedmapbook', 'URLLayers');
			var layers = new String(util.param('layers'));
			var layersArr = layers.split(',');
			for(var i = 0; i < layersArr.length; i++) {
				var map = getObjectByTitle(xmlDoc, 'map', layersArr[i]);
				if(map) {
					EventManager.triggerEvent('addlayer',map);
				}
			}
		});

	getMapbook(MapBook,EventManager,oMap);
	/*DEFAULT_VIEW = false;*/
}

var CONFIRM_REMOVE = false;

function GEvents() {
	var events = new Array();
	
	this.addEventListener = function(eventType, eventName, action) {
		if(!events[eventType]) {
			events[eventType] = new Array();
		} 
		events[eventType][eventName] = action;
	}

	this.removeEventListener = function(eventType, eventName) {
		if(events[eventType] && events[eventType][eventName]) {
			delete events[eventType][eventName];
		}
	}
	this.triggerEvent = function(eventName, object) {
		if(eventName == 'removelayer') {
			return this.onRemoveLayer(object);
		} else if(eventName == 'removeservice') {
			return this.onRemoveService(object);
		}
		var eventArray = events[eventName];
		for(var i in eventArray) {
			eventArray[i](object);
		}

	}

	this.onRemoveLayer = function(layerName) {
		var eventArray = events['removelayer'];
		if(!CONFIRM_REMOVE || confirm('Do you want to remove layer '+layerName+'?')) {
			for(var i in eventArray) {
				eventArray[i](layerName);
			}
			return true;
		} else {
			return false;
		}
		return false;
		
	}

	this.onRemoveService = function(serviceName) {
		var eventArray = events['removeservice'];
		if(!CONFIRM_REMOVE || confirm('Do you want to remove service '+serviceName+'?')) {
			for(var i in eventArray) {
				eventArray[i](serviceName);
			}
			return true;
		} else {
			return false;
		}
		
	}



	this.onLoadedMapbook = function(mapBookXML,objMap) {
		var eventArray = events['loadedmapbook'];
		for(var i in eventArray) {
			eventArray[i](mapBookXML, this, objMap);
		}

	}

	this.changePointScale = function(centerPoint, scale) {
		var circ = new Circle();
		circ.Radius = scale;
		circ.Center = centerPoint;
		this.triggerEvent('navigate',circ);
	}
}
var MAP_MAX_ZINDEX = 500;
var MAP_CONTROL_INDEX = 1000;
var MAP_DRAWING_INDEX = 900;
var MAP_POPUP_ZINDEX = 5000;


var GMAP_INSTANCE = 0;

var MAP_LOAD_IMAGE = 0;
var MAP_LOAD_DATA = 1;
var MAP_LOAD_LEGEND = 2;

function GMap (EventManager) {
	var MySelf = this;
	var MapId = 'Map'+GMAP_INSTANCE+'-';
	var LayerId = MapId+'Layer-';
	GMAP_INSTANCE++;


	var MapParentId;
	var ImageParentId = MapId+'ImageParent';

	var ScaleFactor = 1;
	var MapReference = null; 

	var CoordinateObjectId = null;
	
	var ImageControlId;
	var ImageMapControlId;

	var ReferenceIndicatorId = MapId+'ReferenceIndicator';
	var DrawBoxId = MapId+'DrawBox';

	var PopupMenuId = MapId+'PopupMenu';
	
	var ImageControlCanvasId = MapId+'Canvas'

	var FunctionOffset = new Point();
	var FunctionSemaphore = false;

	var DrawnPoints = new Array();
	var PreviousViews = new Array();

	function refreshDrawBox() {
		var DrawBox = document.getElementById(DrawBoxId);

		if(!DrawBox) {
			DrawBox = document.createElement('div');
		}
		if(!DrawBox.parentNode) {
			document.getElementById(MapParentId).appendChild(DrawBox);
		}

		DrawBox.setAttribute('id', DrawBoxId);
		DrawBox.style.position = 'absolute';
		DrawBox.style.border = 'solid 2px red';
		DrawBox.style.zIndex = MAP_DRAWING_INDEX;
		DrawBox.style.visibility = 'hidden';
		DrawBox.style.overflow = 'hidden';
		DrawBox.style.width = '1px';
		DrawBox.style.height = '1px';

		DrawBox = null;
	}

	var MapCenterPoint = new Point(576000, 157909.5);
	var MapScale = 25;


	var LayerURLs = new Array();
	var LayerLoadData = new Array();	/* Stores whether data should be loaded for the layer or not */
	var LayerLegends = new Array();
	var LayerImages = new Array();
	var LayerParameters = new Array();

	var min_z_index = MAP_MAX_ZINDEX;

	var VisibilityState = 'visible';
	function setVisibility(state) {
		for(var t in LayerImages) {
			var img = document.getElementById(LayerId+l);
			img.style.visibility = state;
			img = null;
		}
		VisibilityState = state;
	}

	this.setVisibility = setVisibility;

	this.hideMap = function() {
		setVisibility('hidden');
	}

	this.showMap = function() {
		setVisibility('visible');
	}

	this.setCoordinateObject = function(objId) {
		CoordinateObjectId = objId;
	}


	this.setScaleFactor = function(scF) {
		ScaleFactor = scF;
	}
	
	this.setMapReference = function(m) {
		MapReference = m;
	}

	this.changePointScale = function(point,scale) {
		var circ = new Circle(point,scale);
		PreviousViews[PreviousViews.length] = circ;
		MapScale = scale;
		MapCenterPoint = point;
	}

	var LastActionTime = 0;

	this.setLastActionTime = function() {
		var d = new Date();
		LastActionTime = d.getTime();
		return LastActionTime;
	}

	this.getLastActionTime = function() {
		return LastActionTime;
	}


	/*
	 * This will refresh the map when a coordinate change happens
	 */
	var MapViewSet = false;
	var MAP_MARKUP_RADIUS = 25;
	this.onChangePointScale = function(psc) {
		if(MapReference && !REFERENCE_MAP_DYNAMIC && MapViewSet) {
			return true;
		}
		MapViewSet = true;
		MySelf.clearDrawingArea();
		if(psc.Markup) {
			var imageControlCanvas = document.getElementById(ImageControlCanvasId);
			var ctx = imageControlCanvas.getContext("2d");

			var mSize = MySelf.getMapSize();
			ctx.save();
				ctx.beginPath();
					ctx.strokeStyle = "rgba(255,0,0,.75)";
					ctx.lineWidth = 1;


					ctx.moveTo(mSize.x/2,mSize.y/2 - MAP_MARKUP_RADIUS);
					ctx.lineTo(mSize.x/2,mSize.y/2 + MAP_MARKUP_RADIUS);
					ctx.stroke();
					ctx.moveTo(mSize.x/2-MAP_MARKUP_RADIUS, mSize.y/2);
					ctx.lineTo(mSize.x/2+MAP_MARKUP_RADIUS, mSize.y/2);
					ctx.stroke();
				ctx.closePath();
			ctx.restore();

		//	ctx.lineWidth = 3;
			ctx.save();
				ctx.beginPath();
				ctx.strokeStyle = 'rgba(255,0,0,.75)';
				ctx.lineWidth = 3;
				ctx.arc(mSize.x/2, mSize.y/2, MAP_MARKUP_RADIUS, 0, Math.PI*2,true);
				ctx.stroke();
				ctx.closePath();
			ctx.restore();
/*			ctx.save();
				ctx.strokeStyle = "red";
				ctx.fillStyle = "red";
				ctx.arc(ZoomPoint.x, ZoomPoint.y, 3, 0, Math.PI * 2, true);
				ctx.fill();
				ctx.closePath();
			ctx.restore();*/

		}
		PreviousViews[PreviousViews.length] = psc;
		MapScale = psc.Radius * ScaleFactor;
		MapCenterPoint = psc.Center;

		MySelf.refreshLayers();
	}

	/* Drawing Tools! */

	var DrawingShape = 'point';

	function startDrawing(event) {
		if(window.event) { event = window.event; event.which = event.button; }
		if(event.which != 1) { return false; }
		FunctionOffset = objectClientPosition(document.getElementById(ImageControlId));
		DrawnPoints = new Array();
		attachEvent(document, 'mouseup',stopDrawing);

		if(DrawingShape == 'box' || DrawingShape=='evenbox' || DrawingShape=='scalebox') {
			DrawnPoints[0] = new Point(event.clientX-FunctionOffset.x, event.clientY-FunctionOffset.y);
		} else {
			MySelf.drawPoint(event.clientX-FunctionOffset.x, event.clientY-FunctionOffset.y);
		}
		FunctionSemaphore = true;
	}

	function whileDrawing(event) {
		if(window.event) { event = window.event; }
		if(FunctionSemaphore) {
			MySelf.drawPoint(event.clientX-FunctionOffset.x, event.clientY-FunctionOffset.y);
		}
	}

	function whileDrawingBox(event) {
		if(window.event) { event = window.event; }
		if(FunctionSemaphore) {
			var DrawBox = document.getElementById(DrawBoxId);
			DrawBox.style.visibility = 'visible';

			var currentPoint = new Point(event.clientX-FunctionOffset.x, event.clientY-FunctionOffset.y);
			var left = DrawnPoints[0].x - 2;
			var width = currentPoint.x - left;
			var top = DrawnPoints[0].y - 2;
			var height = currentPoint.y - top;

			var mSize = MySelf.getMapSize();
			var perspective = mSize.x / mSize.y;

			if(DrawingShape == 'evenbox') {
				perspective = 1;
			}

			if(DrawingShape =='scalebox' || DrawingShape == 'evenbox') {
				if(width > height) { width = Math.abs(height * perspective) * width / Math.abs(width); }
				else { height = Math.abs(width * 1/perspective) * height / Math.abs(height); }
			}

			if(width < 0) {
				left = left + width;
				width = Math.abs(width);
			}

			if(height < 0) {
				top = top + height;
				height = Math.abs(height);
			}
			/* These are here because they keep IE happy and FF doesn't care.
			 * I would love for someone to explain why this is necessary.
			 */
			width = width;
			height = height;

			DrawBox.style.top = top+'px';
			DrawBox.style.left = left+'px';
			DrawBox.style.height = height+'px';
			DrawBox.style.width = width+'px';

			DrawBox = null;
		}
	}
	function cancelDrawing() {
		if (document.all) {
			document.detachEvent("onmouseup",   stopDrawing);
		} else {
			document.removeEventListener("mouseup",   stopDrawing, true);
		}
		if(FunctionSemaphore) {
			FunctionSemaphore = false;
			var DrawBox = document.getElementById(DrawBoxId);
			DrawBox.style.visibility = 'hidden';
			DrawBox.style.left = '0px';
			DrawBox.style.top = '0px';
			DrawBox.style.height = '1px';
			DrawBox.style.width = '1px';
			var divPoints = document.getElementById(MapParentId).getElementsByTagName('div');
			for(var i = divPoints.length; i >= 0; i--) {
				if(divPoints[i] && divPoints[i].parentNode && divPoints[i].className == 'DrawnPoint') {
					var p = divPoints[i].parentNode;
					p.removeChild(divPoints[i]);
				}
			}
		}

	}
	function stopDrawing() {
		if (document.all) {
			document.detachEvent("onmouseup",   stopDrawing);
		} else {
			document.removeEventListener("mouseup",   stopDrawing, true);
		}

		if(FunctionSemaphore) {
			FunctionSemaphore = false;
			/* Convert Drawn Points into Ground Units */
			var GroundUnits = new Array();
			for(var i = 0; i < DrawnPoints.length; i++) {
				GroundUnits[i] = MySelf.pointToGroundUnits(new Point(DrawnPoints[i].x,DrawnPoints[i].y));
			}
			EventManager.triggerEvent('finishdrawing', GroundUnits);

			var DrawBox = document.getElementById(DrawBoxId);
			DrawBox.style.visibility = 'hidden';
			DrawBox.style.left = '0px';
			DrawBox.style.top = '0px';
			DrawBox.style.height = '1px';
			DrawBox.style.width = '1px';
			DrawBox = null;

			var divPoints = document.getElementById(MapParentId).getElementsByTagName('div');
			for(var i = divPoints.length; i >= 0; i--) {
				if(divPoints[i] && divPoints[i].parentNode && divPoints[i].className == 'DrawnPoint') {
					var p = divPoints[i].parentNode;
					p.removeChild(divPoints[i]);
				}
			}

		}
	}

	this.mapStats = function() {
		logDebugMessage('Map Statistics', false, MapCenterPoint.toString(",")+' '+MapScale);
	}
	
	this.pointToGroundUnits = function(p) {
		var ImageControl = document.getElementById(ImageControlId);
		MapCenterPoint.clean();
		p.x = (MapCenterPoint.x - (ImageControl.offsetWidth / 2 - p.x) * MapScale);
		p.y = (MapCenterPoint.y - (p.y - ImageControl.offsetHeight / 2) * MapScale);
		ImageControl = null;
		return p;
		
	}

	this.pointDrawTool = function(callFn) {
		this.loadDrawTool('poly', true);
	}

	var DrawingArea = new Array();

	this.polyDrawTool = function(shape, title) {
		this.loadDrawTool(shape,true); // measurearea = poly
		this.setMapCursor(CURSOR_DRAW);
		EventManager.addEventListener('changetool', 'MapZoomTool', function () {
			EventManager.removeEventListener('changetool','MapZoomTool');
			EventManager.removeEventListener('finishdrawing','MapZoomTool');
		});
		EventManager.addEventListener('finishdrawing', 'MapZoomTool', function () { 
			if(shape == 'measurearea') { shape = 'poly'; }
			/* Ground Unit String is in WKT/GML Format */
			var groundUnitString = '';
			for(var i = 0; i < DrawnPoints.length; i++) {
				var p = new Point(DrawnPoints[i].x,DrawnPoints[i].y);
				groundUnitString = groundUnitString + MySelf.pointToGroundUnits(p).toString(",") + ' ';
			}

			/* Close the polygon */
			if(shape == 'poly' || shape == 'circle') {
				groundUnitString = groundUnitString + MySelf.pointToGroundUnits(new Point(DrawnPoints[0].x,DrawnPoints[0].y)).toString(",");
			}
			/*logDebugMessage('Points', false, groundUnitString);*/

			var pass = false;
			if(!title) {
				var msg = '';
				while(!pass) {
					title = prompt(msg+"Name for the object?", "Title");
					pass = true;
					if(LayerImages[title]) {
						pass = false;
						msg  = 'LAYER ALREADY EXISTS --';
					}
				}
				
			}

			if(title) {
				var bufferSize = prompt('Buffer Size?','0');
				if(!bufferSize) { bufferSize = 0; }
				
				/*var saveDrawingURL = '/mapscript/cgi-bin/saveDrawing.pl?coords=%COORDS%&shape=%SHAPE%&buffer=%BUFFER%&title=%TITLE%';*/
				var saveDrawingURL = DRAWING_URL;
				saveDrawingURL = saveDrawingURL.replace('%COORDS%', groundUnitString);
				saveDrawingURL = saveDrawingURL.replace('%SHAPE%', shape);
				saveDrawingURL = saveDrawingURL.replace('%BUFFER%', bufferSize);
				saveDrawingURL = saveDrawingURL.replace('%TITLE%', title);

				var DrawingMethod = 'GET';
				var DrawingPostInfo = true;
				if(saveDrawingURL.length > 1000) {
					DrawingMethod = 'POST';
					var saveDrawingArr = saveDrawingURL.split('?');
					saveDrawingURL = saveDrawingArr[0];
					DrawingPostInfo = saveDrawingArr[1];
				}

				var titleCopy = title;

				var saveDrawingControl = getHTTPObject();
				logDebugMessage('Drawing URL', false, saveDrawingURL);
				saveDrawingControl.open(DrawingMethod, saveDrawingURL, true);
				if(DrawingMethod == 'POST') {
					saveDrawingControl.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
				}


				saveDrawingControl.onreadystatechange = function() {
					if(saveDrawingControl.readyState == 4) {
						if(saveDrawingControl.responseXML) {
							loadDefaultsFromMapBook(saveDrawingControl.responseXML, null, EventManager);
							var selection = saveDrawingControl.responseXML.getElementsByTagName('selection');
							for(var s = 0; s < selection.length; s++) {
								DrawingArea[selection[s].getAttribute('title')] = selection[s].getAttribute('id');
							}

						}
					}
				}
				saveDrawingControl.send(DrawingPostInfo);
			}
			title = '';
			MySelf.resetDrawingArea();
		});

	}

	this.addSelection = function(s) {
		DrawingArea[s.getAttribute('title')] = s.getAttribute('id');
	}

	this.getDrawingAreas = function() {
		return DrawingArea;
	}

	this.resetDrawingArea = function() {
		MySelf.clearDrawingArea();
		DrawnPoints = new Array();
	}

	this.clearDrawingArea = function() {
	//	DrawnPoints = new Array();	
		refreshCanvas();
		document.getElementById(ImageControlCanvasId).getContext("2d").clearRect(0,0,1000,1000);
		EventManager.triggerEvent('measuredlength', 0);
	}

	function measureLines() {
		var linesInGroundUnits = 0;
		for(var i = 1; i < DrawnPoints.length ; i++) {
			var gA = MySelf.pointToGroundUnits(new Point(DrawnPoints[i-1].x, DrawnPoints[i-1].y));
			var gB = MySelf.pointToGroundUnits(new Point(DrawnPoints[i].x, DrawnPoints[i].y));
						
			var gUnitSeg =new LineSegment(gA,gB);
			linesInGroundUnits += gUnitSeg.getLength();
		}
		EventManager.triggerEvent('measuredlength', linesInGroundUnits);
	}

	function measureArea() {
		var groundUnits = new Array();
		for(var i = 0; i < DrawnPoints.length ; i++) {
			var gA = MySelf.pointToGroundUnits(new Point(DrawnPoints[i].x, DrawnPoints[i].y));
			groundUnits[i] = gA;
		}	
		var poly = new Polygon();
		groundUnits[groundUnits.length] = new Point(groundUnits[0].x, groundUnits[0].y);
		poly.buildFromPoints(groundUnits);
		EventManager.triggerEvent('measuredlength', Math.abs(poly.area()));
	}

	function clearImageControlFn() {
		var ImageControl = document.getElementById(ImageControlId);
		ImageControl.onmousedown = null;
		ImageControl.onmouseup = null;
		ImageControl.onmousemove = null;
		ImageControl.onclick = null;
		ImageControl.dblclick = null;

		removeEvent(ImageControl, 'click', measureLines);
		removeEvent(ImageControl, 'click', measureArea);
		removeEvent(ImageControl, 'mousedown', startDrawing);
		removeEvent(ImageControl, 'mouseup', stopDrawing);
		removeEvent(ImageControl, 'mousemove', whileDrawing);
		removeEvent(ImageControl, 'mousemove', whileDrawingBox);

		ImageControl = null;
	}

	this.onChangeTool = function() {
		clearImageControlFn();
	}


	this.loadMeasureTool = function(mType) { // area or length
		var ImageControl = document.getElementById(ImageControlId);
		if(mType == 'length') {
			MySelf.loadDrawTool('measureline',true);
			attachEvent(ImageControl, 'click', measureLines);
		} else if(mType == 'area') {
			MySelf.loadDrawTool('measurearea',true);
			attachEvent(ImageControl, 'click', measureArea);
		} else {
			alert('Unknown Measure Tool Type!');
		}
	}

	this.setMapCursor = function(pCursor) {
		var ImageControl = document.getElementById(ImageControlId);
		ImageControl.style.cursor = pCursor;
		ImageControl = null;
	}

	this.loadDrawTool = function(shape,override) {
		var ImageControl = document.getElementById(ImageControlId);

		refreshDrawBox();
		ImageControl.style.zIndex = MAP_CONTROL_INDEX;
		document.getElementById(ImageMapControlId).style.zIndex = MAP_CONTROL_INDEX - 1;
		ImageControl.style.cursor = CURSOR_DRAW;

		if(!override) {
			EventManager.triggerEvent('changetool', 'Draw');
		}

		EventManager.addEventListener('changetool','MapDrawingTools', function () {
			removeEvent(ImageControl, 'mousedown', startDrawing);
			removeEvent(ImageControl, 'mousemove', whileDrawing);
			removeEvent(ImageControl, 'mousemove', whileDrawingBox);
			removeEvent(ImageControl, 'mouseup', stopDrawing);
			EventManager.removeEventListener('changetool','MapDrawingTools');
			EventManager.removeEventListener('finishdrawing','MapZoomTool');
		});
		clearImageControlFn();
		DrawingShape = shape;
		if(shape == 'box' || shape == 'evenbox' || shape == 'scalebox') {
			attachEvent(ImageControl, 'mousedown', startDrawing);
			attachEvent(ImageControl, 'mousemove', whileDrawingBox);
			attachEvent(ImageControl, 'mouseup', stopDrawing);
		} else if(shape == 'measureline') {
			ImageControl.style.cursor = CURSOR_MEASURE;
			DrawnPoints = new Array();
			MySelf.clearDrawingArea();
			ImageControl.onclick = function(event) {
				if(window.event) { event = window.event; }
				refreshCanvas();
				FunctionOffset = objectClientPosition(document.getElementById(ImageControlId));
				ZoomPoint.x = event.clientX - FunctionOffset.x;
				ZoomPoint.y = event.clientY - FunctionOffset.y;
				var ctx = document.getElementById(ImageControlCanvasId).getContext("2d");

				if(DrawnPoints.length <= 0) { ctx.beginPath(); }

				DrawnPoints[DrawnPoints.length] = new Point(ZoomPoint.x, ZoomPoint.y);
				if(DrawnPoints.length > 1) {
					var i = DrawnPoints.length-1;
					ctx.strokeStyle = "red";
					ctx.save();
						ctx.beginPath();
						ctx.moveTo(DrawnPoints[i-1].x, DrawnPoints[i-1].y);
						ctx.lineTo(DrawnPoints[i].x, DrawnPoints[i].y);
						ctx.stroke();
						ctx.closePath();
					ctx.restore();
				}
				ctx.save();
					ctx.strokeStyle = "red";
					ctx.fillStyle = "red";
					ctx.arc(ZoomPoint.x, ZoomPoint.y, 3, 0, Math.PI * 2, true);
					ctx.fill();
					ctx.closePath();
				ctx.restore();
			}

			ImageControl.ondblclick = function(event) {
				for(var i = 0; i < DrawnPoints.length; i++) {
					DrawnPoints[i] = MySelf.pointToGroundUnits(DrawnPoints[i]);
				}
				MySelf.clearDrawingArea();
				Events.triggerEvent('finishdrawing', DrawnPoints);
				DrawnPoints = new Array();
			}
		} else if(shape == 'measurearea') {
			ImageControl.style.cursor = CURSOR_MEASURE;
			DrawnPoints = new Array();
			
			MySelf.clearDrawingArea();

			ImageControl.onclick = function(event) {
				if(window.event) { event = window.event; }
				refreshCanvas();
				FunctionOffset = objectClientPosition(document.getElementById(ImageControlId));
				ZoomPoint.x = event.clientX - FunctionOffset.x;
				ZoomPoint.y = event.clientY - FunctionOffset.y;
				var ctx = document.getElementById(ImageControlCanvasId).getContext("2d");

				if(DrawnPoints.length <= 0) { ctx.beginPath(); }

				DrawnPoints[DrawnPoints.length] = new Point(ZoomPoint.x, ZoomPoint.y);
				MySelf.clearDrawingArea();
				ctx.save();
					ctx.fillStyle = "rgba(0,200,0,.5)";
					ctx.beginPath();
					ctx.moveTo(DrawnPoints[0].x, DrawnPoints[0].y);
					for(var i = 1; i < DrawnPoints.length; i++) {
						ctx.lineTo(DrawnPoints[i].x,DrawnPoints[i].y);
					}
					ctx.closePath();
					ctx.fill();
				ctx.restore();

				ctx.save();
					ctx.strokeStyle = "red";
					ctx.lineWidth = 2;
					if(DrawnPoints.length == 1) {
						ctx.save();
						ctx.fillStyle = "red";
						ctx.beginPath();
							ctx.arc(DrawnPoints[0].x, DrawnPoints[0].y, 2,0,Math.PI*2,true);
							ctx.fill();
						ctx.closePath();
						ctx.restore();
					}
					ctx.beginPath();
					ctx.moveTo(DrawnPoints[0].x, DrawnPoints[0].y);

					for(var i = 1; i < DrawnPoints.length; i++) {
						ctx.lineTo(DrawnPoints[i].x,DrawnPoints[i].y);
					}
					ctx.closePath();
					ctx.stroke();

				ctx.restore();

			}

			ImageControl.ondblclick = function(event) {
				for(var i = 0; i < DrawnPoints.length; i++) {
					DrawnPoints[i] = MySelf.pointToGroundUnits(DrawnPoints[i]);
				}
				Events.triggerEvent('finishdrawing', DrawnPoints);
				DrawnPoints = new Array();
			}

		} else {
			attachEvent(ImageControl, 'mousedown', startDrawing);
			attachEvent(ImageControl, 'mousemove', whileDrawing);
			attachEvent(ImageControl, 'mouseup', stopDrawing);
		}
		ImageControl = null;
	}


	function zoomWheel(event) {
		if(window.event) { event = window.event; }
		var dir = 2;
		var delta = 0;
		if(event.wheelDelta) {
			delta = event.wheelDelta/120;
		} else if(event.detail) {
			delta = -event.detail/3;
		}
		if(delta > 0) {
			dir = .7;
		} else if (delta < 0) {
			dir = 1.5;
		}
		MySelf.zoomTo(ZoomPoint.x, ZoomPoint.y, dir);
	}

	/*
	 * Zooming Related Code
	 */

	function zoomTrackMouse(event) {
		var ImageControl = document.getElementById(ImageControlId);
		if(window.event) { event = window.event; }
		FunctionOffset = objectClientPosition(ImageControl);
		ZoomPoint.x = event.clientX - FunctionOffset.x;
		ZoomPoint.y = event.clientY - FunctionOffset.y;
		var CoordinateObject = document.getElementById(CoordinateObjectId);
		if(CoordinateObject) {
			ZoomPoint.clean();
			var gUnit = MySelf.pointToGroundUnits(new Point(ZoomPoint.x, ZoomPoint.y));
			var llString = '';
			var sysArr = (new String(MAP_COORDINATE_SYSTEM)).split('-');
			if(window.toLatLong) {
				var unitConversion = new Point(gUnit.x, gUnit.y);
				var llPoint = window.toLatLong(sysArr[1], unitConversion);
				llPoint.x = round(llPoint.x,4);
				llPoint.y = round(llPoint.y,4);
				var temp = llPoint.x;
				llPoint.x = llPoint.y;
				llPoint.y = temp;
				llString = ' <b>Lat, Long:</b> '+llPoint.toString(",");
				//llString = getLatLon(gUnit.x, gUnit.y).toString(',');
			}
			var mSize = MySelf.getMapSize();
			mSize.x = Math.round(mSize.x * MapScale);
			mSize.y = Math.round(mSize.y * MapScale);
			CoordinateObject.innerHTML = '<B>X,Y:</B> '+round(gUnit.x,2)+","+round(gUnit.y,2)+llString;
			if(SHOW_MAP_WIDTH) {
				CoordinateObject.innerHTML += " <b>Width, Height: </b>"+mSize.toString(',')+" "+MAP_GROUND_UNITS;
			}

		}
		ImageControl = null;
	}

	var ZoomDirection = 1;
	var ZoomPoint = new Point();	
	function zoomToPoint(event) {
		var ImageControl = document.getElementById(ImageControlId);
		if(window.event) { event = window.event; }
		FunctionOffset = objectClientPosition(ImageControl);
		MySelf.zoomTo(ZoomPoint.x, ZoomPoint.y, ZoomDirection);
		ImageControl = null;
	}


	this.recenterImage = function(x,y,v) {
		var ImageControl = document.getElementById(ImageControlId);
		if(!v) { v = 1; }
		var PanPoint = new Point(x,y);
		var shiftX = ImageControl.offsetWidth / 2 - PanPoint.x;
		var shiftY = ImageControl.offsetHeight / 2 - PanPoint.y;
		var oldW = ImageControl.offsetWidth;
		var oldH = ImageControl.offsetHeight;
		var newW = oldW * (1/v);
		var newH = oldH * (1/v);
		shiftX *= (1/v);
		shiftY *= (1/v);
		for(var l in LayerImages) {
			var img = document.getElementById(LayerId+l);
			img.style.left = img.offsetLeft + shiftX - (newW - oldW) / 2 + 'px';
			img.style.top = img.offsetTop + shiftY - (newH - oldH) / 2 + 'px';
			img.style.width = newW + 'px';
			img.style.height = newH + 'px';
			img = null;
		}
		MySelf.zoomTo(0,0,1);
		ImageControl = null;
	}

	this.recenterOnZoomPoint = function(v) {
		MySelf.recenterImage(ZoomPoint.x, ZoomPoint.y, v);
	}


	/*
	 * Panning Code
	 */
	var PanPoint = new Point(0,0);
	function startPan(e) {
		if(window.event) { e = window.event; e.which = e.button; }
		if(e.which != 1) { return false; } /* Pan only on the left/first mouse button */

		var ImageControl = document.getElementById(ImageControlId);
		ImageControl.Origin = new Point(e.clientX, e.clientY);
		for(var l in LayerImages) {
			var img = document.getElementById(LayerId+l);
			img.originalLeft = img.offsetLeft;
			img.originalTop = img.offsetTop;
			img = null;
		}
		PanPoint.x = 0;
		PanPoint.y = 0;
		ImageControl = null;
	}

	function whilePanning(e) {
		if(window.event) { e = window.event; }

		var ImageControl = document.getElementById(ImageControlId);
		if(ImageControl.Origin) {
			for(var l in LayerImages) {
				var img = document.getElementById(LayerId+l);
				img.style.left = img.originalLeft + (e.clientX - ImageControl.Origin.x) + 'px';
				img.style.top = img.originalTop + (e.clientY - ImageControl.Origin.y) + 'px';
				img = null;
			}
			PanPoint.x = e.clientX - ImageControl.Origin.x;
			PanPoint.y = e.clientY - ImageControl.Origin.y;
		}
		ImageControl = null;
		return preventDefaultEvent(e);
	}

	function endPan(e) {
		var ImageControl = document.getElementById(ImageControlId);
		if(window.event) { e = window.event; e.which = e.button; }
		if(e.which != 1) { return false; }
		MySelf.zoomTo(ImageControl.offsetWidth / 2 - PanPoint.x, ImageControl.offsetHeight / 2 - PanPoint.y, 1);
		ImageControl.Origin = null;
		ImageControl = null;
	}

	function cancelPan(event) {
		var ImageControl = document.getElementById(ImageControlId);
		if(window.event) { event = window.event; }
		var keyCode = getKeyCode(event);
		if(keyCode == 'CTRL+z') {
			ImageControl.Origin = null;
			for(var l in LayerImages) {
				var img = document.getElementById(LayerId+l);
				img.style.left = img.originalLeft+'px';
				img.style.top = img.originalTop+'px';
				img = null;
			}
		}
		ImageControl = null;

	}

	this.loadZoomTool = function(v) {
		var ImageControl = document.getElementById(ImageControlId);
		if(!ImageControl) { return false; }
		clearImageControlFn();
		if(v == 1) { /* Pan */
			ImageControl.style.cursor = CURSOR_ZOOM_PAN;
			ImageControl.onmousedown = startPan;
			ImageControl.onmouseup = endPan;
			ImageControl.onmousemove = whilePanning;
			ImageControl.onkeypress = cancelPan;
		} else if(v != 1) { /* Zoom In  and Zoom Out */
			MySelf.loadDrawTool(ZOOM_BOX_SHAPE,true);

			if(v < 1) {
				ImageControl.style.cursor = CURSOR_ZOOM_IN;
			} else {
				ImageControl.style.cursor = CURSOR_ZOOM_OUT;
			}
	
			EventManager.addEventListener('finishdrawing', 'MapZoomTool', function () { 
				var ImageControl = document.getElementById(ImageControlId);
				var DrawBox = document.getElementById(DrawBoxId);
				var Ratio = DrawBox.offsetWidth / ImageControl.offsetWidth;
				var hRatio = DrawBox.offsetHeight / ImageControl.offsetHeight;
				if(hRatio > Ratio) { Ratio = hRatio; }
				if(DrawBox.offsetWidth < 7) {
					MySelf.recenterImage(DrawnPoints[0].x, DrawnPoints[0].y, v);
				} else {	
					var PanPoint = new Point(DrawBox.offsetLeft + DrawBox.offsetWidth / 2, DrawBox.offsetTop + DrawBox.offsetHeight / 2);
					if(v > 1) { v = 1 / Ratio; }
					else { v = Ratio; }
					var shiftX = ImageControl.offsetWidth / 2 - PanPoint.x;
					var shiftY = ImageControl.offsetHeight / 2 - PanPoint.y;
					var oldW = ImageControl.offsetWidth;
					var oldH = ImageControl.offsetHeight;
					var newW = oldW * (1/v);
					var newH = oldH * (1/v);
					shiftX *= (1/v);
					shiftY *= (1/v);
					for(var l in LayerImages) {
						var img = document.getElementById(LayerId+l);
						if(l == 'NAVIGATION' || MAP_ZOOMIN_STYLE == 'scale' || v > 1) {
							img.style.left = img.offsetLeft + shiftX - (newW - oldW) / 2 + 'px';
							img.style.top = img.offsetTop + shiftY - (newH - oldH) / 2 + 'px';
							img.style.width = newW + 'px';
							img.style.height = newH + 'px';
						} else if(MAP_ZOOMIN_STYLE == 'clip') {
							var x2 = DrawBox.offsetLeft + DrawBox.offsetWidth;
							var y2 = DrawBox.offsetTop + DrawBox.offsetHeight;
							img.style.clip = 'rect('+DrawBox.offsetTop+'px,'+x2+'px,'+y2+'px,'+DrawBox.offsetLeft+'px)';
						} else if(MAP_ZOOMIN_STYLE == 'blank') {
							img.src = BLANK_IMAGE;
						}
						img = null;
					}
					MySelf.zoomTo(0,0,1);

				}
				ImageControl = null;
			});
			EventManager.addEventListener('changetool', 'MapZoomTool', function () {
				EventManager.removeEventListener('finishdrawing','MapZoomTool');
				EventManager.removeEventListener('changetool', 'MapZoomTool');
			});
		}

		ImageControl.style.zIndex = MAP_CONTROL_INDEX;
		if(ImageMapControlId) {
			document.getElementById(ImageMapControlId).style.zIndex = MAP_CONTROL_INDEX - 1;
		}
		ZoomDirection = v;
		ImageControl = null;
	}
	


	this.clearLayerParams = function(mapTitle) {
		LayerParameters[mapTitle] = new Array();
	}

	this.addLayerParam = function(mapTitle,paramName,paramValue) {
		if(!LayerParameters[mapTitle]) { LayerParameters[mapTitle] = new Array(); }
		if(!LayerParameters[mapTitle][paramName]) {
			LayerParameters[mapTitle][paramName] = new Array();
		}
		LayerParameters[mapTitle][paramName].push(paramValue);
	}

	this.getLayerParamString = function(mapTitle) {
		if(!LayerParameters[mapTitle]) { return false; }
		var urlString = '';
		for(var p in LayerParameters[mapTitle]) {
			if(LayerParameters[mapTitle][p]) {
				for(var i = 0; i < LayerParameters[mapTitle][p].length; i++) {
					urlString += p+'='+LayerParameters[mapTitle][p][i]+'&';
				}
			}
		}
		urlString = urlString.substring(0,urlString.length - 1);
		return urlString;
	}

	this.addLayer = function (mapXMLObj) {
		var mapFile = mapXMLObj.getElementsByTagName('file')[0].firstChild.nodeValue;
		var mapTitle = mapXMLObj.getAttribute('title');
		var mapOpacity = mapXMLObj.getAttribute('opacity');
		var mapData = mapXMLObj.getAttribute('popups');
		var zIndex = mapXMLObj.getAttribute('zindex');


		if(!mapData) {
			mapData = MAP_ASSUME_DATA; 
		} else {
			if(mapData.match(/true/i)) {
				mapData = true;
			} else {
				mapData = false;
			}
		}

		LayerLoadData[mapTitle] = mapData;
			

		mapFile = trim(mapFile);
		LayerURLs[mapTitle] = mapFile;
		var newLayer = document.getElementById(LayerId+mapTitle);

		if(!newLayer) {
			LayerImages[mapTitle] = 1;
			newLayer = document.createElement('img');
			newLayer.style.position = 'absolute';
			newLayer.setAttribute('id', LayerId+mapTitle);
			if(ImageParentId) {
				document.getElementById(ImageParentId).appendChild(newLayer);
			}
		}
		if(mapOpacity) {
			if(OPACITY_CONTROLS == 'full') {
				setOpacity(newLayer, mapOpacity);
			}
		}
		

		if(MAP_MAPBOOK_ORDER) {
			if(zIndex) {
				newLayer.style.zIndex = MAP_MAX_ZINDEX - parseInt(zIndex);
			} else {
				newLayer.style.zIndex = MAP_MAX_ZINDEX;
			}
		} else {
			if(!zIndex || zIndex > 0) {
				newLayer.style.zIndex = MAP_MAX_ZINDEX;
				for(var title in LayerImages) {
					var img = document.getElementById(LayerId+title);
					img.style.zIndex = parseInt(img.style.zIndex)-1;
					img = null;
				}
				min_z_index--;
			} else {
				min_z_index--;
				newLayer.style.zIndex = min_z_index;
			}
		}

		MySelf.autoFitLayer(mapTitle);
		MySelf.clearLayerParams(mapTitle);
		var mapParams = mapXMLObj.getElementsByTagName('param');
		for(var i = 0; i < mapParams.length; i++) {
			MySelf.addLayerParam(mapTitle, mapParams[i].getAttribute('name'), mapParams[i].getAttribute('value'));
		}
		/*
		 * Custom Legend settings
		 */
		if(mapXMLObj.getElementsByTagName('legend')[0] && mapXMLObj.getElementsByTagName('legend')[0].firstChild.nodeValue) {
			LayerLegends[mapTitle] = mapXMLObj.getElementsByTagName('legend')[0].firstChild.nodeValue;
		}
		newLayer = null;
		Events.triggerEvent('refreshlayer', mapTitle);
	}

	this.confirmRemoveLayer = function(mapTitle) {
		MySelf.removeLayer(mapTitle);
	}

	this.removeLayer = function(mapTitle) {
		var imageMap = document.getElementById(MapId+'ImageMap-'+mapTitle);
		if(imageMap) {
			var areas = imageMap.getElementsByTagName('area');
			for(var a = areas.length - 1; a >= 0; a--) {
				var p = document.getElementById(areas[a].popup[0]).parentNode;
				for(var pop = 0; pop < areas[a].popup.length; pop++) {
					p.removeChild(document.getElementById(areas[a].popup[pop]));
				}
				imageMap.removeChild(areas[a]);
			}
			var p = imageMap.parentNode;
			p.removeChild(imageMap);
		}
		if(LayerImages[mapTitle]) {
			var img = document.getElementById(LayerId+mapTitle);
			if(img && img.parentNode) {
				var p = img.parentNode;
				p.removeChild(img);
			}
			delete LayerImages[mapTitle];
			delete LayerURLs[mapTitle];
			if(DrawingArea[mapTitle]) {
				delete DrawingArea[mapTitle];
			}
			img = null;
		}
	}

	this.getLayerURLs = function() {
		var zOrdered = MySelf.getLayersByZIndex();
		var urls = new Array();
		for(var i = 0; i < zOrdered.length; i++) {
			if(zOrdered[i] != 'NAVIGATION') {
				urls[zOrdered[i]] = LayerURLs[zOrdered[i]];
			}
		}
		return urls;
	}

	this.getLayerTitles = function() {
		var layers = new Array();
		for(var title in LayerImages) {
			if(title != 'NAVIGATION') {
				layers[layers.length] = title;
			}
		}
		return layers;
	}

	this.getLayersByZIndex = function() {
		var orderedLayers = new Array();
		for(var title in LayerImages) {
			orderedLayers[orderedLayers.length] = title;
		}
		for(var i = 0; i < orderedLayers.length; i++) {
			for(var  j = i; j < orderedLayers.length; j++) {
				var iImage = document.getElementById(LayerId+orderedLayers[i]);
				var jImage = document.getElementById(LayerId+orderedLayers[j]);
				if(parseInt(iImage.style.zIndex) < parseInt(jImage.style.zIndex)) {
					var temp = orderedLayers[i];
					orderedLayers[i] = orderedLayers[j];
					orderedLayers[j] = temp;
				}
				iImage = null;
				jImage = null;
			}
		}
		return orderedLayers;
	}

	this.showOrderedLayers = function() {
		var str = '';
		var layers = this.getLayersByZIndex();
		for(var i = 0; i < layers.length; i++) {
			str = str+layers[i]+'('+LayerImages[layers[i]].style.zIndex+'),';
		}
	}
	
	this.shiftLayer = function(mapTitle, direction) {
		var orderedLayers = this.getLayersByZIndex();
		var selectedLayerIndex = -1;
		
		for(var i = 0; i < orderedLayers.length && selectedLayerIndex < 0; i++) {
			if(orderedLayers[i] == mapTitle) {
				selectedLayerIndex = i;
			}
		}
		if(selectedLayerIndex >= 0) {
			var baseImage = document.getElementById(LayerId+mapTitle);
			var targetImage = null; 
			/* Move the layer up, if it's not at the top*/
			if(direction > 0 && selectedLayerIndex > 0) {
				targetImage = orderedLayers[selectedLayerIndex-1];
			/* Move the layer down, if it's not at the bottom */
			} else if(direction < 0 && selectedLayerIndex < orderedLayers.length - 1) {
				targetImage = orderedLayers[selectedLayerIndex+1];
			}
			if(targetImage) {
				targetImage = document.getElementById(LayerId+targetImage);
				var tempIndex = baseImage.style.zIndex;
				baseImage.style.zIndex = targetImage.style.zIndex;
				targetImage.style.zIndex = tempIndex;
			}
		}
	}

	var REFRESH_LOCKOUT = false;

	this.zoomToScaledImage = function() {
		var ImageControl = document.getElementById(ImageControlId);
		/* zoomLayer will be Scaled to whatever the new image should be */
		var zoomLayer = document.getElementById(LayerId+'NAVIGATION');
		var imageTop = zoomLayer.offsetTop;
		var imageLeft = zoomLayer.offsetLeft;

		var imageWidth = zoomLayer.offsetWidth;
		var imageHeight = zoomLayer.offsetHeight;

		/* ImageControl never changes size */
		var viewportTop = ImageControl.offsetTop;
		var viewportLeft = ImageControl.offsetLeft;
		var viewportWidth = ImageControl.offsetWidth;
		var viewportHeight = ImageControl.offsetHeight;

		/* Scale Change between the two images */
		var scale = viewportWidth / imageWidth * MapScale; 
		
		var groundTop = MapCenterPoint.y - MapScale * viewportHeight / 2;
		var groundLeft = MapCenterPoint.x - MapScale * viewportWidth / 2;

		/* Find the new Centerpoint in Pixel Space */
		var viewportMidX = (viewportLeft - imageLeft) + viewportWidth / 2;
		var viewportMidY = (viewportTop - imageTop) + viewportHeight / 2;
		var imagePercentX = viewportMidX / imageWidth;
		var imagePercentY = 1 - (viewportMidY / imageHeight);	

		var unscaledImageX = imagePercentX*viewportWidth;
		var unscaledImageY = imagePercentY*viewportHeight;

		/* Convert to ground units */
		var groundCenterX = groundLeft + unscaledImageX * MapScale;
		var groundCenterY = groundTop + unscaledImageY * MapScale; 
		EventManager.changePointScale(new Point(groundCenterX, groundCenterY), scale / ScaleFactor);
	}

	this.zoomToViewString = function(str) {
		var psC = convertViewString(str,MySelf.getMapSize());
		EventManager.changePointScale(psC.Center, psC.Radius);
	}
	
	this.zoomTo = function(x,y,direction) {
		var ImageControl = document.getElementById(ImageControlId);
		if(REFRESH_LOCKOUT) return false;

		var zoomLayer = document.getElementById(LayerId+'NAVIGATION');
		var imgW = zoomLayer.offsetWidth;
		var imgH = zoomLayer.offsetHeight;

		var mapSize = MySelf.getMapSize();
		var diffY = ImageControl.offsetTop - zoomLayer.offsetTop;
		var diffX = ImageControl.offsetLeft - zoomLayer.offsetLeft;
		
		var PointImageX = diffX + x;
		var PointImageY = diffY + y;

		var wPercent = PointImageX / imgW;
		var hPercent = PointImageY / imgH;
		// Do some zooming tricks
		var imgSizeFactor = 1 / direction; // Inverse of the scale change
		var newW = imgSizeFactor * imgW;
		var newH = imgSizeFactor * imgH;

		var xLoc = x-(mapSize.x / 2);
		var yLoc = ((mapSize.y / 2) - y);
		var wRatio = newW / ImageControl.offsetWidth;
		var hRatio = newH / ImageControl.offsetHeight;
		if(hRatio > wRatio) { wRatio = hRatio; }

		var newScale = MapScale * wRatio;

		if(newScale < .01) {
			return false;
		}
				
		if(direction != 1) {
			for(var layer in LayerImages) {
				//var layer = 'NAVIGATION';
				var img = document.getElementById(LayerId+layer);
				//img.style.visibility = 'hidden';
				img.style.width = newW + 'px';
				img.style.height = newH + 'px';
				img.style.left = (newW * wPercent * -1 +  x)+'px';
				img.style.top = (newH * hPercent * -1 +  y)+'px';
				//img.style.visibility = 'visible';
				img = null;
			}
		}

		

		window.triggerMapRefresh = function(time) {
			REFRESH_LOCKOUT = true;
			if(time == LastActionTime) {
				MySelf.zoomToScaledImage();
				LastActionTime = 0;
			}
			REFRESH_LOCKOUT = false;
		}
		LastActionTime ++;
		setTimeout("window.triggerMapRefresh("+LastActionTime+")", 1000);
		ImageControl = null;
	}

		
	this.drawPoint = function(x,y) {
		var lastPoint = DrawnPoints[DrawnPoints.length-1];
		if(lastPoint) {
			var distx = (lastPoint.x - x);
			var disty = (lastPoint.y - y);
			
			var dist = distx*distx + disty*disty;
			if(dist < 100) {
				return false;
			}
		}

		var newPoint = document.createElement('div');
		newPoint.style.position = 'absolute';
		newPoint.style.background = 'red';
		newPoint.style.border = 'none';
		newPoint.style.overflow = 'hidden';
		newPoint.style.width = '5px';
		newPoint.style.height = '5px';
		newPoint.style.left = x+'px';
		newPoint.style.top = y+'px';
		newPoint.style.zIndex = MAP_DRAWING_INDEX;
		newPoint.className = 'DrawnPoint';
		document.getElementById(ImageParentId).appendChild(newPoint);
		newPoint = null;

		DrawnPoints[DrawnPoints.length] = new Point(x,y);
	}

	this.getMapSize = function() {
		var ImageControl = document.getElementById(ImageControlId);
		var SizeObject = ImageControl;
		if(!SizeObject.offsetWidth) {
			SizeObject = document.getElementById(MapParentId);
		}
		var point = new Point(SizeObject.offsetWidth, SizeObject.offsetHeight);
		ImageControl = null;
		return point;
	}


	function showPopupMenuFn(popupDivId) {
		return function(event) {
			var ReferenceIndicator = document.getElementById(ReferenceIndicatorId);
			if(ReferenceIndicator) { return true; }
			popupDiv = document.getElementById(popupDivId);

			if(window.event) { event = window.event; event.which = event.button; }
			if(event.which > 1) {
				if(popupDiv.style.visibility == 'hidden') {
					var objLocation = objectClientPosition(document.getElementById(MapParentId));
					popupDiv.style.left = ZoomPoint.x+objLocation.x+'px';
					popupDiv.style.top = ZoomPoint.y+objLocation.y+'px';
					popupDiv.style.visibility = 'visible';
				} else {
					popupDiv.style.visibility = 'hidden';
				}
			}
			return preventDefaultEvent(event);
		}
	}


	function hidePopupMenuFn(popupMenuId) {
		return function () {
			document.getElementById(popupMenuId).style.visibility = 'hidden';
		}
	}

	this.clearMenu = function (event) {
		if(window.event) { event = window.event; }
		var popupDiv = document.getElementById(PopupMenuId);
		if(popupDiv.style.visibility == 'visible') {
			popupDiv.style.visibility = 'hidden';
		}
		popupDiv = null;
	}

	this.setParent = function(p) {
		MapParentId = p;
		if(MAP_ZOOMIN_STYLE == 'auto' && getBrowser().match(/microsoft/i) && getBrowserVersion() < MAP_IE_SCALE_STYLE_SWITCH) {
			MAP_ZOOMIN_STYLE = 'clip';
		} else if(MAP_ZOOMIN_STYLE == 'auto') {
			MAP_ZOOMIN_STYLE = 'scale';
		}
		
		var MapParent = document.getElementById(MapParentId);

		var ImageParent = document.createElement('div');
		ImageParent.setAttribute('id', ImageParentId);
		ImageParent.style.position = 'absolute';
		ImageParent.style.left = '0px';
		ImageParent.style.top = '0px';
		var ImageControl = document.createElement('input');
		ImageControlId = MapId+'ImageControl';
		ImageControl.setAttribute('id',ImageControlId);
		ImageControl.type = 'Image';
		ImageControl.src = BLANK_IMAGE;
		ImageControl.style.zIndex = MAP_CONTROL_INDEX;

		ImageControl.style.position = 'absolute';
		ImageControl.style.border = 'none';
		ImageControl.style.left = '0px';
		ImageControl.style.top = '0px';
		
		var ImageMapControl = document.createElement('img');
		ImageMapControlId = MapId+'ImageMapControl';
		ImageMapControl.setAttribute('id', ImageMapControlId);
		ImageMapControl.src = BLANK_IMAGE;
		ImageMapControl.style.zIndex = MAP_CONTROL_INDEX - 1;

		ImageMapControl.style.position = 'absolute';
		ImageMapControl.style.border = 'none';
		ImageMapControl.style.left = '0px';
		ImageMapControl.style.top = '0px';

		var imageControlCanvas = document.createElement('canvas');
		imageControlCanvas.style.position = 'absolute';
		imageControlCanvas.style.border = 'none';
		imageControlCanvas.style.left = '0px';
		imageControlCanvas.style.top = '0px';
		imageControlCanvas.style.zIndex = MAP_CONTROL_INDEX - 2;
		imageControlCanvas.setAttribute('id', ImageControlCanvasId);


		MapParent.appendChild(ImageParent);
		MapParent.appendChild(ImageControl);
		MapParent.appendChild(ImageMapControl);
		MapParent.appendChild(imageControlCanvas);
		
		ImageParent = null;

		if(MAP_POPUP_MENU) {
			var popupMenu = document.createElement('div');
			document.getElementsByTagName('body')[0].appendChild(popupMenu);

			popupMenu.setAttribute('id',PopupMenuId);
			popupMenu.style.position = 'absolute';
			popupMenu.style.zIndex = MAP_POPUP_ZINDEX; 
			popupMenu.style.background = '#EEE';
			popupMenu.style.border = 'solid 1px #333';
			popupMenu.style.borderLeft = 'solid 1px #AAA';
			popupMenu.style.borderTop = 'solid 1px #AAA';
			popupMenu.style.cursor = 'pointer';
			popupMenu.style.visibility = 'hidden';
			popupMenu.style.left = '100px';
			popupMenu.style.top = '100px';


			for(menu in MAP_POPUP_FUNCTIONS) {
				var newItem = document.createElement('a');
				newItem.appendChild(document.createTextNode(menu));
				newItem.style.display = 'block';
				newItem.style.textDecoration = 'none';
				newItem.style.color = 'black';
				newItem.setAttribute('id', MapId+'Popup-'+menu);
				newItem.setAttribute('href', MAP_POPUP_FUNCTIONS[menu]);
				newItem.onclick = hidePopupMenuFn(PopupMenuId);
				popupMenu.appendChild(newItem);
				newItem = null;
			}

			popupMenu = null;
			attachEvent(ImageMapControl, 'mousedown', showPopupMenuFn(PopupMenuId));
			attachEvent(ImageControl, 'mousedown', showPopupMenuFn(PopupMenuId));
			attachEvent(document, 'click', MySelf.clearMenu);
		}

		

		ImageControl.oncontextmenu = preventDefaultEvent;
		ImageMapControl.oncontextmenu = preventDefaultEvent;

		attachEvent(ImageControl, 'mousemove', zoomTrackMouse);
		attachEvent(ImageMapControl, 'mousemove', zoomTrackMouse);
		if(MAP_DYNAMIC_ZOOM) {
			attachEvent(ImageControl, 'mousewheel', zoomWheel);
		}

		//MapParent.appendChild(DrawBox);
		refreshDrawBox();
		MapParent.style.overflow = 'hidden';
		ImageControl = null;
		ImageMapControl = null;
		MapParent = null;
		imageControlCanvas = null;

		var navMap = document.createElement('map');
		var mapFile = document.createElement('file');
		navMap.appendChild(mapFile);
		mapFile.appendChild(document.createTextNode('relative://'+BLANK_IMAGE+'?'));
		navMap.setAttribute('title','NAVIGATION');
		MySelf.addLayer(navMap);
		mapFile = null;
		navMap = null;

		this.autoFitLayers();	
	}


	this.processURL = function(url, extents, mSize, mapfile) {
		url = url.replace("%EXTENTS%",extents);
		var extentsArr = extents.split(' ');
		url = url.replace("%MINX%", extentsArr[0]);
		url = url.replace("%MINY%", extentsArr[1]);
		url = url.replace("%MAXX%", extentsArr[2]);
		url = url.replace("%MAXY%", extentsArr[3]);
		url = url.replace("%SIZE%", mSize.toString(" "));
		url = url.replace("%WIDTH%", mSize.x);
		url = url.replace("%HEIGHT%", mSize.y);
		url = url.replace("%MAPBOOK%", MAPBOOK_FILE);
		url = url.replace("%MAPFILE%", mapfile);
		return url;

	}

	this.preprocessURL = function(type, mapTitle) {
		var layerURL = MAPSERVER_IMAGE_URL;
		if(type == 'legend') {
			layerURL = MAPSERVER_LEGEND_URL;
		} else if(type == 'data') {
			layerURL = MAPSERVER_DATA_URL;
		}

		if(LayerURLs[mapTitle].match(/http/i)) {
			layerURL = LayerURLs[mapTitle];
		} else if(LayerURLs[mapTitle].match(/relative/i)) {
			layerURL = new String(LayerURLs[mapTitle]).replace('relative://','');
		} else {
			layerURL = layerURL.replace('%MAPFILE%', LayerURLs[mapTitle]);
		}
		var parameters = MySelf.getLayerParamString(mapTitle);
		if(!parameters) { parameters = ''; }
		if(parameters) {
			layerURL  = layerURL + '&' + parameters;
		}
		var date = new Date();
		layerURL = layerURL + '&MOOSETIME='+date.getTime();
		return layerURL;
	}

	this.getExtent = function (delim) {
		var mapSize = MySelf.getMapSize();
		var gisOffsetW = mapSize.x / 2 * MapScale;
		var gisOffsetH = mapSize.y / 2 * MapScale;
		MapCenterPoint.clean();
		var minPoint = new Point(MapCenterPoint.x - gisOffsetW, MapCenterPoint.y - gisOffsetH);
		var maxPoint = new Point(MapCenterPoint.x + gisOffsetW, MapCenterPoint.y + gisOffsetH);
		var MapCoords = new LineSegment(minPoint,maxPoint);

		var mSize = this.getMapSize();

		var extent = MapCoords.toString(delim);
		return extent;
	}


	function onloadNewImage() {
		var layerName = this.getAttribute('layerName');
		var img = document.getElementById(LayerId+layerName);
		if(img) {
			var p = img.parentNode;
			var zIndex = img.style.zIndex;
			this.style.zIndex = zIndex;
			this.style.visibility = img.style.visibility;
			var origOpacity = getOpacity(img);

			if(MAP_FADE_EFFECTS) {
				setOpacity(this, 0);
				img.setAttribute('id',LayerId+layerName+'-old');
				fadeObject(LayerId+layerName+'-old', origOpacity, 0, MAP_FADE_EFFECTS_DURATION, true);
			
				fadeObject(LayerId+layerName, 0, origOpacity, MAP_FADE_EFFECTS_DURATION, false);	
			} else {
				this.style.visibility = 'visible';
				if(OPACITY_CONTROLS == 'full') {
					setOpacity(this, origOpacity);	
				}
			}
			this.setAttribute('id',LayerId+layerName);
			p.removeChild(img);
			img = null;
			/* This is required to make mode='blank' work. */
			this.onload = null;
		} else {
			var MapParent = document.getElementById(MapParentId);
			this.setAttribute('id',LayerId+layerName);
			MapParent.appendChild(this);
			MapParent = null;
		}
		EventManager.triggerEvent('imagelayer', layerName);
		LayerImages[layerName] = 1;
		MySelf.autoFitLayer(layerName);
	}

	function onAbortNewImage() {
		this.src = this.src+'?1=1';
	}

	this.refreshLayer = function(mapTitles) {
		var mapSize = MySelf.getMapSize();
		
		var mSize = this.getMapSize();

		var extent = this.getExtent(" ");
		var size = this.getMapSize().toString(" ");

		for(var i = 0; i < mapTitles.length; i++) {
			if(!LayerURLs[mapTitles[i]]) {
				break;
			}

			/*
			 * Do a little clean up on the Layer Image Maps 
			 */
			var imageMap = document.getElementById(MapId+'ImageMap-'+mapTitles[i]);
			if(imageMap) {
				var areas = imageMap.getElementsByTagName('area');
				for(var a = areas.length - 1; a >= 0; a--) {
					var p = document.getElementById(areas[a].popup[0]).parentNode;
					for(var pop = 0; pop < areas[a].popup.length; pop++) {
						p.removeChild(document.getElementById(areas[a].popup[pop]));
					}
					imageMap.removeChild(areas[a]);
				}
			}

			if(!MapReference) { /* Main Map Takes care of Popups and Legend, no need to make extra calls */
				if(MAP_POPUPS_ENABLED) {
					var parameters = MySelf.getLayerParamString(mapTitles[i]);
					if(LayerLoadData[mapTitles[i]] && !LayerURLs[mapTitles[i]].match(/http/i) && !LayerURLs[mapTitles[i]].match(/relative:\/\//)) {
						var dataURL = MAPSERVER_DATA_URL;
						dataURL = this.processURL(dataURL, extent, mSize, LayerURLs[mapTitles[i]]);
						if(parameters) {
							dataURL = dataURL + '&' + parameters;
						}
						this.loadData(mapTitles[i], dataURL);
						logDebugMessage('Data URL', false, dataURL);
					}
				}

				if(MAP_LEGENDS_ENABLED && (!LayerURLs[mapTitles[i]].match(/http/i) || LayerLegends[mapTitles[i]])) {
					var legendURL = MAPSERVER_LEGEND_URL;
					if(LayerLegends[mapTitles[i]]) {
						legendURL = LayerLegends[mapTitles[i]];
					}
					legendURL = this.processURL(legendURL, extent, mSize, LayerURLs[mapTitles[i]]);
					if(parameters) {
						legendURL = legendURL + '&' + parameters;
					}
					
					var obj = new LegendObj(mapTitles[i], legendURL);
					EventManager.triggerEvent('legendlayer', obj);

					logDebugMessage('Legend URL', false, legendURL);
				}
			}
	
			var imageURL = MAPSERVER_IMAGE_URL;
			/*if(LayerURLs[mapTitles[i]].match(/http/i)) {
				imageURL = LayerURLs[mapTitles[i]];
			}*/
			imageURL = this.preprocessURL('image', mapTitles[i]);
			imageURL = this.processURL(imageURL, extent, mSize, LayerURLs[mapTitles[i]]);
			var newImage = document.createElement('img');
			newImage.setAttribute('layerName',mapTitles[i]);
			newImage.setAttribute('id',LayerId+mapTitles[i]+'-Loading');
			newImage.style.position = 'absolute';
			newImage.style.visibility = 'hidden';
			document.getElementById(ImageParentId).appendChild(newImage);
			newImage.width = 1;
			newImage.height = 1;
			newImage.onload = onloadNewImage;
			/* This is an IE Hack to make jumping around work correctly, you may carry on ... */
			if(document.all) {
				newImage.onabort = onAbortNewImage;
			}
			//newImage.setAttribute('src', imageURL+'&'+parameters);
			newImage.src = imageURL+'&'+parameters;
			logDebugMessage('Image URL', false, imageURL);
			newImage = null;	

		}

	}

	var Popups = new Array();
	this.getPopupData = function(i) {
		return Popups[i];
	}

	this.getPointScale = function() {
		return new Circle(MapCenterPoint, MapScale);
	}
	
	this.clearPopups = function() {
		var p = document.getElementById(MapParentId); //ImageControl.parentNode;
		var d = p.getElementsByTagName('div');
		for(var i = 0; i < d.length; i++) {
			if(d[i].className == 'Popup') {
				d[i].style.visibility = 'hidden';
			}
		}
	}



	function popupAreaMove(event) {
		if(window.event) { event = window.event; }
		var area = getEventTarget(event);
		if(!area.popupMove) { return false; }
		var ImageControl = document.getElementById(ImageControlId);
		var offsetPoint = objectClientPosition(ImageControl);
		var mouseY = event.clientY-offsetPoint.y;
		var mouseX = event.clientX-offsetPoint.x;
		var offsetX = 5;
		var offsetY = 5;
		for(var i = 0; i < area.popup.length; i++) {
			popup = document.getElementById(area.popup[i]);
			popup.style.zIndex =  MAP_POPUP_ZINDEX;
			/*if(mouseY + popup.offsetHeight < ImageControl.offsetHeight) {*/
			if(mouseY < ImageControl.offsetHeight / 2) {
				popup.style.top = mouseY+offsetY+'px';
				popup.style.bottom = 'auto';
			} else {
				popup.style.bottom = (ImageControl.offsetHeight - mouseY)+offsetY+5+'px';
				popup.style.top = 'auto';
			}
			/*if(mouseX + popup.offsetWidth < ImageControl.offsetWidth) {*/
			if(mouseX < ImageControl.offsetWidth /2 ) {
				popup.style.left = mouseX+offsetX+'px';
				popup.style.right = 'auto';
			} else {
				popup.style.right = (ImageControl.offsetWidth - (mouseX-offsetX))+'px';
				popup.style.left = 'auto';
			}

			popup.style.visibility = 'visible';
			offsetY += 10;
			offsetX += 10;
		}
		area = null;
		ImageControl = null;
	}

	function popupAreaOut(event) {
		if(window.event) { event = window.event; }
		var area = getEventTarget(event);
		if(area.popup) {
			for(var i = 0; i < area.popup.length; i++) {
				document.getElementById(area.popup[i]).style.visibility = 'hidden';
			}
		}
		area = null;
	}

	function popupAreaUp(event,area) {
		if(event || window.event) { area = getEventTarget(event); } 
		if(!area) { area = this; }
		if(area.popupMove) {
			removeEvent(area, 'mouseout', popupAreaOut);
			area.popupMove = false;
		} else {
			attachEvent(area, 'mouseout', popupAreaOut);
			area.popupMove = true;
		}
		area = null;
	}

	function closePopup (event) {
		if(window.event) { event = window.event; }
		var area = getEventTarget(event).parentNode; /* Here via a click, so the image gets the target for some reason */
		var myArea = document.getElementById(area.parentNode.getAttribute('AreaId'));
		area.parentNode.style.visibility = 'hidden';
		if(myArea) {
			popupAreaUp(null,myArea);
		}
		area = null;
	}



	this.loadData = function(id, dataCall) {
		var http = getHTTPObject();
		/*var xmlhttp = getHTTPObject();
		xmlhttp.open('HEAD', dataCall, true);
		xmlhttp.onreadystatechange = function() {
			if(xmlhttp.readyState == 4) {
				if(xmlhttp.status == 200) {
					//alert(xmlhttp.getAllResponseHeaders());
				} else {
					return false;
				}
			}
		}
		xmlhttp.send(null);*/

		http.open('GET', dataCall, true);
		http.onreadystatechange = function() {
			if(http.readyState == 4) {
				if(http.responseXML) {
					var ImageControl = document.getElementById(ImageControlId);
					var imageMap = document.createElement('map');
					var imageMapName = MapId+'ImageMap-'+id;
					imageMap.id = imageMapName;
					if(document.all) {
						imageMap = document.createElement('<map name="'+imageMapName+'">');
					} else {
						imageMap.name = imageMapName;
					}
					var imageMapTest = document.getElementById(imageMapName);

					imageMap.className = 'ImageMaps';
					if(imageMapTest) { imageMap = imageMapTest; }
					var popups = http.responseXML.getElementsByTagName('popup');
					if(popups.length > LAYER_MAXIMUM_OBJECTS) { return false; }

					var styleObject = http.responseXML.getElementsByTagName('style')[0];
					var contentsFunction = function () { };
					var titleFunction = function () { };
					
					if(styleObject) {
						contentsFunction = getStyleFunction('contents', styleObject);
						titleFunction = getStyleFunction('title', styleObject);
					}
					var xCoord = new Array();
					var yCoord = new Array();
					var allShapes = new Array();
					var allShapesTypes = new Array();

					var bin = new Array();
					for(var i = 0; i < popups.length; i++) {
						var coords = popups[i].getAttribute('coords');
							var coordsArr = coords.split(',');
							var found = false;

							for(var x in xCoord) {
								var w = Math.pow(xCoord[x] - coordsArr[0],2);
								var h = Math.pow(yCoord[x] - coordsArr[1],2);
								if(w+h < 49) {
									found = true;
									bin[x][bin[x].length] = popups[i];
								}
							}
							if(!found) {
								xCoord[coords] = coordsArr[0];
								yCoord[coords] = coordsArr[1];
								bin[coords] = new Array();
								bin[coords][0] = popups[i];
								allShapes[coords] = popups[i].getAttribute('shape');
							}


					}

					var popupId = 0;
					var areaId = 0;
					for(var b in bin) {
						var area = document.createElement('area');
						imageMap.appendChild(area);
						if(allShapes[b].match(/line/i)) {
							area.shape = 'poly';
							var points = b.split(' ');
							var pA = new Point(), pB = new Point();
							pA.fromString(points[0], ',');
							pB.fromString(points[1], ',');
							var lineSeg = new LineSegment(pA,pB);
							var poly = lineSeg.buffer(3);
							area.coords = poly.toString(',');
						} else {
							area.shape = allShapes[b];
							area.coords = b;
						}
						area.popup = new Array();
						area.setAttribute('id', MapId+id+'Area-'+areaId);
						areaId++;

						for(var i = 0; i < bin[b].length; i++) {
							popupId++;
							var myPopup = document.createElement('div');
							myPopup.style.border = 'solid 1px #333';
							myPopup.style.borderLeft = 'solid 1px #AAA';
							myPopup.style.borderTop = 'solid 1px #AAA';

							myPopup.setAttribute('id', MapId+id+'-Popup-'+popupId);
							myPopup.setAttribute('AreaId', area.getAttribute('id'));
							myPopup.className = 'Popup';
							document.getElementById(MapParentId).appendChild(myPopup);

							var myPopupTitle = document.createElement('div');
							myPopupTitle.setAttribute('id', myPopup.getAttribute('id')+'-Title');
							myPopupTitle.appendChild(document.createTextNode(bin[b][i].getElementsByTagName('title')[0].firstChild.nodeValue));
							myPopup.appendChild(myPopupTitle);
							myPopup.draggable = new Draggable(myPopup.getAttribute('id'), myPopupTitle, true,true,true);

							titleFunction(myPopupTitle);
							contentsFunction(myPopup);
							var items = bin[b][i].getElementsByTagName('item');

							for(var item = 0; item < items.length; item++) {
								var title = items[item].getAttribute('title');
								var val = '';
								if(items[item].firstChild) {
									val = items[item].firstChild.nodeValue;
								}
								var titleObj = document.createElement('span');
								var valObj = document.createElement('span');
								myPopup.appendChild(titleObj);
								myPopup.appendChild(valObj);
								myPopup.appendChild(document.createElement('br'));

								titleObj.style.fontWeight = 'bold';
								if(title) {
									titleObj.appendChild(document.createTextNode(title));
								}
								if(items[item].getAttribute('href')) {
									var link = document.createElement('a');
									link.href = items[item].getAttribute('href');
									var tgt = items[item].getAttribute('target');
									if(!tgt) { tgt = '_blank'; }
									link.setAttribute('target',tgt);
									link.innerHTML = val;
									valObj.appendChild(link);
									link = null;
								} else {
									if(val.match(/href/i)) { /* There's links in here, possibly messed up ones */
										val = val.replace(/\\\"/g, "");
										val = val.replace(/\\\'/g, "'");
									}
									valObj.innerHTML = val;
								}
								titleObj = null;
								valObj = null;
							}

							var closeL = document.createElement('a');
							closeL.style.position = 'absolute';
							closeL.style.right = '0px';
							closeL.style.top = '0px';
							myPopup.appendChild(closeL);

							var closeImg = document.createElement('img');
							closeImg.src = REMOVE_ICON;
							closeImg.style.width = '8px';
							closeImg.style.height = '8px';
							closeL.appendChild(closeImg);

							attachEvent(closeL, 'click', closePopup);

							area.popup[area.popup.length] = myPopup.getAttribute('id');
							closeL = null;
							myPopup = null;
							myPopupTitle = null;
						}
						area.popupMove = true;
						attachEvent(area, 'mousemove', popupAreaMove);
						attachEvent(area, 'mouseup', popupAreaUp);
						attachEvent(area, 'mouseout', popupAreaOut);
						area = null;
					}
					if(!imageMapTest) {
						document.getElementsByTagName('body')[0].appendChild(imageMap);
					}
					if(popups.length > 0) {
						EventManager.triggerEvent('datalayer',id);
					}
					imageMap = null;
				}
				http = null;
			}
		}
		http.send(true);
	}

	this.activateLayer = function(mapTitle) {
		var imageMapName = MapId+'ImageMap-'+mapTitle;
		var ImageMapControl = document.getElementById(ImageMapControlId);
		var imageControlCanvas = document.getElementById(ImageControlCanvasId);
		ImageMapControl.setAttribute('usemap', '#'+imageMapName);
		ImageMapControl.setAttribute('useMap', '#'+imageMapName);
		ImageMapControl.ismap = true;
		
		ImageMapControl.style.zIndex = MAP_CONTROL_INDEX;
		var ImageControl = document.getElementById(ImageControlId);
		ImageControl.style.zIndex = MAP_CONTROL_INDEX - 1;
		imageControlCanvas.style.zIndex = MAP_CONTROL_INDEX - 2;

		ImageControl = null;
		ImageMapControl = null;
		imageControlCanvas = null;
	}

	function refreshCanvas() {
		var imageControlCanvas = document.getElementById(ImageControlCanvasId);
		imageControlCanvas.style.zIndex = MAP_CONTROL_INDEX - 2;
		imageControlCanvas.style.position = 'absolute';
		imageControlCanvas.style.top = '0px';
		imageControlCanvas.style.left = '0px';
		imageControlCanvas = null;
	}



	this.autoFitLayers = function() {
		var MapParent = document.getElementById(MapParentId);
		var mapSize = new Point(MapParent.offsetWidth, MapParent.offsetHeight);
		var ImageParent = document.getElementById(ImageParentId);
		var ImageControl = document.getElementById(ImageControlId);
		var ImageMapControl = document.getElementById(ImageMapControlId);

		ImageParent.style.width = mapSize.x+'px';
		ImageParent.style.height = mapSize.y+'px';
		for(var title in LayerImages) {
			MySelf.autoFitLayer(title);
		}
		ImageControl.style.width = mapSize.x+'px';
		ImageControl.style.height = mapSize.y+'px';
		if(ImageMapControl) {
			ImageMapControl.style.width = mapSize.x+'px';
			ImageMapControl.style.height = mapSize.y+'px';
		}
		refreshCanvas();
		var imageControlCanvas = document.getElementById(ImageControlCanvasId);
		imageControlCanvas.width = mapSize.x;
		imageControlCanvas.height = mapSize.y;
		if(document.all) {
			imageControlCanvas.style.width = mapSize.x+'px';
			imageControlCanvas.style.height = mapSize.y+'px';
		}
	}

	this.autoFitLayer = function(mapTitle) {
		var mapSize = this.getMapSize();
		this.resizeLayer(mapTitle,mapSize.x,mapSize.y);
	}

	this.resizeLayer = function(mapTitle,w,h) {
		//LayerImages[mapTitle].style.visibility = 'hidden';
		var img = document.getElementById(LayerId+mapTitle);
		img.style.top = '0px';
		img.style.left = '0px';
		img.style.width = w+'px';
		img.style.height = h+'px';
		img = null;
		//LayerImages[mapTitle].style.visibility = 'visible';
	}

	this.reloadLayers = function() {
		MySelf.autoFitLayers();
		MySelf.refreshLayers();
	}

	/*
	 * These functions are for Reference Map behaviour
	 */

	function toggleReferenceIndicatorFunction(event) {
		if(window.event) { event = window.event; event.which = event.button; }
		if(event.which > 1) {
			// If you right click, hide the reference map box for better zooming.
			var ReferenceIndicator = document.getElementById(ReferenceIndicatorId);
			event.returnValue = false;
			if(ReferenceIndicator.style.zIndex < MAP_CONTROL_INDEX) {
				ReferenceIndicator.style.zIndex = MAP_CONTROL_INDEX + 1;
			} else {
				ReferenceIndicator.style.zIndex = MAP_CONTROL_INDEX - 1;
			}
			var currentBg = ReferenceIndicator.style.background;
			ReferenceIndicator.style.background = ReferenceIndicator.swapBG;
			ReferenceIndicator.swapBG = currentBg;

			var currentBorder = ReferenceIndicator.style.border;
			ReferenceIndicator.style.border = ReferenceIndicator.swapBorder;
			ReferenceIndicator.swapBorder = currentBorder;

			ReferenceIndicator = null;
		} else {
			if(this._onmousedown) {
				this._onmousedown(event);
			}
		}
		return preventDefaultEvent(event);
	}


	this.refreshLayers = function() {
		if(MapReference) {
			var ImageControl = document.getElementById(ImageControlId);
			var oldMap = MapReference.getMapSize();
			var newW = oldMap.x / ScaleFactor;
			var newH = oldMap.y / ScaleFactor;
			var MySize = MySelf.getMapSize();
			var left = (MySize.x - newW) / 2;
			var top = (MySize.y - newH) / 2;
			var ReferenceIndicator = document.getElementById(ReferenceIndicatorId);
			if(!ReferenceIndicator) { 
				ReferenceIndicator = document.createElement('div');
				ReferenceIndicator.setAttribute('id', ReferenceIndicatorId);
				ReferenceIndicator.gDRAGGER = new Draggable(ReferenceIndicatorId, ReferenceIndicator, true, true, true);

				ImageControl._onmousedown = ImageControl.onmousedown;
				attachEvent(ImageControl,'mousedown', toggleReferenceIndicatorFunction);
				ImageControl.oncontextmenu = preventDefaultEvent;
				ReferenceIndicator.oncontextmenu = preventDefaultEvent;

				attachEvent(ReferenceIndicator, 'mousedown', toggleReferenceIndicatorFunction);


				ReferenceIndicator.gDRAGGER.onDrop = function (trig,obj) {
					trig = obj;
					var diffX = trig.offsetLeft - left;
					var diffY = trig.offsetTop - top;
					for(var l in LayerImages) {
						var img = document.getElementById(LayerId+l);
						img.style.left = img.offsetLeft - diffX + 'px';
						img.style.top = img.offsetTop - diffY + 'px';
						img = null;
					}
					MySelf.zoomToScaledImage();
				}

			}
						
			ReferenceIndicator.style.position = 'absolute';
			ReferenceIndicator.style.top = top + 'px';
			ReferenceIndicator.style.left = left + 'px';
			ReferenceIndicator.style.background = 'white';
			ReferenceIndicator.style.border = 'solid 1px black';
			ReferenceIndicator.style.width = newW+'px';
			ReferenceIndicator.style.height = newH+'px';
			ReferenceIndicator.style.zIndex = MAP_POPUP_ZINDEX;
			ReferenceIndicator.style.overflow = 'hidden';
			ReferenceIndicator.swapBG = '';
			ReferenceIndicator.swapBorder = 'solid 3px #A0A';
			setOpacity(ReferenceIndicator, 75);
			document.getElementById(MapParentId).appendChild(ReferenceIndicator);
			ReferenceIndicator.style.zIndex = MAP_CONTROL_INDEX+1;

			ImageControl = null;
			ReferenceIndicator = null;
		}
		var arr = new Array();
		for(var title in LayerImages) {
			arr[arr.length] = title;
		}
		MySelf.refreshLayer(arr);
	}

	function getStyleFunction(tagType, styleXML) {
		if(!styleXML) { return function(obj) { }; }
		var tag = styleXML.getElementsByTagName(tagType);
		var evalString = '';

		for(var t = 0; t < tag.length; t++) {
			var attrs = tag[t].getElementsByTagName('attr');
			for(var a = 0; a < attrs.length; a++) {
				evalString += ('obj.style.'+attrs[a].getAttribute('name')+'="'+attrs[a].getAttribute('value')+'";');
			}
		}	
		return function(obj) { eval(evalString); };
	}

	this.moveLayerUp = function(title) {
		MySelf.shiftLayer(title, 1);
	}

	this.moveLayerDown = function(title) {
		MySelf.shiftLayer(title, -1);

	}

	this.changeLayerOpacity = function(layerName, amount) {
		var img = document.getElementById(LayerId+layerName);
		var opac = getOpacity(img);
		opac+=amount;
		if(opac < 0) { opac = 0; }
		if(opac > 100) { opac = 100; }
		setOpacity(img, opac);
	}

	this.getOpacity = function(layerName) {
		return getOpacity(document.getElementById(LayerId+layerName));
	}
	
	function checkOpacityAbility() {
		if(OPACITY_CONTROLS == 'prompt' || (OPACITY_CONTROLS == 'auto' && getBrowser().match(/microsoft/i) && getBrowserVersion() < MAP_IE_SCALE_STYLE_SWITCH)) {
			if(OPACITY_CONTROLS_DISABLED_MESSAGE) {
				alert(OPACITY_CONTROLS_DISABLED_MESSAGE);
			}
			return false;
		} else if(OPACITY_CONTROLS == 'auto') { /* Everyone else plays nice with opacity, so just do it! */
			return true;
		}

		if(OPACITY_CONTROLS == 'none') {
			return false;
		}

		/* If it's 'full' or something unknown, return true. */
		return true;

	}
	
	this.fadeLayer = function(title) {
		if(checkOpacityAbility()) {
			MySelf.changeLayerOpacity(title, -10);
		}
	}

	this.unfadeLayer = function(title) {
		if(checkOpacityAbility()) {
			MySelf.changeLayerOpacity(title, 10);
		}
	}

	this.reloadLayer = function(title) {
		var arr = new Array();
		arr[0] = title;
		MySelf.refreshLayer(arr);
	}
	
}
/*
 * Initial Version of the Tree Viewer
 */

var TREEVIEW_INSTANCE = 0;
function TreeView(EventManager) {
	var MySelf = this;
	var TreeViewId = 'TreeView'+TREEVIEW_INSTANCE+'-';
	TREEVIEW_INSTANCE++;

	var MapBookData = null;

	var Parent;

	this.removeObject = function(id) {
		var checkBox = document.getElementById(TreeViewId+id);
		if(checkBox) {
			checkBox.checked = false;
		}
	}

	this.addObject = function(mapXMLObj) {
		var title = mapXMLObj.getAttribute('title');
		var checkBox = document.getElementById(TreeViewId+title);
		if(checkBox) {
			checkBox.checked = true;
		}
	}

	this.clearAll = function() {
		var inputs = document.getElementsByTagName('input');
		for(var i = 0; i < inputs.length; i++) {
			inputs[i].checked = false;
		}
	}

	function makeInvisible(elm) {
		elm.style.height = '0px';
	}

	var visibilityOverride = false;

	this.setVisibilityOverride = function (v) {
		visibilityOverride = v;
	}

	function onclickLeaf(event) {
		if(window.event) { event = window.event; }
		if(getEventTarget(event) != this) { return true; }
		var childTree = this.getElementsByTagName('ul')[0];
		var vis = childTree.style.visibility;
		if(childTree.style.height != '0px') {
			height = '0px';
			var allUl = childTree.getElementsByTagName('ul');
			for(var u = 0; u < allUl.length; u++) {
				makeInvisible(allUl[u]);
			}
		} else {
			height = 'auto';
		}
		childTree.style.height = height;
		return false;

	}

	var Branches = 0;
	
	function createBranch(xmlObj) { // Assumes the Element Has Children
		var root = document.createElement('ul');
		//root.className = 'TreeView';
		//root.style.marginLeft = '24px';
		root.setAttribute('id',TreeViewId+'Branch-'+Branches);
		Branches++;
		
		if(document.all) {
			root.style.marginLeft = '24px';
		} else {
			root.style.paddingLeft = '24px';
		}
		for(var i = 0; i < xmlObj.childNodes.length; i++) {
			if(xmlObj.childNodes[i].tagName != null && (!xmlObj.childNodes[i].getAttribute('invisible') || !xmlObj.childNodes[i].getAttribute('invisible').match(/true/i) || visibilityOverride)) {
				if(xmlObj.childNodes[i].tagName == 'group') {
					var rootLeaf = getLeaf(xmlObj.childNodes[i]);
					var underList = createBranch(xmlObj.childNodes[i]);
					underList.style.height = '0px';
					underList.style.overflow = 'hidden';
					rootLeaf.appendChild(underList);
					root.appendChild(rootLeaf);
					rootLeaf.onclick = onclickLeaf;
					if(xmlObj.childNodes[i].getAttribute('expand') && xmlObj.childNodes[i].getAttribute('expand').match(/true/)) {
						underList.style.height = 'auto';
					}
				} else if(xmlObj.childNodes[i].tagName != null && xmlObj.childNodes[i].getAttribute('title') != null) {
					/*var newItem = getLeaf(xmlObj.childNodes[i]);	
					root.appendChild(newItem);*/
					root.appendChild(getLeaf(xmlObj.childNodes[i]));
				}
			}
		}
		return root;
	}

	function onclickLayer() {
		if(this.checked) {
			EventManager.triggerEvent('addlayer', this.selfXML);
		} else {
			this.checked=true;
			if(EventManager.onRemoveLayer(this.selfXML.getAttribute('title'))) {
				this.checked=false;
			}
		}
	}

	function onclickService() {
		if(this.checked) {
			EventManager.triggerEvent('addservice', this.selfXML);
		} else {
			this.checked=true;
			if(EventManager.onRemoveService(this.selfXML.getAttribute('title'))) {
				this.checked=false;
			}
		}
	}

	function onclickView() {
		if(this.checked) {
			EventManager.triggerEvent('addview', this.selfXML);
		} else {
			this.checked=true;
			if(EventManager.triggerEvent('removeview',this.selfXML.getAttribute('title'))) {
				this.checked=false;
			}
		}
	}
	
	function getLeaf(obj) {
		var newLi = document.createElement('li');
		var objTitle = obj.getAttribute('title');
		newLi.style.cursor = 'pointer';

		newLi.className = 'Folder';
		newLi.id = TreeViewId+'-List-'+objTitle;

		if(obj.getAttribute('tip')) {
			newLi.title = obj.getAttribute('tip');
		}

		if(obj.getAttribute('icon')) {
			var iconUrl = obj.getAttribute('icon');
			newLi.style.listStyleType = 'none';
			newLi.style.listStyleImage = 'url('+iconUrl+')';
		} else if(obj.tagName.match(/map/)) {
			newLi.className = 'Map';
		} else if(obj.tagName.match(/service/)) {
			newLi.className = 'Service';
		} else if(obj.tagName.match(/view/)) {
			newLi.className = 'View';
		}
		if(obj.tagName != 'group') {
			var layerCheckBox = document.createElement('input');
			layerCheckBox.setAttribute('type','checkbox');
			layerCheckBox.id = TreeViewId+objTitle;
			layerCheckBox.selfXML = obj;
			newLi.appendChild(layerCheckBox);	

			if(obj.tagName.match(/map/i)) {
				layerCheckBox.onclick = onclickLayer; 
			} else if(obj.tagName.match(/service/i)) {
				layerCheckBox.onclick = onclickService;
			} else if(obj.tagName.match(/view/i)) {
				layerCheckBox.onclick = onclickView;
			}

		}

		newLi.appendChild(document.createTextNode(objTitle));
		if(obj.getElementsByTagName('meta')[0] && obj.getElementsByTagName('meta')[0].firstChild.nodeValue) {
			if(obj.getElementsByTagName('meta')[0].parentNode == obj) {
				var metaLink = document.createElement('a');
				newLi.appendChild(metaLink);
				metaLink.href = obj.getElementsByTagName('meta')[0].firstChild.nodeValue;
				var metaImage = document.createElement('img');
				metaImage.src = META_DEFAULT_ICON;
				metaLink.appendChild(metaImage);
				metaImage.title = 'Metadata';
				metaImage.alt = 'Metadata';
				metaLink.className = 'Metadata';
				metaLink.target = '_blank';
				metaImage = null;
				metaLink = null;
			}
		}
		if(TREEVIEW_SHOW_CONTROLS && obj.tagName == 'map') {
			newLi.appendChild(document.createElement('br'));
			newLi.appendChild(createIcon(EventManager, UP_ICON,'movelayerup', objTitle));
			newLi.appendChild(createIcon(EventManager, DOWN_ICON,'movelayerdown',objTitle));
			newLi.appendChild(createIcon(EventManager, FADE_ICON,'fadelayer',objTitle));
			newLi.appendChild(createIcon(EventManager, UNFADE_ICON,'unfadelayer',objTitle));
			newLi.appendChild(createIcon(EventManager, RELOAD_ICON,'refreshlayer', objTitle));
		}

		if(TREEVIEW_SHOW_LEGENDS && obj.tagName == 'map') {
			newLi.appendChild(document.createElement('br'));
			var legendImage = document.createElement('img');
			legendImage.setAttribute('id', TreeViewId+'Legend-'+objTitle);
			newLi.appendChild(legendImage);
			legendImage = null;
		}
		return newLi;
	}

	this.onLoadLegend = function(legendObj) {
		var obj = document.getElementById(TreeViewId+'Legend-'+legendObj.id);
		if(obj) {
			obj.src = legendObj.legend;
		}
	}
	function getLeaf_xx(obj, depth) {
		var newLi = document.createElement('span');
		var imgIcon = document.createElement('img');
		/*newLi.onmousedown = clearSelection;*/
		newLi.style.cursor = 'pointer';

		var metaIcon, helpIcon;

		if(obj.tagName == 'group') {
			newLi.onclick = function () {
				if(this.parentNode.className == 'Selected') {
					alert('Add All Children of '+this.parentNode.id);
				}
			}
			imgIcon.src = FOLDER_ICON;
		} else {
			var layerCheckBox = document.createElement('input');
			layerCheckBox.setAttribute('type','checkbox');
			var objTitle = obj.getAttribute('title');

			layerCheckBox.id = TreeViewId+objTitle;
	
			/*if(layerObjs[obj.getAttribute('title')]) {
				checked = true;
				layerCheckBox.defaultChecked = true;
				layerCheckBox.setAttribute('checked', 'true');
				if(obj.getAttribute('locked') && obj.getAttribute('locked').match(/true/i)) {
					layerCheckBox.disabled = true;
				}

			}*/


			if(obj.tagName.match(/map/i)) {
				layerCheckBox.onclick = function() {
					if(this.checked) {
					//	addMapLayer(L, objTitle, objTitle, true);
						EventManager.triggerEvent('addlayer', obj);
					} else {
						this.checked=true;
						if(EventManager.onRemoveLayer(objTitle)) {
							this.checked=false;
						}
					}

				}
			} else if(obj.tagName.match(/service/i)) {
				layerCheckBox.onclick = function() {
					if(this.checked) {
						//addService(objTitle,objTitle);
						EventManager.triggerEvent('addservice',obj);
					} else {
						this.checked=true;
						if(EventManager.onRemoveService(objTitle)) {
							this.checked=false;
						}
					}

				}
			} else if(obj.tagName.match(/view/i)) {
				layerCheckBox.onclick = function() {
					if(this.checked) {
						EventManager.triggerEvent('addview', obj);
					} else {
						this.checked=true;
						EventManager.triggerEvent('removeview', objTitle);
						this.checked = false;
					}
				}
			}




			//function addService(serviceName, serviceTitle) {
			//layerCheckBox = createCheckBox(checked,enabled,onAction,doNadda);
			newLi.appendChild(layerCheckBox);
			if(obj.getAttribute('locked') && obj.getAttribute('locked').match(/true/i)) {
				layerCheckBox.disabled = true;
			}

			var iconUrl = UNKNOWN_DEFAULT_ICON;
			/* If no custom icon is specified - then use a default one */
			if(obj.getAttribute('icon')) {
				iconUrl = obj.getAttribute('icon');
			} else if(obj.tagName.match(/map/)) {
				iconUrl = MAP_DEFAULT_ICON;
			} else if(obj.tagName.match(/service/)) {
				iconUrl = SERVICE_DEFAULT_ICON;
			} else if(obj.tagName.match(/view/)) {
				iconUrl = VIEW_DEFAULT_ICON;
			}
			imgIcon.style.paddingTop = '1px';
			imgIcon.style.marginTop = '1px';
			imgIcon.src = iconUrl;

			/*newLi.onclick = function(event) {
				if(window.event) {
					event = window.event;
				}
			}*/

			if(obj.getElementsByTagName('meta')[0] && obj.getElementsByTagName('meta')[0].firstChild.nodeValue) {
				metaIcon = document.createElement('img');
				metaIcon.src = META_DEFAULT_ICON;
				metaIcon.style.paddingTop = '1px';
				metaIcon.style.marginTop = '1px';
				metaIcon.onclick = function () {
					window.open(obj.getElementsByTagName('meta')[0].firstChild.nodeValue);
				}
			}

			if(obj.getElementsByTagName('help')[0] && obj.getElementsByTagName('help')[0].firstChild.nodeValue) {
				helpIcon = document.createElement('img');
				helpIcon.src = HELP_DEFAULT_ICON;
				helpIcon.style.paddingTop = '1px';
				helpIcon.style.marginTop = '1px';
				helpIcon.onclick = function () {
					window.open(obj.getElementsByTagName('help')[0].firstChild.nodeValue);
				}
			}

		}
		newLi.appendChild(imgIcon);
		var titleText = obj.getAttribute('title');
		newLi.appendChild(document.createTextNode(titleText));

		if(metaIcon) { newLi.appendChild(metaIcon); }
		if(helpIcon) { newLi.appendChild(helpIcon); }

		//newLi.innerHTML = newLi.innerHTML + titleText;

		newLi.appendChild(document.createElement('br'));
		return newLi;
	}

	this.getTree = function() {
		return createBranch(parentXML);
	}

	this.reloadMapbook = function(mbXML) {
		document.getElementById(Parent).removeChild(Tree);
		this.loadMapbook(mbXML);
	}

	this.loadMapbook = function(mbXML) {
		MapBookData = mbXML.getElementsByTagName('mapbook')[0];
		if(Parent) {
			document.getElementById(Parent).appendChild(createBranch(MapBookData));
		}
	}

	this.setParent = function(p) {
		Parent = p;
		if(MapBookData) {
			document.getElementById(Parent).appendChild(createBranch(MapBookData));
		}
	}
}

function triggerSelfEvent() {
	this.EventManager.triggerEvent(this.eventTrigger, this.mapTitle);
}



var VISIBLE_LAYERS_INSTANCE = 0;

function VisibleLayers(EventManager,VisibleMap) {
	var ListParent;
	var LayersListId = 'LayersList'+VISIBLE_LAYERS_INSTANCE+'-';
	VISIBLE_LAYERS_INSTANCE++;

	var LayerIcons = new Array();
//	var LayerIconObjs = new Array();
	var MySelf = this;

	this.clearActiveLayerSelection = function() {
		var layersList = document.getElementById(LayersListId);
		var radios = layersList.getElementsByTagName('input');
		for(var i = 0; i < radios.length; i++) {
			radios[i].checked = false;
		/*	radios[i].parentNode.style.border = 'none';*/
			if(radios[i].parentNode.className == 'SelectedLayer') {
				radios[i].parentNode.className = 'ActiveLayer';
			}
		}
	}

	function activateLayer() {
		var title = this.getAttribute('LayerName');
		EventManager.triggerEvent('changetool', title);
		EventManager.triggerEvent('activatelayer', title);
		/** Old Selection is Cleared by the Event Manager **/
		if(!this.disabled) {
			this.checked = true;
			this.parentNode.className = 'SelectedLayer';
		}
	}

	this.sortListByZIndex = function() {
		var ordered = VisibleMap.getLayersByZIndex();
		var p = document.getElementById(LayersListId);
		for(var i = 0; i < ordered.length; i++) {
			var obj = document.getElementById(LayersListId+ordered[i]);
			if(obj) {
				p.appendChild(obj);
			}
		}
	}

	this.addLayer = function(mapXMLObj) {
		var title = mapXMLObj.getAttribute('title');

		var ListItem = document.createElement('li');
		ListItem.id = LayersListId+title;

		var alreadyHere = document.getElementById(ListItem.id);
		if(alreadyHere) return false;

		layersList = document.getElementById(LayersListId);
		layersList.appendChild(ListItem);	
		if(MAP_POPUPS_ENABLED) {
			var ActivateLayer = document.createElement('input');
			ActivateLayer.type = 'radio';
			ActivateLayer.name = 'activateLayer';
			ActivateLayer.disabled = true;
			ActivateLayer.id = 'DataLayer-'+title;
			ActivateLayer.setAttribute('LayerName', title);
			ActivateLayer.onclick = activateLayer;
			ListItem.appendChild(ActivateLayer);
		}

		var LegendImg = document.createElement('img');
		LegendImg.id = 'LayerLegend-'+title;
		//LegendImg.src = BLANK_IMAGE;
		LegendImg.style.visibility = 'hidden';

		var LayerIcon = document.createElement('img');
		if(mapXMLObj.getAttribute('icon')) {
			LayerIcons[title] = mapXMLObj.getAttribute('icon');
		} else {
			LayerIcons[title] = MAP_DEFAULT_ICON;
		}
		LayerIcon.src = LayerIcons[title];
		LayerIcon.id = LayersListId+'Icon-'+title;
		//LayerIconObjs[title] = LayerIcon;

		ListItem.appendChild(LayerIcon);
		ListItem.appendChild(document.createTextNode(title));
		if(mapXMLObj.getElementsByTagName('meta')[0] && mapXMLObj.getElementsByTagName('meta')[0].firstChild.nodeValue) {
			var metaIcon = document.createElement('img');
			metaIcon.src = META_DEFAULT_ICON;
			metaIcon.onclick = function () {
				window.open(mapXMLObj.getElementsByTagName('meta')[0].firstChild.nodeValue);
			}
			ListItem.appendChild(metaIcon);
		}
		if(mapXMLObj.getElementsByTagName('help')[0] && mapXMLObj.getElementsByTagName('help')[0].firstChild.nodeValue) {
			var helpIcon = document.createElement('img');
			helpIcon.src = HELP_DEFAULT_ICON;
			helpIcon.onclick = function () {
				window.open(mapXMLObj.getElementsByTagName('help')[0].firstChild.nodeValue);
			}
			ListItem.appendChild(helpIcon);
		}

		ListItem.appendChild(document.createElement('br'));
		ListItem.appendChild(createIcon(EventManager, UP_ICON,'movelayerup', title));
		ListItem.appendChild(createIcon(EventManager, DOWN_ICON,'movelayerdown',title));
		ListItem.appendChild(createIcon(EventManager, FADE_ICON,'fadelayer',title));
		ListItem.appendChild(createIcon(EventManager, UNFADE_ICON,'unfadelayer',title));
		ListItem.appendChild(createIcon(EventManager, RELOAD_ICON,'refreshlayer', title));
		if(!mapXMLObj.getAttribute('locked') || !mapXMLObj.getAttribute('locked').match(/true/i)) {
			ListItem.appendChild(createIcon(EventManager, REMOVE_ICON,'removelayer',title));
		}
		ListItem.appendChild(document.createElement('br'));
		ListItem.appendChild(LegendImg);

		if(parseInt(mapXMLObj.getAttribute('zindex')) == -1 || !layersList.firstChild) {
			layersList.appendChild(ListItem);
		} else {
			layersList.insertBefore(ListItem, layersList.firstChild);
		}
		MySelf.sortListByZIndex();

	}

	this.activateLayer = function(mapTitle) {
		var opt = document.getElementById('DataLayer-'+mapTitle);
		if(opt) {
			opt.disabled = false;
			opt.parentNode.className = 'ActiveLayer';
		}
	}

	this.disableLayers = function() {
		var layersList = document.getElementById(LayersListId);
		var opts = layersList.getElementsByTagName('input');
		for(var i = 0; i < opts.length; i++) {
			if(opts[i].type == 'radio') {
				opts[i].disabled = true;
				opts[i].checked = false;
				opts[i].parentNode.className = '';
			}
		}
	}

	this.removeLayer = function(mapTitle) {
		var ListItem = document.getElementById(LayersListId+mapTitle);
		if(LayerIcons[mapTitle]) {
			delete LayerIcons[mapTitle];
		}
		if(ListItem) {
			document.getElementById(LayersListId).removeChild(ListItem);
		}
	}

	this.showLegend = function(xmlObj) {
		var title = xmlObj.id;
		var src = xmlObj.legend;
		

		// This is hack for IE, it does not properly resize the image upon
		// loading.
		var tempImage = new Image();
		tempImage.onload = function () {
			var LegendImg = document.getElementById('LayerLegend-'+title);
			if(!LegendImg) {
				return false;
			}
			LegendImg.style.visibility = 'visible';
			LegendImg.style.width = 'auto';
			LegendImg.style.height = 'auto';
			LegendImg.src = src;
			LegendImg.style.width = this.width+'px';
			LegendImg.style.height = this.height+'px';
		}
		tempImage.src=  src;
	}
	
	this.moveLayerUp = function(title) {
		var listItem = document.getElementById(LayersListId+title);
		var layersList = document.getElementById(LayersListId);
		if(listItem && listItem.previousSibling) {
			layersList.insertBefore(listItem, listItem.previousSibling);
		}
		MySelf.sortListByZIndex();
	}

	this.moveLayerDown = function(title) {
		var listItem = document.getElementById(LayersListId+title);
		var layersList = document.getElementById(LayersListId);
		if(listItem && listItem.nextSibling) {
			layersList.insertBefore(listItem.nextSibling, listItem);
		}
		MySelf.sortListByZIndex();
	}

	this.setAllRefreshIcon = function () {
		for(var title in LayerIcons) {
			//LayerIconObjs[title].src = MAP_LOADING_ICON;
			document.getElementById(LayersListId+'Icon-'+title).src = MAP_LOADING_ICON;
		}
	}
	
	this.setRefreshIcon = function(title) {
		var icon = document.getElementById(LayersListId+'Icon-'+title);
		if(icon) {
			icon.src = MAP_LOADING_ICON;
		}
	}
	
	this.setLoadedIcon = function(title) {
		var icon = document.getElementById(LayersListId+'Icon-'+title);
		if(icon) {
			icon.src = LayerIcons[title];
		}
	}



	function enableOpacityControls() {
		if(confirm(OPACITY_CONTROLS_ENABLE_WARNING)) {
			OPACITY_CONTROLS = 'full';
			this.onclick = null;
			this.style.color = 'black';
			this.innerHTML = 'Opacity Controls Enabled.';
			setTimeout("document.getElementById('"+this.id+"').style.visibility = 'hidden';", 2000);
		}
	}
	
	this.setParent = function(parent) {
		ListParent = parent;
		var layersList = document.createElement('ul');
		layersList.className = 'VisibleLayers';
		layersList.setAttribute('id',LayersListId);
		document.getElementById(ListParent).appendChild(layersList);
		if(OPACITY_CONTROLS == 'prompt' || (OPACITY_CONTROLS == 'auto' && getBrowser().match(/microsoft/i) && getBrowserVersion() < MAP_IE_SCALE_STYLE_SWITCH)) {
			var a = document.createElement('a');
			document.getElementById(ListParent).appendChild(a);
			a.href = 'javascript:function nil() { }';
			a.appendChild(document.createTextNode('Enable Opacity Controls'));
			a.onclick = enableOpacityControls;
			a.setAttribute('id', LayersListId+'OpacityControl');
			with(a.style) {
				display = 'block';
				position = 'absolute';
				bottom = '20px';
				padding = '5px';
				textDecoration = 'none';
				width = '90%';
				color = 'red';
				textAlign = 'center';
				fontWeight = 'bold';
				
			}
			a = null;
		}	
	}
}
/*
 * 
 */

var TOOLBAR_ITERATIONS = 0;

function GTools(EventManager) {
	var ToolsParent;

	/*
	 * Some settings taken from the globals
	 */
	this.SHOW_TEXT = TOOLBAR_TEXT;
	this.SHOW_IMAGE = TOOLBAR_IMAGE;

	var ServiceLinkId = 'ServiceLink-'+TOOLBAR_ITERATIONS+'-';
	var ServiceToolId = 'ServiceTool-'+TOOLBAR_ITERATIONS+'-';

	var ToolsListId = 'ToolsList-'+TOOLBAR_ITERATIONS+'-';
	var ToolsListClassName = 'ToolList';
	TOOLBAR_ITERATIONS++;

	this.setType = function(cName) {
		ToolsListClassName = cName;
		var toolsList = document.getElementById(ToolsListId);
		if(toolsList) {
			toolsList.className = cName;
		}
	}
	
	var MySelf = this;

	var ToolIcons = new Array();

	function showNone(link) {
		if(link.highlightIcon) {
			link.highlightIcon.style.visibility = 'hidden';
			link.highlightIcon.style.width = '1px';
			link.highlightIcon.style.paddingLeft = '0px';
		}
		if(link.toolIcon) {
			link.toolIcon.style.visibility = 'hidden';
			link.toolIcon.style.width = '1px';
			link.toolIcon.style.paddingRight = '0px';
		}

	}
	
	function showUnselected(link) {
		if(link.toolIcon) {
			link.toolIcon.style.visibility = 'visible';
			link.toolIcon.style.width = 'auto';
			link.toolIcon.style.paddingRight = '1px';
		}
		if(link.highlightIcon) {
			link.highlightIcon.style.visibility = 'hidden';
			link.highlightIcon.style.width = '1px';
			link.highlightIcon.style.paddingLeft = '0px';
		}
	}

	function showSelected(link) {
		if(link.toolIcon && link.highlightIcon) {
			link.toolIcon.style.visibility = 'hidden';
			link.toolIcon.style.width = '1px';
			link.toolIcon.style.paddingRight = '0px';
		}
		if(link.highlightIcon) {
			link.highlightIcon.style.visibility = 'visible';
			link.highlightIcon.style.width = 'auto';
			link.highlightIcon.style.paddingLeft = '1px';
		}
	}

	this.clearToolSelection = function(title) {
		var links = document.getElementById(ToolsParent).getElementsByTagName('a');//toolsList.getElementsByTagName('a');
		for(var i = 0; i < links.length; i++) {
			if(links[i].className == 'Selected') {
				links[i].className = '';
			}
			showUnselected(links[i]);
					
		}
	}

	this.showSelectedTool = function(toolName) {
		var tool = document.getElementById(ServiceLinkId+toolName);	
		MySelf.clearToolSelection();
		if(tool) {
			showSelected(tool);
		}
	}

	function onclickServiceLink() {
		var title = this.title;
		var sel = this.selectable;
		if(sel) {
			EventManager.triggerEvent('changetool', title);
			if(TOOLS_SHOW_AS_SELECTED) {
				this.className = 'Selected';
			}
			showSelected(this);
		}
		if(this.callService) {
			this.callService();
		}

	}

	function onclickCallService() {
		EventManager.triggerEvent('serviceform', this.selfXML);
	}

	var KnownServices = new Array();
	var SelectToolAtStartup = null;

	this.addService = function(serviceXMLObj) {
		var title = serviceXMLObj.getAttribute('title');
		KnownServices[title] = serviceXMLObj;
		var toolsList = document.getElementById(ToolsListId);
		if(toolsList) {
			renderServiceInToolbar(title);
		}
	}

	function renderServiceInToolbar(title) {
		var serviceXMLObj = KnownServices[title];
		var rendered = serviceXMLObj.getAttribute(ToolsListId+'rendered');
		if(rendered && rendered == '1') { return true; }	/* Already rendered this one cheif! */
		serviceXMLObj.setAttribute(ToolsListId+'rendered','1');

		var toolsList = document.getElementById(ToolsListId);

		var newTool = document.createElement('li');
		var ServiceLink = document.createElement('a');
		ServiceLink.selfXML = serviceXMLObj;
		
		//document.getElementById(ToolsListId).appendChild(newTool);
		toolsList.appendChild(newTool);
		newTool.appendChild(ServiceLink);

		var action = '';
		var selectable = serviceXMLObj.getAttribute('selectable');
		if(selectable && selectable.match(/false/i)) {
			ServiceLink.selectable = false;
		} else {
			ServiceLink.selectable = true;
		}
		
		if(serviceXMLObj.getAttribute('type') && serviceXMLObj.getAttribute('type').match(/internal/i)) {
			action = 'javascript:'+serviceXMLObj.getAttribute('command');
		} else if(!serviceXMLObj.getAttribute('type') || !serviceXMLObj.getAttribute('type').match(/internal/i)) {
			action = 'javascript:function nil() {return false; }';
			ServiceLink.callService = onclickCallService;
		}
		ServiceLink.title = title;
		ServiceLink.href = action;
		ServiceLink.id = ServiceLinkId+title;
		ServiceLink.onclick = onclickServiceLink;
		var icon = serviceXMLObj.getAttribute('icon');
		if(!icon) { icon = SERVICE_DEFAULT_ICON; }
		var newToolIcon = document.createElement('img');
		newToolIcon.src = icon;
		newToolIcon.id = ServiceToolId+'Icon-'+title;

		ToolIcons[title] = icon;

		var altIcon = serviceXMLObj.getAttribute('highlight');
		if(MySelf.SHOW_IMAGE && altIcon) {
			ServiceLink.setAttribute('highlightIconSrc',altIcon);

			var highlightImg = document.createElement('img');
			highlightImg.src = altIcon;
			highlightImg.id = ServiceToolId+'Highlight-'+title;
			ServiceLink.appendChild(highlightImg);
			ServiceLink.highlightIcon = highlightImg;
		}
		if(MySelf.SHOW_IMAGE) {
			ServiceLink.appendChild(newToolIcon);
			ServiceLink.toolIcon = newToolIcon;
			ServiceLink.setAttribute('toolIconSrc',ToolIcons[title]);
		}
		if(MySelf.SHOW_TEXT) {
			ServiceLink.appendChild(document.createTextNode(title));
		}
		ServiceLink.title = title;

		newTool.id = ServiceToolId+title;
		if(serviceXMLObj.getElementsByTagName('help')[0] && serviceXMLObj.getElementsByTagName('help')[0].firstChild.nodeValue) {
			var helpButton = document.createElement('img');
			helpButton.src = HELP_DEFAULT_ICON;
			helpButton.onclick = function () {
				window.open(serviceXMLObj.getElementsByTagName('help')[0].firstChild.nodeValue);
				return false;
			}
			ServiceLink.appendChild(helpButton);
		}	

		if(!serviceXMLObj.getAttribute('locked') || !serviceXMLObj.getAttribute('locked').match(/true/i)) {
			var removeButton = createIcon(EventManager, REMOVE_ICON,'removeservice',title);
			ServiceLink.appendChild(removeButton);
		}

		showUnselected(ServiceLink);
	}
	
	this.setParent = function(p) {
		ToolsParent = p;
		var toolsList = document.createElement('ul');
		toolsList.setAttribute('id', ToolsListId);
		toolsList.className = ToolsListClassName;
		document.getElementById(ToolsParent).appendChild(toolsList);
		toolsList = null;
		for(var s in KnownServices) {
			renderServiceInToolbar(s);
		}
		if(SelectToolAtStartup) {
			this.selectTool(SelectToolAtStartup);
			SelectToolAtStartup = null;
		}
	}

	this.removeService = function(serviceTitle) {
		var serv = document.getElementById(ServiceToolId+serviceTitle);
		if(serv) {
			var p = serv.parentNode;
			p.removeChild(serv);
			if(KnownSerivces[serviceTitle]) {
				delete KnownServices[serviceTitle];
			}
		}
	}

	this.toolWaiting = function(serviceXMLObj) {
		if(serviceXMLObj.getAttribute('type') && serviceXMLObj.getAttribute('type').match(/search/i)) {
			var title = serviceXMLObj.getAttribute('title');
			var link = document.getElementById(ServiceLinkId+title);
			if(link) { 
				showNone(link);
				link.waitIcon = document.createElement('img');
				link.waitIcon.src = MAP_LOADING_ICON;
				link.insertBefore(link.waitIcon,link.childNodes[0]);
			}
		}
	}
	
	this.restoreTool = function(serviceXMLObj) {
		var title = serviceXMLObj.getAttribute('title');
		if(!title) { 
			var root = serviceXMLObj.getElementsByTagName('results')[0]; 
			title = root.getAttribute('title');
		}
		var link = document.getElementById(ServiceLinkId+title);
		if(link && link.waitIcon) {
			link.removeChild(link.waitIcon);
			showUnselected(link);
		}
	}

	this.selectTool = function(toolName) {
		var tool = document.getElementById(ServiceLinkId+toolName);	
		if(tool) {
			tool.onclick();
			if(tool.href) {
				var js = tool.href;
				js = js.replace('javascript:','');
				eval(js);
			}
		} else {
			SelectToolAtStartup = toolName;
		}
	}
}
var SCALEBAR_WIDTH = 200;
var SCALEBAR_HEIGHT = 20;

function Scalebar(EventManager, sbMap, sbWidth, sbHeight) {
	var MySelf = this;
	
	var imgObj;
	
	function buildURL(sbCenterPoint, sbScale, mapWidth,mapHeight) {
		var url = SCALEBAR_CALL;
		var gisW = mapWidth / 2 * sbScale;
		var gisH = mapHeight / 2 * sbScale;
		var minCoords = new Point(sbCenterPoint.x - gisW, sbCenterPoint.y - gisH);
		var maxCoords = new Point(sbCenterPoint.x + gisW, sbCenterPoint.y + gisH);

		var sizeString = mapWidth+'+'+mapHeight;
		var coordsString = minCoords.toString('+')+'+'+maxCoords.toString('+');
		url = url.replace('[SIZE]',sizeString);
		url = url.replace('[EXTENTS]',coordsString);
		return url;
	}

	this.update = function(circ) {
		var cp = circ.Center;
		var scale = circ.Radius;
		var mapSize = sbMap.getMapSize();
		imgObj.src = buildURL(cp, scale, mapSize.x, mapSize.y);
	}

	this.setParent = function(p) {
		imgObj = document.createElement('img');
		/*imgObj.style.width = sbWidth+'px';
		imgObj.style.height = sbHeight+'px';*/
		p.appendChild(imgObj);
	}

}
function Message(EventManager) {
	var Parent;
	var MessageContainer = document.createElement('span');

	MessageContainer.className = 'Message';
	MessageContainer.style.display = 'block';

	this.setParent = function(p) {
		Parent = p;
		p.appendChild(MessageContainer);		
	}

	this.changeMessage = function(msg) {
		MessageContainer.innerHTML = msg;
	}
	this.clearMessage = function() {
		MessageContainer.innerHTML = '';
	}
}
/*
 * Renders results from services
 */
function renderResults(parent, resultsXML, EventManager) {
	var title = resultsXML.getAttribute('title');
	var rows = resultsXML.getElementsByTagName('row');
	var header = resultsXML.getElementsByTagName('header')[0];

	if(rows.length > LAYER_MAXIMUM_OBJECTS) { alert(SERVICE_OVERLOAD_MESSAGE); return true; }
	var titleElement = document.createElement('h3');
	if(title) {
		titleElement.appendChild(document.createTextNode(title));
	}
	parent.appendChild(titleElement);

	var resultsTable = document.createElement('table');
	var resultsBody = document.createElement('tbody');
	resultsTable.appendChild(resultsBody);

	if(header) {
		var headerElms = header.getElementsByTagName('col');
		
		var headerRow = document.createElement('tr');
		var headerSpace = document.createElement('td');
		headerSpace.className='RowId';
		headerRow.appendChild(headerSpace);
		for(var i = 0; i < headerElms.length; i++) {
			var val = headerElms[i].getAttribute('value');
			var cell = document.createElement('th');
			cell.appendChild(document.createTextNode(val));
			headerRow.appendChild(cell);
		}
		resultsBody.appendChild(headerRow);
	}
	for(var i = 0; i < rows.length; i++) {
		var resultsRow = document.createElement('tr');

		var rowTitle = rows[i].getAttribute('value');
		if(!rowTitle) {
			rowTitle = i+1;
		}
		var rowId = document.createElement('td');
		rowId.className = 'RowId';
		rowId.appendChild(document.createTextNode(rowTitle));

		resultsRow.appendChild(rowId);
		var cols = rows[i].getElementsByTagName('col');
		for(var c = 0; c < cols.length; c++) {
			var resultsCell = document.createElement('td');
			var cellType = cols[c].getAttribute('type');
			var valueParent = resultsCell;
			if(!cellType) {
				valueParent.appendChild(document.createTextNode(cols[c].getAttribute('value')));
			} else if(cellType.match(/pan/i) || cellType.match(/zoomto/i)) {
				var panLink = document.createElement('a');
				var panX = cols[c].getAttribute('x');
				var panY = cols[c].getAttribute('y');
				var pVal = cols[c].getAttribute('label');
				if(pVal == null) { pVal = 'Point of Interest'; }
				/*panLink.href = 'javascript:jumpToXY("'+pVal+'",'+panX+','+panY+');';
				panLink.href = 'javascript:opener.Events["'+window.name+'"].changePointScale(new Point('+panX+','+panY+'), 8)';*/
				panLink.href = 'javascript:function nil() { return; }';
				panLink.x = panX;
				panLink.y = panY;
				panLink.onclick = function () {
					var modCirc = new Circle();
					modCirc.Center = new Point(this.x, this.y);
					modCirc.Radius = 4;
					modCirc.Markup = true;
					EventManager.triggerEvent('navigate', modCirc);
				}
				panLink.appendChild(document.createTextNode(cols[c].getAttribute('value')));
				valueParent.appendChild(panLink);
			} else if(!cellType.match(/image/i) && cols[c].getAttribute('href')) {
				var panLink = document.createElement('a');
				panLink.target = "_blank";
				panLink.href = unescape(cols[c].getAttribute('href'));
				panLink.appendChild(document.createTextNode(cols[c].getAttribute('value')));
				valueParent.appendChild(panLink);
			} else if(cellType.match(/internal/i)) {
				var panLink = document.createElement('a');
				panLink.href = 'javascript:'+cols[c].getAttribute('command');
				panLink.appendChild(document.createTextNode(cols[c].getAttribute('value')));
				valueParent.appendChild(panLink);
			} else if(cellType.match(/image/i)) {
				var w = cols[c].getAttribute('width');
				var h = cols[c].getAttribute('height');
				var src = cols[c].getAttribute('src');
				var href = cols[c].getAttribute('href');

				var newImg = document.createElement('img');
				if(w) { newImg.width = w; }
				if(h) { newImg.height = h; }
				newImg.src = src;
				if(href) {
					var link = document.createElement('a');
					link.href = href;
					link.target = "_blank";
					link.appendChild(newImg);
					valueParent.appendChild(link);
				} else {
					valueParent.appendChild(newImg);
				}
			} else {
				valueParent.appendChild(document.createTextNode(cols[c].getAttribute('value')));
			}
			resultsRow.appendChild(valueParent);
		}
		resultsBody.appendChild(resultsRow);
	}
	
	parent.appendChild(resultsTable);
}


var SERVICE_WINDOW_WIDTH = 600;
var SERVICE_WINDOW_HEIGHT = 300;

function ServiceWindow (EventManager) {

	this.resultsAction = function(serviceElements) {
		var serviceTitle = serviceElements.getElementsByTagName('results')[0].getAttribute('title');
		
		window.processElements[serviceTitle] = serviceElements;
		window.Events[serviceTitle] = EventManager;
		
		var serviceWindow = window.open('services/ServiceWindow.html', serviceTitle, 'width='+SERVICE_WINDOW_WIDTH+',height='+SERVICE_WINDOW_HEIGHT+',toolbar=0,status=0,menubar=0,location=0,scrollbars=yes,resizable=1');

	}

	this.passthroughHandler = function(url) {
		alert('internal');
		window.open(url);
	}

	this.csvHandler = function(url) {
		return false;
	}

	var MySelf = this;

	this.spawn = function(serviceElements) {
		//var serviceWindow = window.open('services/ServiceWindow.html');
		if(!window.processElements) {
			window.processElements = new Array();
		}

		loadDefaultsFromMapBook(serviceElements, null, EventManager);

		var windowsToOpen = serviceElements.getElementsByTagName('window');
		for(var i = 0; i < windowsToOpen.length; i++) {
			var url = windowsToOpen[i].firstChild.nodeValue;
			if(url) {
				window.open(url);
			}
		}
		var root = serviceElements.getElementsByTagName('return')[0];
		var showWarnings = true;
		if(!SERVICE_SHOW_WARNINGS || (root && root.getAttribute('warnings') && root.getAttribute('warnings').match(/false/i))) {
			showWarnings = false;
		}

		if(!serviceElements.getElementsByTagName('results')[0]) { 
			if(showWarnings) { alert('No active layer(s) to query!'); }
			return true;
		}
		if(serviceElements.getElementsByTagName('row').length <= 0) {
			if(showWarnings) { alert('Nothing found for active layer(s)!'); }
			return true; 
		}
		if(serviceElements.getElementsByTagName('error')[0]) {
			var errorString = serviceElements.getElementsByTagName('error')[0].firstChild.nodeValue;
			alert(errorString);
			return true;
		}
		MySelf.resultsAction(serviceElements);

	}

	this.callService = function(serviceXMLObj) {
		var url = serviceXMLObj.getElementsByTagName('url')[0].firstChild.nodeValue+'?';
		//alert(serviceXMLObj.getAttribute('selectable')); // Indicates whether the user can do something or its just clicked.
		var inputs = serviceXMLObj.getElementsByTagName('input');
		for(var i = 0; i < inputs.length; i++) {
			url = url + inputs[i].getAttribute('name') +'='+inputs[i].getAttribute('value')+'&';
		}

		logDebugMessage('Service Call', false, url);
		var serviceType = serviceXMLObj.getAttribute('type');
		var returnType = serviceXMLObj.getAttribute('return');
		var displayType = serviceXMLObj.getAttribute('display');

		if(!returnType) { returnType = 'xml'; }
		if(!displayType) { displayType = 'div'; }

		if(displayType.match(/window/i)) {
			window.open(url);
		} else if(returnType.match(/html/i)) {
			MySelf.passthroughHandler(url);
		} else if(returnType.match(/csv/i)) {
			MySelf.csvHandler(url);
		} else if(returnType.match(/xml/) && (!serviceType || serviceType.match(/default/i))) {
			var tmpParent = document.createElement('parent');
			var tmpResults = document.createElement('results');
			tmpParent.appendChild(tmpResults);
			tmpResults.setAttribute('title', serviceXMLObj.getAttribute('title'));
			EventManager.triggerEvent('servicereturn', tmpParent);
			tmpParent = null;
			tmpResults = null;
		} else {
			var http = getHTTPObject();
			var method = 'GET';
			var postInfo = null;
			if(url.length > 1000) {
				method = 'POST';
				var urlArr = url.split('?');
				url = urlArr[0];
				postInfo = urlArr[1];
			}
			
			http.open(method, url, true);
			if(method == 'POST') {
				http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");	
			}
			
			http.onreadystatechange = function () { 
				if(http.readyState == 4) {
					var results = null;
					if(http.responseXML) {
						results = http.responseXML.getElementsByTagName('return')[0];
						if(!results) {
							results = http.responseXML.getElementsByTagName('results')[0];
						}
					}
					if(!results) {
						var ret = document.createElement('return');
						
						results = document.createElement('return');
						ret.setAttribute('title', serviceXMLObj.getAttribute('title'));
						results.appendChild(ret);
						
					}
					results.setAttribute('title', serviceXMLObj.getAttribute('title'));
					EventManager.triggerEvent('servicereturn', results);
				}
			};
			http.send(postInfo);
		}
	}

}
function ServiceForm (EventManager, serviceXMLObj, serviceMap) {
	var ServiceFormId = 'ServiceForm-';
	var FormTableId = ServiceFormId+serviceXMLObj.getAttribute('title');

	this.clearForm = function () {
		var rootElement = document.getElementById(FormTableId);
		if(rootElement && rootElement.parentNode) {
			var p = rootElement.parentNode;
			p.removeChild(rootElement);
		}
	}
	
	function createInputArray(tableId) {
		var table = document.getElementById(tableId);
		var inputs = table.getElementsByTagName('input');
		var inputValues = new Array();
		for(var i = 0; i < inputs.length; i++) {
			inputValues[inputs[i].name] = inputs[i].value;
		}
		inputs = null;

		var selects = table.getElementsByTagName('select');
		for(var i = 0; i < selects.length; i++) {
			var options = selects[i].getElementsByTagName('option');
			inputValues[selects[i].name] = options[selects[i].selectedIndex].value;
			options = null; 
		}
		selects = null;
		table = null;

		return inputValues;
	}

	this.setParent = function(p) {
		var formTable = document.createElement('table');
		document.getElementById(p).appendChild(formTable);
		formTable.setAttribute('id', FormTableId);
		
		var formBody = document.createElement('tbody');
		formTable.appendChild(formBody);

		var inputs = serviceXMLObj.getElementsByTagName('input');
		var userInputs = 0;
		for(var i = 0; i < inputs.length; i++) {
			var newInputRow = document.createElement('tr');
			var inputTitleCell = document.createElement('th');
			if(inputs[i].getAttribute('title') && inputs[i].getAttribute('title') != 'null') {
				inputTitleCell.appendChild(document.createTextNode(inputs[i].getAttribute('title')));
			}
			formBody.appendChild(newInputRow);
			var newInputCell = document.createElement('td');
			newInputRow.appendChild(inputTitleCell);
			newInputRow.appendChild(newInputCell);
			if(inputs[i].getAttribute('type').match(/user/i)) {
				var inputObj = document.createElement('input');
				if(inputs[i].getAttribute('value') && inputs[i].getAttribute('value') != 'null') {
					inputObj.value = inputs[i].getAttribute('value');
				}

				inputObj.name = inputs[i].getAttribute('name');
				inputObj.setAttribute('subtype', 'user');

				newInputCell.appendChild(inputObj);
				userInputs++;
			} else if(inputs[i].getAttribute('type').match(/select/i)) {
				var selectObj = document.createElement('select');
				selectObj.name = inputs[i].getAttribute('name');
				selectObj.setAttribute('subtype', 'select');
				var options = inputs[i].getElementsByTagName('option');
				for(var o = 0; o < options.length; o++ ) {
					var option = document.createElement('option');
					option.value = options[o].getAttribute('value');
					option.appendChild(document.createTextNode(options[o].firstChild.nodeValue));
					selectObj.appendChild(option);
				}
				newInputCell.appendChild(selectObj);
				userInputs++;
			} else if(inputs[i].getAttribute('type').match(/hidden/i)) {
				var newInput = document.createElement('input');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.value = inputs[i].getAttribute('value');
				newInput.setAttribute('subtype','user');
				/*inputsTable.appendChild(newInput);*/
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/layers/i)) {
				var layers = serviceMap.getLayerURLs();
				var name = inputs[i].getAttribute('name');
				var delim = inputs[i].getAttribute('delim');
				var value = '';
				for(var layer in layers) {
					value = value + serviceMap.preprocessURL('image',layer) + delim;
				}
				value = value.substring(0,value.length-1);
				var newInput = document.createElement('input');
				newInput.name = name;
				newInput.value = escape(value);
				newInput.type = 'hidden';
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/legends/i)) {
				var layers = serviceMap.getLayerURLs();
				var name = inputs[i].getAttribute('name');
				var delim = inputs[i].getAttribute('delim');
				var value = '';
				for(var layer in layers) {
					value = value + serviceMap.preprocessURL('legend',layer) + delim;
				}
				value = value.substring(0,value.length-1);
				var newInput = document.createElement('input');
				newInput.name = name;
				newInput.value = escape(value);
				newInput.type = 'hidden';
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/opacity/i)) {
				var layers = serviceMap.getLayerURLs();
				var name = inputs[i].getAttribute('name');
				var delim = inputs[i].getAttribute('delim');
				var value = '';
				for(var layer in layers) {
					value = value + serviceMap.getOpacity(layer) + delim;
				}
				value = value.substring(0,value.length-1);
				var newInput = document.createElement('input');
				newInput.name = name;
				newInput.value = escape(value);
				newInput.type = 'hidden';
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/extent/i)) {
				var newInput = document.createElement('input');
				var delim = inputs[i].getAttribute('delim');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.value = serviceMap.getExtent(delim);
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/layertitles/i)) {
				var newInput = document.createElement('input');
				var delim = inputs[i].getAttribute('delim');
				
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				var layers = serviceMap.getLayerTitles();
				var str = '';
				for(var l = 0; l < layers.length; l++) {
					if(delim == " ") {
						layers[l] = layers[l].replace(' ','');
					}
					str += delim+layers[l];
				}
				str = str.substring(1);
				newInput.value = str;
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/centerpoint/i)) {
				var newInput = document.createElement('input');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.value = serviceMap.getPointScale().Center.toString(inputs[i].getAttribute('delim'));
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/scale/i)) {
				var newInput = document.createElement('input');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.value = serviceMap.getPointScale().Radius;
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/width/i)) {
				var newInput = document.createElement('input');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.value = serviceMap.getMapSize().x;
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/height/i)) {
				var newInput = document.createElement('input');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.value = serviceMap.getMapSize().y;
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/imagesize/i)) {
				var newInput = document.createElement('input');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				var delim = inputs[i].getAttribute('delim');
				if(!delim) { delim = ','; }
				newInput.value = serviceMap.getMapSize().toString(delim);
				formBody.appendChild(newInput);
			} else if(inputs[i].getAttribute('type').match(/title/i)) {
				var newInput = document.createElement('input');
				newInput.name = inputs[i].getAttribute('name');
				var v = inputs[i].getAttribute('value');
				if(v && v != 'null') {
					newInput.value = v;
				}
				newInputCell.appendChild(newInput);
				newInput.onchange = function (event) {
					if(window.event) { event = window.event; }
					var layers = serviceMap.getLayerTitles();
					for(var l = 0; l < layers.length; l++) {
						if(this.value == layers[l]) {
							alert('Already a layer named '+this.value);
							this.value = '';
							return preventDefaultEvent(event);
						}
					}
					return true;
				}
				userInput++;
				newInput = null;

			} else if(inputs[i].getAttribute('type').match(/area/i)) {
				var selectObj = document.createElement('select');
				selectObj.name = inputs[i].getAttribute('name');
				selectObj.setAttribute('subtype', 'select');
				var areas = serviceMap.getDrawingAreas();
				for(var area in areas) {
					var option = document.createElement('option');
					option.value = areas[area];
					option.appendChild(document.createTextNode(area));
					selectObj.appendChild(option);
				}
				newInputCell.appendChild(selectObj);
				userInputs++;
			} else if(inputs[i].getAttribute('type').match(/visiblelayer/i)) {
				var layers = serviceMap.getLayerTitles();
				var selectObj = document.createElement('select');
				selectObj.name = inputs[i].getAttribute('name');
				newInputCell.appendChild(selectObj);

				var nullOpt = document.createElement('option');
				selectObj.appendChild(nullOpt);
				nullOpt = null;

				for(var layer = 0; layer < layers.length; layer++) {
					var option = document.createElement('option');
					option.value = layers[layer];
					selectObj.appendChild(option);
					option.appendChild(document.createTextNode(layers[layer]));
					option = null;
				}

				selectObj = null;
				userInputs++;
			} else if(inputs[i].getAttribute('type').match(/length/i)) {
				var newInput = document.createElement('input');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.setAttribute('id','ServiceForm-'+newInput.name);
				
				var userInput = document.createElement('input');
				userInput.setAttribute('id', newInput.getAttribute('id')+'-Source');
				userInput.targetObj = newInput.getAttribute('id');
				userInput.onchange = function () {
					var obj = document.getElementById(this.targetObj);
					obj.value = this.value;	
				}
				var v = inputs[i].getAttribute('value');
				if(!v || v == 'null') {
					userInput.setAttribute('value', '0');
				} else {
					userInput.setAttribute('value', v);
				}
				newInput.setAttribute('value', userInput.getAttribute('value'));
			
				newInputCell.appendChild(newInput);
				newInputCell.appendChild(userInput);
				newInputCell.appendChild(document.createElement('br'));

				var groundUnits = document.createElement('select');
				newInputCell.appendChild(groundUnits);
				populateUnitConversionSelection('length', groundUnits);
				
				groundUnits.targetId = newInput.getAttribute('id');
				groundUnits.sourceId = userInput.getAttribute('id'); 
				groundUnits.onchange = function () {
					var src = document.getElementById(this.sourceId);
					var target = document.getElementById(this.targetId);
					var opts = this.getElementsByTagName('option');
					var v = src.value;
					if(this.selectedIndex >= 0) { 
						var selOpt = opts[this.selectedIndex];
						var unitCheck = new String(selOpt.value);
						v = Math.round(v*UNIT_CONVERSION[selOpt.value][MAP_GROUND_UNITS]);
						target.value = v;
					}
				}
				groundUnits = null;
				newInput = null;
				userInput = null;
				userInputs++;
			}
			newInputCell = null;
			newInputRow = null;
		}

		var cancelSubmitRow = document.createElement('tr');

		var cancelButton = document.createElement('button');
		cancelButton.appendChild(document.createTextNode('Cancel'));
		var searchButton = document.createElement('button');
		searchButton.appendChild(document.createTextNode('OK'));

		var cancelCell = document.createElement('td');
		var searchCell = document.createElement('td');
		searchCell.style.textAlign = 'right';
		cancelCell.appendChild(cancelButton);
		searchCell.appendChild(searchButton);
		cancelSubmitRow.appendChild(cancelCell);
		cancelSubmitRow.appendChild(searchCell);
		formBody.appendChild(cancelSubmitRow);

		cancelButton.onclick = function() {
			EventManager.triggerEvent('cancelservice', serviceXMLObj);
		}


		searchButton.onclick = function () {
			var values = createInputArray(FormTableId);
			var inputs = serviceXMLObj.getElementsByTagName('input');
			for(var i = 0; i < inputs.length; i++) {
				inputs[i].setAttribute('value', values[inputs[i].getAttribute('name')]); 
			}
			EventManager.triggerEvent('callservice', serviceXMLObj);
		}

		if(userInputs <= 0) {
			searchButton.onclick();
		}
		searchButton = null;
		cancelButton = null;
		cancelCell = null;
		searchCell = null;
		cancelSubmitRow = null;
		formBody = null;
		formTable = null;

	}
}
function ServiceManager(EventManager, ServiceMap) {
	var ServiceFormParent = null;
	var ServiceChildren = new Array();
	
	var knownServices = new Array();

	this.addService = function(servXMLobj) {
		var title = servXMLobj.getAttribute('title');
		knownServices[title] = servXMLobj;
	}

	this.removeService = function(servTitle) {
		if(knownServices[servTitle]) {
			delete knownServices[servTitle];
		}
	}

	/* set the spawn parent function to do something other than this default 
	 * for specific interfaces
	 */
	this.spawnParent = function(title) {
		return document.getElementById(ServiceFormParent);
	}

	/* set if a parent from a custom spawn function needs to be removed
	*/
	this.clearParent = function(title) {
		return false;
	}

	var MySelf = this;

	function renderServiceForm(serviceXMLObj) {
		var servTitle = serviceXMLObj.getAttribute('title');
		var serviceForm = new ServiceForm(EventManager, serviceXMLObj, ServiceMap);
		var formParent = MySelf.spawnParent(servTitle);
		if(formParent) {
			ServiceChildren[servTitle] = serviceForm;
			serviceForm.setParent(formParent.getAttribute('id'));
		} else {
			alert('Service Form Parent not set! Unable to render service object!');
		}
		serviceXMLObj = null;
	}

	this.serviceForm = function(serviceXMLObj) {
		var servTitle = serviceXMLObj.getAttribute('title');
		if(serviceXMLObj.getAttribute('type') && serviceXMLObj.getAttribute('type').match(/inspect/i)) {
			var mapBook = serviceXMLObj;
			while(mapBook.parentNode) {
				mapBook = mapBook.parentNode;
			}

			var specificType = serviceXMLObj.getAttribute('type').split('-');
			if(!specificType[1]) {
				specificType[1] = 'default';
			}
			if(ServiceFormParent) {
				document.getElementById(ServiceFormParent).innerHTML = '<b>'+'Please Choose a Point on the Map'+'</b>';
			}
			EventManager.addEventListener('changetool','ServInspect', function () {
				EventManager.removeEventListener('changetool','ServInspect');
				EventManager.removeEventListener('finishdrawing', 'ServInspect');

			});
			EventManager.addEventListener('finishdrawing','ServInspect', function (p) {
					var serviceXML = mapBook.createElement('service');
					//serviceXML.setAttribute('title', serviceXMLObj.getAttribute('title'));


					var url = mapBook.createElement('url');
					url.appendChild(mapBook.createTextNode(serviceXMLObj.getElementsByTagName('url')[0].firstChild.nodeValue));
					serviceXML.appendChild(url);

					var inputs = serviceXMLObj.getElementsByTagName('input');
					var copyAttributes = ['name','title','value','delim','display','return','selectable','shape'];

					for(var i = 0; i < copyAttributes.length; i++) {
						var v = serviceXMLObj.getAttribute(copyAttributes[i]);
						if(v) {
							serviceXML.setAttribute(copyAttributes[i], v);
						}
					}

					var latLong = new Point();
					if(window.toLatLong) {
						var sysArr = (new String(MAP_COORDINATE_SYSTEM)).split('-');
						var unitConversion = new Point(p[0].x, p[0].y);
						var llPoint = window.toLatLong(sysArr[1], unitConversion);
						latLong = llPoint;
					}



					for(var i = 0; i < inputs.length; i++) {
						var newInput = mapBook.createElement('input');
						var inputType = new String(inputs[i].getAttribute('type')).toLowerCase();
						for(var attr = 0; attr < copyAttributes.length; attr++) {
							newInput.setAttribute(copyAttributes[attr], new String(inputs[i].getAttribute(copyAttributes[attr])));
						}
						if(inputType == 'point') {
							var delim = inputs[i].getAttribute('delim');
							if(!delim) { delim = ','; }
							var pValue = p[0].toString(delim);
							newInput.setAttribute('type','hidden');
							newInput.setAttribute('value', pValue);
						} else if(inputType.match(/point.x/)) {
							newInput.setAttribute('type','hidden');
							newInput.setAttribute('value', p[0].x);
						} else if(inputType.match(/point.y/)) {
							newInput.setAttribute('type','hidden');
							newInput.setAttribute('value', p[0].y);
						} else if(inputType.match(/point.lat/)) {
							newInput.setAttribute('type','hidden');
							newInput.setAttribute('value', latLong.y);
						} else if(inputType.match(/point.long/)) {
							newInput.setAttribute('type','hidden');
							newInput.setAttribute('value', latLong.x);
						} else if(inputType.match(/multipoint/)) {
							var delim = inputs[i].getAttribute('delim');
							var pairDelim = inputs[i].getAttribute('pointdelim');
							if(!delim) { delim = ','; }
							if(!pairDelim) { pairDelim = ' '; }
							var value = '';
							var shape = serviceXMLObj.getAttribute('shape');
							if(shape && (shape.match(/poly/i)  || shape.match(/measurearea/i))) {
								p[p.length] = p[0];
							}
							for(var pi = 0; pi < p.length; pi++) {
								var pValue = p[pi].toString(delim);
								value = value+pairDelim+pValue;
							}
							value = value.substring(1);
							newInput.setAttribute('type','hidden');
							newInput.setAttribute('value', value);
						} else if(inputType.match(/multipoint.x/)) {
							var delim = inputs[i].getAttribute('delim');
							if(!delim) { delim = ','; }
							var value = '';
							p[p.length] = p[0];
							for(var pi = 0; pi < p.length; pi++) {
								var pValue = p[pi].x;
								value = value+delim+pValue;
							}
							value = value.substring(1);
							newInput.setAttribute('type','hidden');
							newInput.setAttribute('value', value);
						} else if(inputType.match(/multipoint.y/)) {
							var delim = inputs[i].getAttribute('delim');
							if(!delim) { delim = ','; }
							var value = '';
							p[p.length] = p[0];
							for(var pi = 0; pi < p.length; pi++) {
								var pValue = p[pi].y;
								value = value+delim+pValue;
							}
							value = value.substring(1);
							newInput.setAttribute('type','hidden');
							newInput.setAttribute('value', value);
						} else {
							newInput.setAttribute('type', inputs[i].getAttribute('type'));
						}
						serviceXML.appendChild(newInput);
					}
					//serviceXML.setAttribute('type', 'default');
					serviceXML.setAttribute('type', specificType[1]);
					//EventManager.removeEventListener('changetool','ServInspect');
					//EventManager.removeEventListener('finishdrawing', 'ServInspect');
					renderServiceForm(serviceXML);
					serviceXML = null;
					ServiceMap.clearDrawingArea();
			});
			//ServiceMap.pointDrawTool();
			//ServiceMap.loadDrawTool('measurearea', true);
			var shape = serviceXMLObj.getAttribute('shape');
			if(!shape || shape == 'null') {
				shape = 'poly';
			}
			ServiceMap.loadDrawTool(shape, true);
			if(serviceXMLObj.getAttribute('cursor')) {
				ServiceMap.setMapCursor(serviceXMLObj.getAttribute('cursor'));
			}
			return false;
		}
		renderServiceForm(serviceXMLObj);

	}

	this.clearServiceForm = function(serviceXMLObj) {
		var servTitle = serviceXMLObj.getAttribute('title');
		if(ServiceChildren[servTitle]) {
			ServiceChildren[servTitle].clearForm();
		}
		MySelf.clearParent(serviceXMLObj);
	}

	this.setParent = function(p) {
		ServiceFormParent = p;
	}

	this.serviceCount = function() {
		var i = 0;
		for(var l in knownServices) {
			i++;
		}
		alert(i+' known services.');
	}
}

var VIEWBOX_INSTANCE = 0;	/* Instance Counter */

function ViewBox(EventManager, ViewMap) {
	var ViewListId = 'ViewBox'+VIEWBOX_INSTANCE+'-';
	VIEWBOX_INSTANCE++;

	var KnownViews = new Array();	/* Track the known views */

	this.addView = function(viewXMLObj) {
		var title = viewXMLObj.getAttribute('title');
		KnownViews[title] = viewXMLObj;
		refreshViewBox();
	}


	function addViewToList(viewList, viewXMLObj) {
		var title = viewXMLObj.getAttribute('title');
		var option = document.createElement('option');
		option.appendChild(document.createTextNode(title));
		option.setAttribute('id',ViewListId+'Option-'+title);
		option.value = viewXMLObj.firstChild.nodeValue;
		viewList.appendChild(option);
		option = null;
	}

	this.removeView = function(viewTitle) {
//		viewsList.removeChild(document.getElementById('ViewBoxOption-'+viewTitle));
		if(KnownViews[viewTitle]) {
			delete KnownViews[viewTitle];
		}
		refreshViewBox();
	}

	function refreshViewBox() {
		if(document.getElementById(ViewListId+'List')) {
			var vList = document.getElementById(ViewListId+'List');
			var vOptions = vList.getElementsByTagName('option');
			for(var i = vOptions.length - 1; i > 0; i--) {		/* The Zero-th Option should be the blank one. */
				vList.removeChild(vOptions[i]);
			}
			for(view in KnownViews) {
				addViewToList(vList, KnownViews[view]);
			}
		}
	}

	function changeView() {
		var viewsList = this;
		var viewOptions = viewsList.getElementsByTagName('option');
		var circView = convertViewString(viewOptions[viewsList.selectedIndex].value, ViewMap.getMapSize());
		EventManager.changePointScale(circView.Center, circView.Radius);
		for(var i = 0; i < viewOptions.length; i++) {
			viewOptions.selected = false;
		}
		viewOptions[0].selected = true;
	}

	this.setParent = function(p) {
		var viewsList = document.createElement('select');		/* Create the select box */
		viewsList.setAttribute('id', ViewListId+'List');		/* Set the List Id */
		viewsList.appendChild(document.createElement('option'));	/* Create the blank/reset option */
		viewsList.onchange = changeView;
		refreshViewBox();
		p.appendChild(document.createTextNode('Jump To:'));
		p.appendChild(viewsList);
		viewsList = null;
	}
}
/*
 * Updates the scale every navigate event.
 * Generates a navigate event when "Enter" is pressed.
 */

var SCALE_BOX_INSTANCE = 0;

function ScaleBox(EventManager, ScaleMap) {
	var ScaleCenterPoint;
	var ScaleBoxInstance = 'ScaleBox'+SCALE_BOX_INSTANCE+'-';
	var ScaleInputId = ScaleBoxInstance+'Input-';
	var ScaleJumperId = ScaleBoxInstance+'Jumper-';

	SCALE_BOX_INSTANCE++;



	function changeScale() {
		EventManager.changePointScale(ScaleCenterPoint, inchScaleToPixelScale(ScaleMap, stripCommas(document.getElementById(ScaleInputId).value)));
	}

	function jumpToScale() {
		var ScaleInput = document.getElementById(ScaleInputId);
		ScaleInput.value = this.getAttribute('scale');
		ScaleInput.onblur();
		changeScale();
	}

	function getBackgroundFunction(color) {
		return function () {
			this.style.background = color;
		};
	}

	function populateScaleJumper(scaleInput, scaleJumper) {
		for(var i in SCALEBOX_JUMPER_INCREMENTS) {
			var link = document.createElement('input');
			link.style.display = 'block';
			link.style.color = 'black';
			link.style.textAlign = 'right';
			var offsetW = 0;
			if(document.all) {
				offsetW -= 6;
			}
			link.style.width = scaleInput.offsetWidth + offsetW + 'px';
			link.style.border = 'none';
			link.value = i;
			link.setAttribute('scale',SCALEBOX_JUMPER_INCREMENTS[i]);
			link.style.cursor = 'default';
			link.onfocus = onFocus;
			link.onmouseover = getBackgroundFunction('#DDD');
			link.onmouseout = getBackgroundFunction('white');
			link.onclick = jumpToScale;
			scaleJumper.appendChild(link);
			link = null;
		}
	}

	


	function onFocus(event) {
		var ScaleInput = document.getElementById(ScaleInputId);
		var ScaleJumper = document.getElementById(ScaleJumperId);
		if(window.event) { event = window.event; }
		var left = ScaleInput.offsetLeft;
		var top = ScaleInput.offsetTop+ScaleInput.offsetHeight;
		var width = ScaleInput.offsetWidth;
		if(document.all) {
			left = objectClientPosition(ScaleInput).x;
			top += 4;
		}

		ScaleInput.style.background = '#DDD';
		ScaleJumper.style.visibility = 'visible';
		ScaleJumper.style.top = 'auto';
		ScaleJumper.style.bottom = top+'px';
		ScaleJumper.style.left = left + 'px';
		ScaleJumper.style.right = 'auto';
		ScaleJumper.style.zIndex = 10000;
	}

	function onBlur(event) {
		if(window.event) { event = window.event; }
		var ScaleJumper = document.getElementById(ScaleJumperId);
		var ScaleInput = document.getElementById(ScaleInputId);
		if(document.all && event.srcElement == ScaleJumper) {
			return false;
		}
		ScaleInput.style.background = 'white';
		ScaleJumper.style.visibility = 'hidden';
	}
	
//	ScaleInput.onkeypress = function(event) {

	function onKeyPress(event) {
		if(window.event) { event = window.event; } 
		if(event.keyCode == 13) {
			EventManager.changePointScale(ScaleCenterPoint, inchScaleToPixelScale(ScaleMap, stripCommas(this.value)));
		}
		if(event.keyCode == 27) {
			this.value = Math.round(this.originalValue);
		}
	}

	var CurrentScale;
	this.onNavigate = function (psScale) {
		var scale = Math.round(pixelScaleToInchScale(ScaleMap, psScale.Radius));
		var ScaleInput = document.getElementById(ScaleInputId);
		if(ScaleInput) {
			ScaleInput.value = commifyNumber(scale);
			ScaleInput.originalValue = scale;
		}
		CurrentScale = scale;
		ScaleCenterPoint = psScale.Center;
	}
	
	this.setParent = function (p) {
		p = document.getElementById(p);

		var ScaleInput = document.createElement('input');
		ScaleInput.style.textAlign = 'right';
		ScaleInput.style.height = '15px';
		ScaleInput.style.fontSize = '13px';
		ScaleInput.size = '12';
		ScaleInput.onblur = onBlur;
		ScaleInput.onkeypress = onKeyPress;
		if(CurrentScale) {
			ScaleInput.value = commifyNumber(CurrentScale);
			ScaleInput.originalValue = CurrentScale;
		}
		ScaleInput.setAttribute('id', ScaleInputId);
		p.appendChild(ScaleInput);

		var ScaleGo = document.createElement('input');
		ScaleGo.onclick = changeScale;
		if(document.all) {
			ScaleGo.style.height = '22px';
		}
		ScaleGo.style.fontSize = '13px';
		ScaleGo.value = 'Go!';
		ScaleGo.type = 'button';
		p.appendChild(ScaleGo);
		ScaleGo = null; /* Clean up the JS object */

		var ScaleJumper = document.createElement('div');
		ScaleJumper.style.zIndex = 10000;
		ScaleJumper.style.border = 'solid 3px black';
		ScaleJumper.style.visibility = 'hidden';
		ScaleJumper.style.position = 'absolute';
		ScaleJumper.style.bottom = '1px';
		ScaleJumper.style.height = 'auto';
		ScaleJumper.style.background = 'white';
		ScaleJumper.setAttribute('id', ScaleJumperId);
		if(document.all) {
			document.getElementsByTagName('body')[0].appendChild(ScaleJumper);
		} else {	
			p.appendChild(ScaleJumper);
		}

		ScaleJumper.onfocus = onFocus;
		ScaleInput.onfocus = onFocus;
		
		populateScaleJumper(ScaleInput, ScaleJumper);

		ScaleInput = null;
		ScaleJumper = null;
	}

}
var LOADING_BAR_INSTANCE = 0;
/*
Events.addEventListener('imagelayer', 'Loading', LoadingBar.loadedLayer);
Events.addEventListener('addlayer', 'Loading', LoadingBar.addLayer);
Events.addEventListener('removelayer', 'Loading', LoadingBar.removeLayer);
Events.addEventListener('refreshlayer', 'Loading', LoadingBar.reloadLayer);
Events.addEventListener('navigate', 'Loading', LoadingBar.reloadAll);
*/

function GLoadingBar(EventManager, LoadingMap) {
	var LayersInQueue = new Array();
	var LoadingBarId;
	var MyInstance = LOADING_BAR_INSTANCE;
	LOADING_BAR_INSTANCE++;

	this.addLayer = function (mapObjXML) {
		var title = mapObjXML.getAttribute('title');
		LayersInQueue[title] = 0;
	}


	this.removeLayer = function (title) {
		var deleted = 'no';
		if(LayersInQueue[title] || LayersInQueue[title] == 0) {
			delete LayersInQueue[title];
			deleted = 'yes';
		}
		if(allLoaded()) {
			hideLoadingPanel();
		}
	}

	function allLoaded() {
		for(var l in LayersInQueue) {
			if(LayersInQueue[l] > 0) {
				return false;
			}
		}
		return true;
	}


	function hideLoadingPanel () {
		if(document.getElementById(LoadingBarId)) {
			document.getElementById(LoadingBarId).style.visibility = 'hidden';
		}
	}

	function showLoadingPanel () {
		positionLoadingPanel();
		if(document.getElementById(LoadingBarId)) {
			document.getElementById(LoadingBarId).style.visibility = 'visible';
		}
	}
	
	this.loadedLayer = function (title) {
		LayersInQueue[title] = 0;
		if(allLoaded()) {
			hideLoadingPanel();
		}
	}

	this.getUnloadedList = function () {
		var list = new String();
		for(var l in LayersInQueue) {
			if(LayersInQueue[l] > 0) {
				list = list+l+'\n';
			}
		}
		return list;
	}
	function positionLoadingPanel() {
		if(!LoadingBarId || !LoadingMap) { return false; } /* Can't align an object if it doesn't exist yet! */
		var loadingBar = document.getElementById(LoadingBarId);
		var barWidth = loadingBar.offsetWidth;
		var mapWidth = LoadingMap.getMapSize().x;
		var mapHeight = LoadingMap.getMapSize().y;
		var mapLeft = 0;
		var barHeight = loadingBar.offsetHeight;
		var barLeft = mapLeft + (mapWidth - barWidth ) / 2;
		var barTop = (mapHeight - barHeight) / 2;
		loadingBar.style.left = barLeft + 'px';
		loadingBar.style.top = barTop + 'px';
	}
	
	var ReloadIteration = 0;

	this.reloadAll = function () {
		for(var l in LayersInQueue) {
			LayersInQueue[l] = 1;
		}
		showLoadingPanel();
		ReloadIteration++;
		setTimeout("document.getElementById('"+LoadingBarId+"').timeout("+ReloadIteration+")",LOADING_TIMEOUT);
	}

	this.reloadLayer = function (title) {	
		LayersInQueue[title] = 1;
		showLoadingPanel();
	}

	MySelf = this;

	function timeoutCheck (iteration) {
		if(iteration == ReloadIteration && !allLoaded()) {
			var timeoutInSeconds = LOADING_TIMEOUT / 1000;
			var unloadedList = MySelf.getUnloadedList();
			if(confirm('Timeout! The following layers took longer than '+timeoutInSeconds+' seconds to load:\n'+unloadedList+'Would you like me to remove it?')) {
				var arr = unloadedList.split('\n');
				for(var i = 0; i < arr.length; i++) {
					EventManager.onRemoveLayer(arr[i]);
				}
			}
			
			hideLoadingPanel();
		}
			
	}
	this.setParent = function (p) {
		var parent = document.getElementById(p);
		var loadingBar = document.createElement('div');
		loadingBar.className = 'LoadingBar';
		parent.appendChild(loadingBar);

		LoadingBarId = 'LoadingBar'+MyInstance;
		loadingBar.setAttribute('id', LoadingBarId);
		loadingBar.appendChild(document.createTextNode(LOADING_MESSAGE));
		loadingBar.appendChild(document.createElement('br'));
		loadingBar.timeout = timeoutCheck;

		var loadingImage = document.createElement('img');
		loadingImage.src = LOADING_IMAGE;
		loadingBar.appendChild(loadingImage);

		loadingImage = null;
		loadingBar=null;
		positionLoadingPanel();
		
	}
}
/*
 * Manages Tab Display Functionality
 */
 
function TabManager(titleBarObj, hiddenClassName, visibleClassName, EventManager) {
	var tabs = new Array();
	var MySelf = this;
	var lastTab;
	var currentTab;

	this.getFirstTab = function() {
		for(var t in tabs) {
			return t;
		}
	}
	

	this.addTab = function(tabName, tabObject) {
		tabs[tabName] = tabObject;
	}

	this.removeTab = function(tabName) {
		MySelf.hideAllTabs();
		if(tabs[tabName]) {
			delete tabs[tabName];
		}
		MySelf.showLastTab();
	}
	
	this.hideAllTabs = function() {
		for(var t in tabs) {
			/*tabs[t].scrollTop = 0;
			tabs[t].scrollLeft = 0;*/
			var tab = document.getElementById(tabs[t]);	
			tab.className = hiddenClassName;
			tab.scrollTop = 0;
			tab = null;
		}
	}

	this.getLastTab = function() {
		return lastTab;
	}

	this.showTab = function(tabName) {
//		if(tabName == currentTab) { return true; }
		MySelf.hideAllTabs();
		if(tabs[tabName]) {
			document.getElementById(tabs[tabName]).className = visibleClassName;
			document.getElementById(titleBarObj).innerHTML =new String(tabName).toUpperCase();
			lastTab= currentTab;
			currentTab = tabName;
		}
	}

	this.showLastTab = function() {
		if(!tabs[lastTab]) {
			lastTab = MySelf.getFirstTab();
		}
		EventManager.triggerEvent('changetab', lastTab);
	}	

	this.checkTab = function(tName) {
		return (tabs[tName]);
	}

	this.alertTabs = function () {
		var message = '';
		for(t in tabs) {
			message=message+t+' '+tabs[t]+'\n';
		}
		alert(message);
	}
	
}
function MapNavigator (EventManager, Map) {
	var SLIDER_MIN_PX = 140;
	var SLIDER_MAX_PX = 22;

	var SLIDER_SCALE_PER_PX = (SLIDER_MIN_PX - SLIDER_MAX_PX) / (SLIDER_MAX_SCALE - SLIDER_MIN_SCALE);


	var Parent;
	var SliderControl;
	var IncreaseScale;
	var DecreaseScale;

	this.createNavIcon = function(alt) {
		var obj = document.createElement('div');
		obj.title = alt;
		obj.alt = alt;
		obj.style.position = 'absolute';
		return obj;
	}

	this.navigate = function(percentEast, percentNorth,scaleChange) {
		var pointScale = Map.getPointScale();
		pointScale.Radius *= scaleChange;
		if(pointScale.Radius > SLIDER_MAX_SCALE) { pointScale.Radius = SLIDER_MAX_SCALE; }	
		if(pointScale.Radius < SLIDER_MIN_SCALE) { pointScale.Radius = SLIDER_MIN_SCALE; }	
		var mapDimension = Map.getMapSize();
		pointScale.Center.x += (mapDimension.x*percentEast*pointScale.Radius);
		pointScale.Center.y += (mapDimension.y*percentNorth*pointScale.Radius);

		EventManager.triggerEvent('navigate', pointScale);
	}

	this.onNavigate = function(psScale) {
		var Bottom = SLIDER_MIN_PX;
		if(SLIDER_MIN_PX > SLIDER_MAX_PX) { Bottom = SLIDER_MAX_PX; }
		var length = Math.abs(SLIDER_MAX_PX - SLIDER_MIN_PX - SliderControl.offsetHeight);
		var scaleLen = SLIDER_MAX_SCALE - SLIDER_MIN_SCALE;
		var percent =  pixelScaleToInchScale(Map,psScale.Radius) / scaleLen;
		if(percent > 1) { percent = 1; }
		SliderControl.style.top = Bottom+(percent*length) + 'px';	
	}

	var MySelf = this;

	this.onResize = function() {
		SLIDER_MAX_PX = DecreaseScale.offsetTop;
		SLIDER_MIN_PX = IncreaseScale.offsetTop + IncreaseScale.offsetHeight;
		SLIDER_SCALE_PER_PX = (SLIDER_MIN_PX - SLIDER_MAX_PX) / (SLIDER_MAX_SCALE - SLIDER_MIN_SCALE);
	}

	this.setParent = function(p) {
		Parent = p;
		var slider = this.createNavIcon('Scale Slider Bar');
		slider.style.border = '0px';
		Parent.appendChild(slider);

		var sliderControl = document.createElement('div');
		sliderControl.id = 'navigatorSliderControl';
		SliderControl = sliderControl;

		var draggable = new Draggable(sliderControl.id, sliderControl, false, true, true);
		slider.onclick = function (event) {
			if(window.event) { event = window.event; }
			var pos = objectClientPosition(this);
			var top = event.clientY-pos.y + this.offsetTop;
			sliderControl.style.top = top + 'px';
			draggable.onDrop();

			if(event.preventDefault) { event.preventDefault(); }
		}
		Parent.appendChild(sliderControl);
		SliderControl = sliderControl;

		var increaseScale = document.createElement('div');
		increaseScale.id = 'navigatorIncreaseScale';
		increaseScale.title = 'Zoom In';
		Parent.appendChild(increaseScale);
		IncreaseScale = increaseScale;

		var decreaseScale = document.createElement('div');
		decreaseScale.id = 'navigatorDecreaseScale';
		decreaseScale.title = 'Zoom Out';
		Parent.appendChild(decreaseScale);
		DecreaseScale = decreaseScale;
		
		SLIDER_MAX_PX = decreaseScale.offsetTop;
		SLIDER_MIN_PX = increaseScale.offsetTop + increaseScale.offsetHeight;
		SLIDER_SCALE_PER_PX = (SLIDER_MIN_PX - SLIDER_MAX_PX) / (SLIDER_MAX_SCALE - SLIDER_MIN_SCALE);
		

		increaseScale.onclick = function (event) {
			MySelf.navigate(0,0,.7);
		}
		decreaseScale.onclick = function (event) {
			MySelf.navigate(0,0,1.2);
		}


//		sliderControl.style.top = Top + SLIDER_MAX_PX + 'px';

		var n = this.createNavIcon('North');
		var s = this.createNavIcon('South');
		var e = this.createNavIcon('East');
		var w = this.createNavIcon('West');
		Parent.appendChild(n);
		Parent.appendChild(s);
		Parent.appendChild(e);
		Parent.appendChild(w);

		n.id = 'navigatorNorth';
		e.id = 'navigatorEast';
		w.id = 'navigatorWest';
		s.id = 'navigatorSouth';
		slider.id = 'navigatorBar';


		function changeVisibility(vis) {
			n.style.visibility = vis;
			e.style.visibility = vis;
			w.style.visibility = vis;
			s.style.visibility = vis;

			slider.style.visibility = vis;
			sliderControl.style.visibility = vis;
		}
	
		this.getVisibility = function () {
			return(n.style.visibility);
		}

		this.hideNavigator = function() {
			changeVisibility('hidden');
		}

		this.showNavigator = function() {
			changeVisibility('visible');
		}
		
		n.onclick = function () {
			MySelf.navigate(0,.25,1);
		}

		s.onclick = function () {
			MySelf.navigate(0,-.25,1);
		}

		e.onclick = function () {
			MySelf.navigate(.25,0,1);
		}

		w.onclick = function () {
			MySelf.navigate(-.25,0,1);
		}
	

		draggable.whileDrag = function(trigger,obj) {
			if(obj.offsetTop < SLIDER_MIN_PX - 5) {
				obj.style.top = SLIDER_MIN_PX - 3 + 'px';
			}
			if(obj.offsetTop+obj.offsetHeight > SLIDER_MAX_PX + 3) {
				obj.style.top = SLIDER_MAX_PX-obj.offsetHeight+'px';
			}
		}
		draggable.onDrop = function(trigger,obj) {
			var mapScale = Map.getPointScale();
			var scaleLen = SLIDER_MAX_SCALE - SLIDER_MIN_SCALE;
			var length = Math.abs(SLIDER_MAX_PX - SLIDER_MIN_PX);
			var slTop = SliderControl.offsetTop+SliderControl.offsetHeight;
			var percent = slTop - SLIDER_MIN_PX;
			percent = percent / length;
			if(percent < .01) { percent = 0; }
			if(percent > 1) { percent = 1; }
			mapScale.Radius = inchScaleToPixelScale(Map, scaleLen * percent + SLIDER_MIN_SCALE);
			EventManager.triggerEvent('navigate', mapScale);
		}
	}


}
/*
 * Provides the functionality for backwards/forwards buttons 
 * Written by Dan "Ducky" Little 3/17/2007
 */


function NavigatorHistory(EventManager) {
	var ViewHistory = new Array();
	var currentIndex = -1;
	
	this.onNavigate = function(viewCirc) {
		if(!viewCirc.navigatorLog) {
			currentIndex++;
			ViewHistory[currentIndex] = viewCirc;
			ViewHistory[currentIndex].navigatorLog = true;
		}
	}

	this.previous = function () {
		currentIndex--;
		if(ViewHistory[currentIndex]) {
			EventManager.triggerEvent('navigate', ViewHistory[currentIndex]);
		} else {
			currentIndex++;
		}
	}

	this.next = function () {
		currentIndex++;
		if(currentIndex >= ViewHistory.length) {
			currentIndex--;
		} else {
			EventManager.triggerEvent('navigate', ViewHistory[currentIndex]);
		}
	}
}
