
/* Map Constructor */
function Map(mapDiv, tocDiv, legDiv, overviewDiv) {
	/*Variables of this object*/	
	this.minX = 0;
	this.maxX = 0;
	this.minY = 0;
	this.maxY = 0;

	this.div = null;
	this.tocdiv = null;
	this.legenddiv = null;
	this.overviewdiv = null;
	this.state = null;
	this.selectionDiv = null;
	
	this.mapUrl = null;
	this.tocUrl = null;
	
	this.activeLayer = null;
	this.coordSys = null;
	
	this.activationWarning = 'Please activate a layer first.';
	this.objectSelectionWarning = 'No object found that can be used as point for the line.';
	
	this.bBoxMap = null;

	/*Function assignments*/
	this.initMap = initMap;
	this.idle = idle;
	this.zoomIn = zoomIn;
	this.zoomOut = zoomOut;
	this.zoomFullExtent = zoomFullExtent;
 	this.pan = pan;
	this.identify = identify;
	this.select = select;
	this.selectPolygon = selectPolygon;
	this.centerMapAt = centerMapAt;
	this.crossSection = crossSection;
	this.initActiveLayer = initActiveLayer;
	
	// Getters and setters
	this.getMapDiv = getMapDiv;
	this.setMapDiv = setMapDiv;
	this.getTocDiv = getTocDiv;
	this.setTocDiv = setTocDiv;
	this.getLegendDiv = getLegendDiv;
	this.setLegendDiv = setLegendDiv;
	this.getOverviewDiv = getOverviewDiv;
	this.setOverviewDiv = setOverviewDiv;
	
	this.getState = getState;
	this.setState = setState;
	this.getPolyLineState = getPolyLineState;
	
	this.getOverview = getOverview;
	
	this.getMinX = getMinX;
	this.setMinX = setMinX;
	this.getMinY = getMinY;
	this.setMinY = setMinY;
	
	this.getBBoxMap = getBBoxMap;
	this.setBBoxMap = setBBoxMap;
	
	this.getMapUrl = getMapUrl;
	this.getMapUrlUnique = getMapUrlUnique;
	this.setMapUrl = setMapUrl;
	this.getTocUrl = getTocUrl;
	this.setTocUrl = setTocUrl;
	
	this.updateMap = updateMap;
	this.refreshMap = refreshMap;
	this.refreshToc = refreshToc;
	this.refreshLegend = refreshLegend;
	this.refreshOverview = refreshOverview;
	this.toggleVisibleLayer = toggleVisibleLayer;
	this.moveLayerUp = moveLayerUp;
	this.moveLayerToTop = moveLayerToTop;
	this.moveLayerDown = moveLayerDown;
	this.identifyMapImage = identifyMapImage;
	this.drawSelectionAreas = drawSelectionAreas;
	this.getIdProperty = getIdProperty;
	this.setIdProperty = setIdProperty;
	this.getSelectProperty = getSelectProperty;
	this.setSelectProperty = setSelectProperty;
	this.isSelectPropertyUndefined = isSelectPropertyUndefined;
	this.getActiveLayer = getActiveLayer;
	this.setActiveLayer = setActiveLayer;
	this.getCoordSys = getCoordSys;
	this.setCoordSys = setCoordSys;
	this.getActivationWarning = getActivationWarning;
	this.setActivationWarning = setActivationWarning;
	this.getObjectSelectionWarning = getObjectSelectionWarning;
	this.setObjectSelectionWarning = setObjectSelectionWarning;
	this.addLayer = addLayer;
	this.setLayer = setLayer;
	this.removeLayer = removeLayer;
	
	this.waitImageUrl = null;
	this.waitImage = waitImage;
	this.getWaitImageUrl = getWaitImageUrl;
	this.setWaitImageUrl = setWaitImageUrl;
	
	this.getSelectionDiv = getSelectionDiv;
	this.setSelectionDiv = setSelectionDiv;	
	this.startBox = startBox;
	this.stopBox = stopBox;
	
	this.polyLineRemoveLast = polyLineRemoveLast;
	this.polyLineRemoveAll = polyLineRemoveAll;
	this.getRealWorldCoordinates = getRealWorldCoordinates;
	this.getPixelCoordinates = getPixelCoordinates;
	
	// Identify notification
	this.onIdentify = onIdentify;
	this.onIdentifyCallback = null;
	this.onIdentifyProperty = null;
	this.getOnIdentify = getOnIdentify;
	// Selection notification
	this.onSelect = onSelect;
	this.onSelectCallback = null;
	this.onSelectProperty = null;
	this.getOnSelect = getOnSelect;
	// Zoom notification
	this.onZoomChangeCallback = null;
	this.onZoomChange = onZoomChange;
	this.zoomChanged = zoomChanged;
	
	// Layer activation
	this.activationEnabled = false;
	this.setActivationEnabled = setActivationEnabled;
	this.getActivationEnabled = getActivationEnabled;

	// Layer activation callback
	this.onActivelayerChangedCallback = null;
	this.onActivelayerChanged = onActivelayerChanged;
	this.getOnActivelayerChanged = getOnActivelayerChanged;
	
	// Show backgroundlayers indication
	this.showBackgroundLayers = false;
	this.setShowBackgroundLayers = setShowBackgroundLayers;
	this.getShowBackgroundLayers = getShowBackgroundLayers;

	// Callbacks used when starting and ending an action on a map that requires server response
	this.onStartOfActionCallback = null;
	this.onStartOfAction = onStartOfAction;
	this.onEndOfActionCallback = null;
	this.onEndOfAction = onEndOfAction;
	
	/*Execution of constructor...*/
	this.initMap(mapDiv, tocDiv, legDiv, overviewDiv);
	
	/*Initializes the map*/
	function initMap(mapDiv, tocDiv, legDiv, overviewDiv) {
		if(!mapDiv) {
			throw 'DIV where map is placed on, is undefined.';
		}
		if(!tocDiv) {
			throw 'DIV where the table of contents is placed, is undefined.';
		}
		setMapDiv(mapDiv);
		setTocDiv(tocDiv);
		setLegendDiv(legDiv);
		setOverviewDiv(overviewDiv);
		setState(new IdleState());
		overviewRefreshNeeded = true;
	}
	
	/*Gets map state*/
	function getState() {
		return this.state;
	}
	
	/*Sets map state*/
	function setState(s) {
		this.state = s;
		/*Register event handlers*/
		getMapDiv().onmousedown = this.state.mouseClick;
		getMapDiv().onmouseup = this.state.mouseClick;
		getMapDiv().onmousemove = this.state.mouseMove;
		getMapDiv().ondblclick = this.state.mouseDoubleClick;
	}
	
	/*Gets map div*/
	function getMapDiv(){
		return this.div;
	}
	
	/*Sets map div*/
	function setMapDiv(mapDiv){
		this.div = mapDiv
	}
	
	/*Gets toc div*/
	function getTocDiv(){
		return this.tocdiv;
	}
	
	/*Sets toc div*/
	function setTocDiv(d){
		this.tocdiv = d;
	}
	
	/*Gets legend div*/
	function getLegendDiv(){
		return this.legenddiv;
	}
	
	/*Sets legend div*/
	function setLegendDiv(ldiv){
		this.legenddiv = ldiv;
	}
	
	/*Gets overview div*/
	function getOverviewDiv(){
		return this.overviewdiv;
	}
	
	/*Sets overview div*/
	function setOverviewDiv(odiv){
		this.overviewdiv = odiv;
	}
	
	/*Object bounding box*/
	function BoundingBox(width, height) {
		/*Variables*/
		this.box = null;
		this.minX = 0; //real world x of upper left corner (0,0 in pixelcoordinates)
		this.maxY = 0; //real world y of upper left corner (0,0 in pixelcoordinates)
		this.pixelX = 0; // number of real world units per pixel in horizontal direction
		this.pixelY = 0; // number of real world units per pixel in horizontal direction
		this.width = 0;
		this.height = 0;
		
		/*Functions*/
		this.initBox = initBox;
		this.getBox = getBox;
		this.getMinX = getMinX;
		this.getMaxY = getMaxY;
		this.getPixelX = getPixelX;
		this.getPixelY = getPixelY;
		this.getWidth = getWidth;
		this.getHeight = getHeight;
		
		/* Execute constructor */
		this.initBox(width, height);
		
		function initBox(width, height) {
			/*Get bounding box, in real world coordinates, of the map*/
			var bbox = new Array();
			var minx = 0;
			var maxx = 0;
			var miny = 0;
			var maxy = 0;
			
			var rwBbox = getRealWorldCoordinates("0,0;" + 
					width + ",0;0," + height+";" + 
					width + "," + height);
			var rwBboxCoords = rwBbox.split(";");
			for (var i = 0; i < rwBboxCoords.length; i++) {
				var rwBboxPoints = rwBboxCoords[i].split(",");
				if(i == 0){
					minx = rwBboxPoints[0];
					maxx = rwBboxPoints[0];
					miny = rwBboxPoints[1];
					maxy = rwBboxPoints[1];
				}
				
				if(rwBboxCoords[i] != ""){
					minx = Math.min(rwBboxPoints[0], minx);
					maxx = Math.max(rwBboxPoints[0], maxx);
					miny = Math.min(rwBboxPoints[1], miny);
					maxy = Math.max(rwBboxPoints[1], maxy);
				}
			}
			bbox.push(new Number(minx));
			bbox.push(new Number(maxx));
			bbox.push(new Number(miny));
			bbox.push(new Number(maxy));

			this.minX = bbox[0];
			this.maxY = bbox[3];
			this.pixelX = (bbox[1] - bbox[0])/width;
			this.pixelY = (bbox[3] - bbox[2])/height;
			this.box = bbox;
			this.width = width;
			this.height = height;
		}

		/*Gets boundingbox in rw coordinates*/
		function getBox() {
			return this.box;
		}
		
		/*Gets min x */
		function getMinX(){
			return this.minX;
		}
	
		/*Gets max y of map*/
		function getMaxY(){
			return this.maxY;
		}
		
		/*Gets units per x-pixel of map*/
		function getPixelX(){
			return this.pixelX;
		}
		
		/*Gets units per y-pixel of map*/
		function getPixelY(){
			return this.pixelY;
		}
		
		/*Gets pixel width of map*/
		function getWidth(){
			return this.width;
		}
		
		/*Gets pixel height of map*/
		function getHeight(){
			return this.height;
		}
	}

	/*Object overview*/
	function Overview() {
		/*Variables*/
		this.jg = null;
		this.bbox = null;
		this.refreshNeeded = false;
		this.started = false;
		this.startX = 0;
		this.startY = 0;
		this.x1 = 0;
		this.y1 = 0;
		this.x2 = 0;
		this.y2 = 0;
		
		/*Functions*/
		this.getBoundingBox = getBoundingBox;
		this.setBoundingBox = setBoundingBox;
		this.getRefreshNeeded = getRefreshNeeded;
		this.setRefreshNeeded = setRefreshNeeded;
		this.setGraphics = setGraphics;
		this.getGraphics = getGraphics;
		this.showArea = showArea;
		this.showCoordinates = showCoordinates;
		
		this.setStarted = setStarted;
		this.getStarted = getStarted;
		this.getStartX = getStartX;
		this.setStartX = setStartX;
		this.getStartY = getStartY;
		this.setStartY = setStartY;		
		this.getOffsetLeft = getOffsetLeft;
		this.setOffsetLeft = setOffsetLeft;
		this.getOffsetTop = getOffsetTop;
		this.setOffsetTop = setOffsetTop;
		this.getX1 = getX1;
		this.getY1 = getY1;
		this.getX2 = getX2;
		this.getY2 = getY2;
		this.setX1 = setX1;
		this.setY1 = setY1;
		this.setX2 = setX2;
		this.setY2 = setY2;
		
		this.mouseDoubleClick = mouseDoubleClick;
		this.mouseClick = mouseClick;
		this.mouseMove = mouseMove;
		
		var overviewDiv = getOverviewDiv();
		this.setGraphics(new jsGraphics(overviewDiv.id));  

		var txtNode = overviewDiv.nextSibling;			// try to locate a div to display text
		if (txtNode) txtNode = txtNode.firstChild;

		function showArea(bboxArea) {
			var overviewGraphics = this.getGraphics();
			overviewGraphics.clear();
			var overviewBoundingBox = this.getBoundingBox();
			var x1 = (bboxArea[0] - overviewBoundingBox.getMinX())/overviewBoundingBox.getPixelX();
			if (x1 < 0) x1 = 0;
			var x2 = (bboxArea[1] - overviewBoundingBox.getMinX())/overviewBoundingBox.getPixelX();
			if (x2 > overviewBoundingBox.getWidth()) x2 = overviewBoundingBox.getWidth();
			var y1 = (overviewBoundingBox.getMaxY() - bboxArea[3])/overviewBoundingBox.getPixelY();
			if (y1 < 0) y1 = 0;
			var y2 = (overviewBoundingBox.getMaxY() - bboxArea[2])/overviewBoundingBox.getPixelY();
			if (y2 > overviewBoundingBox.getHeight()) y2 = overviewBoundingBox.getHeight();
			if (x2 >= x1 && x1 >= 0 &&  
				y2 >= y1 && y1 >= 0 && 
				((x2-x1)/overviewBoundingBox.getWidth() <= 0.5 || 
				(y2-y1)/overviewBoundingBox.getHeight() <= 0.5)) {
				overviewGraphics.setStroke(2);
				overviewGraphics.setColor("#ff0000");
				overviewGraphics.drawLine(x1, y1, x2, y1);
				overviewGraphics.drawLine(x2, y1, x2, y2);
				overviewGraphics.drawLine(x2, y2, x1, y2);
				overviewGraphics.drawLine(x1, y2, x1, y1);
				overviewGraphics.paint();
				setX1(x1);
				setY1(y1);
				setX2(x2);
				setY2(y2);
			}
		}


		function showCoordinates(coordinates) {
			if (txtNode) {
				txtNode.nodeValue = "RD:" + coordinates;
			}
		}
		
		function mouseDoubleClick(e) {
			setStarted(false);
			return;
		}
			
		function mouseClick(e) {
			if (!e) var e = window.event;
			var xy = getMouseCoordinates(e);//getMouseCoordinatesWindow(e);
			if(getStarted()) {
				setStarted(false);
				if(getStartX() != 'undefined' && getStartY() != 'undefined') {
					var overview = getOverview();
					var overviewBoundingBox = overview.getBoundingBox();
					var bboxMap = getBBoxMap();
					var xmv = (getStartX() - xy[0])*overviewBoundingBox.getPixelX()/bboxMap.getPixelX();
					var ymv = (xy[1] - getStartY())*overviewBoundingBox.getPixelY()/bboxMap.getPixelY();
					var action = "action=pan&xMove="+xmv+"&yMove="+ymv;
					refreshMap(action);
					zoomChanged(0, getMapDiv().clientWidth, 0, getMapDiv().clientHeight);
				}
			} else if (e.type == 'mousedown'){
				// clicked coordinates must fall into the rectangle shown in the overview
				var leftTop = getOverviewDivLeftTop(e);
				if (xy[0] >= (getX1()+leftTop[0]) && xy[0] <= (getX2()+leftTop[0]) && xy[1] >= (getY1()+leftTop[1]) && xy[1] <= (getY2()+leftTop[1])) { 
					setOffsetLeft(0);
					setOffsetTop(0);				
					setStarted(true);
					setStartX(xy[0]);
					setStartY(xy[1]);	
				}
			}
			//Prevent event from bubbling up.
			return false;
		}
		
		function mouseMove(e) {
			if (!targetIsId(e, getOverviewDiv().id)) return false;
			var xy = getMouseCoordinates(e);
			var overview = getOverview();
			if(getStarted()) {
				var x1 = getX1() + (xy[0] - getStartX());
				var x2 = getX2() + (xy[0] - getStartX());
				var y1 = getY1() + (xy[1] - getStartY());
				var y2 = getY2() + (xy[1] - getStartY());
				var overviewGraphics = overview.getGraphics();
				overviewGraphics.clear();
				overviewGraphics.setStroke(2);
				overviewGraphics.setColor("#ff0000");
				overviewGraphics.drawLine(x1, y1, x2, y1);
				overviewGraphics.drawLine(x2, y1, x2, y2);
				overviewGraphics.drawLine(x2, y2, x1, y2);
				overviewGraphics.drawLine(x1, y2, x1, y1);
				overviewGraphics.paint();
			} else {
				var leftTop = getOverviewDivLeftTop(e);
				var overviewBoundingBox = overview.getBoundingBox();
				var coordString = "" + Math.round(((xy[0]-leftTop[0])*overviewBoundingBox.getPixelX()) + overviewBoundingBox.getMinX()) 
	    				+ " , " + Math.round(overviewBoundingBox.getMaxY() - (xy[1]-leftTop[1])*overviewBoundingBox.getPixelY());	
				showCoordinates(coordString);
			}
   			//Prevent event from bubbling up.
   			return false;
		}

		function setGraphics(j){
			this.jg = j;
		}
		function getGraphics(){
			return this.jg;
		}
		function setBoundingBox(b){
			this.bbox = b;
		}
		function getBoundingBox(){
			return this.bbox;
		}
		function setRefreshNeeded(r){
			this.refreshNeeded = r;
		}
		function getRefreshNeeded(){
			return this.refreshNeeded;
		}
		function getOffsetLeft() {
			return this.offsetLeft;
		}
		function setOffsetLeft(o){
			this.offsetLeft = o;
		}
		function getOffsetTop() {
			return this.offsetTop;
		}
		function setOffsetTop(o){
			this.offsetTop = o;
		}
		function getStartX() {
			return this.startX;
		}
		function setStartX(x){
			this.startX = x;
		}
		function getStartY() {
			return this.startY;
		}
		function setStartY(y){
			this.startY = y;
		}
		function setStarted(s){
			this.started = s;
		}
		function getStarted(){
			return this.started;
		}
		function getX1(){
			return this.x1;
		}
		function getY1(){
			return this.y1;
		}
		function getX2(){
			return this.x2;
		}
		function getY2(){
			return this.y2;
		}
		function setX1(x){
			this.x1 = x;
		}
		function setY1(y){
			this.y1 = y;
		}
		function setX2(x){
			this.x2 = x;
		}
		function setY2(y){
			this.y2 = y;
		}
	}
	
	/*Object identify*/
	function IdentifyState() {
		/*Variables*/
		/*--None--*/
		
		/*Functions*/		
		this.mouseDoubleClick = mouseDoubleClick;
		this.mouseClick = mouseClick;
		this.mouseMove = mouseMove;
		
		function mouseDoubleClick(e) {
			return;
		}
			
		function mouseClick(e) {
			if (!e) var e = window.event;
			if (e.type == 'mousedown'){
				var xyOnMap = getMouseCoordinates(e);
				identifyMapImage(xyOnMap[0], xyOnMap[1], getIdProperty(), getOnIdentify());
			}
			return;
		}
		
		function mouseMove(e) {
			showCoordinates(e);
			return;
		}
	}
	
	/*Object idle*/
	function IdleState() {
		/*Variables*/
		/*--None--*/
		
		/*Functions*/		
		this.mouseDoubleClick = mouseDoubleClick;
		this.mouseClick = mouseClick;
		this.mouseMove = mouseMove;
		
		function mouseDoubleClick(e) {
			return;
		}
			
		function mouseClick(e) {
			return;
		}
		
		function mouseMove(e) {
			showCoordinates(e);
			return false;
		}
	}
	
	/*Object zoomIn*/
	function ZoomInState() {
		/*Variables*/
		this.started = false;
		this.startX = 0;
		this.startY = 0;
		this.startXMap = 0;
		this.startYMap = 0;
		

		/*Functions*/						
		this.setStarted = setStarted;
		this.getStarted = getStarted;
		this.getStartX = getStartX;
		this.setStartX = setStartX;
		this.getStartY = getStartY;
		this.setStartY = setStartY;
		this.getStartXMap = getStartXMap;
		this.setStartXMap = setStartXMap;
		this.getStartYMap = getStartYMap;
		this.setStartYMap = setStartYMap;
		
		this.mouseDoubleClick = mouseDoubleClick;
		this.mouseClick = mouseClick;
		this.mouseMove = mouseMove;
		//this.mouseMoveOverSelectionBox = mouseMoveOverSelectionBox;
				
		function mouseDoubleClick(e) {
			setStarted(false);
			stopBox(e);
			return;
		}
			
		function mouseClick(e) {
			if (!e) var e = window.event;
			var xy = getMouseCoordinatesWindow(e);
			var xyOnMap = getMouseCoordinates(e);
			if(getStarted()) {
				setStarted(false);
				stopBox(e);
				var action = "action=zoomin&minx=" + getMinX() + "&maxx=" + getMaxX() + "&miny=" + getMinY() + "&maxy=" + getMaxY();
				refreshMap(action);
				zoomChanged(getMinX(), getMaxX(), getMinY(), getMaxY());
			} else if (e.type == 'mousedown'){
				setStarted(true);
				setStartX(xy[0]);
				setStartY(xy[1]);	
				setStartXMap(xyOnMap[0]);
				setStartYMap(xyOnMap[1]);		
				startBox(xy);
				
				// Make sure events still reach this function when the 
				// mouse is hovering over the selectionbox.
				getSelectionDiv().onmouseup = mouseClick;
			}		
			return false;
		}
		
		function mouseMove(e) {
			var xy = getMouseCoordinatesWindow(e);
			var xyOnMap = getMouseCoordinates(e);
			if(getStarted()) {			
				var minx = Math.min(xy[0], getStartX());
				var miny = Math.min(xy[1], getStartY());
				var maxx = Math.max(xy[0], getStartX());
				var maxy = Math.max(xy[1], getStartY());
				var width = Math.abs(xy[0] - getStartX());
				var height = Math.abs(xy[1] - getStartY());
				
				/*Fix mouse over selection box*/
				if(width > 5) {
					width = width - 4;
				}
				if(height > 5) {
					height = height - 4;
				}
				
				if(xyOnMap[0] < getMapDiv().clientWidth
					&& xyOnMap[1] < getMapDiv().clientHeight) {								
					getSelectionDiv().style.left = minx + "px";
					getSelectionDiv().style.top = miny + "px";
					getSelectionDiv().style.width = width + "px";
					getSelectionDiv().style.height = height + "px";

					var minxMap = Math.min(xyOnMap[0], getStartXMap());
					var minyMap = Math.min(xyOnMap[1], getStartYMap());
					var maxxMap = Math.max(xyOnMap[0], getStartXMap());
					var maxyMap = Math.max(xyOnMap[1], getStartYMap());
					var widthMap = Math.abs(xyOnMap[0] - getStartXMap());
					var heightMap = Math.abs(xyOnMap[1] - getStartYMap());	

					setMinX(minxMap);
					setMaxX(maxxMap);
					setMinY(minyMap);
					setMaxY(maxyMap);
				}
				showMapXY();
			} else {
				showCoordinates(e);
			}
			return false;
		}		
		function getStartX() {
			return this.startX;
		}
		function setStartX(x){
			this.startX = x;
		}
		function getStartY() {
			return this.startY;
		}
		function setStartY(y){
			this.startY = y;
		}		
		function getStartXMap() {
			return this.startXMap;
		}
		function setStartXMap(x){
			this.startXMap = x;
		}
		function getStartYMap() {
			return this.startYMap;
		}
		function setStartYMap(y){
			this.startYMap = y;
		}		
		function setStarted(s){
			this.started = s;
		}
		function getStarted(){
			return this.started;
		}		
	}
	
	/*Object zoomOut*/
	function ZoomOutState() {
	}
	
	/*Object zoomFullExtent*/
	function ZoomFullExtentState() {
	}
	
	/*Object pan*/
	function PanState() {
		this.started = false;
		this.startX = 0;
		this.startY = 0;
		
		this.setStarted = setStarted;
		this.getStarted = getStarted;
		this.getStartX = getStartX;
		this.setStartX = setStartX;
		this.getStartY = getStartY;
		this.setStartY = setStartY;		
		this.getOffsetLeft = getOffsetLeft;
		this.setOffsetLeft = setOffsetLeft;
		this.getOffsetTop = getOffsetTop;
		this.setOffsetTop = setOffsetTop;
		
		this.mouseDoubleClick = mouseDoubleClick;
		this.mouseClick = mouseClick;
		this.mouseMove = mouseMove;
		
		function mouseDoubleClick(e) {
			setStarted(false);
			return;
		}
			
		function mouseClick(e) {
			if (!e) var e = window.event;
			var xy = getMouseCoordinatesWindow(e);
			if(getStarted()) {
				setStarted(false);
				if(getOffsetLeft() != 'undefined' && getOffsetTop() != 'undefined') {
					var div = getMapDiv();
					div.style.left =  getOffsetLeft() + "px";
					div.style.top =  getOffsetTop() + "px";
					
					var xmv = xy[0] - getStartX();
					var ymv = 0-(xy[1] - getStartY());
					var action = "action=pan&xMove="+xmv+"&yMove="+ymv;
					refreshMap(action);
					zoomChanged(0, getMapDiv().clientWidth, 0, getMapDiv().clientHeight);
				}
			} else if (e.type == 'mousedown'){
				setOffsetLeft(0);
				setOffsetTop(0);				
				setStarted(true);
				setStartX(xy[0]);
				setStartY(xy[1]);	
			}
			//Prevent event from bubbling up.
			return false;
		}
		
		function mouseMove(e) {
			var xy = getMouseCoordinatesWindow(e);
			if(getStarted()) {
				var div = getMapDiv();
				var left = getOffsetLeft() + (xy[0] - getStartX());
				var top = getOffsetTop() + (xy[1] - getStartY());
				div.style.left =  left + "px";
				div.style.top =  top + "px";
			} else {
				showCoordinates(e);
			}
   			//Prevent event from bubbling up.
   			return false;
		}

		function getOffsetLeft() {
			return this.offsetLeft;
		}
		function setOffsetLeft(o){
			this.offsetLeft = o;
		}
		function getOffsetTop() {
			return this.offsetTop;
		}
		function setOffsetTop(o){
			this.offsetTop = o;
		}
		function getStartX() {
			return this.startX;
		}
		function setStartX(x){
			this.startX = x;
		}
		function getStartY() {
			return this.startY;
		}
		function setStartY(y){
			this.startY = y;
		}
		function setStarted(s){
			this.started = s;
		}
		function getStarted(){
			return this.started;
		}
	}

	/*Object polyline*/
	function PolyLineState() {
		/*Variables*/
		this.jg = null;
		this.started = false;
		this.coords = null;
		this.polygonSelection = false;
		this.add = true;
		this.snapToObject = false;
		
		/*Functions*/		
		this.mouseDoubleClick = mouseDoubleClick;
		this.mouseClick = mouseClick;
		this.mouseMove = mouseMove;
		
		this.setGraphics = setGraphics;
		this.getGraphics = getGraphics;
		this.setStarted = setStarted;
		this.getStarted = getStarted;
		this.getCoords = getCoords;
		this.setCoords = setCoords;
		this.setPolygonSelection = setPolygonSelection;
		this.getPolygonSelection = getPolygonSelection;
		this.setSnapToObject = setSnapToObject;
		this.getSnapToObject = getSnapToObject;
		this.cancel = cancel;
		this.removeLast = removeLast;
		this.paint = paint;
		this.drawLine = drawLine;
		this.clearLine = clearLine;
		this.clipPolyLine= clipPolyLine;
		this.getFirstCoordLastPolygon = getFirstCoordLastPolygon;
		this.getRealClippedCoordinates = getRealClippedCoordinates;
		this.polygonDrawn = polygonDrawn;
		this.drawPolygonFromCoordinates = drawPolygonFromCoordinates;
		
		var cnv = getMapDiv();
		setCoords(new Array());
		setGraphics(new jsGraphics(cnv.id));  //"map"));
		onZoomChange(clipPolyLine);
		
		// Coordinates notification
		this.onNewPoint = onNewPoint;
		this.onPointCallback = null;
		this.getOnNewPoint = getOnNewPoint;
		
		/*Register callback*/
		function onNewPoint(callback) {
			onPointCallback = callback;
		}
		
		/*Gets reference to the callback*/
		function getOnNewPoint() {
			return onPointCallback;
		}
		
		function mouseDoubleClick(e) {
			if (!e) var e = window.event;
			if (getPolygonSelection()) {
				// this will close the polygon
				if(this.onStartOfActionCallback) {
					this.onStartOfActionCallback();
				}
				var coords = getCoords();
				if (!e.shiftKey) setAdd(false);
				var firstIdx = getFirstCoordLastPolygon(coords.length-2);
				if (firstIdx >= 0) {
					var firstCoord = coords[firstIdx];
					if (firstCoord) {
						coords.push(firstCoord);
						paint(true);
						var callback = getOnNewPoint();
						if(callback) {
							callback(firstCoord, getActiveLayer());
						}
					}
				}
				if(this.onEndOfActionCallback) {
					this.onEndOfActionCallback();
				}
				return false;
			}
			return;
		}
			
		function mouseClick(e) {
			if (!e) var e = window.event;
			var xyOnMap = getMouseCoordinates(e);
			// FF-bug : check the xy coordinates; both must be > 0
			if (e.type == 'mouseup' && xyOnMap[0] > 0 && xyOnMap[1] > 0) {
				if(this.onStartOfActionCallback) {
					this.onStartOfActionCallback();
				}
				var coords = getCoords();
				if (e.shiftKey) {
					if (coords.length == 0) setAdd(true);
				} else {
					setAdd(false);
				}
				if (polygonDrawn()) {
					// clear previous polygon when shift key is not pressed
					if (!e.shiftKey) clearLine(); 
				}
				var newpoint = null;
				if (getSnapToObject()) {
					newpoint = identifyMapImage(xyOnMap[0], xyOnMap[1], getIdProperty(), null, true);
					if(newpoint == ""){
						alert(getObjectSelectionWarning());
				        if(this.onEndOfActionCallback) {
					        this.onEndOfActionCallback();
				        }
						return false;
					}
					coords.push(newpoint.substring(newpoint.indexOf(',')+1));
				} else {
					var coord = "" + xyOnMap[0] + "," + xyOnMap[1];
					var rwCoord = getRealWorldCoordinates(coord);
					rwCoord = rwCoord.replace(";", "");
					if (coords.length > 0 && coords[coords.length-1] == rwCoord) {
					    // don't add the same point twice
				        if(this.onEndOfActionCallback) {
					        this.onEndOfActionCallback();
				        }
						return false;
					}
					coords.push(rwCoord);
					newpoint = rwCoord;
				}
				paint(true);
				var callback = getOnNewPoint();
				if(callback) {
					callback(newpoint, getActiveLayer());
				}
				if(this.onEndOfActionCallback) {
					this.onEndOfActionCallback();
				}
			}
			return false;
		}
		
		function mouseMove(e) {
			showCoordinates(e);
			return false;
		}
		
		/*Cancels drawing the line*/
		function cancel() {
			if(getPolygonSelection()) {
				// reset a selection
				polygonSelect(true);
			}
			clearLine();
		}
		
		/*Clear the line*/
		function clearLine() {
			var coords = getCoords();
			while(coords.length > 0) {
				coords.pop();
			}
			var jg = getGraphics();
			jg.clear();
		}
		
		/*Removes the last drawn line*/
		function removeLast() {
			// or repaint.....
			if(this.onStartOfActionCallback) {
				this.onStartOfActionCallback();
			}
			if(getPolygonSelection()) {
				// reset a selection
				polygonSelect(true);
			}
			var jg = getGraphics();
			jg.clear();
			
			var coords = getCoords();
			//coords.pop();
			if(coords.length > 0) {
				if (coords[coords.length - 1].length < 2) {
					coords.splice(coords.length - 1, 1);
				}
				coords.splice(coords.length - 1, 1);
			}
			paint(true);
			if(this.onEndOfActionCallback) {
				this.onEndOfActionCallback();
			}
		}

		/*Paints all the lines stored.*/
		function paint(selectPolygon) {
			if(getStarted()) return;
			setStarted(true);

			var coords = getCoords();
			var realCoords = getRealClippedCoordinates(coords);

			if (realCoords != '') {
				var pxCoords = getPixelCoordinates(realCoords);
				if (pxCoords.length > 0) {
					pxCoords = pxCoords.substring(0,pxCoords.length - 1); // remove last semicolon
				}
				coords = pxCoords.split(";");
	
				
				for ( var i = 0; i < coords.length; i++) {
					var pointOrigin = coords[i].split(",");
					var coordsDest = null;
					if (i < coords.length - 1 || getPolygonSelection()) {
						if (i >= coords.length - 1 || coords[i+1].length < 2) {
							var firstIdx = getFirstCoordLastPolygon(i-1);
							if (firstIdx >= 0) {
								coordsDest = coords[firstIdx];
							}
						} else {
							coordsDest = coords[i+1];
						}
					}
					if (coordsDest && pointOrigin.length == 2) {
						var pointDest = coordsDest.split(",");
	
						if (pointDest.length == 2) {
							
								var coord1 = pointOrigin;
								var coord2 = pointDest;
								
								if (i < coords.length - 1) {
									drawLine(coord1, coord2);
								} else if (i > 0 && 
											coordinatesEqual(coord1,coord2) && 
											selectPolygon) {
									polygonSelect(false);
								}
						}
					} else if (pointOrigin.length == 2 && 
								(i == 0 || i < coords.length - 1 || getPolygonSelection())) {
						drawCross(pointOrigin);
					}
				}
			}
			setStarted(false);
		}
		
		/* Gets a string of real coordinates clipped to the current bounding box */
		function getRealClippedCoordinates(coords) {
			var bbox = getBBoxMap().getBox(); 
			var realCoords = '';
			var i=0;
			while (i < coords.length) {
				if (coords.length > 1 && i < coords.length-1) {
					var rwPointOrigin = coords[i].split(",");
					var rwPointDest = coords[i+1].split(",");
					if (rwPointOrigin.length == 2 && rwPointDest.length == 2) {
						// clip the coordinates to the bounding box
						var clippedCoords = clip(new Number(rwPointOrigin[0]), 
								new Number(rwPointOrigin[1]), new Number(rwPointDest[0]),
								new Number(rwPointDest[1]), bbox[0], bbox[1], bbox[2],
								bbox[3]);
						if(clippedCoords[0] == true) {
							realCoords = realCoords + (i > 0 ? ";" : "") + clippedCoords[1] +
												"," + clippedCoords[2];
							realCoords = realCoords + ";" + clippedCoords[3] + "," + clippedCoords[4];
							i+=2
						} else {
							realCoords = realCoords + (i > 0 ? ";" : "") + coords[i];
							realCoords = realCoords + ";" + coords[i+1];
							i+=2
						}
					} else {
						realCoords = realCoords + (i > 0 ? ";" : "") + coords[i];
						realCoords = realCoords + ";" + coords[i+1];
						i+=2
					}
				} else {
					realCoords = realCoords + (i > 0 ? ";" : "") + coords[i];
					i++;
				}
			}
			return realCoords;
		}
		
		/*Draw a single line, using pixel coordinates*/
		function drawLine(coord1, coord2) {
			if(!coord1 || !coord2) {
				return;
			}			
			try{
				var x1 = parseInt(coord1[0]);
				var y1 = parseInt(coord1[1]);
				var x2 = parseInt(coord2[0]);
				var y2 = parseInt(coord2[1]);
				jg.setColor("#ff0000");
				jg.drawLine(x1, y1, x2, y2);
				jg.paint();
			} catch (err) {
				alert("Unable to draw line: " + err);
			}
		}

		/*Draw a cross, using pixel coordinates*/
		function drawCross(coord) {
			if(!coord) {
				return;
			}			
			try{
				var x1 = parseInt(coord[0])-5;
				var y1 = parseInt(coord[1])-5;
				var x2 = x1 + 10;
				var y2 = y1 + 10;
				jg.setColor("#ff0000");
				jg.drawLine(x1, y1, x2, y2);
				jg.drawLine(x1, y2, x2, y1);
				jg.paint();
			} catch (err) {
				alert("Unable to draw line: " + err);
			}
		}

		/*Check coordinates on equality with a certain margin*/
		function coordinatesEqual(coord1, coord2) {
			if(!coord1 || !coord2) {
				return false;
			}			
			var x1 = parseInt(coord1[0]);
			var y1 = parseInt(coord1[1]);
			var x2 = parseInt(coord2[0]);
			var y2 = parseInt(coord2[1]);
			var margin = 5;
			if (x1 < x2 - margin || x1 > x2 + margin) return false;
			if (y1 < y2 - margin || y1 > y2 + margin) return false;
			return true;
		}
		
		function polygonSelect(reset) {
			var realCoords = "";
			if (!reset) {
				var coords = getCoords();
				var addterminator = false;
				// only send the coordinates of the last drawn polygon to the server
				if (coords.length < 3) return;
				var i = getFirstCoordLastPolygon(coords.length-2);
				var startI = i;
				if (coords.length - i < 3) return;
				for ( ; i < coords.length; i++) {
					realCoords = realCoords + (i > startI ? ";" : "") + coords[i];
					if (i == (coords.length - 1) && coords[i].length > 1) addterminator = true;
				}
				// the coordinates of different polygons are separated by
				// an empty element in the coordinate array; 
				// add one if necessary
				if (addterminator) coords.push("");
			}
			var action = "action=select&realCoords=" + realCoords
						+ "&layer=" + getActiveLayer();
			if (getAdd()) {
				// add the polygon to the existing selection, otherwise the selection is cleared first
				action = action + "&add";
			}
			refreshMap(action);
			identifyMapImage("", "", getSelectProperty(), getOnSelect());
			setAdd(true);
		}
		
		function clipPolyLine(minx, maxx, miny, maxy) {
			var jg = getGraphics();
			jg.clear();
			paint(true);
		}
		
		function getFirstCoordLastPolygon(start) {
			// when more polygons are selected find the index of the start coordinate
			// of the last drawn polygon before the given start-index
			// the coordinates of different polygons are separated by
			// an empty element in the coordinate array
			if (start < 0) return -1;
			var coords = getCoords();
			if (coords.length == 0) return -1;
			for ( var i = start; i >= 0; i--) {
				if (coords[i].length <= 1) {
					if (i == start) return -1;
					return i+1;
				}
			}
			return 0;
		}
		
		function polygonDrawn() {
			// returns true when at least one polygon has been drawn;
			// so there must be a separator element present in coordinates array
			var coords = getCoords();
			if (coords.length == 0) return false;
			for ( var i = 0; i < coords.length; i++) {
				if (coords[i].length <= 1) return true;
			}
			return false;
		}
		
		function drawPolygonFromCoordinates(newCoords) {
			var coords = getCoords();
			clearLine();
			for ( var i = 0; i < newCoords.length; i++) {
				coords.push(newCoords[i]);
			}
			paint(false);
		}
		
		function setGraphics(j){
			this.jg = j;
		}
		function getGraphics(){
			return this.jg;
		}
		function setCoords(s){
			this.coords = s;
		}
		function getCoords(){
			return this.coords;
		}		
		function setStarted(s){
			this.started = s;
		}
		function getStarted(){
			return this.started;
		}
		function setPolygonSelection(s){
			polygonSelection = s;
		}
		function getPolygonSelection(){
			return this.polygonSelection;
		}
		function setSnapToObject(s){
			snapToObject = s;
		}
		function getSnapToObject(){
			return this.snapToObject;
		}
		function setAdd(s){
			add = s;
		}
		function getAdd(){
			return this.add;
		}
	}
	
	function SelectionState() {
		/*Variables*/
		this.started = false;
		this.startX = 0;
		this.startY = 0;
		this.startXMap = 0;
		this.startYMap = 0;
		
		/*Functions*/						
		this.setStarted = setStarted;
		this.getStarted = getStarted;
		this.getStartX = getStartX;
		this.setStartX = setStartX;
		this.getStartY = getStartY;
		this.setStartY = setStartY;
		this.getStartXMap = getStartXMap;
		this.setStartXMap = setStartXMap;
		this.getStartYMap = getStartYMap;
		this.setStartYMap = setStartYMap;
		
		/*Functions*/		
		this.mouseDoubleClick = mouseDoubleClick;
		this.mouseClick = mouseClick;
		this.mouseMove = mouseMove;
		
		function mouseDoubleClick(e) {
			setStarted(false);
			stopBox(e);
			return;
		}
			
		function mouseClick(e) {
			if (!e) var e = window.event;
			var xy = getMouseCoordinatesWindow(e);
			var xyOnMap = getMouseCoordinates(e);
			if(getStarted()) {
				setStarted(false);
				stopBox(e);
				var action = "action=select&layer=" + getActiveLayer();
				if (getMinX() > 0) action = action + "&minx=" + getMinX();
				if (getMaxX() > 0) action = action + "&maxx=" + getMaxX();
				if (getMinY() > 0) action = action + "&miny=" + getMinY();
				if (getMaxY() > 0) action = action + "&maxy=" + getMaxY();
				if (e.shiftKey) {
					action = action + "&add";
				} else {
					// clear a polygon
					polyLineClear();
				}
				refreshMap(action);
				identifyMapImage("", "", getSelectProperty(), getOnSelect());			
				
			} else if (e.type == 'mousedown'){
				if(!getActiveLayer()){
					alert(getActivationWarning());
					return false;
				}			
				setStarted(true);
				setStartX(xy[0]);
				setStartY(xy[1]);	
				setStartXMap(xyOnMap[0]);
				setStartYMap(xyOnMap[1]);		
				startBox(xy, 'selectBox');
				
				// init x and y of selected rectangle
				setMinX(0);
				setMaxX(0);
				setMinY(0);
				setMaxY(0);
				
				// Make sure events still reach this function when the 
				// mouse is hovering over the selectionbox.
				getSelectionDiv().onmouseup = mouseClick;
			}		
			return false;
		}
		
		function mouseMove(e) {
			var xy = getMouseCoordinatesWindow(e);
			var xyOnMap = getMouseCoordinates(e);
			if(getStarted()) {			
				var minx = Math.min(xy[0], getStartX());
				var miny = Math.min(xy[1], getStartY());
				var maxx = Math.max(xy[0], getStartX());
				var maxy = Math.max(xy[1], getStartY());
				var width = Math.abs(xy[0] - getStartX());
				var height = Math.abs(xy[1] - getStartY());
				
				/*Fix mouse over selection box*/
				if(width > 5) {
					width = width - 4;
				}
				if(height > 5) {
					height = height - 4;
				}
				
				if(xyOnMap[0] < getMapDiv().clientWidth
					&& xyOnMap[1] < getMapDiv().clientHeight) {								
					getSelectionDiv().style.left = minx + "px";
					getSelectionDiv().style.top = miny + "px";
					getSelectionDiv().style.width = width + "px";
					getSelectionDiv().style.height = height + "px";

					var minxMap = Math.min(xyOnMap[0], getStartXMap());
					var minyMap = Math.min(xyOnMap[1], getStartYMap());
					var maxxMap = Math.max(xyOnMap[0], getStartXMap());
					var maxyMap = Math.max(xyOnMap[1], getStartYMap());
					var widthMap = Math.abs(xyOnMap[0] - getStartXMap());
					var heightMap = Math.abs(xyOnMap[1] - getStartYMap());	

					setMinX(minxMap);
					setMaxX(maxxMap);
					setMinY(minyMap);
					setMaxY(maxyMap);
				}
				showMapXY();
			} else {
				showCoordinates(e);
			}	
			return false;
		}
		function getStartX() {
			return this.startX;
		}
		function setStartX(x){
			this.startX = x;
		}
		function getStartY() {
			return this.startY;
		}
		function setStartY(y){
			this.startY = y;
		}		
		function getStartXMap() {
			return this.startXMap;
		}
		function setStartXMap(x){
			this.startXMap = x;
		}
		function getStartYMap() {
			return this.startYMap;
		}
		function setStartYMap(y){
			this.startYMap = y;
		}		
		function setStarted(s){
			this.started = s;
		}
		function getStarted(){
			return this.started;
		}
	}
	
	/*Start drawing a (selection) box*/
	function startBox(xy, className) {
		setSelectionDiv(document.createElement('div'));
		getSelectionDiv().id = "zoomInBox";
		if(!className) {
			getSelectionDiv().className = "zoomInBox";
		} else {
			getSelectionDiv().className = className;
		}
		getSelectionDiv().style.left = xy[0] + "px";
		getSelectionDiv().style.top = xy[1] + "px";
		getSelectionDiv().style.width = 0;
		getSelectionDiv().style.height = 0;
		//getSelectionDiv().innerHTML = "This is the area where we'll zoom in...";
		
		document.body.appendChild(getSelectionDiv());
	}
	
	/*Stop drawing a (selection) box*/
	function stopBox(e) {
		document.body.removeChild(getSelectionDiv());
		var div = document.getElementById(getSelectionDiv().id);
		if(div) {
			document.body.removeChild(getSelectionDiv());
		}		
	}
	
	/*Moves a layer up*/
	function moveLayerUp(layerId) {
		if(layerId) {
			var action = "action=movelayerup&layer=" + layerId;
			refreshMap(action);
			refreshTocForMap(this);
		}
	}

	function moveLayerToTop(layerId) {
		if(layerId) {
			var action = "action=movelayertotop&layer=" + layerId;
			refreshMap(action);
			refreshTocForMap(this);
		}
	}

	
	/*Moves a layer down*/
	function moveLayerDown(layerId) {
		if(layerId) {
			var action = "action=movelayerdown&layer=" + layerId;
			refreshMap(action);
			refreshTocForMap(this);
		}
	}
	
	/*Sets the wait image url*/
	function setWaitImageUrl(url) {
		waitImageUrl = url;
	}
	
	/*Gets the wait image url*/
	function getWaitImageUrl() {
		return waitImageUrl;
	}
	
	/*Refreshes the toc*/
	function refreshToc() {
		refreshTocForMap(this);
	}

	/*Refreshes the toc for the specified map*/
	function refreshTocForMap(thisMap) {
		if(!getMapUrl() || !getTocUrl()) {
			throw "No map or toc url was set.";
		}
		if(thisMap.onStartOfActionCallback) {
			thisMap.onStartOfActionCallback();
		}
		var url = getMapUrlUnique() + "&getTOC&url=" + getTocUrl(); 
		if (thisMap.showBackgroundLayers) {
			// include backgroundlayers in TOC
			url = url + "&showBackgroundLayers=true";
		}
		var xmlhttp = getXMLHttpRequest();
		xmlhttp.open("GET", url, true);
		xmlhttp.onreadystatechange=function() {
			if (xmlhttp.readyState==4) {
				getTocDiv().innerHTML = xmlhttp.responseText;
			}
			if(thisMap.onEndOfActionCallback) {
				thisMap.onEndOfActionCallback();
			}
		}
		xmlhttp.send(null);
	}
		
	/*Updates the map without getting a new image*/
	function updateMap(urlAction) {
		if(!urlAction) {
			return;
		}
		var url = getMapUrlUnique() + "&dontGetMapImage";
		url = url + "&" + urlAction;
		
		var xmlhttp = getXMLHttpRequest();
		xmlhttp.open("GET", url, false);
		xmlhttp.send(null);
	}
	
	/*Refreshes the map*/
	function refreshMap(urlAction) {
		if(this.onStartOfActionCallback) {
			this.onStartOfActionCallback();
		}
		waitImage(true); 
		
		// IE6 support : because of IE6 behaviour that causes the loading of the image 
		// to be performed twice, the urlAction is executed in a seperate call to the server
		if(urlAction) {
			updateMap(urlAction);
		}
		
		var img = new Image;
		var thisMap = this;
		img.onload = function() {refreshMapImage(thisMap,img.src);}
		img.onerror = function() {refreshMapImageFailed(thisMap,"Failed to refresh this map. Try refreshing the page or selecting a map.",getMapDiv());}
		img.onabort = function() {refreshMapImageFailed(thisMap,"Aborted refreshing map.",getMapDiv());}
		
		var defaultAction = "&getImage&width="+getMapDiv().clientWidth+"&height="+getMapDiv().clientHeight;
		
		// IE6 support
		//if(!urlAction) {
		//	urlAction = defaultAction;
		//}
		//else {
		//	urlAction = defaultAction + "&" + urlAction;
		//}		
		//img.src = getMapUrlUnique() + urlAction;
		img.src = getMapUrlUnique() + defaultAction;
	}
	
	/*Shows the loading image*/
	function waitImage(show) {
		waitImageForDiv(show, getMapDiv())
	}
	
	/*Shows the loading image for a div*/
	function waitImageForDiv(show, div) {
		if(show) {
			div.style.background = "url(" + getWaitImageUrl()+")";
			div.style.backgroundRepeat = "no-repeat";
			div.style.backgroundPosition = "center";
		} else {
			div.style.background = "";
		}
	}
	
	/*Sets the image url on the map div*/
	function refreshMapImage(thisMap,url) {
		waitImage(false); 
		getMapDiv().style.background = "url("+url+")";
		getMapDiv().style.backgroundRepeat = "no-repeat";
		if(thisMap.onEndOfActionCallback) {
			thisMap.onEndOfActionCallback();
		}
		var bbox = new BoundingBox(getMapDiv().clientWidth, getMapDiv().clientHeight);// also recalculates the scale of this map
		setBBoxMap(bbox);		
		if (overviewRefreshNeeded) {
			refreshOverview(thisMap);
			overviewRefreshNeeded = false;
		} else {
			getOverview().showArea(getBBoxMap().getBox());
		}
		
		if (getLegendDiv()) refreshLegend();
	}
	
	/*Shows an error message*/
	function refreshMapImageFailed(thisMap,msg,div) {
		waitImageForDiv(false, div);
		if(thisMap.onEndOfActionCallback) {
			thisMap.onEndOfActionCallback();
		}
		alert(msg);
	}

	/*Refreshes the legend*/
	function refreshLegend() {
		waitImageForDiv(true, getLegendDiv() );
		
		var div = getLegendDiv();
		var oChild=document.getElementById('legendImage');	
		if (oChild) div.removeChild(oChild);
		
		var img = new Image;
		var thisMap = this;
		img.onload = function() {refreshLegendImage(thisMap,img.src);}
		img.onerror = function() {refreshMapImageFailed(thisMap,"Failed to refresh this legend. Try refreshing the page or selecting a map.",getLegendDiv());}
		img.onabort = function() {refreshMapImageFailed(thisMap,"Aborted refreshing legend.",getLegendDiv());}
		
		var urlAction = "&getLegend&width="+getLegendDiv().clientWidth+"&height="+getLegendDiv().clientHeight;
		if (getActiveLayer()) urlAction = urlAction + "&layer=" + getActiveLayer();
		img.src = getMapUrlUnique() + urlAction;
		img.id = 'legendImage';
		div.appendChild(img);
	}

	/*Sets the image url on the legend div*/
	function refreshLegendImage(thisMap,url) {
		waitImageForDiv(false,getLegendDiv()); 
//		getLegendDiv().style.background = "url("+url+")";
//		getLegendDiv().style.backgroundRepeat = "no-repeat";
	}
	
	/*Refreshes the overview*/
	function refreshOverview(thisMap) {
		var img = new Image;
		img.onload = function() {refreshOverviewImage(thisMap,img.src);}
		img.onerror = function() {refreshMapImageFailed(thisMap,"Failed to refresh the overview map. Try refreshing the page or selecting a map.",getOverviewDiv());}
		img.onabort = function() {refreshMapImageFailed(thisMap,"Aborted refreshing legend.",getOverviewDiv());}
		
		var urlAction = "&getImage&resetBoundingBox&savePrevious&width="+getOverviewDiv().clientWidth+"&height="+getOverviewDiv().clientHeight;
		img.src = getMapUrlUnique() + urlAction;
	}

	/*Sets the image url on the overview div*/
	function refreshOverviewImage(thisMap,url) {
		getOverviewDiv().style.background = "url("+url+")";
		getOverviewDiv().style.backgroundRepeat = "no-repeat";
		
		var overview = getOverview();
		var bbox = new BoundingBox(getOverviewDiv().clientWidth, getOverviewDiv().clientHeight);
		overview.setBoundingBox(bbox);
		
		// restore the bounding box and width and height because the overview request will reset the bounding box and
		// use other width and height
		updateMap("restorePreviousBoundingBox&width="+getMapDiv().clientWidth+"&height="+getMapDiv().clientHeight);
		
		// show the initial extent on the overview map
		overview.showArea(getBBoxMap().getBox());
	}
	
	
	/*Performs identification of a point on the map*/
	function identifyMapImage(x, y, idProperty, callbackFunction, closest, nowarning) {
		var ac = getActivationEnabled();
		if(ac && !getActiveLayer()){
			if (nowarning) return;
			alert(getActivationWarning());
			return;
		}		
		var xmlhttp = getXMLHttpRequest();
	  	if(xmlhttp) {
			if(this.onStartOfActionCallback) {
				this.onStartOfActionCallback();
			}
	  		var idLayer = "*";
	  		if(getActiveLayer()) {
	  			idLayer = getActiveLayer();
	  		}
		    var url = getMapUrlUnique() + "&identify&property="+ idProperty +"&x="+x+"&y="+y+"&layer=" + idLayer;
		    if (closest) url = url + "&closest";
		    var async = false;
			if(callbackFunction) async = true;
		    xmlhttp.open("GET", url, async);
		    var thisMap = this;
		    if (async) {
		    	// asynchronous call : define a callback function
				xmlhttp.onreadystatechange = function() {
				  if (xmlhttp.readyState==4) {
					if(callbackFunction) {
						callbackFunction(xmlhttp.responseText, getActiveLayer());
					}
				  }
				  if(thisMap.onEndOfActionCallback) {
					  thisMap.onEndOfActionCallback();
				  }
				 }
			 }
			 xmlhttp.send(null);
			 if (!async) {
				 // synchronous call : return the response
				 if (xmlhttp.status == 200) {
					return xmlhttp.responseText;
				 } else {
					throw "Unable to identify an object";
				 }		
			 }
		}    
	}
	
	function drawSelectionAreas() {
		var xmlhttp = getXMLHttpRequest();
	  	if(xmlhttp) {
		    var url = getMapUrlUnique() + "&getSelectionCoordinates";
		    xmlhttp.open("GET", url, false);
			xmlhttp.send(null);
			if (xmlhttp.status == 200) {
				var coordString = xmlhttp.responseText;
				if (coordString != "") {
					var bbox = new BoundingBox(getMapDiv().clientWidth, getMapDiv().clientHeight);
					setBBoxMap(bbox);
					var state = getPolyLineState();
					var newCoords = coordString.split(";");
					state.drawPolygonFromCoordinates(newCoords);
				}
			}		
		}    
	}
	
	/*Adds a layer to the map, updates the map, no refresh is performed*/
	function addLayer(layerId) {
		if(layerId) {
			var action = "action=addLayer&layer=" + layerId;
			updateMap(action);
		}
	}
	
	/*Adds a layer to the map, updates the map, no refresh of the map is performed*/
	function setLayer(layerId) {
		if(layerId) {
			var action = "action=setLayer&keepDuplicate&layer=" + layerId;
			updateMap(action);
			overviewRefreshNeeded = true;

		}
	}
	
	/*Removes a layer from the map*/
	function removeLayer(layerId) {
		if(layerId) {
			var action = "action=removeLayer&layer=" + layerId;
			refreshMap(action);
			refreshTocForMap(this);
		}
	}
		
	/*Register callback*/
	function onSelect(callback, property) {
		onSelectCallback = callback;
		this.setSelectProperty(property);
	}
	
	/*Gets reference to the callback*/
	function getOnSelect() {
		return onSelectCallback;
	}
	
	/*Register callback*/
	function onIdentify(callback, property) {
		onIdentifyCallback = callback;
		this.setIdProperty(property);
	}
	
	/*Gets reference to the callback*/
	function getOnIdentify() {
		return onIdentifyCallback;
	}
	
	/*Gets the property to look for when identifying*/
	function getIdProperty() {
		return onIdentifyProperty;
	}
	
	/*Sets the property to look for when identifying*/
	function setIdProperty(property) {
		onIdentifyProperty = property;
	}
	
	/*Gets the property to look for when selecting*/
	function getSelectProperty() {
		return onSelectProperty;
	}
	
	/*Sets the property to look for when selecting*/
	function setSelectProperty(property) {
		onSelectProperty = property;
	}
	
	/*Returns true when onSelectProperty is undefined*/
	function isSelectPropertyUndefined() {
		return (typeof(onSelectProperty)=="undefined");
	}
	
	/*Sets the toc servlet url*/
	function setTocUrl(url) {
		tocUrl = url;
	}
	
	/*Gets the toc servlet url*/
	function getTocUrl() {
		return tocUrl;
	}
	
	/*Sets the map servlet url*/
	function setMapUrl(url) {
		mapUrl = url;
	}
	
	/*Gets the map servlet url*/
	function getMapUrl() {
		return mapUrl;
	}
	
	/*Gets the map servlet url + unique identifier, so it cannot be found in cache*/
	function getMapUrlUnique() {
		return getMapUrl() + "?currentTime=" + (new Date()).getTime();
	}
	
	/*Gets min X*/
	function getMinX(){
		return this.minX;
	}
	
	/*Gets max X*/
	function getMaxX(){
		return this.maxX;
	}																																														
	
	/*Gets min Y*/
	function getMinY(){
		return this.minY;
	}
	
	/*Gets max Y*/
	function getMaxY(){
		return this.maxY;
	}	
	
	/*Sets min X*/
	function setMinX(x){
		this.minX = x;
	}
	
	/*Sets max X*/
	function setMaxX(x){
		this.maxX = x;
	}
	
	/*Sets min Y*/
	function setMinY(y){
		this.minY = y;
	}
	
	/*Sets max Y*/
	function setMaxY(y){
		this.maxY = y;
	}
	
	/*Gets boundingbox of map */
	function getBBoxMap() {
		return this.bBoxMap;
	}
	
	/*Sets boundingbox of map */
	function setBBoxMap(bb) {
		this.bBoxMap = bb;
	}
	
	/*Gets selection div*/
	function getSelectionDiv() {
		return this.selectionDiv;
	}
	
	/*Sets the selection div*/
	function setSelectionDiv(d) {
		this.selectionDiv = d;
	}
	
	/*Gets the name of the activated layer (selection & identify)*/
	function getActiveLayer() {
		return this.activeLayer;
	}
	
	/*Sets the name of the activated layer (selection & identify)*/
	function setActiveLayer(name) {
		activeLayer = name;
		
		//var callback = getOnActivelayerChanged();
		if(this.onActivelayerChangedCallback) {
			this.onActivelayerChangedCallback(name);
		}
		
		var action = "action=activateLayer&layer=" + name;
		updateMap(action);
		if (getLegendDiv()) refreshLegend();
	}
	
	/*Gets the warning to activate a layer*/
	function getActivationWarning() {
		return this.activationWarning;
	}
	/*Sets the warning to activate a layer*/
	function setActivationWarning(a) {
		activationWarning = a;
	}

	/*Gets the warning for object selection failure*/
	function getObjectSelectionWarning() {
		return this.objectSelectionWarning;
	}
	/*Sets the warning for object selection failure*/
	function setObjectSelectionWarning(a) {
		objectSelectionWarning = a;
	}

	/*Gets the id of the coordinate system*/
	function getCoordSys() {
		return this.coordSys;
	}
	
	/*Sets the id of the coordinate system*/
	function setCoordSys(id) {
		coordSys = id;
		var action = "action=setcoordsys&coordsys=" + coordSys;
		refreshMap(action);
	}
	
	/*Centers the map at a specified pixel coordinate*/
	function centerMapAt(x, y) {
		var action = "action=centerAt&x=" + x + "&y=" + y;
		refreshMap(action);
	}
	
	/*Activates the selection mode*/
	function select() {
		var state = getState();
		/*Push the select button twice to deselect*/
		if(state instanceof SelectionState) {
			var action = "action=select&minx=0&maxx=0&miny=0&maxy=0&layer=" + getActiveLayer();
			refreshMap(action);
			identifyMapImage("", "", this.onIdentifyProperty, getOnSelect());
		}			
		setState(new SelectionState());
	}
	
	/*Activates the zoom in mode*/
	function zoomIn() {
		setState(new ZoomInState());
	}
	
	/*Activates the zoom out mode*/
	function zoomOut() {
		refreshMap("action=zoomout");
		zoomChanged(0, getMapDiv().clientWidth, 0, getMapDiv().clientHeight);
		setState(new IdleState());
		window.status = "";
	}
	
	/*Activates the zoom full extent mode*/
	function zoomFullExtent() {
		refreshMap("action=zoomFullExtent");
		zoomChanged(0, getMapDiv().clientWidth, 0, getMapDiv().clientHeight);
		setState(new IdleState());
		window.status = "";	
		setMinX(0);
		setMaxX(0);
		setMinY(0);
		setMaxY(0);
	}
	
	/*Activates the pan mode*/
	function pan() {
		setState(new PanState());
		window.status = "";	
	}
	
	/*Activates the identify mode*/
	function identify() {
		setState(new IdentifyState());
	}
	
	/*Activates the idle mode*/
	function idle() {
		setState(new IdleState());
	}
	
	/*Activates the polyline mode for a cross section*/
	function crossSection(snapToObject) {
		var state = getPolyLineState();
		state.setPolygonSelection(false);
		state.setSnapToObject(snapToObject);
		setState(state);
		window.status = "";
	}
	
	/*Activates the polyline mode for a polygon selection*/
	function selectPolygon() {
		if(!getActiveLayer()){
			alert(getActivationWarning());
			return false;
		}			
		var state = getState();
		/*Push the select button twice to deselect*/
		if(state instanceof PolyLineState) {
			state.cancel();
		}
		var state = getPolyLineState();
		state.setPolygonSelection(true);
		state.setSnapToObject(false);
		setState(state);
		window.status = "";
	}
	
	/*Gets the singleton PolyLineState*/
	function getPolyLineState() {
		if(!this.singletonCS){
			this.singletonCS = new PolyLineState();
		}	
		return this.singletonCS;			
	}
	
	/*Removes the last drawn line*/
	function polyLineRemoveLast() {
		var state = getPolyLineState();
		if(state instanceof PolyLineState) {
			state.removeLast();
		}
	}
	
	/*Removes all the drawn lines*/
	function polyLineRemoveAll() {
		var state = getPolyLineState();
		if(state instanceof PolyLineState) {
			state.cancel();
		}
	}
	
	/*Clear all the drawn lines*/
	function polyLineClear() {
		var state = getPolyLineState();
		if(state instanceof PolyLineState) {
			state.clearLine();
		}
	}
	
	/*Gets the singleton Overview*/
	function getOverview() {
		if(!this.singletonOverview){
			this.singletonOverview = new Overview();
			/*Register event handlers*/
			getOverviewDiv().onmousedown = this.singletonOverview.mouseClick;
			getOverviewDiv().onmouseup = this.singletonOverview.mouseClick;
			getOverviewDiv().onmousemove = this.singletonOverview.mouseMove;
			getOverviewDiv().ondblclick = this.singletonOverview.mouseDoubleClick;
		}	
		return this.singletonOverview;			
	}

	/*Gets position of the left top corner (x,y relative to parent element) of the overview div*/
	function getOverviewDivLeftTop(e) {
		var div = getOverviewDiv();
		var x = 0;
		var y = 0;
		if (!e) var e = window.event;
		if (e.x || e.y) 	{
			x = div.offsetLeft;
			y = div.offsetTop;
		}
		var c = [x, y];
		return c;
	}	
	
	/* Checks whether the target of an event has the specified id */
	function targetIsId(e, id) {
		var targ;
		if (!e) var e = window.event;
		if (e.target) targ = e.target;
		else if (e.srcElement) targ = e.srcElement;
		return (targ.id == id);
	}
	
	/*Turns a layer's visibility on or off*/
	function toggleVisibleLayer(checkbox, layerName) {
		if(checkbox) {
			if(!checkbox.checked){
				var url = "action=removevisible&layer=" + layerName;
				refreshMap(url);
				refreshTocForMap(this);
			} else {
				var url = "action=addvisible&layer=" + layerName;
				refreshMap(url);
			}
		}
	}
	
	/*Gets mouse position over the map (used to determine map (pixel) coordinates)*/
	function getMouseCoordinates(e) {
		var x = 0;
		var y = 0;
		if (!e) var e = window.event;
		if (e.x || e.y) 	{
			x = e.x;
			y = e.y;
		} else {
			x = e.layerX;
			y = e.layerY;
		}
		var c = [x, y];
		return c;
	}	
	
	/*Gets mouse position over the window (used to position divs on the window)*/
	function getMouseCoordinatesWindow(e) {
		var posx = 0;
		var posy = 0;
		if (!e) var e = window.event;
		if (e.pageX || e.pageY) 	{
			posx = e.pageX;
			posy = e.pageY;
		}
		else if (e.clientX || e.clientY) 	{
			posx = e.clientX + document.body.scrollLeft
				+ document.documentElement.scrollLeft;
			posy = e.clientY + document.body.scrollTop
				+ document.documentElement.scrollTop;
		}
 		var c = [posx, posy];
		return c;
	}

	
	function getMapCoordinatesAsString(xyOnMap) {
		var bbox = getBBoxMap();
	    var coordString = "" + Math.round(bbox.getMinX() + xyOnMap[0]*bbox.getPixelX()) 
	    				+ " , " + Math.round(bbox.getMaxY() - xyOnMap[1]*bbox.getPixelY());	
		return coordString;
	}

	
	function showMapXY() {
		window.status = "minx " + getMinX() + " maxx " + getMaxX() + " miny " + getMinY() + " maxy " + getMaxY();
	}
	
	function showCoordinates(e) {
		var xyOnMap = getMouseCoordinates(e);
	    var u = Math.pow(10,2);
		var mapCoords = getMapCoordinatesAsString(xyOnMap);
	    var mouseString = "Map: " + mapCoords  
	                    + "     Window: " + xyOnMap[0] + " , " + xyOnMap[1];	
	    window.status = mouseString;
	    getOverview().showCoordinates(mapCoords);
	    
		// also display the coordinates on the map itself
		var mapDiv = getMapDiv();
		var txtNode = mapDiv.nextElementSibling;			// try to locate a div to display text
		if (txtNode == null) txtNode = mapDiv.nextSibling;
		if (txtNode) txtNode = txtNode.firstChild;
		if (txtNode) {
			txtNode.nodeValue = mouseString;
		}
		return;
	}
	
	function zoomChanged(minx, maxx, miny, maxy) {
		if(this.onZoomChangeCallback) {
			this.onZoomChangeCallback(minx, maxx, miny, maxy);
		}
	}
	
	function onZoomChange(callback) {
		//Maybe use an this.Array to store handlers?		
		this.onZoomChangeCallback = callback;
	}
	
	function getPixelCoordinates(points) {
		var url = getMapUrlUnique() + "&getCoordinates&realCoords=" + points;
		var xmlhttp = getXMLHttpRequest();
		xmlhttp.open("GET", url, false);
		xmlhttp.send(null);
		if (xmlhttp.status == 200) {
			return xmlhttp.responseText;
		} else {
			throw "Unable to convert coordinates";
		}		
	}
	
	/*Get real-world coordinates from pixels */
	function getRealWorldCoordinates(points) {
		var url = getMapUrlUnique() + "&getCoordinates&pixelCoords=" + points;
		var xmlhttp = getXMLHttpRequest();
		xmlhttp.open("GET", url, false);
		xmlhttp.send(null);
		if (xmlhttp.status == 200) {
			return xmlhttp.responseText;
		} else {
			throw "Unable to convert coordinates";
		}		
	}
	
	/*Init the active layer if present on the servlet */
	function initActiveLayer(firstVisible) {
		var url = getMapUrlUnique() + "&getActiveLayer";
		if (firstVisible) url = url + "&firstVisible";
		var xmlhttp = getXMLHttpRequest();
		xmlhttp.open("GET", url, false);
		xmlhttp.send(null);
		if (xmlhttp.status == 200) {
			var aLayer = xmlhttp.responseText;
			if (aLayer.length) {
				this.setActiveLayer(aLayer);
			}
		} else {
			throw "Unable to initialize the active layer";
		}		
	}
	
	/*Sets reference to the callback*/
	function onActivelayerChanged(callback) {
		this.onActivelayerChangedCallback = callback;
	}
	
	/*Gets reference to the callback*/
	function getOnActivelayerChanged() {
		return this.onActivelayerChangedCallback;
	}
	
	/*Enables or disables activation*/
	function setActivationEnabled(enabled) {
		this.activationEnabled = enabled;
	}
	
	/*Gets the flag if activation is  enabled or disabled*/
	function getActivationEnabled() {
		return this.activationEnabled;
	}
	
	/*Enables or disables flag for showing backgroundlayers */
	function setShowBackgroundLayers(enabled) {
		this.showBackgroundLayers = enabled;
	}
	
	/*Gets the flag if showing backgroundlayers is enabled or disabled*/
	function getShowBackgroundLayers() {
		return this.showBackgroundLayers;
	}

	/* Sets reference to the callback to be used when starting an action */
	function onStartOfAction(callback) {
		this.onStartOfActionCallback = callback;
	}
	
	/* Sets reference to the callback to be used when ending an action */
	function onEndOfAction(callback) {
		this.onEndOfActionCallback = callback;
	}
	
}

