[Geomoose-users] Internet Explorer Error

Devon Piernot piernotd at uwplatt.edu
Wed Oct 7 18:42:12 EDT 2009


Skipped content of type multipart/alternative-------------- next part --------------
/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/*
 * GeoMOOSE Site Configuration
 */

var CONFIGURATION = {
	'links_bar_html' : "",
	'waiting_html' : 'Loading...',
	'mapserver_url' : "",
	'mapfile_root' : "",
	'mapbook' : "php/getmapbook.php",
	'fractional_zoom' : false,
	'scales' : [1,2,4,8,16,32,64,128,256,500,1000,5000],
	'max_extent' : [189783.560000,4816309.330000,761653.524114,5472346.500000],
	'initial_extent' : [410438.542057,5125011.551363637,540998.542057,5163644.278636363],
	'projection' : "EPSG:3071",
	'ground_units' : 'm',
	'coordinate_display' : {
		xy: true,	/* Ground Units */
		latlon: true,	/* Longitude and Latitude */
		usng: true	/* U.S. National Grid */
	},
	'tabs' : {
		'Catalog' : 'catalog-tab',
		'Search' : 'search-tab',
		'Services' : 'service-tab',
		'Results' : 'results-tab'
	},
	'default_tab' : 'Catalog',
	'catalog_name' : 'Catalog',
	'show_service_settings_in' : 'Services',
	'show_results_in' : 'Results',
	'group_checkboxes' : true,
	'zoomto' : {
/*		
		'Jump To:' : {
			'Dakota County' : [521238.614537864,4924218.86673578,473921.947801381,4974430.36885032],
			'Parcel Data' : [497205.409367,4923984.423582,477595.805945,4941970.52988],
			'Full State of MN' : [189783.560000,4816309.330000,761653.524114,5472346.500000]
		}
*/
	},
	'jumpto_scales' : {
		'1:12000' : 12000,
		'1:2400' : 2400,
		'1:1200' : 1200,
		'1:120' : 120
	},
	'startup_service' : false,
	'layer_controls' : {
		'up' : {on: true, tip: 'Move %LAYER% up on the stack'},
		'down' : {on: true, tip: 'Move %LAYER% down on the stack'},
		'fade' : {on: true, change_percent: 10, tip: 'Fade %LAYER%'},
		'unfade' : {on: true, change_percent: 10, tip: 'Unfade %LAYER%'},
		'refresh' : {on: false, tip: 'Manually refresh %LAYER%'},
		'cycle' : {on: false, tip: 'Refresh %LAYER% every 10 seconds', seconds: 10},
		'legend' : {on: true, tip: 'Show legend for %LAYER%'}
	},
	'reference_map' : {
		'enabled' : true,
		'maximized' : true
	},
	'drawing_tools' : {
		'default_fill' : 'blue',
		'default_stroke' : 'red',
		'default_opacity' : '.8'
	},
	'measure_tool' : {
		'precision' : 3,
		'style' : {
			"Point" : {
				pointRadius: 4,
				graphicName: "square",
				fillColor: "white",
				fillOpacity: 1,
				strokeWidth: 1,
				strokeOpacity: 1,
				strokeColor: "#333333"
			},
			"Line" : {
				strokeWidth: 2,
				strokeColor: "#FF0000"
			},
			"Polygon" : {
				strokeWidth: 2,
				strokeColor: "#FF0000",
				fillColor: "#00FF00",
				fillOpacity: .5
			}
		},
		'line_units' : 'ft',
		'area_units' : 'acre'
	}
};
/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

function GeomooseCatalog(parentDivId, olMap) {
	var elementCounter = 0;
	var MyCatalog = false;
	var MySelf = this;

	var MapbookLoaded = false;

	function getCatalogElementId() {
		elementCounter++;
		return 'catalog-'+elementCounter;
	}

	function toggleGroup() {
		/*var g = document.getElementById(groupElementId);*/
		var grpList = this.nextSibling;
		var className = new String(grpList.className);
		if(className.match('catalog-group-collapsed')) {
			grpList.className = className.replace('collapsed','expanded');
		} else {
			grpList.className = className.replace('expanded','collapsed');
		}
	}


	function groupClickAll(event) {
		if(window.event) { event =window.event; }
		event.cancelBubble = true;

		var group = this.parentNode.nextSibling;	
		var layers = group.getElementsByTagName('input');

		for(var i = 0; i < layers.length; i++) {
			if(layers[i].parentNode.className == 'catalog-layer') {
				if(layers[i].checked != this.checked) {
					MySelf.setLayerVisibility(layers[i].value, this.checked);
					layers[i].checked = this.checked;
				}
			}
		}
	}

	function renderGroup(groupElementId, group) {
		var p = document.getElementById(groupElementId);
		var ul = document.createElement('ul');
		ul.className = 'catalog';

		if(p.tagName == 'UL' && group.getAttribute('title')) {
			var li = document.createElement('li');
			p.appendChild(li);

			if(CONFIGURATION.group_checkboxes && parseBoolean(group.getAttribute('multiple'), true)) {
				var group_control = document.createElement('input');
				group_control.type = 'checkbox';
				li.appendChild(group_control);
				group_control.onclick = groupClickAll;
			}

			li.appendChild(document.createTextNode(group.getAttribute('title')));
			li.className = 'catalog-group';
			li.onclick = toggleGroup;
			li = null;
			if(parseBoolean(group.getAttribute('expand'))) {
				ul.className += ' catalog-group-expanded';
			} else {
				ul.className += ' catalog-group-collapsed';
			}
		} else {
			ul.className += ' catalog-group-expanded';
		}
		ul.id = getCatalogElementId();
		p.appendChild(ul);

		for(var i = 0; i < group.childNodes.length; i++) {
			var tagName = group.childNodes[i].tagName;
			if(tagName) {
				if(tagName == 'group') {
					renderGroup(ul.id, group.childNodes[i]);
				} else if(tagName == 'layer') {
					renderLayer(ul.id, group.childNodes[i]);
				}
			}
		}

		ul = null;
		p = null;
	}

	function iterateThroughLayers(pathString, fn) {
		var services = MyCatalog.getElementsByTagName('map-source');
		var paths = new String(pathString).split(':');

		for(var i = 0; i < services.length; i++) {
			var root = services[i].getAttribute('name');
			var layers = services[i].getElementsByTagName('layer');
			for(var l = 0; l < layers.length; l++) {
				var layerName = layers[l].getAttribute('name')
				for(var p = 0; p < paths.length; p++) {
					if(root+'/'+layerName == paths[p]) {
						var mapLayer = Map.getLayersByName(root)[0];
						if(mapLayer) {
							fn(mapLayer, paths[p]);
						}
					}
				}

			}
		}
	
	}

	this.refreshLayer = function(pathString) {
		iterateThroughLayers(pathString, function(mapLayer) {
			/* This forces the image to reload */
			mapLayer.params['GEOMOOSE_REFRESH'] = (new Date()).getTime();
			mapLayer.redraw();
		});
	}

	this.fadeLayer = function(pathString) {
		iterateThroughLayers(pathString, function(mapLayer) {
			if(mapLayer.opacity == null) { mapLayer.opacity = 1; }
			var newOpac = mapLayer.opacity - CONFIGURATION.layer_controls.fade.change_percent/100;
			if(newOpac < 0) { newOpac = 0; }
			mapLayer.setOpacity(newOpac);
		});
	}


	this.unfadeLayer = function(pathString) {
		iterateThroughLayers(pathString, function(mapLayer) {
			if(mapLayer.opacity == null) { mapLayer.opacity = 1; }
			var newOpac = mapLayer.opacity + CONFIGURATION.layer_controls.unfade.change_percent/100;
			if(newOpac > 1) { newOpac = 1; }
			mapLayer.setOpacity(newOpac);
		});
	}

	this.upLayer = function(pathString) {
		iterateThroughLayers(pathString, function(mapLayer) {
			mapLayer.map.raiseLayer(mapLayer, 1);
		});
	}

	this.downLayer = function(pathString) {
		iterateThroughLayers(pathString, function(mapLayer) {
			mapLayer.map.raiseLayer(mapLayer, -1);
		});
	}

	this.cycleLayer = function(pathString) {
	}


	var legendImages = new Array();

	function updateLegendURLs(pathString) {
		iterateThroughLayers(pathString, function(mapLayer,layerPath) {
			var legendURL = false;
			if(!legendImages[layerPath]) {
				return false;	// nothing to do, no image available
			}
			var lname = layerPath.split('/').pop();
			if(mapLayer instanceof OpenLayers.Layer.MapServer) {
				legendURL = mapLayer.getURL(Map.getExtent()).replace('mode=map','mode=legend');

				/* specify THIS layer */
				legendURL = legendURL.replace(/layers=[\+\_\%a-zA-Z0-9\/]*\&/, 'layers='+lname+'&');
			}
			if(mapLayer instanceof OpenLayers.Layer.WMS || mapLayer instanceof OpenLayers.Layer.geomooseWMS) {
				legendURL = mapLayer.getURL(Map.getExtent()).replace('REQUEST=GetMap', 'REQUEST=GetLegendGraphic');
				legendURL += '&LAYER=' + lname;
				legendURL += '&SCALE=' + Map.getScale();
			}
			if(legendURL) {
				document.getElementById(legendImages[layerPath]).setAttribute('src',legendURL);
			}
		});
	}

	this.legendLayer = function(pathString) {
		var id = this.id;
		var parentControl = this;
		var legendParent = document.getElementById(id+'-legend');

		if(legendParent.className.indexOf('Visible') >= 0) {
			legendParent.className = legendParent.className.replace('Visible','Hidden');
			while(legendParent.childNodes[0]) {
				legendParent.removeChild(legendParent.childNodes[0]);
			}
			iterateThroughLayers(pathString, function(mapLayer, layerPath) {
				legendImages[layerPath] = false;
			});
			parentControl.className = parentControl.className.replace('sprite-control-legend-selected', '');
		} else {
			legendParent.className = legendParent.className.replace('Hidden','Visible');
			if(parentControl.className.indexOf('sprite-control-legend-selected') < 0) {
				parentControl.className += ' sprite-control-legend-selected'; 
			}
			iterateThroughLayers(pathString, function(mapLayer, layerPath) {
				if(!legendImages[layerPath]) {
					var legendImg = document.createElement('img');
					legendImg.className = 'catalog-legend-image';
					legendImg.id = 'catalog-legend-'+getCatalogElementId();
					legendImages[layerPath] = legendImg.id;
					legendParent.appendChild(legendImg);
					legendImg.setAttribute('src','images/blank.gif'); /* Default to prevent display issues */
					legendImg = null;
				}
			});

			updateLegendURLs(pathString);
		}
	}


	this.setLayerVisibility = function(pathString, visibility) {
		var services = MyCatalog.getElementsByTagName('map-source');
		var paths = new String(pathString).split(':');

		var foundSomething = false;

		for(var i = 0; i < services.length; i++) {
			var root = services[i].getAttribute('name');
			var layers = services[i].getElementsByTagName('layer');
			var updateRequired = false;
			for(var l = 0; l < layers.length; l++) {
				var layerName = layers[l].getAttribute('name')
				for(var p = 0; p < paths.length; p++) {
					if(root+'/'+layerName == paths[p]) {
						foundSomething = true;
						var v = 'false';
						if(visibility) {
							v = 'true';
						}
						if(parseBoolean(visibility) != parseBoolean(layers[l].getAttribute('visible'), false)) {
							layers[l].setAttribute('visible', v);
							updateRequired = true;
						}
					}
				}
			}

			if(updateRequired) {
				var layersList = new Array();
				for(var l = 0; l < layers.length; l++) {
					if(parseBoolean(layers[l].getAttribute('visible'))) {
						layersList.push(layers[l].getAttribute('name'));
					}
				}
			
				var mapLayer = Map.getLayersByName(root)[0];
				var referenceLayer = [];
				
				if(CONFIGURATION.reference_map.enabled) {
					referenceLayer = ReferenceMap.ovmap.getLayersByName(root);
				}

				var updateLayers = [mapLayer];

				if(referenceLayer.length > 0) {
					referenceLayer = referenceLayer[0];
					updateLayers.push(referenceLayer);
				}


				for(var l = 0; l < updateLayers.length; l++) {
					var layer = updateLayers[l];
					if(layer instanceof OpenLayers.Layer.MapServer) {
						layer.params['layers'] = layersList.join(' ');
					} else if (layer instanceof OpenLayers.Layer.WMS || layer instanceof OpenLayers.Layer.geomooseWMS) {
						layer.params['LAYERS'] = layersList.join(',');
					}
					if(layersList.length == 0) {
						layer.setVisibility(false);
					} else {
						layer.setVisibility(true);
					}
					if(MapbookLoaded) {
						layer.redraw();
					}
				}
			}
			

		}
		return foundSomething;
	}

	function multiSelectLayer() {
		var p = this.parentNode.parentNode.previousSibling;
		if(p && p.getElementsByTagName) {
			var inputs = p.getElementsByTagName('input');
			if(inputs.length > 0) {
				inputs[0].checked = false;
			}
		}
		MySelf.setLayerVisibility(this.value, this.checked);
	}

	function singleSelectLayer() {
		var p = this.parentNode.parentNode;
		var inputs = p.getElementsByTagName('input');
		for(var i = 0; i < inputs.length; i++) {
			if(inputs[i].name == this.name) {
				inputs[i].checked = false;
			}
			MySelf.setLayerVisibility(inputs[i].value, false);
		}
		this.checked = true;
		MySelf.setLayerVisibility(this.value, true);
	}

	function inScale(minscale, maxscale) {
		var scale = olMap.getScale();
		if(!maxscale && !minscale) { return true; }
		if(!maxscale && scale >= minscale) { return true; }
		if(!minscale && scale <= maxscale) { return true; }
		return (minscale <= scale && scale <= maxscale);
	}

	function renderLayer(groupElementId, layer) {
		var p = document.getElementById(groupElementId);
		var li = document.createElement('li');
		li.className = 'catalog-layer';
		p.appendChild(li);
		var cbox = document.createElement('input');
		cbox.id = 'catalog-'+layer.getAttribute('src');

		if(parseBoolean(layer.parentNode.getAttribute('multiple'),true)) {
			cbox.name = layer.parentNode.getAttribute('name');
			cbox.type = 'checkbox';
			cbox.value = layer.getAttribute('src');
			cbox.onclick = multiSelectLayer;
		} else {
			cbox.type = 'radio';
			cbox.value = layer.getAttribute('src');
			cbox.onclick = singleSelectLayer;
		}
		li.appendChild(cbox);

		var layerTitle = document.createElement('span');
		layerTitle.className = 'catalog-layer-title';
		li.appendChild(layerTitle);

		layerTitle.appendChild(document.createTextNode(layer.getAttribute('title')));

		if(layer.getAttribute('tip')) {
			layerTitle.title = layer.getAttribute('tip');
		}

		if(layer.getAttribute('minscale')) {
			layerTitle.minscale = parseFloat(layer.getAttribute('minscale'));
		}

		if(layer.getAttribute('maxscale')) {
			layerTitle.maxscale = parseFloat(layer.getAttribute('maxscale'));
		}

		if(inScale(layerTitle.minscale, layerTitle.maxscale)) {
			layerTitle.className += ' catalog-inscale';
		} else {
			layerTitle.className += ' catalog-outscale';
		}
		
		var controlSpan = document.createElement('span');
		controlSpan.className = 'catalog-controls-container';
		li.appendChild(controlSpan);

		var controlsId = getCatalogElementId();
		var legend = document.createElement('div');
		legend.className = 'catalog-legend Hidden';
		legend.id = controlsId+'-legend-legend';
		// Leave the legend "blank" for the time being.
		//legend.src = 'images/blank.gif';
		li.appendChild(legend);

		for(var control_name in CONFIGURATION.layer_controls) {
			var layerSwitch = CONFIGURATION.layer_controls[control_name].on;
			if(layer.getAttribute(control_name)) {
				layerSwitch = parseBoolean(layer.getAttribute(control_name));
			}
			if(layerSwitch) {
				var toolDiv = document.createElement('div');
				toolDiv.className = 'sprite-control sprite-control-'+control_name;
				toolDiv.value = layer.getAttribute('src');
				toolDiv.controlFn = eval("MySelf."+control_name+"Layer");
				toolDiv.id = controlsId+'-'+control_name;
				toolDiv.onclick = function () {
					this.controlFn(this.value);
				}

				var tooltip = CONFIGURATION.layer_controls[control_name].tip;
				if(tooltip) {
					tooltip = tooltip.replace('%LAYER%', layer.getAttribute('title'));
					toolDiv.title = tooltip;
				}
				controlSpan.appendChild(toolDiv);

				if(control_name == 'legend' && parseBoolean(layer.getAttribute('show-legend'))) {
					toolDiv.onclick();
				}
			}
		}
		/* check to see if the layer is "on" */
		if(parseBoolean(layer.getAttribute('status'))) {
			cbox.checked = true;
			cbox.onclick();
		}

		cbox = null;
		li = null;
		p = null;
	}

	function onRefreshMap () {
		var p = document.getElementById(parentDivId);
		var span = p.getElementsByTagName('span');
		for(var i = 0; i < span.length; i++) {
			if(span[i].className.indexOf('catalog-layer-title') >= 0) {
				var layerTitle = span[i];
				if(inScale(layerTitle.minscale, layerTitle.maxscale)) {
					layerTitle.className = layerTitle.className.replace('catalog-outscale', 'catalog-inscale');
				} else {
					layerTitle.className = layerTitle.className.replace('catalog-inscale', 'catalog-outscale');
				}
				layerTitle = null;
			}
		}

		for(var pathString in legendImages) {
			updateLegendURLs(pathString);
		}
	}

	function toggleTool(obj, tool) {
		var visClassName = 'sprite-control-'+tool+'-selected';

		/* so it's current visible ... so remove it */
		if(obj.className.indexOf(visClassName) >= 0) {
			obj.className = obj.className.replace(' '+visClassName, '');
			obj.className = obj.className.replace(visClassName, '');
		} else {
			obj.className = obj.className + ' ' + visClassName;
			onSelectTool.push(function() {
				toggleTool(obj, tool);
			});
		}

	}

	this.onLoadedMapbook = function(mapbookXML) {
		MyCatalog = mapbookXML;
		var catalog = mapbookXML.getElementsByTagName('catalog')[0];
		var layerControls = mapbookXML.getElementsByTagName('layer-control');
		for(var i = 0; i < layerControls.length; i++) {
			CONFIGURATION.layer_controls[layerControls[i].getAttribute('name')] = {'on' : parseBoolean(layerControls[i].getAttribute('on')), 'tip' : layerControls[i].getAttribute('tip')}	
			var funcString = "function(paths) { selectTool(); toggleTool(this, '%service_name%'); GeoMOOSE.startService('%service_name%', {'%input%' : paths}); }";
			funcString = funcString.replace(/%service_name%/g, layerControls[i].getAttribute('service-name'));
			funcString = funcString.replace('%input%', layerControls[i].getAttribute('layer-input'));


			eval("MySelf."+layerControls[i].getAttribute('name')+"Layer = "+funcString);
		}

		renderGroup(parentDivId, catalog);

		olMap.events.register('moveend', this, onRefreshMap);
		onRefreshMap();
		MapbookLoaded = true;
	}

	/* return an array of the visible layers */
	this.getVisibleLayers = function() {
		var visible_layers = [];
		var sources = MyCatalog.getElementsByTagName('map-source');
		for(var i = 0; i < sources.length; i++) {
			var layers = sources[i].getElementsByTagName('layer');
			var name = sources[i].getAttribute('name')+'/';
			for(var j = 0; j < layers.length; j++) {
				if(parseBoolean(layers[j].getAttribute('visible'))) {
					visible_layers.push(name+layers[j].getAttribute('name'));
				}
			}
		}
		return visible_layers;
	}

	function getSourcesByAttribute(sourceAttribute, sourceValue) {
		var sources = MyCatalog.getElementsByTagName('map-source');
		var return_sources = [];
		for(var i = 0; i < sources.length; i++) {
			if(sources[i].getAttribute(sourceAttribute) == sourceValue) {
				return_sources.push(sources[i]);
			}
		}
		return return_sources;
	}

	function getSourcesByName(sourceName) {
		return getSourcesByAttribute('name', sourceName);
	}

	function setNodeValue(node, value) {
		while(node.childNodes && node.childNodes[0]) { node.removeChild(node.childNodes[0]); }
		node.appendChild(MyCatalog.createTextNode(value));
	}

	/* set the layer url */
	this.setLayerUrl = function(layerName, layerUrl) {
		var source = getSourcesByName(layerName)[0];
		var url = source.getElementsByTagName('url')[0];

		if(!url) {
			url = MyCatalog.createElement('url');
			source.appendChild(url);
		}
		setNodeValue(url, layerUrl);

		var layer = Map.getLayersByName(layerName)[0];
		layer.url = layerUrl;
	}

	/* get the layer url */
	this.getLayerUrl = function(layerName) {
		return Map.getLayersByName(layerName)[0].url;
	}


	/* update layer params */
	this.setLayerParameters = function(layerName, parameters) {
		var source = getSourcesByName(layerName)[0];
		var params = source.getElementsByTagName('param');

		var layer = Map.getLayersByName(layerName)[0];
		for(var p in parameters) {
			layer.params[p] = parameters[p];
		}

		for(var p = 0; p < params.length; p++) {
			var name = params[p].getAttribute('name');
			if(parameters[name]) {
				setNodeValue(params[p], parameters[name]);
				delete parameters[name];
			}
		}

		for(var n in parameters) {
			var param = MyCatalog.createElement('param');
			param.setAttribute('name', n);
			source.appendChild(param);
			/*setNodeValue(param, parameters[n]);*/
			param.setAttribute('value', parameters[n]);
		}

	}

	/* remove a layer parameter */
	this.removeLayerParameter = function(layerName, parameterName) {
		var source = getSourcesByName(layerName)[0];
		var params = source.getElementsByTagName('param');
		var paramToDelete = false;
		for(var p = 0; p < params.length; p++) {
			var name = params[p].getAttribute('name');
			if(name == parameterName) { paramToDelete = params[p]; }
		}
		if(paramToDelete) {
			source.removeChild(paramToDelete);
		}

		var layer = Map.getLayersByName(layerName)[0];
		if(layer.params[parameterName]) {
			delete layer.params[parameterName];
		}
	}
	/* get the layer parameters as an object */
	this.getLayerParameters = function(layerName) {
		var source = getSourcesByName(layerName)[0];
		var params = source.getElementsByTagName('param');
		var parameters = {};
		for(var p = 0; p < params.length; p++) {
			parameters[params[p].getAttribute('name')] = params[p].getAttribute('value');
		}
		return parameters;
	}
}
/*
 * GeoMOOSE Site Configuration
 */

var CONFIGURATION = {
	'links_bar_html' : "",
	'waiting_html' : 'Loading...',
	'mapserver_url' : "",
	'mapfile_root' : "",
	'mapbook' : "php/getmapbook.php",
	'fractional_zoom' : false,
	'scales' : [1,2,4,8,16,32,64,128,256,500,1000,5000],
	'max_extent' : [189783.560000,4816309.330000,761653.524114,5472346.500000],
	'initial_extent' : [410438.542057,5125011.551363637,540998.542057,5163644.278636363],
	'projection' : "EPSG:26915",
	'ground_units' : 'm',
	'coordinate_display' : {
		xy: true,	/* Ground Units */
		latlon: true,	/* Longitude and Latitude */
		usng: true	/* U.S. National Grid */
	},
	'tabs' : {
		'Catalog' : 'catalog-tab',
		'Search' : 'search-tab',
		'Services' : 'service-tab',
		'Results' : 'results-tab'
	},
	'default_tab' : 'Catalog',
	'catalog_name' : 'Catalog',
	'show_service_settings_in' : 'Services',
	'show_results_in' : 'Results',
	'group_checkboxes' : true,
	'zoomto' : {
/*		
		'Jump To:' : {
			'Dakota County' : [521238.614537864,4924218.86673578,473921.947801381,4974430.36885032],
			'Parcel Data' : [497205.409367,4923984.423582,477595.805945,4941970.52988],
			'Full State of MN' : [189783.560000,4816309.330000,761653.524114,5472346.500000]
		}
*/
	},
	'jumpto_scales' : {
		'1:12000' : 12000,
		'1:2400' : 2400,
		'1:1200' : 1200,
		'1:120' : 120
	},
	'startup_service' : false,
	'layer_controls' : {
		'up' : {on: true, tip: 'Move %LAYER% up on the stack'},
		'down' : {on: true, tip: 'Move %LAYER% down on the stack'},
		'fade' : {on: true, change_percent: 10, tip: 'Fade %LAYER%'},
		'unfade' : {on: true, change_percent: 10, tip: 'Unfade %LAYER%'},
		'refresh' : {on: false, tip: 'Manually refresh %LAYER%'},
		'cycle' : {on: false, tip: 'Refresh %LAYER% every 10 seconds', seconds: 10},
		'legend' : {on: true, tip: 'Show legend for %LAYER%'}
	},
	'reference_map' : {
		'enabled' : true,
		'maximized' : true
	},
	'drawing_tools' : {
		'default_fill' : 'green',
		'default_stroke' : 'red',
		'default_opacity' : '.8'
	},
	'measure_tool' : {
		'precision' : 3,
		'style' : {
			"Point" : {
				pointRadius: 4,
				graphicName: "square",
				fillColor: "white",
				fillOpacity: 1,
				strokeWidth: 1,
				strokeOpacity: 1,
				strokeColor: "#333333"
			},
			"Line" : {
				strokeWidth: 2,
				strokeColor: "#FF0000"
			},
			"Polygon" : {
				strokeWidth: 2,
				strokeColor: "#FF0000",
				fillColor: "#00FF00",
				fillOpacity: .5
			}
		},
		'line_units' : 'ft',
		'area_units' : 'acre'
	}
};
/**
 * @requires OpenLayers/Handler/Feature.js
 * @requires OpenLayers/Handler/Keyboard.js
 * @requires OpenLayers/Handler/Box.js
 */

/**
 * Class: OpenLayers.Control.DeleteFeature
 * Control to delete multiple features with a user-friendly interface.  When
 *     activated, you can basically do 3 actions :
 *     1) Select feature(s)
 *        a) Click on a feature to select a single feature
 *        b) Click on a feature while holding ctrlKey to select an additionnal
 *           feature.
 *        c) If this.box == true, hold shiftKey to select features within
 *           a box.
 *        d) If this.box == true, hold shiftKey and ctrlKey to add features
 *           within a box to the selection.
 *     2) Unselect feature(s)
 *        a) Click on a selected feature while holding ctrlKey to unselect it.
 *        b) Press the escKey to unselect all features
 *     3) Delete the selected feature(s) with the deleteKey or dKey by default.
 *
 * Inherits From:
 *  - <OpenLayers.Control>
 */
OpenLayers.Control.DeleteFeature = OpenLayers.Class(OpenLayers.Control, {
    /**
     * Constant: EVENT_TYPES
     * {Array(String)} Supported application event types.  Register a listener
     *     for a particular event with the following syntax:
     * (code)
     * control.events.register(type, obj, listener);
     * (end)
     *
     *  - *beforefeaturesdeleted* Triggered before features are deleted.  Can
     *      be used to
     *  - *deletefeatures* Triggered 
     */
    EVENT_TYPES: ["beforefeaturesdeleted","deletefeatures"],

    /**
     * Property: features
     * {Array(<OpenLayers.Feature.Vector>)} Features to be deleted.
     */
    features: null,

    /**
     * Property: handlers
     * {Object}
     */
    handlers: null,

    /**
     * APIProperty: deleteCodes
     * {Array(Integer)} Keycodes for deleting features.  Set to null to disable
     *     feature deltion by keypress.  If non-null, keypresses with codes
     *     in this array will delete features in this.features array. Default
     *     is 46 and 68, the 'delete' and lowercase 'd' keys.
     */
    deleteCodes : null,

    /**
     * APIProperty: unselectAllCodes
     * {Array(Integer)} Keycodes for unselecting all features.  Set to null to
     *     disable feature unselection by keypress.  If non-null, keypresses
     *     with codes in this array will unselect features in this.features
     *     array. Default is 27, the 'esc' key.
     */
    unselectAllCodes : null,

    /**
     * APIProperty: box
     * {Boolean} Allow feature selection by drawing a box.
     */
    box: false,

    /**
     * Constructor: OpenLayers.Control.DeleteFeature
     * Create a new delete feature control.
     *
     * Parameters:
     * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
     *     will be deleted.
     * options - {Object} Optional object whose properties will be set on the
     *     control.
     */
    initialize: function(layer, options) {
        // concatenate events specific to this control with those from the base
        this.EVENT_TYPES =
            OpenLayers.Control.DeleteFeature.prototype.EVENT_TYPES.concat(
            OpenLayers.Control.prototype.EVENT_TYPES
        );

        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.features = [];
        this.layer = layer;
        this.deleteCodes = [46, 68];
        this.unselectAllCodes = [27];

        var featureOptions = {
          click: this.clickFeature
        };
        var keyboardOptions = {
          keydown: this.handleKeypress
        };
        this.handlers = {
          feature: new OpenLayers.Handler.Feature(this, layer, featureOptions),
          keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions)
        };

        if (this.box) {
            this.handlers.box = new OpenLayers.Handler.Box(
                this, {done: this.selectBox},
                {boxDivClassName: "olHandlerBoxSelectFeature",
                 keyMask:OpenLayers.Handler.MOD_SHIFT}
            );
            this.handlers.boxCtrlKey = new OpenLayers.Handler.Box(
                this, {done: this.selectBox},
                {boxDivClassName: "olHandlerBoxSelectFeature",
                 keyMask:OpenLayers.Handler.MOD_SHIFT |
                         OpenLayers.Handler.MOD_CTRL}
            );  
        }
    },

    /**
     * Method: clickFeature
     * Using a feature handler, the clicked feature becomes selected and its
     * State is set to DELETE.  Holding the ctrl key while clicking on a feature
     * enables multiselection and unselection.
     *
     * Parameters :
     * feature - {OpenLayers.Feature.Vector}
     */
    clickFeature: function(feature) {
        if(!this.handlers.feature.evt.ctrlKey) { // single feature selection
            this.unselectAllFeatures();
        }
        
        if(feature.fid == undefined) {
            //oHoverRoadControl.resetHoverFeature(); // ### HARDCODED LINE ###
            this.layer.destroyFeatures([feature]);
        } else if (feature.state != OpenLayers.State.DELETE){
            this.selectFeature(feature);
        } else {
            this.unselectFeature(feature);
        }
    },

    /**
     * Method: selectBox
     * Callback from the handlers.box set up when <box> selection is true
     *     on.
     *
     * Parameters:
     * position - {<OpenLayers.Bounds>}
     */
    selectBox: function(position) {
        var selectBoxOnly = !this.handlers.box.dragHandler.evt.ctrlKey;     

        if (position instanceof OpenLayers.Bounds) {
            var minXY = this.map.getLonLatFromPixel(
                new OpenLayers.Pixel(position.left, position.bottom)
            );
            var maxXY = this.map.getLonLatFromPixel(
                new OpenLayers.Pixel(position.right, position.top)
            );
            var bounds = new OpenLayers.Bounds(
                minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
            );

            for(var i=0, len = this.layer.features.length; i<len; ++i) {
                var feature = this.layer.features[i];
                if (this.geometryTypes == null || OpenLayers.Util.indexOf(
                        this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
                    if (bounds.toGeometry().intersects(feature.geometry)) {
                        if (!this.isFeatureSelected(feature) &&
                           (selectBoxOnly || OpenLayers.Util.indexOf(this.features, feature) == -1)) {
                            this.selectFeature(feature);
                        }
                    } else if(selectBoxOnly && this.isFeatureSelected(feature)) {
                        this.unselectFeature(feature);
                    }
                }
            }
        }

    },

    /**
     * Method: selectFeature
     * Add feature to this.features array, set its renderIntent to
     * "select" and its state to DELETE.  Redraw the feature in the end.
     *
     * Parameters:
     * feature - {OpenLayers.Feature.Vector}
     */
    selectFeature: function(feature){
        this.features.push(feature);
        feature.state = OpenLayers.State.DELETE;
        feature.renderIntent = "select";
        this.layer.drawFeature(feature);        
    },

    /**
     * Method: unselectFeature
     * Remove feature from this.features array, reset its renderIntent to
     * default and its state to null.  Redraw the feature in the end.
     *
     * Parameters:
     * feature - {OpenLayers.Feature.Vector}
     */
    unselectFeature: function(feature){
        this.features = OpenLayers.Util.removeItem(this.features, feature);
        feature.state = null;
        feature.renderIntent = "default";
        this.layer.drawFeature(feature);    
    },

    /**
     * Method: isFeatureSelected
     * Check if the given feature is selected.
     *
     * Parameters:
     * feature - {OpenLayers.Feature.Vector}
     *
     * Returns:
     * {Boolean} The feature was selected.
     */
    isFeatureSelected: function(feature){
        return feature.renderIntent == "select";
    },

    /**
     * Method: unselectAllFeatures
     * Browse this.features array and unselect each feature in it.
     */
    unselectAllFeatures: function(){
        for (var i=this.features.length-1; i>=0; i--){
            this.unselectFeature(this.features[i]);
        }
    },

    /**
     * Method: handleKeypress
     * Called by the feature handler on keypress.  This is used to delete
     *     vertices. If the <deleteCode> property is set, vertices will
     *     be deleted when a feature is selected for modification and
     *     the mouse is over a vertex.
     *
     * Parameters:
     * {Integer} Key code corresponding to the keypress event.
     */
    handleKeypress: function(evt) {
        var code = evt.keyCode;

        var delKey = OpenLayers.Util.indexOf(this.deleteCodes, code) != -1;
        if(delKey && this.features && this.features.length > 0) {
            this.deleteFeatures();
            return;
        }

        var escKey = OpenLayers.Util.indexOf(this.unselectAllCodes, code) != -1;
        if(escKey){
            this.unselectAllFeatures();
        }
    },

    /**
     * Method: deleteFeatures
     * Empties the features array if beforefeaturesdeleted event returns true.
     * 
     * options - {Object}
     *     if {silent: true}, the function will skip the beforefeaturesdeleted
     *     and trigger deletefeatures
     */
    deleteFeatures: function (options){
        var silent = options && options.silent;
        var ret;
        var event = {features: this.features};
        
        if (!silent){
            ret = this.events.triggerEvent("beforefeaturesdeleted");
        } else {
            ret = true;
        }

        if(ret === true) {
            this.events.triggerEvent("deletefeatures", event);
            this.features = [];
        }
    },

    /**
     * Method: setMap
     * Set the map property for the control and all handlers.
     *
     * Parameters:
     * map - {<OpenLayers.Map>} The control's map.
     */
    setMap: function(map) {
        this.handlers.feature.setMap(map);
        this.handlers.keyboard.setMap(map);
        if (this.box) {
            this.handlers.box.setMap(map);
            this.handlers.boxCtrlKey.setMap(map);
        }
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
    },

    /**
     * Method: activate
     * Explicitly activates a control and it's associated
     * handlers if one has been set.  Controls can be
     * deactivated by calling the deactivate() method.
     * 
     * Returns:
     * {Boolean}  True if the control was successfully activated or
     *            false if the control was already active.
     */
    activate: function () {
        if (this.active) {
            return false;
        }
        if (this.handlers) {
            for (var key in this.handlers){
                this.handlers[key].activate();                
            }
        }
        this.active = true;
        this.events.triggerEvent("activate");
        return true;
    },

    /**
     * Method: deactivate
     * Deactivates a control and it's associated handlers if any.  Unselect all
     * selected features.
     * 
     * Returns:
     * {Boolean} True if the control was effectively deactivated or false
     *           if the control was already inactive.
     */
    deactivate: function () {
        if (this.active) {
            if (this.handlers) {
                for (var key in this.handlers){
                    this.handlers[key].deactivate();                
                }
            }
            this.active = false;
            this.unselectAllFeatures();
            this.events.triggerEvent("deactivate");
            return true;
        }
        return false;
    },
    CLASS_NAME: "OpenLayers.Control.DeleteFeature"
});
/*
 * This is the class that serves as the feedback for how to react
 * between services and javascript.
 */

/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

window.GeoMOOSE = {
	splitPaths : function(layerPaths) {
		return (new String(layerPaths)).split(':');
	},
	/* Layer Functions */
	changeLayerVisibility : function(layerPath, visibility) {
		var paths = this.splitPaths(layerPath);
		for(var p = 0; p < paths.length; p++) {
			var e = document.getElementById('catalog-'+paths[p]);
			if(!e && !Catalog.setLayerVisibility(paths[p], visibility)) {
				OpenLayers.Console.log('Layer '+paths[p]+' does not exist');
			} else if(e && e.checked != visibility) {
				e.checked = true;
				e.onclick();
			}
		}
	},

	turnLayerOn : function(layerPaths) {
		this.changeLayerVisibility(layerPaths, true);
	},

	turnLayerOff : function(layerPaths) {
		this.changeLayerVisibility(layerPaths, false);
	},

	refreshLayers : function(layerNames) {
		if(!(layerNames instanceof Array)) {
			layerNames = [layerNames];
		}
		Catalog.refreshLayer(layerNames.join(':'));
	},

	changeLayerUrl : function(layerName, url) {
		Catalog.setLayerUrl(layerName, url);
	},

	updateLayerParameters : function(layerName, paramObject) {
		Catalog.setLayerParameters(layerName, paramObject);
	},

	getLayerUrl : function(layerName) {
		return Catalog.getLayerUrl(layerName);
	},

	getLayerParameters : function(layerName) {
		return Catalog.getLayerParameters(layerName);
	},

	getVisibleLayers : function() {
		return Catalog.getVisibleLayers();
	},

	/* Start a service with some given parameters */
	startService : function(serviceName, settingsObj, forceStart) {
		Services.startService(serviceName, settingsObj, forceStart);
	},

	/* Change to a different tab in the interface */
	changeTab : function(tabName) {
		Tabs.showTab(tabName);
	},

	/* Zoom to a point and a buffer */
	zoomToPoint : function(x,y,buffer) {
		Map.zoomToExtent(OpenLayers.Bounds.fromArray([x-buffer, y-buffer, x+buffer, y+buffer]));
	},

	/* Zoom to an extent */
	zoomToExtent : function(minx,miny,maxx,maxy) {
		Map.zoomToExtent(OpenLayers.Bounds.fromArray([minx,miny,maxx,maxy]));
	},

	/* Popup Functions */
	addPopup : function(x,y,w,h,html) {
		addPopup(x,y,w,h,html);
	},

	clearPopups : function() {
		while(Map.popups[0]) {
			Map.removePopup(Map.popups[0]);
		}
	}
};
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */


/**
 * @requires OpenLayers/Layer/Grid.js
 * @requires OpenLayers/Tile/Image.js
 */

/**
 * Class: OpenLayers.Layer.geomooseWMS
 * Instances of OpenLayers.Layer.geomooseWMS are used to display data from OGC Web
 *     Mapping Services. Create a new geomooseWMS layer with the <OpenLayers.Layer.geomooseWMS>
 *     constructor.
 * 
 * Inherits from:
 *  - <OpenLayers.Layer.Grid>
 */
OpenLayers.Layer.geomooseWMS = OpenLayers.Class(OpenLayers.Layer.WMS, {
    /**
     * Constructor: OpenLayers.Layer.geomooseWMS
     * Really just passes information back to OpenLayers.Layer.WMS
     */
    initialize: function(name, url, params, options) {
        var newArguments = [];
        //uppercase params
        params = OpenLayers.Util.upperCaseObject(params);
        newArguments.push(name, url, params, options);

        OpenLayers.Layer.WMS.prototype.initialize.apply(this, newArguments);
	this.unprojectedExtent = this.maxExtent;
    },    

   
    /**
     * This is changed to support reprojecting the bounds on the fly
     */
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);

	/* reproject the bounds */
	if(this.singleTile && this.projection.projCode != this.map.getProjection()) {
		bounds = bounds.transform(this.map.projection, this.projection);
	}
        
        var imageSize = this.getImageSize(); 
        var newParams = {
            'BBOX': this.encodeBBOX ?  bounds.toBBOX() : bounds.toArray(),
            'WIDTH': imageSize.w,
            'HEIGHT': imageSize.h
        };
        var requestString = this.getFullRequestString(newParams);
        return requestString;
    },

    /** 
     * This is changed to send the layer's Projection Code instead of the map's
     */
    getFullRequestString:function(newParams, altUrl) {
        var projectionCode = this.map.getProjection();
	if(this.projection) {
		projectionCode = this.projection.projCode;
	}
        this.params.SRS = (projectionCode == "none") ? null : projectionCode;

        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
                                                    this, arguments);
    },

    initGriddedTiles:function(bounds) {
        var projected_bounds = bounds.clone();
	if(this.unprojectedExtent == null) { this.unprojectedExtent = this.maxExtent; }
	var extent = this.unprojectedExtent.clone();
	if(this.projection.projCode != this.map.getProjection()) {
		projected_bounds = projected_bounds.transform(this.map.projection, this.projection);
		extent = extent.transform(this.map.projection, this.projection);
	}
	this.maxExtent = extent;
    	OpenLayers.Layer.Grid.prototype.initGriddedTiles.apply(this, [projected_bounds,]);
	//console.log('bounds: ',projected_bounds.toString());
	//console.log('extent: ',extent.toString());
    },

    calculateGridLayout: function(bounds, extent, resolution) {
    	var projected_resolution = resolution;
	if(this.projection.projCode != this.map.getProjection()) {
		projected_resolution = resolution * (this.maxExtent.getHeight() / this.unprojectedExtent.getHeight());
//		projected_resolution = resolution * (this.unprojectedExtent.getWidth() / this.maxExtent.getWidth());
	}
    	return OpenLayers.Layer.Grid.prototype.calculateGridLayout.apply(this, [bounds,extent,projected_resolution]);
    },


    CLASS_NAME: "OpenLayers.Layer.geomooseWMS"
});
/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

function manageLayout () {
	var header = document.getElementById('header');
	var footer = document.getElementById('footer');
	var middle = document.getElementById('middle');

	middle.style.top = header.offsetHeight + 'px';
	middle.style.height = footer.offsetTop - header.offsetHeight + 'px';

	/* I'm not happy about having to have this code here, but if anyone has
		suggestions I'm totally willing to use it. */
	var controlPanel = document.getElementById('control-panel');
	var tabStrip = document.getElementById('tab-controls');
	var tabContainer = document.getElementById('tab-container');

	var cpHeight = controlPanel.offsetHeight;
	var tabStripPos = tabStrip.offsetTop;

	var p = tabStrip.offsetParent;
	while(p) {
		tabStripPos += p.offsetTop;
		p = p.offsetParent;
	}
	
	tabStripPos += tabStrip.offsetHeight;

	tabContainer.style.height = footer.offsetTop - tabStripPos + 'px';
	tabContainer.style.width = tabStrip.offsetWidth + 'px';

	if(Map instanceof OpenLayers.Map) {
		Map.updateSize();
	}
}
/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/*
 * main()
 * Start up script.
 */

var Map = false;
var ReferenceMap = false;
var Services = false; /* Service Manager */
var LatLongProjection = false;
var Tabs;
var Catalog = false;

var Tools = new Array();

function main() {
	/* This sets up the layout functionality in the interface */
	manageLayout();

	Proj4js.libPath = './proj4js/lib/';
	/*
	Proj4js.defs["EPSG:26915"] = "+title=NAD83 / UTM zone 15N +proj=utm +zone=15 +ellps=GRS80 +datum=NAD83 +units=m +no_defs";
	Proj4js.defs["EPSG:26914"] = "+title=NAD83 / UTM zone 14N +proj=utm +zone=14 +ellps=GRS80 +datum=NAD83 +units=m +no_defs";
	*/

	/* Capture onresize so that we can manage the layout as necessary */
	window._onresize = window.onresize;
	window.onresize = function () {
		manageLayout();
		if(window._onresize) {
			window._onresize();
		}
	}

	/* Load the Mapbook */
	OpenLayers.loadURL(CONFIGURATION['mapbook'], {}, {}, onLoadedMapbook);
}

function configureMap () {
	CONFIGURATION.scales = CONFIGURATION.scales.sort().reverse();

	/* Setup the Map */	
	Map = new OpenLayers.Map('mapContainer', 
		{
			resolutions : CONFIGURATION.scales,
			maxExtent : OpenLayers.Bounds.fromArray(CONFIGURATION.max_extent),
			controls : [new OpenLayers.Control.PanZoomBar(), new OpenLayers.Control.ScaleJumper({target: 'scale-jumper'})],
			units: CONFIGURATION.ground_units,
			projection: new OpenLayers.Projection(CONFIGURATION.projection),
			displayProjection: new OpenLayers.Projection(CONFIGURATION.projection),
			fractionalZoom: CONFIGURATION.fractional_zoom
		}
	);

	if(CONFIGURATION.reference_map.enabled) {
		ReferenceMap = new OpenLayers.Control.OverviewMap();
		Map.addControl(ReferenceMap);
	}


	/* This is the only base layer and allows all of the rest of the layers to be selectively
		shown/hidden without effecting the navigation */
	var navigationLayer = new OpenLayers.Layer.Image('GeoMOOSE Navigation Layer',
		'./images/blank.gif',
		new OpenLayers.Bounds.fromArray(CONFIGURATION.max_extent),
		new OpenLayers.Size(500,500), {
			projection: new OpenLayers.Projection(CONFIGURATION.projection),
			resolutions: CONFIGURATION.scales
		});

	Map.addLayers([navigationLayer]);
	if(CONFIGURATION.reference_map.enabled && CONFIGURATION.reference_map.maximized) {
		ReferenceMap.maximizeControl();
	}

	/* Setup the Built-in Tools */
	Tools['pan'] = new OpenLayers.Control.Navigation();
	Tools['zoomin'] = new OpenLayers.Control.ZoomBox();
	Tools['zoomout'] = new OpenLayers.Control.ZoomBox({out: true});

	var measureToolStyle = new OpenLayers.Style();
	measureToolStyle.addRules([
		new OpenLayers.Rule({symbolizer: CONFIGURATION.measure_tool.style})
	]);
	var styleMap = new OpenLayers.StyleMap({"default": measureToolStyle});

	var measureToolOptions = {
		handlerOptions: {
		    style: "default", // this forces default render intent
		    layerOptions: {styleMap: styleMap},
		    persist: true
		}
	};


	Tools['measure'] = new OpenLayers.Control.GeoMooseMeasure(OpenLayers.Handler.MeasurePath, measureToolOptions);
	Tools['measurearea'] = new OpenLayers.Control.GeoMooseMeasure(OpenLayers.Handler.Polygon, measureToolOptions);


	var showMeasureTab = function () { Tabs.showTab(CONFIGURATION['show_results_in']); };

	Tools['measure'].events.register('activate', Tools['measure'], startupMeasureLength);
	Tools['measure'].events.register('measuremove', Tools['measure'], measureToolLengthLog);
	Tools['measure'].events.register('measurepartial', Tools['measure'], measureToolPartial);

	Tools['measurearea'].events.register('activate', Tools['measurearea'], startupMeasureArea);
	Tools['measurearea'].events.register('measuremove', Tools['measurearea'], measureToolAreaLog);
	Tools['measurearea'].events.register('measurepartial', Tools['measurearea'], measureToolAreaPartial);

	var NavHistory = new OpenLayers.Control.NavigationHistory();
	Map.addControl(NavHistory);

	/* displayCoordinates not only formats the output but adds reprojection when available */
	var coordDisplay = new OpenLayers.Control.MousePosition({div: document.getElementById('coordinate-display')});
	coordDisplay.formatOutput = displayCoordinates;
	Map.addControl(coordDisplay);

	LatLongProjection = new OpenLayers.Projection('WGS84');

	for(var t in Tools) {
		Map.addControl(Tools[t]);
	}
	/* Wrapper objects for navigation history */
	var previous = { activate: function() { NavHistory.previous.trigger(); }, deactivate: function() { } };
	var next = { activate: function() { NavHistory.next.trigger(); }, deactivate: function() { } };

	var clear_drawings = { 
		activate: function() { 
			var layer = Map.getLayersByName('sketchlayer')[0];
			while(layer.features[0]) { layer.removeFeatures(layer.features[0]); }
		}, deactivate: function() { } 
	};

	var fullExtent = { activate: function() { Map.zoomToMaxExtent(); }, deactivate: function () { } };

	Tools['previous'] = previous;
	Tools['next'] = next;
	Tools['fullextent'] = fullExtent;
	Tools['clear_drawings'] = clear_drawings;

//	Map.zoomToMaxExtent(); /* Zoom to the initial extent for startup */

	Map.zoomToExtent(OpenLayers.Bounds.fromArray(CONFIGURATION.initial_extent));

}

function displayCoordinates(lonLat) {
        var digits = parseInt(this.numDigits);
	var html = '';
	if(CONFIGURATION.coordinate_display.xy) {
		html += '<span class="coordinate-type">X,Y: </span>';
		html += lonLat.lon.toFixed(digits) + ', ' + lonLat.lat.toFixed(digits);
	}
	var degrees = {'x': lonLat.lon, 'y' : lonLat.lat};
	OpenLayers.Projection.transform(degrees, 
		Map.getProjectionObject(),
		LatLongProjection);

	if(CONFIGURATION.coordinate_display.latlon) {
		html += '<span class="coordinate-type">Lat, Lon: </span>';
		html += degrees.x.toFixed(3) + ', ' + degrees.y.toFixed(3);
	}

	if(CONFIGURATION.coordinate_display.usng) {
		html += '<span class="coordinate-type">USNG: </span>';
		// Map.getResolution returns map units per pixel
        	var inches = OpenLayers.INCHES_PER_UNIT;
		/* Convert the map units to inches, and then inches to meters */
		/*
		var metersPerPx = Map.getResolution() * (inches[Map.getUnits()] * (1/inches['m']));
		var digits = 6;
		if(metersPerPx > 10000) {
			digits = 0;
		} else if(metersPerPx > 1000) {
			digits = 1;
		} else if(metersPerPx > 100) {
			digits = 2;
		} else if(metersPerPx > 10) {
			digits = 3;
		} else if(metersPerPx > 1) {
			digits = 4;
		} else if(metersPerPx > .1) {
			digits = 5; 
		}
		*/
		var digits = Math.ceil(Math.log(Map.getResolution())/2.302585092994046);
		html += (new USNG()).fromLatLong(degrees, digits);
	}
/*        var newHtml =
            this.prefix +
            lonLat.lon.toFixed(digits) +
            this.separator + 
            lonLat.lat.toFixed(digits) +
            this.suffix;
*/
        return html;
}


var MENU_TOGGLE_ICON = '';

function displayMenu(rXML) {
	var menuBar = new Menubar();
	menuBar.setParent('menubar');
	menuBar.populateMenuBar(rXML.responseXML);
}

function loadMenu() {
	OpenLayers.loadURL('menu.xml', {}, {}, displayMenu);
}


function onLoadedMapbook(response) {
	var toolbarId = 'toolbar';
	var maxSelectedTool = null;
	if(response.responseXML) {
		/* check version */
		var version = parseFloat(response.responseXML.documentElement.getAttribute('version'));
		if(version < 2.0) {
			alert('Specified mapbook version '+version+' is older than 2.0. No promises this will work.');
		}

		/* Load the configuration bits */
		var configuration = response.responseXML.getElementsByTagName('configuration')[0];
		if(configuration && configuration.childNodes) {
			var params = configuration.getElementsByTagName('param');
			for(var p = 0; p < params.length; p++) {
				var param = params[p];
				var name = param.getAttribute('name');
				var value = OpenLayers.Util.getXmlNodeValue(param);

				/* this is a bit naughty... but it works quite well */
				var typeValue = eval('CONFIGURATION.'+name);
				var jsString = 'CONFIGURATION.'+name+' = ';
				if(typeValue instanceof Array) {
					if(value[0] == '[' && value[value.length-1] == ']') {
						value = value.substring(1,value.length-1);
					}
					jsString += '['+value+']';
				} else if(typeof(typeValue) == "string" || typeValue instanceof String) {
					jsString += 'value';
				} else if(typeValue == true || typeValue == false) {
					jsString += 'parseBoolean(value)';
				} else {
					value = '('+value+')';
					jsString += 'eval(value)';
				}
				//console.log(name, typeof(typeValue));
				//console.log(jsString);
				//console.log(eval(value));
				eval(jsString);
			}
		}

		var mandatory = ['mapfile_root', 'mapserver_url'];
		for(var i =0; i < mandatory.length; i++) {
			if(CONFIGURATION[mandatory[i]] == "") {
				alert(mandatory[i]+' must be configured in the mapbook'); 
			}
		}

		configureMap();

		var toolbar = response.responseXML.getElementsByTagName('toolbar')[0];
		for(var i = 0; i < toolbar.childNodes.length; i++) {
			if(toolbar.childNodes[i].tagName && 
				toolbar.childNodes[i].tagName.toLowerCase() == 'tool') {

				addTool(toolbarId, toolbar.childNodes[i]);

				if(parseBoolean(toolbar.childNodes[i].getAttribute('selected'))) {
					maxSelectedTool = toolbar.childNodes[i];
				}
			} else if(toolbar.childNodes[i].tagName && toolbar.childNodes[i].tagName.toLowerCase() == 'drawer') {
				var drawer = document.createElement('div');
				drawer.className = 'Tool Drawer';
				drawer.id = 'toolbar-drawer-'+i;
				drawer.onclick = showDrawer;
				document.getElementById(toolbarId).appendChild(drawer);

				var tools = toolbar.childNodes[i].getElementsByTagName('tool');
				for(var x = 0; x < tools.length; x++) {
					addTool(drawer.id, tools[x]);
				}

				var tools = drawer.getElementsByTagName('a');

				for(var x = 1; x < tools.length; x++) {
					tools[x].className += ' Hidden';
				}
			}
		}
	
		var services = response.responseXML.getElementsByTagName('map-source');
		for(var i = services.length - 1; i >= 0; i--) {
			var type = services[i].getAttribute('type').toLowerCase();
			var layer = false;
			var params = services[i].getElementsByTagName('param');
			var paramHash = {};

			var url = OpenLayers.Util.getXmlNodeValue(services[i].getElementsByTagName('url')[0]);

			var tiled = !parseBoolean(services[i].getAttribute('tiled'), false);

			for(var p = 0; p < params.length; p++) {
				var param = params[p];
				paramHash[param.getAttribute('name')] = param.getAttribute('value');
			}
			
			var reference_layers = '';

			var layers = services[i].getElementsByTagName('layer');
			var reference_layer_names = [];
			var reference_layer_param_name = 'LAYERS';
			for(var x = 0; x < layers.length; x++) {
				if(parseBoolean(layers[x].getAttribute('reference'))) {
					reference_layer_names.push(layers[x].getAttribute('name'));
				}
			}
			if(type == 'wms') {
				reference_layers = reference_layer_names.join(',');

				var wms_projection = Map.projection;
				if(services[i].getAttribute('projection')) {
					wms_projection = new OpenLayers.Projection(services[i].getAttribute('projection'));
				}

				var options = {
					singleTile : tiled,
					isBaseLayer: false,
					projection: wms_projection
				};

				var w = services[i].getAttribute('width');
				var h = services[i].getAttribute('height');
				if(w && h) {
					options.tileSize = new OpenLayers.Size(parseFloat(w), parseFloat(h));
				}

				layer = new OpenLayers.Layer.geomooseWMS(services[i].getAttribute('name'),
					url, paramHash, options); 
			} else if(type == 'mapserver') {
				reference_layers = reference_layer_names.join(' ');
				reference_layer_param_name = 'layers';

				if(!url) {
					url = CONFIGURATION.mapserver_url;
				}

				var file = '';
				try {
					file = OpenLayers.Util.getXmlNodeValue(services[i].getElementsByTagName('file')[0]);
					if(file.substring(0,1) == '.') {
						file = CONFIGURATION.mapfile_root + file.substring(1);
					}
				} catch(e) {
					file = '';
				}
				paramHash['map'] = file;
				layer = new OpenLayers.Layer.MapServer(services[i].getAttribute('name'),
					url, paramHash, {singleTile : tiled, isBaseLayer: false});
			}


			if(layer) {
				if(layer.params['map_imagetype']) {
					delete layer.params['map_imagetype'];
				}
				layer.setVisibility(false);

				if(services[i].getAttribute('opacity')) {
					layer.setOpacity(parseFloat(services[i].getAttribute('opacity')));
				}

				Map.addLayer(layer);
				if(parseBoolean(services[i].getAttribute('reference')) && CONFIGURATION.reference_map.enabled) {
					var ref_layer = layer.clone();
					ReferenceMap.ovmap.addLayer(ref_layer);

					if(reference_layer_names.length > 0) {
						ref_layer.params[reference_layer_param_name] = reference_layers;
						ref_layer.setVisibility(true);
					}
				}
			}
		}

		/* Add the drawing tools */
		var drawingLayer = new OpenLayers.Layer.Vector('/drawinglayer');
		Map.addLayer(drawingLayer);

		/* okay, this is a little ridiculous but we have a /drawinglayer and a sketching layer
			and yes, they are different.*/
		var symbolizer = OpenLayers.Util.applyDefaults({'strokeColor' : "${stroke}", 'strokeOpacity' : "${opacity}",
								'fillColor' : "${fill}", 'fillOpacity' : "${opacity}",
								'strokeWidth' : 3},
				OpenLayers.Feature.Vector.style['default']);
		var styleMap = new OpenLayers.StyleMap({"default" : symbolizer});
		var sketchLayer = new OpenLayers.Layer.Vector('sketchlayer', {styleMap: styleMap});//, {styleMap: new OpenLayers.StyleMap(style)});
		sketchLayer.events.register("beforefeatureadded", sketchLayer, onSketchFeatureAdded);
		Map.addLayer(sketchLayer);

		Tools['point'] = new OpenLayers.Control.DrawFeature(drawingLayer, OpenLayers.Handler.Point);
		Tools['line'] = new OpenLayers.Control.DrawFeature(drawingLayer, OpenLayers.Handler.Path);
		Tools['polygon'] = new OpenLayers.Control.DrawFeature(drawingLayer, OpenLayers.Handler.Polygon);
		Tools['edit-polygon'] = new OpenLayers.Control.ModifyFeature(drawingLayer);

		Tools['draw_polygon'] = new OpenLayers.Control.DrawFeature(sketchLayer, OpenLayers.Handler.Polygon);
		Tools['draw_point'] = new OpenLayers.Control.DrawFeature(sketchLayer, OpenLayers.Handler.Point);
		Tools['draw_line'] = new OpenLayers.Control.DrawFeature(sketchLayer, OpenLayers.Handler.Path);
		Tools['draw_edit_shapes'] = {
			control: null,

			setMap: function() {
			},

			draw: function() {
			},

			activate: function() {
				this.control = new OpenLayers.Control.ModifyFeature(sketchLayer);
				Map.addControl(this.control);
				this.control.activate();
			},

			deactivate: function() {
				if(this.control != null) {
					this.control.deactivate();
					Map.removeControl(this.control);
				}
				this.control = null;
			}
		}
		Tools['draw_remove'] = new OpenLayers.Control.DeleteFeature(sketchLayer);

		/* render intent is set to "default" so as not to change the overall look of the feature */
		Tools['draw_edit_attributes'] = new OpenLayers.Control.SelectFeature(sketchLayer,
							{onSelect: onStartAttributeEditing,
							 onUnselect: onFinishAttributeEditing, renderIntent : 'default'});


		var drawingtools = ['point','line','polygon','edit-polygon'];
		drawingtools = drawingtools.concat(['draw_polygon','draw_line','draw_point', 'draw_remove', 'draw_edit_attributes', 'draw_edit_shapes']);

		for(var d = 0; d < drawingtools.length; d++) {
			Map.addControl(Tools[drawingtools[d]]);
		}

		/* Add the layer for the popus */
		var popupsLayer = new OpenLayers.Layer.Markers('/popups');
		Map.addLayer(popupsLayer);

		configureUserInterface(response.responseXML);


		/* reload for the layers to update */
		Map.zoomToExtent(OpenLayers.Bounds.fromArray(CONFIGURATION.initial_extent));
	}

	document.getElementById('menubar').innerHTML = CONFIGURATION.links_bar_html;

	manageLayout();
	if(maxSelectedTool != null) {
		var name = maxSelectedTool.getAttribute('name');
		var tool = document.getElementById('tool-'+name);
		tool.onclick();
		tool = null;
	}


	var args = OpenLayers.Util.getParameters();
	if(args.call) {
		GeoMOOSE.startService(args.call, args, true);
	}
}

function changeExtentBySelect() {
	var options = this.getElementsByTagName('option');
	var selectedIndex = this.selectedIndex;

	/* reset to the "blank" option */
	for(var i = 0; i < options.length; i++) {
		options[i].selected = false;
	}
	options[0].selected = true;
	
	/* zoom to the extents */
	var arr = new String(options[selectedIndex].value).split(',');
	GeoMOOSE.zoomToExtent(arr[0],arr[1],arr[2],arr[3]);
}

function configureUserInterface(mapbook) {
	/* Configure The Zoom To's */
	var zoomToObject = CONFIGURATION.zoomto;
	var zoomToParent = document.getElementById('zoomto-boxes');
	for(var zoomToTitle in zoomToObject) {
		var zoomToContainer = document.createElement('div');
		zoomToParent.appendChild(zoomToContainer);
		zoomToContainer.className = 'zoomto-container';
		zoomToContainer.appendChild(document.createTextNode(zoomToTitle));

		var zoomToSelect = document.createElement('select');
		zoomToSelect.className = 'zoomto-select';
		zoomToContainer.appendChild(zoomToSelect);

		zoomToSelect.onchange = changeExtentBySelect;

		var blankOption = document.createElement('option');
		zoomToSelect.appendChild(blankOption);

		for(var extentTitle in zoomToObject[zoomToTitle]) {
			var option = document.createElement('option');
			option.value = zoomToObject[zoomToTitle][extentTitle].join(',');
			zoomToSelect.appendChild(option);
			option.appendChild(document.createTextNode(extentTitle));
		}
	}

	/* Tabs */
	Tabs = new GeomooseTabs('tab-controls');
	Tabs.initialize();

	/* Catalog */
	Catalog = new GeomooseCatalog(Tabs.getTab(CONFIGURATION.catalog_name), Map);
	Catalog.onLoadedMapbook(mapbook);

	/* Service Manager */
	Services = new ServiceManager(mapbook, Tabs.getTab(CONFIGURATION.show_service_settings_in));
	Services.initialize();

	Services.onStart = function (service) {
		if(parseBoolean(service.getAttribute('display'), true)) {
			Tabs.showTab(CONFIGURATION.show_service_settings_in);
		}
	}

	Services.onFinish = function () {
		document.getElementById(Tabs.getTab(CONFIGURATION.show_service_settings_in)).innerHTML = CONFIGURATION['waiting_html'];
	}

	Services.onServiceReturn = function (response) {
		clearPopups();
		var showResults = false;
		var contentType = new String(response.getResponseHeader('Content-type')).toLowerCase();
		if(contentType.indexOf('xml') > 0 && response.responseXML) {
			var xml = new OpenLayers.Format.XML();
			var scripts = response.responseXML.getElementsByTagName('script');
			var js = '';
			for(var s = 0; s < scripts.length; s++) {
				js += xml.concatChildValues(scripts[s]);
			}
			if(scripts.length > 0) { eval(js); }

			var texts = response.responseXML.getElementsByTagName('html');
			var html = '';
			for(var h = 0; h < texts.length; h++) {
				html += xml.concatChildValues(texts[h]);
			}
			document.getElementById(Tabs.getTab(CONFIGURATION['show_results_in'])).innerHTML = html;
			if(html != '') {
				showResults = true;
			}


			var popups = response.responseXML.getElementsByTagName('popup');
			for(var p = 0; p < popups.length; p++) {
				var pp = popups[p];
				addPopup(parseFloat(pp.getAttribute('x')), parseFloat(pp.getAttribute('y')),
					parseFloat(pp.getAttribute('width')), parseFloat(pp.getAttribute('height')),
					xml.concatChildValues(pp));
			}
		} else if(response.responseText) {
			document.getElementById(Tabs.getTab(CONFIGURATION['show_results_in'])).innerHTML = response.responseText;
			showResults = true;
		} else {
			alert('No Valid Response!');
		}
		if(showResults) {
			Tabs.showTab(CONFIGURATION.show_results_in);
		}
	}

}

var onSelectTool = new Array();
function selectTool(elm) {
	var p = document.getElementById('toolbar');
	var tools = p.getElementsByTagName('a');
	for(var i = 0; i < tools.length; i++) {
		if(tools[i].className.match(/Tool/)) {
			/* remove the tool specific selected styling */
			tools[i].className = tools[i].className.replace(/sprite\-control\-[A-Za-z]*\-selected/,'');
			/* remove the generic selected styling */
			tools[i].className = tools[i].className.replace(' selected', '');
		}
	}
	if(elm) {
		var toolName = elm.id.split('-')[1];
		toolName = 'sprite-control-'+toolName;

		var p = elm.parentNode;
		elm.className += ' '+toolName+'selected';
		elm.className += ' selected';
	}
	for(var i = 0; i < onSelectTool.length; i++) {
		onSelectTool[i]();
	}
	/* run 'em all and then get rid of 'em */
	onSelectTool = new Array();
}

function internalToolAction(event) {
	if(window.event) { event = window.event; }
	if(event) {
		event.cancelBubble = true;
	}

	if(this.selectable) {
		selectTool(this);
		for(var t in Tools) {
			Tools[t].deactivate();
		}
	}
	var action = this.getAttribute('action');
	if(Tools[action]) {
		Tools[action].activate();
	}
}

function serviceToolAction(event) {
	if(window.event) { event = window.event; }
	if(event) {
		event.cancelBubble = true;
	}
	if(this.selectable) {
		selectTool(this);
	}

	var serviceName = this.getAttribute('service');
	Services.startService(serviceName);
}

function closeDrawer() {
	var drawer = document.getElementById(this.drawer_id);
	var tools = drawer.getElementsByTagName('a');
	var name = this.getAttribute('action');
	var name_attr = 'action';
	if(name == undefined || name == null) { name = this.getAttribute('service'); name_attr = 'service'; }
	var clickIndex = -1;

	for(var i = 0; i < tools.length; i++) {
		if(tools[i].className.indexOf(' Hidden') < 0) {
			tools[i].className += ' Hidden';
		}
		if(tools[i].getAttribute(name_attr) == name) {
			tools[i].className = tools[i].className.replace(' Hidden','');
			clickIndex = i;
		}
	}
	if(clickIndex >= 0) { tools[clickIndex].onclick(); }

	/* clean up */
	var p = this.parentNode.parentNode;
	p.removeChild(this.parentNode);
}

function showDrawer() {
	var drawerMenu = document.createElement('div');
	drawerMenu.className = 'Drawer DisplayDrawer Tool';
	drawerMenu.style.position = 'absolute';
	var top = this.offsetTop, left = this.offsetLeft;
	var p = this.offsetParent;
	while(p) {
		top += p.offsetTop;
		left += p.offsetLeft;
		p = p.offsetParent;
	}
	
	drawerMenu.style.left = left+'px';
	drawerMenu.style.top = top + 'px';

	document.getElementsByTagName('body')[0].appendChild(drawerMenu);
	drawerMenu.innerHTML = this.innerHTML;

	var links = drawerMenu.getElementsByTagName('a');
	for(var i = 0; i < links.length; i++) {
		links[i].onclick = closeDrawer;
		links[i].className = links[i].className.replace(' Hidden','');
		links[i].drawer_id = this.id;
	}
}

function addTool(parentId, toolXML) {
	var p = document.getElementById(parentId);
	var tool = document.createElement('a');
	var container = document.createElement('span');
	p.appendChild(tool);
	tool.className = 'Tool sprite-control sprite-control-'+toolXML.getAttribute('name');
	tool.id = 'tool-'+toolXML.getAttribute('name');
	var span = document.createElement('span');
	container.className = 'ToolContent';
	tool.appendChild(container);
	container.appendChild(span);
	span.className = 'ToolText';
	span.appendChild(document.createTextNode(toolXML.getAttribute('title')));
	span.innerHTML = span.innerHTML.replace(/ /g, '&nbsp;');

	tool.href = '#';
	tool.title = toolXML.getAttribute('title');
	tool.selectable = parseBoolean(toolXML.getAttribute('selectable'), true);

	var toolType = toolXML.getAttribute('type').toLowerCase();
	if(toolType == 'internal') {
		tool.setAttribute('action', toolXML.getAttribute('action'));
		tool.onclick = internalToolAction;
	} else if(toolType == 'service') {
		tool.setAttribute('service', toolXML.getAttribute('service'));
		tool.onclick = serviceToolAction;
	}

	container = null;
	span = null;
	tool = null;
	p = null;
}

function parseBoolean(bool, def) {
	if(!bool) { return def; }
	var boolString = new String(bool);
	if(boolString.match(/true/i)) { return true; }
	else if(boolString == '1') { return true; }
	else if(boolString.match(/on/i)) { return true; }
	return false;
}

function commifyNumber(number) {
	var numberOrig = new String(number);
	var numberArr = new String(number).split('.');
	var numberSign = number.substring(0,1);
	/* Oh, snap, it's negative */
	if(numberSign == '-') {
		number = new String(number).substring(1);
		numberArr = new String(number).split('.');
	} else {
		numberSign = '';
	}
	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 numberSign+output;
}

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



function clearPopups() {
	while(Map.popups.length > 0) {
		Map.removePopup(Map.popups[0]);
	}
}

function addPopup(x,y,w,h,html) {
	var size = new OpenLayers.Size(w,h);
	var offset = new OpenLayers.Pixel(-1,-1);
	var icon = new OpenLayers.Icon('images/blank.gif', new OpenLayers.Size(1,1), offset);

	var modifiedHTML = '<div class="popupContainer" style="width: '+w+'px; height: '+h+'px">'+html+'</div>';
	var popup = new OpenLayers.Popup.FramedCloud('id0', new OpenLayers.LonLat(x,y),
			size, modifiedHTML, icon, true);

/*	popup.minSize = size.clone();*/
	popup.autoSize = false;

	Map.addPopup(popup);
}


/*
 * This generates a mapping request.
 * There exist so much data needed to request printing that I simply
 * put it in it's own script/function
 */

function getPrintRequest() {
	var layer_names = [], layer_urls = [], layer_opacity = [];
	for(var i in Map.layers) {
		var layer = Map.layers[i];
		if(layer.getVisibility()) {
			if(layer instanceof OpenLayers.Layer.WMS || layer instanceof OpenLayers.Layer.MapServer || layer instanceof OpenLayers.Layer.geomooseWMS) {

				layer_names.push(layer.name);
				layer_urls.push(layer.getURL(OpenLayers.Bounds.fromArray([0,0,0,0])));

				var o = layer.opacity;
				if(o != null) { o = 1.0; }
				layer_opacity.push(o);
			}
		}
	}

	var x = {
		'layers' : layer_names.join(':'),
		'size' : '500,500',
		'extent' : Map.getExtent().toArray().join(',')
	};

	for(var i = 0; i < layer_names.length; i++) {
		x[layer_names[i]+'_url'] = layer_urls[i];
		x[layer_names[i]+'_opacity'] = layer_opacity[i];
	}

//	console.info(OpenLayers.Util.getParameterString(x));
//	window.open('php/print.php?' + OpenLayers.Util.getParameterString(x));
//	return '='+layer_names.join(':')+OpenLayers.Util.getParameterString(x);
	return x;
}

/*
 * These functions are called when a sketch feature is having the attributes edited.
 */


function onStartAttributeEditing(feature) {
	selectedFeature = feature;
	var fields = {
		'title' : {
			title: 'Title',
			type: 'title'
		},
		'stroke': {
			title: 'Stroke Color',
			type: 'color'
		},
		'fill' : {
			title: 'Fill Color',
			type: 'color'
		},
		'opacity': {
			title: 'Opacity',
			type: 'opacity'
		}
	};

	/* yes this is convoluted, but it makes the form I want */
	var popup_template = '<div style="width: 200px">';
	var field_template = '<b>[title]</b> <input size="8" id="[id]-[field]" value="[value]"/><br/>';
	for(var k in fields) {
		var r_fields = {'title' : fields[k].title, 'id' : feature.id, 'field' : k, 'value' : feature.attributes[k] };
		var t = field_template;
		for(var r in r_fields) {
			t = t.replace('['+r+']', r_fields[r]);
		}
		popup_template += t;
	}
	popup_template += "<input type='button' value='Update Attributes' onclick='updateSketchAttributes(\""+feature.id+"\");'/>";
	popup_template += "</div>";
	var popup = new OpenLayers.Popup.FramedCloud("attribute_editing", 
				feature.geometry.getBounds().getCenterLonLat(),
				null,
			 	popup_template,
				null, true, undefined);
	feature.popup = popup;
	Map.addPopup(popup);
}

function onFinishAttributeEditing(feature) {
	Map.removePopup(feature.popup);
	feature.popup.destroy();
	feature.popup = null;
}    

function onSketchFeatureAdded(event) {
	var feature = event.feature;
	feature.attributes['fill'] = CONFIGURATION.drawing_tools.default_fill; //'#f0f000';
	feature.attributes['opacity'] = CONFIGURATION.drawing_tools.default_opacity;
	feature.attributes['stroke'] = CONFIGURATION.drawing_tools.default_stroke;
	feature.attributes['title'] = 'Currently Unavailable';
}

function updateSketchAttributes(featureId) {
	var layer = Map.getLayersByName('sketchlayer')[0];
	var feature = layer.getFeatureById(featureId);
	var attributes = ['fill', 'stroke', 'opacity', 'title'];
	for(var i = 0; i < attributes.length; i++) {
		var attr = attributes[i];
		feature.attributes[attr] = document.getElementById(featureId+'-'+attr).value;
	}
	layer.redraw();
}

function openURL(uri, params, caller, onComplete, onFailure) {
        var success = (onComplete) ? OpenLayers.Function.bind(onComplete, caller)
                                : OpenLayers.nullHandler;

        var failure = (onFailure) ? OpenLayers.Function.bind(onFailure, caller)
                           : OpenLayers.nullHandler;
        var req = null;
        if(params.length > 3000) {
                 req = new OpenLayers.Ajax.Request(uri,
                                     {   method: 'post',
                                         postBody: params,
                                         onComplete: success,
                                         onFailure: failure,
                                         contentType: 'application/x-www-form-urlencoded'
                                      }
                                     );
        } else {
                 req = new OpenLayers.Ajax.Request(uri+'?'+params,
                                     {   method: 'get',
                                         parameters: params,
                                         onComplete: success,
                                         onFailure: failure
                                      }
                                     );
        }
        return req;
}

/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/


function startupMeasureLength(event) {
	startupMeasureTool('length');
}

function startupMeasureArea(event) {
	startupMeasureTool('area');
}

function startupMeasureTool(mType) {
	Tabs.showTab(CONFIGURATION.show_results_in);

	var trans = {'length' : 'Length', 'area' : 'Area' };

	var p = document.getElementById(Tabs.getTab(CONFIGURATION.show_results_in));

	p.innerHTML = '';

	var title = document.createElement('div');
	p.appendChild(title);
	title.appendChild(document.createTextNode('Measure '+trans[mType]));

	p.appendChild(document.createElement('br'));

	var infoTable = document.createElement('table');
	var infoTbody = document.createElement('tbody');
	p.appendChild(infoTable);
	infoTable.appendChild(infoTbody);

	var totalRow = document.createElement('tr');
	var totalTitle = document.createElement('th');
	var totalCell = document.createElement('td');

	infoTbody.appendChild(totalRow);
	totalRow.appendChild(totalTitle);
	totalRow.appendChild(totalCell);

	var segmentRow = document.createElement('tr');
	var segmentTitle = document.createElement('th');
	var segmentCell = document.createElement('td');

	infoTbody.appendChild(segmentRow);
	segmentRow.appendChild(segmentTitle);
	segmentRow.appendChild(segmentCell);

	var unitsRow = document.createElement('tr');
	var unitsTitle = document.createElement('th');
	var unitsCell = document.createElement('td');

	infoTbody.appendChild(unitsRow);
	unitsRow.appendChild(unitsTitle);
	unitsRow.appendChild(unitsCell);

	totalTitle.appendChild(document.createTextNode('Total '+trans[mType]+':'));
	
	var totalInput = document.createElement('input');
	totalInput.size = 14;
	totalInput.id = 'measuretool-input-total';
	totalCell.appendChild(totalInput);


	segmentTitle.appendChild(document.createTextNode('Segment '+trans[mType]+':'));
	var segmentInput = document.createElement('input');
	segmentInput.size = 14;
	segmentInput.id = 'measuretool-input-segment';
	segmentCell.appendChild(segmentInput);

	unitsTitle.appendChild(document.createTextNode('Units:'));

	var unitsDropdown = document.createElement('select');
	var units = {'Miles' : 'mi', 'Feet' : 'ft', 'Meters' : 'm'};

	unitsDropdown.id = 'measuretool-units';
	unitsCell.appendChild(unitsDropdown);

	if(mType == 'area') {
		unitsDropdown.onchange = measureToolUpdateAreaUnits;
		units['Yards'] = 'yd';
		units['Acres'] = 'acre';
	} else {
		unitsDropdown.onchange = measureToolUpdateLengthUnits;
	}

	for(var u in units) {
		var opt = document.createElement('option');
		opt.value = units[u];
		unitsDropdown.appendChild(opt);
		opt.appendChild(document.createTextNode(u));
		if((mType != 'area' && units[u] == CONFIGURATION.measure_tool.line_units)
			|| (mType == 'area' && units[u] == CONFIGURATION.measure_tool.area_units)) {
			opt.selected = true;
		}
	}

	var clearRow = document.createElement('tr');
	var blankCell = document.createElement('td');
	var clearCell = document.createElement('td');

	infoTbody.appendChild(clearRow);
	clearRow.appendChild(blankCell);
	clearRow.appendChild(clearCell);

	var clearButton = document.createElement('input');
	clearButton.type = 'button';
	clearButton.value = 'Clear';
	clearButton.onclick = measureToolClear;
	clearCell.appendChild(clearButton);

	var measureTable = document.createElement('table');
	var measureTbody = document.createElement('tbody');
	measureTbody.id = 'measuretool-tbody';

	p.appendChild(measureTable);
	measureTable.appendChild(measureTbody);
}

function measureToolClear(event) {
	var measureTable = document.getElementById('measuretool-tbody');
	while(measureTable.childNodes[0]) {
		measureTable.removeChild(measureTable.childNodes[0]);
	}

	var measureTools = ['measure','measurearea'];
	for(var m = 0; m < measureTools.length; m++) {
		var tool = measureTools[m];
		Tools[tool].measure(new OpenLayers.Geometry.Point(), 'measure');
		if(Tools[tool].handler.layer) {
			Tools[tool].handler.layer.removeFeatures(Tools[tool].handler.layer.features);
		}
	}
}

function measureToolPartial(event) {
	var units = document.getElementById('measuretool-units');
	units = units.getElementsByTagName('option')[units.selectedIndex].value;

	var length = this.getLength(event.geometry, units);
	var total = document.getElementById('measuretool-input-total');
	var segment = document.getElementById('measuretool-input-segment');

	var t = document.getElementById('measuretool-tbody');

	var totalValue = stripCommas(total.value);

	if(length < totalValue) {
		while(t.firstChild) {
			t.removeChild(t.firstChild);
		}
		total.value = 0;
		segment.value = 0;
	}

	if(length > 0) {
		var rows = t.getElementsByTagName('tr');
		segment.value = commifyNumber((length - totalValue).toFixed(CONFIGURATION.measure_tool.precision));
		total.value = commifyNumber(length.toFixed(CONFIGURATION.measure_tool.precision));

		var newRow = document.createElement('tr');
		if(rows.length < 1) {
			t.appendChild(newRow);
		} else {
			t.insertBefore(newRow, rows[0]);
		}
		var segmentTitle = document.createElement('th');
		newRow.appendChild(segmentTitle);
		segmentTitle.appendChild(document.createTextNode(rows.length));

		var segmentLength = document.createElement('td');
		newRow.appendChild(segmentLength);
		segmentLength.appendChild(document.createTextNode(commifyNumber(length.toFixed(CONFIGURATION.measure_tool.precision))));

		var segmentUnits = document.createElement('td');
		newRow.appendChild(segmentUnits);
		segmentUnits.appendChild(document.createTextNode(units));
	}
}

function measureToolLengthLog(event) {
	var units = document.getElementById('measuretool-units');
	units = units.getElementsByTagName('option')[units.selectedIndex].value;
	var segment = document.getElementById('measuretool-input-segment');
	var total = document.getElementById('measuretool-input-total');
	var length = this.getLength(event.geometry, units);
	length -= stripCommas(total.value);
	segment.value = commifyNumber(length.toFixed(CONFIGURATION.measure_tool.precision));
}

/* converts from sq inches to another unit */
var AREA_CONVERSION = {
	'mi' : .000000000249097669,
	'acre' : .000000159422508,
	'yd' : 0.000771604938,
	'ft' : 0.00694444444,
	'm' : 0.00064516,
	'in' : 1
};

function measureToolUpdateAreaUnits() {
	var t = document.getElementById('measuretool-tbody');
	var rows = t.getElementsByTagName('tr');

	/* get the units */
	var units = document.getElementById('measuretool-units');
	target_units = units.getElementsByTagName('option')[units.selectedIndex].value;

	/* convert the areas in the boxes */
	var assumed_units = '';
	for(var i = 0; i < rows.length; i++) {
		var cells = rows[i].getElementsByTagName('td');
		var sqinches = (parseFloat(stripCommas(cells[0].innerHTML)) * (1 / AREA_CONVERSION[cells[1].innerHTML]));
		assumed_units = cells[1].innerHTML;
		cells[0].innerHTML = (sqinches * AREA_CONVERSION[target_units]).toFixed(CONFIGURATION.measure_tool.precision);
		cells[1].innerHTML = target_units;
	}

	var updateIds = ['measuretool-input-total', 'measuretool-input-segment'];
	for(var i = 0 ; i < updateIds.length; i++) {
		var e = document.getElementById(updateIds[i]);
		var sqinches = (parseFloat(stripCommas(e.value)) * (1 / AREA_CONVERSION[assumed_units]));
		e.value = (sqinches * AREA_CONVERSION[target_units]).toFixed(CONFIGURATION.measure_tool.precision);
	}

}

function measureToolUpdateLengthUnits() {
	var t = document.getElementById('measuretool-tbody');
	var rows = t.getElementsByTagName('tr');

	/* get the units */
	var units = document.getElementById('measuretool-units');
	target_units = units.getElementsByTagName('option')[units.selectedIndex].value;

		//var inches = OpenLayers.INCHES_PER_UNIT;
		/* Convert the map units to inches, and then inches to meters */
		/*
		var metersPerPx = Map.getResolution() * (inches[Map.getUnits()] * (1/inches['m']));

	/* convert the areas in the boxes */
	var assumed_units = '';
	for(var i = 0; i < rows.length; i++) {
		var cells = rows[i].getElementsByTagName('td');
		var sqinches = parseFloat(stripCommas(cells[0].innerHTML)) * OpenLayers.INCHES_PER_UNIT[cells[1].innerHTML];
		assumed_units = cells[1].innerHTML;
		cells[0].innerHTML = (sqinches / OpenLayers.INCHES_PER_UNIT[target_units]).toFixed(CONFIGURATION.measure_tool.precision)
		cells[1].innerHTML = target_units;
	}

	var updateIds = ['measuretool-input-total', 'measuretool-input-segment'];
	for(var i = 0 ; i < updateIds.length; i++) {
		var e = document.getElementById(updateIds[i]);
		var sqinches = parseFloat(stripCommas(e.value)) * OpenLayers.INCHES_PER_UNIT[assumed_units];
		e.value = (sqinches / OpenLayers.INCHES_PER_UNIT[target_units]).toFixed(CONFIGURATION.measure_tool.precision);
	}

}

function measureToolAreaLog(event) {
	var units = document.getElementById('measuretool-units');
	units = units.getElementsByTagName('option')[units.selectedIndex].value;
	var segment = document.getElementById('measuretool-input-segment');
	var total = document.getElementById('measuretool-input-total');
	var area = 0;
	area = this.getArea(event.geometry, 'in') * AREA_CONVERSION[units];
	area -= stripCommas(total.value);
	segment.value = commifyNumber(area.toFixed(CONFIGURATION.measure_tool.precision));
}

function measureToolAreaPartial(event) {
	var units = document.getElementById('measuretool-units');
	units = units.getElementsByTagName('option')[units.selectedIndex].value;

	var length = this.getArea(event.geometry, 'in') * AREA_CONVERSION[units];
	var total = document.getElementById('measuretool-input-total');
	var segment = document.getElementById('measuretool-input-segment');

	var t = document.getElementById('measuretool-tbody');

	var totalValue = stripCommas(total.value);

	/* count the number of points in the polygon, if they are less than 3
		then reset the counters */

	if(countPoints(event.geometry) < 3) {
		while(t.firstChild) {
			t.removeChild(t.firstChild);
		}
		total.value = parseFloat(0).toFixed(CONFIGURATION.measure_tool.precision);
		segment.value = total.value;
	}

	if(length > 0) {
		var rows = t.getElementsByTagName('tr');
		segment.value = commifyNumber((length - totalValue).toFixed(CONFIGURATION.measure_tool.precision));
		total.value = commifyNumber(length.toFixed(CONFIGURATION.measure_tool.precision));

		var newRow = document.createElement('tr');
		if(rows.length < 1) {
			t.appendChild(newRow);
		} else {
			t.insertBefore(newRow, rows[0]);
		}
		var segmentTitle = document.createElement('th');
		newRow.appendChild(segmentTitle);
		segmentTitle.appendChild(document.createTextNode(rows.length));

		var segmentLength = document.createElement('td');
		newRow.appendChild(segmentLength);
		segmentLength.appendChild(document.createTextNode(commifyNumber(length.toFixed(CONFIGURATION.measure_tool.precision))));

		var segmentUnits = document.createElement('td');
		newRow.appendChild(segmentUnits);
		segmentUnits.appendChild(document.createTextNode(units));
	}
}

function countPoints(g) {
	var points = 0;
	if(g.components) {
		for(var i = 0; i < g.components.length; i++) {
			if(g.components[i] instanceof OpenLayers.Geometry.Point) {
				points += 1;
			} else {
				points += countPoints(g.components[i]);
			}
		}
	}
	return points;

}
/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

OpenLayers.Handler.MeasurePath = OpenLayers.Class(OpenLayers.Handler.Path, {
	initialize: function(control, callbacks, options) {
		OpenLayers.Handler.Path.prototype.initialize.apply(this, arguments);
	},

	mousemove: function(evt) {
		if(this.drawing) { 
			var lonlat = this.map.getLonLatFromPixel(evt.xy);
			this.point.geometry.x = lonlat.lon;
			this.point.geometry.y = lonlat.lat;
			this.point.geometry.clearBounds();
			if(this.mouseDown && this.freehandMode(evt)) {
				this.addPoint();
			} else {
				this.modifyFeature();
			}
			this.callback("mousemove", [this.point.geometry, this.getGeometry()]);
			this.drawFeature();
		}
		return true;
	}
});
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */

/**
 * @requires OpenLayers/Control.js
 * @requires OpenLayers/Feature/Vector.js
 */

/**
 * Class: OpenLayers.Control.Measure
 * Allows for drawing of features for measurements.
 *
 * Inherits from:
 *  - <OpenLayers.Control>
 */
OpenLayers.Control.GeoMooseMeasure = OpenLayers.Class(OpenLayers.Control, {

    /**
     * Constant: EVENT_TYPES
     * {Array(String)} Supported application event types.  Register a listener
     *     for a particular event with the following syntax:
     * (code)
     * control.events.register(type, obj, listener);
     * (end)
     *
     * Listeners will be called with a reference to an event object.  The
     *     properties of this event depends on exactly what happened.
     *
     * Supported control event types (in addition to those from <OpenLayers.Control>):
     *  - *measure* Triggered when a measurement sketch is complete.  Listeners
     *      will receive an event with measure, units, order, and geometry
     *      properties.
     *  - *measurepartial* Triggered when a new point is added to the
     *      measurement sketch.  Listeners receive an event with measure,
     *      units, order, and geometry.
     */
    EVENT_TYPES: ['measure', 'measurepartial','measuremove'],

    /**
     * APIProperty: handlerOptions
     * {Object} Used to set non-default properties on the control's handler
     */
    handlerOptions: null,
    
    /**
     * Property: callbacks
     * {Object} The functions that are sent to the handler for callback
     */
    callbacks: null,
    
    /**
     * Property: displaySystem
     * {String} Display system for output measurements.  Supported values
     *     are 'english', 'metric', and 'geographic'.  Default is 'metric'.
     */
    displaySystem: 'metric',
    
    /**
     * Property: displaySystemUnits
     * {Object} Units for various measurement systems.  Values are arrays
     *     of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing
     *     order of length.
     */
    displaySystemUnits: {
        geographic: ['dd'],
        english: ['mi', 'ft', 'in'],
        metric: ['km', 'm']
    },

    /**
     * Constructor: OpenLayers.Control.Measure
     * 
     * Parameters:
     * handler - {<OpenLayers.Handler>} 
     * options - {Object} 
     */
    initialize: function(handler, options) {
        // concatenate events specific to measure with those from the base
        this.EVENT_TYPES =
            OpenLayers.Control.GeoMooseMeasure.prototype.EVENT_TYPES.concat(
            OpenLayers.Control.prototype.EVENT_TYPES
        );
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.callbacks = OpenLayers.Util.extend(
            {done: this.measureComplete, point: this.measurePartial, mousemove: this.measureMove},
            this.callbacks
        );
        this.handler = new handler(this, this.callbacks, this.handlerOptions);
    },
    
    /**
     * Method: updateHandler
     *
     * Parameters:
     * handler - {Function} One of the sketch handler constructors.
     * options - {Object} Options for the handler.
     */
    updateHandler: function(handler, options) {
        var active = this.active;
        if(active) {
            this.deactivate();
        }
        this.handler = new handler(this, this.callbacks, options);
        if(active) {
            this.activate();
        }
    },

    /**
     * Method: measureComplete
     * Called when the measurement sketch is done.
     *
     * Parameters:
     * geometry - {<OpenLayers.Geometry>}
     */
    measureComplete: function(geometry) {
        this.measure(geometry, "measure");
    },
    
    /**
     * Method: measurePartial
     * Called each time a new point is added to the measurement sketch.
     *
     * Parameters:
     * point - {<OpenLayers.Geometry.Point>} The last point added.
     * geometry - {<OpenLayers.Geometry>} The sketch geometry.
     */
    measurePartial: function(point, geometry) {
        this.measure(geometry, "measurepartial");
    },

    measureMove: function(point, geometry) {
    	this.measure(geometry, "measuremove");
    },

    /**
     * Method: measure
     *
     * Parameters:
     * geometry - {<OpenLayers.Geometry>}
     * eventType - {String}
     */
    measure: function(geometry, eventType) {
        var stat, order;
        if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
            stat = this.getBestLength(geometry);
            order = 1;
        } else {
            stat = this.getBestArea(geometry);
            order = 2;
        }
        this.events.triggerEvent(eventType, {
            measure: stat[0],
            units: stat[1],
            order: order,
            geometry: geometry
        });
    },
    
    /**
     * Method: getBestArea
     * Based on the <displaySystem> returns the area of a geometry.
     *
     * Parameters:
     * geometry - {<OpenLayers.Geometry>}
     *
     * Returns:
     * {Array([Float, String])}  Returns a two item array containing the
     *     area and the units abbreviation.
     */
    getBestArea: function(geometry) {
        var units = this.displaySystemUnits[this.displaySystem];
        var unit, area;
        for(var i=0, len=units.length; i<len; ++i) {
            unit = units[i];
            area = this.getArea(geometry, unit);
            if(area > 1) {
                break;
            }
        }
        return [area, unit];
    },
    
    /**
     * Method: getArea
     *
     * Parameters:
     * geometry - {<OpenLayers.Geometry>}
     * units - {String} Unit abbreviation
     *
     * Returns:
     * {Float} The geometry area in the given units.
     */
    getArea: function(geometry, units) {
        var area = geometry.getArea();
        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
        if(inPerDisplayUnit) {
            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[this.map.getUnits()];
            area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
        }
        return area;
    },
    
    /**
     * Method: getBestLength
     * Based on the <displaySystem> returns the length of a geometry.
     *
     * Parameters:
     * geometry - {<OpenLayers.Geometry>}
     *
     * Returns:
     * {Array([Float, String])}  Returns a two item array containing the
     *     length and the units abbreviation.
     */
    getBestLength: function(geometry) {
        var units = this.displaySystemUnits[this.displaySystem];
        var unit, length;
        for(var i=0, len=units.length; i<len; ++i) {
            unit = units[i];
            length = this.getLength(geometry, unit);
            if(length > 1) {
                break;
            }
        }
        return [length, unit];
    },

    /**
     * Method: getLength
     *
     * Parameters:
     * geometry - {<OpenLayers.Geometry>}
     * units - {String} Unit abbreviation
     *
     * Returns:
     * {Float} The geometry length in the given units.
     */
    getLength: function(geometry, units) {
        var length = geometry.getLength();
        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
        if(inPerDisplayUnit) {
            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[this.map.getUnits()];
            length *= (inPerMapUnit / inPerDisplayUnit);
        }
        return length;
    },

    CLASS_NAME: "OpenLayers.Control.Measure"
});
/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

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);
		}
	}
}
/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

OpenLayers.Control.ScaleJumper = OpenLayers.Class(OpenLayers.Control, {
    
    element: null,

    initialize: function(options) {
    	if(options.target) {
		this.element = document.getElementById(options.target);
		this.div = this.element;
	}
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
    },

    destroy: function() {
         if (this.map) {
             this.map.events.unregister('moveend', this, this.redraw);
         }
         OpenLayers.Control.prototype.destroy.apply(this, arguments);
     },

    draw: function() {
        OpenLayers.Control.prototype.draw.apply(this, arguments);

        if (!this.element) {
            this.element = this.div;
        }

	var span = document.createElement('span');
	span.className = 'scale-jumper-text';
	this.element.appendChild(span);
	span.appendChild(document.createTextNode('Scale: '));

	var select = document.createElement('select');
	this.element.appendChild(select);

	select.appendChild(document.createElement('option'));
	var enter = document.createElement('option');
	enter.value = 'enter-scale';
	select.appendChild(enter);
	enter.appendChild(document.createTextNode('Enter Scale...'));
	for(var e in CONFIGURATION.jumpto_scales) {
		var opt = document.createElement('option');
		opt.value = CONFIGURATION.jumpto_scales[e];
		select.appendChild(opt);
		opt.appendChild(document.createTextNode(e));
	}

	select.map = this.map;
	select.className = 'scale-jumper-select';
	select.onchange = function () {
		var opt = this.options[this.selectedIndex].value;
		if(opt == 'enter-scale') {
			var scale = prompt('Enter a scale:', this.map.getScale());
			this.map.zoomToScale(parseInt(scale), !CONFIGURATION.fractional_zoom);
		} else if(opt == 'current-scale') {
			// Do nothing ... no need to move.
		} else {
			this.map.zoomToScale(parseInt(opt), !CONFIGURATION.fractional_zoom);
		}
		for(var i = 0; i < this.options.length; i++) {
			this.options[i].selected = false;
		}
		this.options[0].selected = true;
	}
        
        this.redraw();
        return this.div;
    },

    /**
     * Method: redraw  
     */
    redraw: function(evt) {
	var opt = this.element.getElementsByTagName('option')[0];
	opt.innerHTML = '1:'+parseInt(this.map.getScale());
    },

    /** 
     * Method: setMap
     */
    setMap: function() {
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
        this.map.events.register( 'moveend', this, this.redraw);
    },     

    CLASS_NAME: "OpenLayers.Control.ScaleJumper"
});
/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/


function ServiceManager(Catalog, ParentId) {
	var MySelf = this;
	var ReturnedResults = false;
	
	function getService(serviceName) {
		var services = Catalog.getElementsByTagName('service');
		for(var i = 0; i < services.length; i++) {
			if(services[i].getAttribute('name') == serviceName) {
				return services[i];
			}
		}
		return null;
	}

	function renderInputStep(step, parentId, settingsObj) {
		var inputs = step.getElementsByTagName('input');
		var nUserInputs = 0;

		var headers = step.getElementsByTagName('header');
		var p = document.getElementById(parentId);

		for(var i = 0; i < headers.length; i++) {	
			var span = document.createElement('span');
			p.appendChild(span);
			span.className = 'service-header';
			span.innerHTML = combineTextNodes(headers[i]);
		}

		for(var i = 0; i < inputs.length; i++) {
			var inputType = new String(inputs[i].getAttribute('type')).toLowerCase();
			var inputName = new String(inputs[i].getAttribute('name'));
			if(InputTypes[inputType]) {
				if(settingsObj) {
					nUserInputs += InputTypes[inputType](parentId, inputs[i], settingsObj[inputName]);
				} else {
					nUserInputs += InputTypes[inputType](parentId, inputs[i], undefined);
				}
			}
		}

		var footnotes = step.getElementsByTagName('footnote');
		var p = document.getElementById(parentId);
		for(var i = 0; i < footnotes.length; i++) {	
			var span = document.createElement('span');
			p.appendChild(span);
			span.className = 'service-footnote';
			span.innerHTML = combineTextNodes(footnotes[i]);
		}

		/* shift focus to the first non-hidden input of the service */
		var inputs = p.getElementsByTagName('input');
		var notfound = true;
		for(var i = 0; i < inputs.length && notfound; i++) {
			if(inputs[i].type != 'hidden') {
				inputs[i].focus();
				notfound = false;
			}
		}

		return nUserInputs;
	}

	function onAddFeature(f) {
		/* Clear the layer */
		while(this.layer.features.length > 0) {
			this.layer.removeFeatures(this.layer.features[0]);
		}
		this.layer.addFeatures(f);

		/* Store the WKT of the feature */
		var parser = new OpenLayers.Format.WKT();
		var str = parser.write(f);
		this.xml.setAttribute('wkt', str);

		var OutputFormat = OpenLayers.Format.WKT;
		var outputType = new String(this.xml.getAttribute('format')).toLowerCase();
		if(outputType == 'kml') {
			OutputFormat = OpenLayers.Format.KML;
		} else if(outputType == 'wfs') {
			OutputFormat = OpenLayers.Format.WFS;
		} else if(outputType == 'json') {
			OutputFormat = OpenLayers.Format.JSON;
		} else if(outputType == 'delim') {
			OutputFormat = OpenLayers.Format.Text;
			var delim = this.xml.getAttribute('delim');
			var point_delim = this.xml.getAttribute('point-delim');
			if(!delim) { delim = ','; }
			if(!point_delim) { point_delim = ','; }
			var getPoints = function (g) {
				var points = [];
				if(g.components) {
					for(var i = 0; i < g.components.length; i++) {
						if(g.components[i] instanceof OpenLayers.Geometry.Point) {
							points += g.components[i];
						} else if(g.components) {
							this(g);
						}
					}
				} else if(g instanceof OpenLayers.Geometry.Point) {
					return [g];
				}
				return points;
			}

			OutputFormat = function() {
				this.write = function(f) {
					// travel through the object until we get to a "point" then 
					// convert the x,y to text separated by a delimiter.
					var points = getPoints(f.geometry);
					for(var i = 0; i < points.length; i++) {
						points[i] = points[i].x+delim+points[i].y;
					}
					return points.join(point_delim);
				}
			}

		}
		var output = new OutputFormat();

		var inputs = this.xml.getElementsByTagName('input');

		/* Remove the old input */
		var stepName = this.xml.getAttribute('name');
		for(var i = 0; i < inputs.length; i++) {
			if(inputs[i].getAttribute('name') == stepName) {
				this.xml.removeChild(inputs[i]);
			}
		}

		if(this.xml.getAttribute('reproject')) {
			var new_proj = this.xml.getAttribute('reproject');
			if(new_proj == 'EPSG:4326' || new_proj == 'WGS84') {
				dest_proj = LatLongProjection;
			} else {
				dest_proj = OpenLayers.Projection(new_proj);
			}

			OpenLayers.Projection.transform(f.geometry, 
				Map.getProjectionObject(),
				dest_proj);
		}
		
		var input = Catalog.createElement('input');
		input.setAttribute('name', stepName);
		input.appendChild(Catalog.createCDATASection(output.write(f)));
		this.xml.appendChild(input);


	
		if(parseBoolean(this.xml.getAttribute('jump-start'), false)) {
			MySelf.callService(this.xml.parentNode.getAttribute('name'));
		} else if(ReturnedResults) {
			/* if results have been returned then we need to show the service form if it's not jump-start able */
			MySelf.startService(this.xml.parentNode.getAttribute('name'), {}, false);
		}
	}


	function changeDrawingTool() {
		/* This code is needed because IE is stupid */
		if(this.type == 'radio') {
			var inputs = this.parentNode.getElementsByTagName('input');
			for(var i = 0; i < inputs.length; i++) {
				if(inputs[i].name == this.name) {
					inputs[i].checked = false;
				}
			}
			this.checked = true;
		}
		if(this.value) {
			for(var t in Tools) {
				Tools[t].deactivate();
			}
			if(Tools[this.value]) {
				Tools[this.value].activate();
				if(Tools[this.value].featureAdded) {
					Tools[this.value].featureAdded = onAddFeature;
				} else {
					Tools[this.value].onModificationEnd = onAddFeature;
				}
				Tools[this.value].xml = this.xml;
			}	
		}
	}

	function renderSpatialStep(step, parentId, settingsObj) {
		/* Restore preview drawing */
		var wkt = false;
		var wktFormat = false;
		if(settingsObj && settingsObj[step.getAttribute('name')]) {
			wkt = settingsObj[step.getAttribute('name')];
			wktFormat = true;
		} else if(step.getAttribute('wkt')) {
			wkt = step.getAttribute('wkt');
			if(!step.getAttribute('format') || step.getAttribute('format').toLowerCase() == 'wkt') {
				wktFormat = true;
			}
		}


		var parser = new OpenLayers.Format.WKT();
		if(wkt) {
			Map.getLayersByName('/drawinglayer')[0].addFeatures(parser.read(wkt));

			var inputs = step.getElementsByTagName('input');
			var input = false;
			for(var i = 0; i < inputs.length; i++) {
				if(inputs[i].getAttribute('name') == step.getAttribute('name')) {
					input = inputs[i];
				}
			}
			if(wktFormat) {
				if(input) {
					step.removeChild(input);
				}
				var input = Catalog.createElement('input');
				input.setAttribute('name', step.getAttribute('name'));
				input.appendChild(Catalog.createCDATASection(wkt));
				step.appendChild(input);
			}
		}
		var p = document.getElementById(parentId);
		p.appendChild(document.createTextNode('Available Tools: '));
		p.appendChild(document.createElement("br"));

		/* Maybe I should move this into configuration ... maybe ... */
		var tools = {
			'pan' : {
				'status' : true,
				'title': 'Navigate'
			}, 
			'edit-polygon' : {
				'status' : false,
				'title' : 'Edit Polygon'
			},
			'point' : {
				'status' : true,
				'title' : 'Draw Point'
			}, 
			'line' : {
				'status' : true,
				'title' : 'Draw Line'
			}, 
			'polygon' : {
				'status' : true,
				'title' : 'Draw Polygon'
			}
		};
		for(var v in tools) {
			if(parseBoolean(step.getAttribute(v), tools[v]['status'])) {
				var radio = document.createElement('input');
				radio.name = 'drawing-tools';
				radio.type = 'radio';
				radio.value = v;
				radio.onclick = changeDrawingTool;
				radio.xml = step;
				p.appendChild(radio);
				p.appendChild(document.createTextNode(tools[v].title));
				p.appendChild(document.createElement('br'));
			}
		}

		if(step.getAttribute('default')) {
			var def = step.getAttribute('default');
			var inputs = p.getElementsByTagName('input');
			for(var input = 0; input < inputs.length; input++) {
				if(inputs[input].name == 'drawing-tools' && inputs[input].value == def) {
					inputs[input].onclick();
				}
			}
		}

		p.appendChild(document.createElement('br'));
		/* return the number of inputs, since the spatial step is one,
			include it in the cound */
		return 1+renderInputStep(step, parentId, settingsObj);
	}

	this.onStart = function(service) {
		/* Override */
	}

	this.onFinish = function() {
		/* Override */
	}

	function changeStep() {
		var panel = document.getElementById(this.step);
		var panels = document.getElementsByTagName('div');
		for(var i = 0; i < panels.length; i++) {
			if(panels[i].className == 'service-step-visible') {
				panels[i].className = 'service-step-hidden';
			}
		}
		panel.className = 'service-step-visible';

		cleanupService();
	}

	function cleanupService() {
		for(var t in Tools) {
			Tools[t].deactivate();
		}

		var drawingLayer = Map.getLayersByName('/drawinglayer')[0];
		while(drawingLayer.features.length > 0) {
			drawingLayer.removeFeatures(drawingLayer.features[0]);
		}

	}

	function cancelService() {
		/* Clear out Service HTML I*/
		var p = document.getElementById(ParentId);	
		p.innerHTML = '';

		cleanupService();
		Tabs.showTab(CONFIGURATION.default_tab);
	}

	this.startService = function(serviceName, settingsObj, forceStart) {
		var service = getService(serviceName);
		if(service) {
			ReturnedResults = false;
			this.onStart(service);
			var p = document.getElementById(ParentId);
			p.innerHTML = '';

			var titleSpan = document.createElement('span');
			titleSpan.className = 'service-title';
			if(service.getAttribute('title')) {
				titleSpan.innerHTML = service.getAttribute('title');
				p.appendChild(titleSpan);
			}

			var steps = service.getElementsByTagName('step');
			/* No "steps" */

			var nInputs = 0;
			var submitParent = ParentId;
			if(steps.length == 0) {
				nInputs += renderInputStep(service, ParentId, settingsObj);
			} else {
				var rootName = 'service-';
				for(var i = 0; i < steps.length; i++) {
					var stepType = new String(steps[i].getAttribute('type')).toLowerCase();
					var div = document.createElement('div');
					if(i > 0) {
						div.className = 'service-step-hidden';
					} else {
						div.className = 'service-step-visible';
					}
					div.id = rootName + i;
					p.appendChild(div);

					if(stepType == 'input') {
						nInputs += renderInputStep(steps[i], div.id, settingsObj);
					} else if(stepType == 'select' || stepType == 'spatial') {
						nInputs += renderSpatialStep(steps[i], div.id, settingsObj);
					}
					var table = document.createElement('table');
					table.width = '95%';
					var tbody = document.createElement('tbody');
					div.appendChild(table);
					table.appendChild(tbody);

					var tr = document.createElement('tr');
					tbody.appendChild(tr);

					var left = document.createElement('td');
					left.align = 'left';
					tr.appendChild(left);

					var right = document.createElement('td');
					right.align = 'right';
					tr.appendChild(right);

					right.id = rootName + '-right-' + i;

					if(i > 0) {
						var prevButton = document.createElement('input');
						prevButton.value = 'Back';
						prevButton.type = 'button';
						left.appendChild(prevButton);
						prevButton.className = 'service-button-back';

						prevButton.step = rootName + (i-1);
						prevButton.onclick = changeStep;
					} else {
						var cancelButton = document.createElement('input');
						cancelButton.type = 'button';
						cancelButton.value = 'Cancel';
						left.appendChild(cancelButton);
						cancelButton.className = 'service-button-back';
						cancelButton.onclick = cancelService;
					}
					if(i < steps.length - 1) {
						var nextButton = document.createElement('button');
						nextButton.innerHTML = 'Next';
						right.appendChild(nextButton);
						nextButton.className = 'service-button-next';

						nextButton.step = rootName + (i+1);
						nextButton.onclick = changeStep;
					}
				}
				submitParent = rootName + '-right-' + (steps.length - 1);
			}
			var buttonTitle = service.getAttribute('submit');
			if(!buttonTitle) {
				buttonTitle = 'Go!';
			}

			var button = document.createElement('button');
			button.id = 'submit-service';
			button.setAttribute('service-name', serviceName);
			button.innerHTML = buttonTitle;
			button.onclick = function () {
				MySelf.callService(this.getAttribute('service-name'));	
			}
			var buttonParent = document.getElementById(submitParent);
			buttonParent.appendChild(button);
			if(nInputs <= 0 || forceStart) {
				button.onclick();
			}


		} else {
			alert('Service with the name '+serviceName+' cannot be found');
		}
	}

	this.callService = function(serviceName, values) {
		var service = getService(serviceName);
		var inputs = service.getElementsByTagName('input');
		var url = combineTextNodes(service.getElementsByTagName('url')[0]);
		var params = '?';
		for(var i = 0; i < inputs.length; i++) {
			var name = inputs[i].getAttribute('name');
			var v = '';
			if(inputs[i].getAttribute('value')) {
				v = inputs[i].getAttribute('value');
			} else {
				var testV = combineTextNodes(inputs[i]);
				if(testV != undefined) {
					v = escape(testV);
				}
			}
			if(values && values[name]) {
				v = values[name];
			}
			params += '&' + name + '=' + v;
		}

		/** Clear out the old spatial stuff **/
		var drawingTools = ['point','line','polygon'];
		for(var d = 0; d < drawingTools.length; d++) {
	//		Tools[drawingTools[d]].deactivate();
		}

		var drawingLayer = Map.getLayersByName('/drawinglayer')[0];
		while(drawingLayer.features.length > 0) {
			drawingLayer.removeFeatures(drawingLayer.features[0]);
		}
	
		if(service.getAttribute('target') == '_blank') {
			window.open(url+'?'+params);
		} else {
			openURL(url, params, MySelf, MySelf.onServiceReturn);
		}

		/* this isn't totally correct but good enough for the events it handles */
		ReturnedResults = true;
		MySelf.onFinish();
	}

	this.onServiceReturn = function (response) {
		/* Override */
		if(response.responseText) {
			document.getElementById(Tabs.getTab(CONFIGURATION['show_results_in'])).innerHTML = response.responseText;
		}
	}

	this.initialize = function() {
		if(CONFIGURATION.startup_service) {
			this.startService(CONFIGURATION.startup_service);
		}
	}

	var InputTypes = new Array();

	function combineTextNodes(node) {
/*		var s = new String();
		for(var i = 0; i < node.childNodes.length; i++) {
			if(node.childNodes[i].nodeName == '#text'
				|| node.childNodes[i].nodeName == '#cdata-section') {
				s += node.childNodes[i].nodeValue;
			}
		}*/
		return OpenLayers.Util.getXmlNodeValue(node);
	}

	function clearChildren(node) {
		if(node.childNodes) {
			while(node.childNodes.length > 0) {
				node.removeChild(node.childNodes[0]);
			}
		}
	}

	function updateServiceSetting() {
		var node = this.xml;
		var currentValue = combineTextNodes(node);

		clearChildren(node);
		node.appendChild(Catalog.createCDATASection(this.value));
	}

	function keyCallService(event) {
		if(window.event) { event = window.event; }
		var keyCode = null;
		if(event.which) {
			keyCode = event.which;
		} else if(event.keyCode) {
			keyCode = event.keyCode;
		}
		if(keyCode == 13) {
			this.onchange();
			document.getElementById('submit-service').onclick();
		}
	}

	InputTypes['user'] = function(inputParent, input, value) {
		var p = document.getElementById(inputParent);
		var inputParent = document.createElement('span');
		inputParent.className = 'service-input-parent';
		p.appendChild(inputParent);

		var i = document.createElement('input');
		i.className = 'service-input';
		i.xml = input;
		i.id = 'service-input-'+input.getAttribute('name');
		
		var xmlValue = combineTextNodes(input);
		if(xmlValue != undefined) {
			i.value = xmlValue;
		}
		if(value != undefined) {
			i.value = value;
		/*	input.setAttribute('value', value);*/
			clearChildren(input);
			input.appendChild(Catalog.createCDATASection(value));
		}
		i.onchange = updateServiceSetting;
		i.onkeypress = keyCallService;

		var title = document.createElement('span');
		title.className = 'service-input-title';
		inputParent.appendChild(title);
		title.innerHTML = input.getAttribute('title');
		inputParent.appendChild(i);
		return 1;	/* 1 = requires user input */

	}

	InputTypes['hidden'] = function(inputParent, input, value) {
		if(value != undefined) {
			clearChildren(input);
			input.appendChild(Catalog.createCDATASection(value));
		}
		return 0;
	}

	InputTypes['extent'] = function(inputParent, input, value) {
		clearChildren(input);
		var delim = input.getAttribute('delim');
		if(!delim) { delim = ','; }
		input.appendChild(Catalog.createCDATASection(Map.getExtent().toArray().join(delim)));
		return 0;
	}

	InputTypes['visiblelayers'] = function(inputParent, input, value) {
		var services = Catalog.getElementsByTagName('map-source');
		var layersList = new Array();
		for(var i = 0; i < services.length; i++) {
			var root = services[i].getAttribute('name');
			var layers = services[i].getElementsByTagName('layer');
			for(var l = 0; l < layers.length; l++) {
				if(parseBoolean(layers[l].getAttribute('visible'))) {
					var n = layers[l].getAttribute('name');
					layersList.push(root+'/'+n);
				}
			}
		}
		var delim = ':';
		if(input.getAttribute('delim')) {
			delim = input.getAttribute('delim');
		}
		clearChildren(input);
		input.appendChild(Catalog.createCDATASection(layersList.join(delim)));
		return 0; /* 0 = no user input required */
	}

	InputTypes['sketches'] = function(inputParent, input, value) {
		var sketch_layer = Map.getLayersByName('sketchlayer')[0];
		var wkt = new OpenLayers.Format.WKT();

		var feature_info = [];
		for(var i = 0; i < sketch_layer.features.length; i++) {
			var feature = sketch_layer.features[i];

			var feature_obj = {};
			feature_obj['wkt'] = wkt.write(feature);
			for(var k in feature.attributes) {
				feature_obj[k] = feature.attributes[k];
			}

			feature_info.push(feature_obj);
		}

		clearChildren(input);

		var json = new OpenLayers.Format.JSON();
		input.appendChild(Catalog.createCDATASection(json.write(feature_info)));

		return 0;
	}

	InputTypes['print_info'] = function(inputParent, input, value) {
		//input.appendChild(Catalog.createCDATASection(getPrintRequest()));
		var info = getPrintRequest(); /* returns an object with all the inputs needed for a print */
		var inputs = input.parentNode.getElementsByTagName('input');
		var p = input.parentNode;
		/*
		for(var i = inputs.length -1; i >= 0; i--) {
			if(inputs[i] != input) {
				p.removeChild(inputs[i]);
			}
		}
		*/

		for(var i in info) {
		/*	var ipt = Catalog.createElement('input');
			p.appendChild(ipt);
			ipt.setAttribute('name', i);
			ipt.setAttribute('type', 'hidden');
			ipt.appendChild(Catalog.createCDATASection(info[i]));*/
			//console.log(i, info[i]);
		}

		var vis_layers = GeoMOOSE.getVisibleLayers();
		var services = {};
	
		var print_obj = {};
		/* specify the layer printing order */
		var service_order = [];
		var found_service = {}
		for(var i = 0; i < vis_layers.length; i++) {
			var service_name = vis_layers[i].split('/')[0];
			if(!found_service[service_name]) {
				service_order.push(service_name);
			}
			found_service[service_name] = true;
		}
		print_obj['order'] = service_order;

		for(var i = 0; i < vis_layers.length; i++) {				
			var path_arr = vis_layers[i].split('/');
			if(!print_obj[path_arr[0]]) {
				print_obj[path_arr[0]] = {};
				print_obj[path_arr[0]]['layers'] = [];
				print_obj[path_arr[0]]['params'] = {};
				print_obj[path_arr[0]]['opacity'] = 100;
				print_obj[path_arr[0]]['url'] = GeoMOOSE.getLayerUrl(path_arr[0]); 
			}
			print_obj[path_arr[0]]['layers'].push(path_arr[1]);

			var params = GeoMOOSE.getLayerParameters(path_arr[0]);
			for(var p in params) {
				print_obj[path_arr[0]]['params'][p] = params[p];
			}
		}

		var json = new OpenLayers.Format.JSON();
		//console.log(json.write(print_obj));

		while(input.childNodes[0]) {
			input.removeChild(input.childNodes[0]);
		}
		input.appendChild(Catalog.createCDATASection(json.write(print_obj)));

		return 0;
	}

	InputTypes['select'] = function(inputParent, input, value) {
		var p = document.getElementById(inputParent);
		var inputParent = document.createElement('span');
		inputParent.className = 'service-input-parent';
		p.appendChild(inputParent);

		var select = document.createElement('select');
		select.xml = input;

		var title = document.createElement('span');
		title.className = 'service-input-title';
		inputParent.appendChild(title);
		title.innerHTML = input.getAttribute('title');
		inputParent.appendChild(select);

		var opts = input.getElementsByTagName('option');
		var selIndex = 0;
		var selectValue =  input.getAttribute('value');

		if(value != undefined) { selectValue = value; }
		if(!selectValue) { selectValue = false; }

		for(var o = 0; o < opts.length; o++) {
			var opt = document.createElement('option');
			opt.value = opts[o].getAttribute('value');
			select.appendChild(opt);
			if(opt.value == selectValue) {
				opt.selected = true;
				selIndex = 0;
			}
			var labelValue = combineTextNodes(opts[o]);
			if(labelValue) {
				opt.appendChild(document.createTextNode(labelValue));
			}
		}
		if(!selectValue) {
			select.getElementsByTagName('option')[0].selected = true;
		}

		select.onchange = function() {
			var opts = this.getElementsByTagName('option');
		/*	clearChildren(this.xml);
			this.xml.appendChild(Catalog.createCDATASection(opts[this.selectedIndex].value));*/
			this.xml.setAttribute('value', opts[this.selectedIndex].value);
		}

		select.onchange();

		return 1;
	}

}
/*
Copyright (c) 2009, Dan "Ducky" Little & GeoMOOSE.org

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 conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/*
 * GeoMOOSE 2.0 Tab Manager
 */

function GeomooseTabs(tabStrip) {
	var elementCounter = 0;

	function getElementId() {
		elementCounter++;
		return 'tabs-'+elementCounter;
	}

	function onTabClick() {
		var p = document.getElementById(tabStrip);
		var tabs = p.getElementsByTagName('span');
		for(var i = 0; i < tabs.length; i++) {
			if(tabs[i].className.match(/tab/)) {
				tabs[i].className = 'tab';
				var e = document.getElementById(tabs[i].value);
				e.className = e.className.replace('tab-visible','tab-hidden');
			}
		}
		this.className = 'tab tab-selected';

		var e = document.getElementById(this.value);
		e.className = e.className.replace('tab-hidden','tab-visible');
	}


	this.initialize = function() {
		var p = document.getElementById(tabStrip);
		p.className += ' tab-strip';
		for(var v in CONFIGURATION.tabs) {
			var span = document.createElement('span');
			span.className = 'tab';
			p.appendChild(span);
			span.id = 'tab-'+v;
			span.appendChild(document.createTextNode(v));
			span.onclick = onTabClick;
			span.value = CONFIGURATION.tabs[v];

			p.appendChild(document.createTextNode(' '));
			var tab = document.getElementById(CONFIGURATION.tabs[v]);
			tab.className += ' tab-hidden';
		}
		if(CONFIGURATION.default_tab) {
			document.getElementById('tab-'+CONFIGURATION.default_tab).onclick();
		}
	}


	this.showTab = function(tabName) {
		var tab = document.getElementById('tab-'+tabName);
		tab.onclick();
	}

	this.getTab = function(tabName) {
		return CONFIGURATION.tabs[tabName];
	}

}
// Library to convert between NAD83 Lat/Lon and US National Grid
// Based on the FGDC-STS-011-2001 spec at http://www.fgdc.gov/standards/projects/FGDC-standards-projects/usng/fgdc_std_011_2001_usng.pdf
// Also based on the UTM library already in GeoMOOSE
// By Jim Klassen 4/2008
// (c)2008 City of Saint Paul released under the City of Saint Paul Open Source License

function UTM() {
	
	// Functions to convert between lat,lon and utm. Derived from visual basic
	// routines from Craig Perault. This assumes a NAD83 datum.
	
	// constants
	var MajorAxis = 6378137.0;
	var MinorAxis = 6356752.3;
	var Ecc = (MajorAxis * MajorAxis - MinorAxis * MinorAxis) / (MajorAxis * MajorAxis);
	var Ecc2 = Ecc / (1.0 - Ecc);
	var K0 = 0.9996;
	var E4 = Ecc * Ecc;
	var E6 = Ecc * E4;
	var degrees2radians = Math.PI / 180.0;
	
	// Computes the meridian distance for the GRS-80 Spheroid.
	// See equation 3-22, USGS Professional Paper 1395.
	function meridianDist(lat) {
		var c1 = MajorAxis * (1 - Ecc / 4 - 3 * E4 / 64 - 5 * E6 / 256);
		var c2 = -MajorAxis * (3 * Ecc / 8 + 3 * E4 / 32 + 45 * E6 / 1024);
		var c3 = MajorAxis * (15 * E4 / 256 + 45 * E6 / 1024);
		var c4 = -MajorAxis * 35 * E6 / 3072;
		
		return(c1 * lat + c2 * Math.sin(lat * 2) + c3 * Math.sin(lat * 4) + c4 * Math.sin(lat * 6));
	}
	
	// Convert lat/lon (given in decimal degrees) to UTM, given a particular UTM zone.
	this.fromLatLong = function(zone, latlon) {
		var utm = new OpenLayers.Geometry.Point();
		
		var centeralMeridian = -((30 - zone) * 6 + 3) * degrees2radians;
		
		var lat = latlon.y * degrees2radians;
		var lon = latlon.x * degrees2radians;
		
		var latSin = Math.sin(lat);
		var latCos = Math.cos(lat);
		var latTan = latSin / latCos;
		var latTan2 = latTan * latTan;
		var latTan4 = latTan2 * latTan2;
		
		var N = MajorAxis / Math.sqrt(1 - Ecc * (latSin*latSin));
		var c = Ecc2 * latCos*latCos;
		var a = latCos * (lon - centeralMeridian);
		var m = meridianDist(lat);
		
		var temp5 = 1.0 - latTan2 + c;
		var temp6 = 5.0 - 18.0 * latTan2 + latTan4 + 72.0 * c - 58.0 * Ecc2;
		var temp11 = Math.pow(a, 5);
		
		utm.x = K0 * N * (a + (temp5 * Math.pow(a, 3)) / 6.0 + temp6 * temp11 / 120.0) + 500000;
		
		var temp7 = (5.0 - latTan2 + 9.0 * c + 4.0 * (c*c)) * Math.pow(a,4) / 24.0;
		var temp8 = 61.0 - 58.0 * latTan2 + latTan4 + 600.0 * c - 330.0 * Ecc2;
		var temp9 = temp11 * a / 720.0;
		
		utm.y = K0 * (m + N * latTan * ((a * a) / 2.0 + temp7 + temp8 * temp9))
			
			return(utm);
	}
	
	function CSq(value) {
		return value*value;
	}
	
	// Convert UTM coordinates (given in meters) to Lat/Lon (in decimal degrees), given a particular UTM zone.
	this.toLatLong = function(zone, utm) {
		var latlon = new Point();
		
		var centeralMeridian = -((30 - zone) * 6 + 3) * degrees2radians;
		
		var temp1 = Math.sqrt(1.0 - Ecc);
		var ecc1 = (1.0 - temp1) / (1.0 + temp1);
		var ecc12 = ecc1 * ecc1;
		var ecc13 = ecc1 * ecc12;
		var ecc14 = ecc12 * ecc12;
		
		utm.x = utm.x - 500000.0;
		
		var m = utm.y / K0;
		var um = m / (MajorAxis * (1.0 - (Ecc / 4.0) - 3.0 * (E4 / 64.0) - 5.0 * (E6 / 256.0)));
		
		var temp8 = (1.5 * ecc1) - (27.0 / 32.0) * ecc13;
		var temp9 = ((21.0 / 16.0) * ecc12) - ((55.0 / 32.0) * ecc14);
		
		var latrad1 = um + temp8 * Math.sin(2 * um) + temp9 * Math.sin(4 * um) + (151.0 * ecc13 / 96.0) * Math.sin(6.0 * um);
		
		var latsin1 = Math.sin(latrad1);
		var latcos1 = Math.cos(latrad1);
		var lattan1 = latsin1 / latcos1;
		var n1 = MajorAxis / Math.sqrt(1.0 - Ecc * CSq(latsin1));
		var t2 = CSq(lattan1);
		var c1 = Ecc2 * CSq(latcos1);
		
		var temp20 = (1.0 - Ecc * CSq(latsin1));
		var r1 = MajorAxis * (1.0 - Ecc) / Math.sqrt(CSq(temp20) * temp20);
		
		var d1 = utm.x / (n1*K0);
		var d2 = CSq(d1);
		var d3 = d1 * d2;
		var d4 = CSq(d2);
		var d5 = d1 * d4;
		var d6 = CSq(d3);
		
		var t12 = CSq(t2);
		var c12 = CSq(c1);
		
		temp1 = n1 * lattan1 / r1;
		temp2 = 5.0 + 3.0 * t2 + 10.0 * c1 - 4.0 * c12 - 9.0 * Ecc2;
		temp4 = 61.0 + 90.0 * t2 + 298.0 * c1 + 45.0 * t12 - 252.0 * Ecc2 - 3.0 * c12;
		temp5 = (1.0 + 2.0 * t2 + c1) * d3 / 6.0;
		temp6 = 5.0 - 2.0 * c1 + 28.0 * t2 - 3.0 * c12 + 8.0 * Ecc2 + 24.0 * t12;
		
		latlon.y = (latrad1 - temp1 * (d2 / 2.0 - temp2 * (d4 / 24.0) + temp4 * d6 / 720.0)) * 180 / Math.PI;
		latlon.x = (centeralMeridian + (d1 - temp5 + temp6 * d5 / 120.0) / latcos1) * 180 / Math.PI;
		utm.x = utm.x + 500000.0;
		
		return (latlon);
	}
	
}

function USNG() {
	
	// Converts a lat, lon point (NAD83) into a USNG coordinate string
	// of precision where precision indicates the number of digits used
	// per coordinate (0 = 100,000m, 1 = 10km, 2 = 1km, 3 = 100m, 4 = 10m, ...)
	this.fromLatLong = function(latlong, precision) {
		usng_string = new String();
		var lon = latlong.x;
		var lat = latlong.y;
		
		//
		// Find Grid Zone Designation
		//
		var GZnumber;
		var GZletter;
		
		while(lon < -180) {
			lon += 180;
		}
		while(lon > 180) {
			lon -= 180;
		}
		// -180 = 180W is grid 1... increment every 6 degrees going east
		// Note [-180, -174) is in grid 1, [-174,-168) is 2, [174, 180) is 60 
		GZnumber = Math.floor((lon - (-180.0)) / 6.0) + 1;
		
		if(! ((lat > -90) && (lat < 90) )) {
			return('error lat'+lat +' must be between in (-90,90)... no playing at the poles, yet');
		} else {
			a = new Array();
			a = ['C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X'];
			GZletter = a[Math.floor((lat - (-80.0)) / 8)]; 
		}
		
		//
		// Find the correct UTM Zone (ok... this is sneaky... it = GZnumber N or S)
		//
		
		var utm = new UTM();
		utm_pt = utm.fromLatLong(GZnumber, latlong);
		
		//
		// Now need to figure out which 100,000m grid square were in
		//
		var GridSq='';
		
		var GridSqSet = (GZnumber % 6);
		if(GridSqSet == 0) GridSqSet = 6;
	//	alert(GridSqSet);
		var NSLetters135 = ['A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V'];
		var NSLetters246 = ['F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','A','B','C','D','E'];
		
		var EWLetters14 = ['A','B','C','D','E','F','G','H'];
		var EWLetters25 = ['J','K','L','M','N','P','Q','R'];
		var EWLetters36 = ['S','T','U','V','W','X','Y','Z'];
		
		var GridEIdx = Math.floor(utm_pt.x / 100000) - 1; //(should be [100000,9000000])
		var GridNIdx = Math.floor((utm_pt.y%2000000) / 100000);
	//	alert(utm_pt.x +','+ utm_pt.y)
	//	alert(GridEIdx + ','+ GridNIdx);
		switch(GridSqSet) {
			case 1:
				GridSq = EWLetters14[GridEIdx] + NSLetters135[GridNIdx];
				break;
			case 2:
				GridSq = EWLetters25[GridEIdx] + NSLetters246[GridNIdx];
				break;
			case 3:
				GridSq = EWLetters36[GridEIdx] + NSLetters135[GridNIdx];
				break;
			case 4:
				GridSq = EWLetters14[GridEIdx] + NSLetters246[GridNIdx];
				break;
			case 5:
				GridSq = EWLetters25[GridEIdx] + NSLetters135[GridNIdx];
				break;
			case 6:
				GridSq = EWLetters36[GridEIdx] + NSLetters246[GridNIdx];
				break;
			default:
				alert("shouldn't get here");
		}

		
		// Calc Easting and Northing integer to 100,000s place
		var Easting = Math.floor(utm_pt.x % 100000).toString();
		var Northing = Math.floor(utm_pt.y % 100000).toString();
		
		while(Easting.length < 5) Easting = '0' + Easting;
		while(Northing.length < 5) Northing = '0' + Northing;
		
		// Add on any fractional digits, no '.'... this is flaky yet use toFixed(precision-6)?
		//Easting = Easting + ((utm_pt.x % 100000) - Math.floor(utm_pt.x % 100000)).toString();
		//Northing = Northing + ((utm_pt.y % 100000) - Math.floor(utm_pt.y % 100000)).toString();
		
		Easting = Easting.substr(0, precision);
		Northing = Northing.substr(0, precision);
		
		//usng_string = "UTM " + utm_pt.x + "," + utm_pt.y + "   " + String(GZnumber) + GZletter + GridSq + Easting + ' '+ Northing; 
		usng_string = String(GZnumber) + GZletter + GridSq + Easting + Northing; 
		return(usng_string);
	}
	
	
}



More information about the Geomoose-users mailing list