
/*	Compass Rose
 * 
 * The Compass Rose object.
 * 
 */
CompassRose = Class.create();
CompassRose.prototype = {
	initialize: function(mapManager,container,config) {
		this.container = container;
		this.mapManager = mapManager;
		this.config = config;
		this.dirX = 0;
		this.dirY = 0;
		this.icon = null;
		this.mapPolys = null;
		
		this.id = "compassRose"+Math.random();
		this.stopMovingObserver = this.stopMoving.bindAsEventListener(this);
		this.updateMousePositionObserver = this.updateMousePosition.bindAsEventListener(this);
	},
	paint: function() {
		if (this.icon == null) {
	
			this.calcPosition();
		
			this.icon = document.createElement("img");
			
			this.icon.id = this.id;
			this.icon.src = this.config.compassRoseSrc;
			
			Element.setStyle(this.icon,{ position:"absolute",left:this.left+"px",top:this.top+"px"});
			Element.addClassName(this.icon,"compassRose");
	
			this.map = document.createElement("map");
			this.map.id = this.id+"_map";
			this.map.name = this.id+"_map";
	
			this.container.appendChild(this.icon);
				
			var oneEighth = Math.round( this.config.compassRoseSize / 8 );
			var oneFourth = Math.round( this.config.compassRoseSize / 4 );
			var oneHalf = Math.round( this.config.compassRoseSize / 2 );
			
			this.mapPolys = new Array(9);
			
			this.down_neObserver = this.down_ne.bindAsEventListener(this);
			this.down_nObserver = this.down_n.bindAsEventListener(this);
			this.down_nwObserver = this.down_nw.bindAsEventListener(this);
			this.down_wObserver = this.down_w.bindAsEventListener(this);
			this.down_swObserver = this.down_sw.bindAsEventListener(this);
			this.down_sObserver = this.down_s.bindAsEventListener(this);
			this.down_seObserver = this.down_se.bindAsEventListener(this);
			this.down_eObserver = this.down_e.bindAsEventListener(this);
			this.down_gamestyleObserver = this.down_gamestyle.bindAsEventListener(this);
			for ( var i = 0 ; i < this.mapPolys.length ; i++ ) {
				this.mapPolys[i] = document.createElement("area");
				this.mapPolys[i].shape = "poly";
				switch(i) {
					case 0: // ne
						this.mapPolys[i].coords = '0,'+oneFourth+',0,0,'+oneFourth+',0,'+(oneFourth+oneEighth)+','+oneFourth+','+oneFourth+','+(oneFourth+oneEighth);
						Event.observe(this.mapPolys[i],"mousedown",this.down_neObserver,false);
						break;
					case 1: // n
						this.mapPolys[i].coords = oneFourth+',0,'+(oneHalf+oneFourth)+',0,'+(oneHalf+oneEighth)+','+oneFourth+','+(oneFourth+oneEighth)+','+oneFourth;
						Event.observe(this.mapPolys[i],"mousedown",this.down_nObserver,false);
						break;
					case 2: // nw
						this.mapPolys[i].coords = (oneHalf+oneFourth)+',0,'+this.config.compassRoseSize+',0,'+this.config.compassRoseSize+','+oneFourth+','+(oneHalf+oneFourth)+','+(oneFourth+oneEighth)+','+(oneHalf+oneEighth)+','+oneFourth;
						Event.observe(this.mapPolys[i],"mousedown",this.down_nwObserver,false);
						break;
					case 3: // w
						this.mapPolys[i].coords = this.config.compassRoseSize+','+oneFourth+','+this.config.compassRoseSize+','+(oneHalf+oneFourth)+','+(oneHalf+oneFourth)+','+(oneHalf+oneEighth)+','+(oneHalf+oneFourth)+','+(oneFourth+oneEighth);
						Event.observe(this.mapPolys[i],"mousedown",this.down_wObserver,false);
						break;
					case 4: // sw
						this.mapPolys[i].coords = this.config.compassRoseSize+','+(oneHalf+oneFourth)+','+this.config.compassRoseSize+','+this.config.compassRoseSize+','+(oneHalf+oneFourth)+','+this.config.compassRoseSize+','+(oneHalf+oneEighth)+','+(oneHalf+oneFourth)+','+(oneHalf+oneFourth)+','+(oneHalf+oneEighth);
						Event.observe(this.mapPolys[i],"mousedown",this.down_swObserver,false);
						break;
					case 5: // s
						this.mapPolys[i].coords = (oneHalf+oneFourth)+','+this.config.compassRoseSize+','+oneFourth+','+this.config.compassRoseSize+','+(oneFourth+oneEighth)+','+(oneHalf+oneFourth)+','+(oneHalf+oneEighth)+','+(oneHalf+oneFourth);
						Event.observe(this.mapPolys[i],"mousedown",this.down_sObserver,false);
						break;
					case 6: // se
						this.mapPolys[i].coords = oneFourth+','+this.config.compassRoseSize+',0,'+this.config.compassRoseSize+',0,'+(oneHalf+oneFourth)+','+oneFourth+','+(oneHalf+oneEighth)+','+(oneFourth+oneEighth)+','+(oneHalf+oneFourth);
						Event.observe(this.mapPolys[i],"mousedown",this.down_seObserver,false);
						break;
					case 7: // e
						this.mapPolys[i].coords = '0,'+(oneHalf+oneFourth)+',0,'+oneFourth+','+oneFourth+','+(oneFourth+oneEighth)+','+oneFourth+','+(oneHalf+oneEighth);
						Event.observe(this.mapPolys[i],"mousedown",this.down_eObserver,false);
						break;
					case 8: // gamestyle
						this.mapPolys[i].coords = oneFourth+','+(oneFourth+oneEighth)+','+(oneFourth+oneEighth)+','+oneFourth+','+(oneHalf+oneEighth)+','+oneFourth+','+(oneHalf+oneFourth)+','+(oneFourth+oneEighth)+','+(oneHalf+oneFourth)+','+(oneHalf+oneEighth)+','+(oneHalf+oneEighth)+','+(oneHalf+oneFourth)+','+(oneFourth+oneEighth)+','+(oneHalf+oneFourth)+','+oneFourth+','+(oneHalf+oneEighth);
						Event.observe(this.mapPolys[i],"mousedown",this.down_gamestyleObserver,false);
						break;
					default:
						break;
				}
				Event.observe(this.mapPolys[i],"mouseup",this.stopMovingObserver,false);
				this.map.appendChild(this.mapPolys[i]);
			}
			this.container.appendChild(this.map);
			
			this.icon.useMap = "#"+this.id+"_map";
		}
	},
	calcPosition: function() {
		switch(this.config.compassRosePosition) {
			// TODO: alle Möglichkeiten implementieren!
			case "bottom-right":
				this.left = this.config.compassRosePadding;
				this.top = this.container.style.height.delPx() - this.config.compassRoseSize - this.config.compassRosePadding;
				break;
			case "top-left":
				this.left = this.container.style.width.delPx() - this.config.compassRoseSize - this.config.compassRosePadding;
				this.top = this.config.compassRosePadding;
				break;
			default:
				this.left = this.container.style.width.delPx() - this.config.compassRoseSize - this.config.compassRosePadding;
				this.top = this.container.style.height.delPx() - this.config.compassRoseSize - this.config.compassRosePadding;
				break;
		}
	},
	down_ne: function(e) {
		this.triggerDirMover(1,1,e);
	},
	down_n: function(e) {
		this.triggerDirMover(0,1,e);
	},
	down_nw: function(e) {
		this.triggerDirMover(-1,1,e);
	},
	down_w: function(e) {
		this.triggerDirMover(-1,0,e);
	},
	down_e: function(e) {
		this.triggerDirMover(1,0,e);
	},
	down_se: function(e) {
		this.triggerDirMover(1,-1,e);
	},
	down_s: function(e) {
		this.triggerDirMover(0,-1,e);
	},
	down_sw: function(e) {
		this.triggerDirMover(-1,-1,e);
	},
	down_gamestyle: function(e) {
		this.moving = true;
		this.gamestyleMouseZero = new Array(Event.pointerX(e),Event.pointerY(e));
		this.mousePosition = new Array(Event.pointerX(e),Event.pointerY(e));
		this.attachMouseMoveObserver();
		this.attachStopMovingObserver();
		this.gamestyleMover();
		Event.stop(e);
	},
	updateMousePosition: function(e) {
		this.mousePosition = new Array(Event.pointerX(e),Event.pointerY(e));
	},
	gamestyleMover: function(e) {
		if (this.moving) {
			this.mapManager.move(Math.round((this.mousePosition[0] - this.gamestyleMouseZero[0])/(1/this.config.compassRoseGamestyleSpeed)), Math.round((this.mousePosition[1] - this.gamestyleMouseZero[1])/(1/this.config.compassRoseGamestyleSpeed)));
			setTimeout(this.gamestyleMover.bind(this),20);
		}
		else this.detachMouseMoveObserver();
	},
	triggerDirMover: function(x,y,e) {
		Event.stop(e);
		if (!this.moving) {
			this.dirX = x;
			this.dirY = y;
			this.moving = true;
			this.attachStopMovingObserver();
			this.dirMover(x,y);
		}
		else this.moving = false;
	},
	dirMover: function() {
		this.mapManager.move(this.dirX * this.config.compassRoseSpeed , this.dirY * this.config.compassRoseSpeed);
		if (this.moving) setTimeout(this.dirMover.bind(this),20);
	},
	attachMouseMoveObserver: function() {
		Event.observe(this.icon,"mousemove",this.updateMousePositionObserver);
		Event.observe(this.container,"mousemove",this.updateMousePositionObserver);
	},
	detachMouseMoveObserver: function() {
		if (this.icon != null) Event.stopObserving(this.icon,"mousemove",this.updateMousePositionObserver);
		Event.stopObserving(this.container,"mousemove",this.updateMousePositionObserver);
	},
	attachStopMovingObserver: function() {
		Event.observe(this.icon,'mouseup',this.stopMovingObserver);
		Event.observe(this.container,'mouseup',this.stopMovingObserver);
		Event.observe(document,'mouseup',this.stopMovingObserver);
	},
	detachStopMovingObserver: function() {
		if (this.icon != null) Event.stopObserving(this.icon,'mouseup',this.stopMovingObserver);
		Event.stopObserving(this.container,'mouseup',this.stopMovingObserver);
		Event.stopObserving(document,'mouseup',this.stopMovingObserver);
	},
	stopMoving: function(e) {
		Event.stop(e);
		this.moving = false;
		this.detachStopMovingObserver();
	},
	remove: function() {
		if (this.mapPolys != null) {
			for ( var i = 0 ; i < this.mapPolys.length ; i++ ) {
				switch(i) {
					case 0: // ne
						Event.stopObserving(this.mapPolys[i],"mousedown",this.down_neObserver,false);
						break;
					case 1: // n
						Event.stopObserving(this.mapPolys[i],"mousedown",this.down_nObserver,false);
						break;
					case 2: // nw
						Event.stopObserving(this.mapPolys[i],"mousedown",this.down_nwObserver,false);
						break;
					case 3: // w
						Event.stopObserving(this.mapPolys[i],"mousedown",this.down_wObserver,false);
						break;
					case 4: // sw
						Event.stopObserving(this.mapPolys[i],"mousedown",this.down_swObserver,false);
						break;
					case 5: // s
						Event.stopObserving(this.mapPolys[i],"mousedown",this.down_sObserver,false);
						break;
					case 6: // se
						Event.stopObserving(this.mapPolys[i],"mousedown",this.down_seObserver,false);
						break;
					case 7: // e
						Event.stopObserving(this.mapPolys[i],"mousedown",this.down_eObserver,false);
						break;
					case 8: // gamestyle
						Event.stopObserving(this.mapPolys[i],"mousedown",this.down_gamestyleObserver,false);
						break;
					default:
						break;
				}
				Event.stopObserving(this.mapPolys[i],"mouseup",this.stopMovingObserver,false);
			}
		}
		
		this.detachMouseMoveObserver();
		this.detachStopMovingObserver();
		
		this.down_neObserver = null;
		this.down_nObserver = null;
		this.down_nwObserver = null;
		this.down_wObserver = null;
		this.down_swObserver = null;
		this.down_sObserver = null;
		this.down_seObserver = null;
		this.down_eObserver = null;
		this.down_gamestyleObserver = null;

		this.updateMousePositionObserver = null;
		this.stopMovingObserver = null;

		try {
			this.container.removeChild(this.map);
			this.container.removeChild(this.icon);
		} catch(e) {
//			//debug.writeline("removing compassrose icon failed");
		}
//		//debug.writeline("compassrose removed");
	}
}
/*	Coordinates
 * 
 * Doing all real world coordinate calculations
 * 
 */
Coordinates = Class.create();
Coordinates.prototype = {
	initialize: function(config,tileMatrix) {
		this.config = config;
		this.tileMatrix = tileMatrix;
		
		this.mouseX = 0;
		this.mouseY = 0;
		
		// docu:	using the LUT provided by Bartosz, converting IDs to Lat
		this.idConvArr = new Array(0.0,1.00668700435474,2.01305915501797,3.0188021006333,4.02360249251984,5.02714848104625,6.02913020607975,7.02924027961646,8.02717425874077,9.02263110714088,10.0153136434893,11.0049289750908,11.9911889153081,12.9738103833864,13.9525157854222,14.9270333753506,15.8970975949536,16.8624493920382,17.8228365160615,18.7780137906281,19.7277433624221,20.6717949262715,21.6099459261829,22.5419817323097,23.4676957939472,24.3868897687665,25.2993736286116,26.2049657422923,27.1034929359004,27.9947905312729,28.8787023632820,29.7550807767831,30.6237866039589,31.4846891230484,32.3376659993363,33.1826032093987,34.0193949495992,34.8479435298631,35.6681592537599,36.4799602859389,37.2832725079563,38.0780293635265,38.8641716942195,39.6416475666031,40.4104120918126,41.1704272384979,41.9216616400704,42.6640903971397,43.3976948759857,44.1224625038963,44.8383865621189,45.5454659771848,46.2437051112803,46.9331135523207,47.6137059043295,48.2855015786883,48.9485245867775,49.6028033344877,50.2483704190425,50.8852624285296,51.5135197445056,52.1331863479955,52.7443096291796,53.3469402010199,53.9411317170504,54.5269406935255,55.1044263360825,55.6736503710697,56.2346768816238,56.7875721486187,57.3324044965118,57.8692441441611,58.3981630606204,58.9192348259263,59.4325344968634,59.9381384776857);
	},
	prepare: function() {
		this.UL = new Array(this.config.xSegments.min(),this.config.ySegments.max());
		this.LR = new Array(this.config.xSegments.max(),this.config.ySegments.min());

		this.pxExtent = new Array( this.config.xSegments.length * this.config.tileWidth ,  this.config.ySegments.length * this.config.tileHeight );

//		this.currentZoomFactor = this.config.zoomFactors[this.config.zoomLevel];
		this.currentZoomFactor = this.config.agsScale[0] / this.config.agsScale[this.config.zoomLevel];		
		this.mouseObserver = this.getMouseCoords.bindAsEventListener(this);

		this.attachMouseCoordsCalc();
	},
	attachMouseCoordsCalc: function() {
		Event.observe(this.tileMatrix.container,"mousemove",this.mouseObserver,true);
		Event.observe(document,"mousemove",this.mouseObserver,true);		
	},
	mapMouseX: function() {
		return this.viewPortMouseX() + this.tileMatrix.mapSource.getPxOff("x");
	},
	mapMouseY: function() {
		return this.viewPortMouseY() + this.tileMatrix.mapSource.getPxOff("y");
	},
	viewPortMouseX: function() {
		// es war nicht klar, warum hier style.left hinzu musste.
		// Ohne funktioniert es richtig.
//		return this.mouseX - this.tileMatrix.container.style.left.delPx() - this.config.DOMParentOffsetX;
//		alert("mouseX "+this.mouseX+" tMstyle.left "+this.tileMatrix.container.style.left.delPx()+" domp "+this.config.DOMParentOffsetX);
		return this.mouseX - this.config.DOMParentOffsetX;
	},
	viewPortMouseY: function() {
		// es war nicht klar, warum hier style.top hinzu musste.
		// Ohne funktioniert es richtig.
//		return this.mouseY - this.tileMatrix.container.style.top.delPx() - this.config.DOMParentOffsetY;
		return this.mouseY - this.config.DOMParentOffsetY;
	},
	getMouseCoords: function(e) {
		this.mouseX = Event.pointerX(e);
		this.mouseY = Event.pointerY(e);
		
		// Accuracy testing: PX -> RW -> PX
		//var rwHin = this.px2rw(this.mapMouseX(),this.mapMouseY());
		//var pxBack = this.rw2px(rwHin[0],rwHin[1]);

		//window.defaultStatus = "Screen "+this.mouseX+"x"+this.mouseY+" | ViewPort "+this.viewPortMouseX()+"x"+this.viewPortMouseY()+" | Map "+this.mapMouseX()+"x"+this.mapMouseY()+" | RW "+this.px2rw(this.mapMouseX(),this.mapMouseY())+" | Accuracy test: "+pxBack+"    ||    pxOff: "+this.tileMatrix.mapSource.getPxOff("x")+"x"+this.tileMatrix.mapSource.getPxOff("y")+" | tileOff "+this.tileMatrix.mapSource.getTileOff("x")+"x"+this.tileMatrix.mapSource.getTileOff("y")+" | ";
	},
	getCurrentZoomFactor: function() {
		return this.currentZoomFactor;
	},
	getCurrentZoomLevel: function() {
		return this.config.zoomLevel;
	},
	getBB : function() {
		var bb = new Array( new Array(0,0), new Array(0,0));
		var ulX = this.tileMatrix.mapSource.getPxOff("x");
		var ulY = this.tileMatrix.mapSource.getPxOff("y");
		var lrX = ulX + this.config.width;
		var lrY = ulY + this.config.height;
	
		var UL = this.px2rw(ulX,ulY);
		var LR = this.px2rw(lrX,lrY);
		bb = new Array( UL , LR );

		return bb;
	},
	getCenter: function() {
		var ulX = this.tileMatrix.mapSource.getPxOff("x");
		var ulY = this.tileMatrix.mapSource.getPxOff("y");
		var centerX = ulX + this.config.width/2;
		var centerY = ulY + this.config.height/2;
		
//		alert("starting with "+centerX+","+centerY);
		return this.px2rw(centerX,centerY);
	},
	getMousePosInRW: function() {
//		window.defaultStatus += " using px off "+this.tileMatrix.mapSource.getPxOff("x")+", "+this.tileMatrix.mapSource.getPxOff("y")+" : viewPort "+this.viewPortMouseX()+", "+this.viewPortMouseY()+" DOMParentOFF "+this.config.DOMParentOffsetX+", "+this.config.DOMParentOffsetY;
		return this.px2rw(this.mapMouseX(),this.mapMouseY());
	},
	px2rw : function(x,y) {
		// docu:	one tiles is supposed to be 1 degree in zF 1
		var pxSizeX = 1 / (this.currentZoomFactor * this.config.tileWidth);
		var lon = this.UL[0] + (pxSizeX * x);
	
		// docu:	y direction is more complicated! First, we go from bottom to top, so switch coordinate direction
		var pxFromBottom = this.pxExtent[1] * this.currentZoomFactor - y;
		// docu:	which tile are we on?
		var yTile = Math.floor(pxFromBottom / (this.config.tileHeight*this.currentZoomFactor));
		// docu:	how many pixels left?
		if (yTile > 0) var yRest = pxFromBottom % (yTile * this.config.tileHeight * this.currentZoomFactor);
		else yRest = pxFromBottom;

		var yID = this.config.ySegments[this.config.ySegments.length - 1] + yTile;



		// AHHHHHH, magic number!! Used to account for px offset reasoned in the non linear representation of the subtiles.
		var zaehler = (this.currentZoomFactor / 2) - ( Math.abs( Math.floor(yRest / this.config.tileHeight) - (this.currentZoomFactor / 2) ) );
		var nenner = (this.currentZoomFactor / 2);
		
		var off = (zaehler / nenner) * this.config.magicNumberPX_Y * this.currentZoomFactor;
				
		yRest += off;



		// docu:	now cartographic bullshit, but better than nothing:
		// docu:	linear interpolation between two id / degree steps to get "exact" lat pos.
		var lat = this.index2lat(yID) + (yRest / (this.config.tileHeight*this.currentZoomFactor)) * (this.index2lat(yID+1) - this.index2lat(yID));

		return new Array(lon, lat);
	},
	rw2px : function(lon, lat) {
		var rwDelta = lon - this.UL[0];
		var rwSize = (this.LR[0]+1) - this.UL[0];
		
		var x = (rwDelta/rwSize) * (this.pxExtent[0] * this.currentZoomFactor);
				
		// docu:	theory to get to the px position:
		// docu:	traverse through id array unit item > lat
		// docu:	fraction of (lat - id array item) / (id array item+1 - id array item) * tile size

		var i = this.lat2index(lat);

		// docu: 	now allright, we got the lower bound
		var lowerYTileRW = this.index2lat(i);

		var yDelta = lat - lowerYTileRW;

		var pxDelta = (yDelta / (this.index2lat(i+1) - this.index2lat(i))) * (this.config.tileHeight * this.currentZoomFactor);
		
		// AHHHHHH, magic number!! Used to account for px offset reasoned in the non linear representation of the subtiles.
		// 		32		-		(	31 	- 	32	)
		var zaehler = (this.currentZoomFactor / 2) - ( Math.abs( Math.floor(pxDelta / this.config.tileHeight) - (this.currentZoomFactor / 2) ) );
		var nenner = (this.currentZoomFactor / 2);
		
		var off = (zaehler / nenner) * this.config.magicNumberPX_Y * this.currentZoomFactor;
		
//		window.defaultStatus = this.currentZoomFactor+" , "+this.config.tileHeight+" , "+lat+" , "+zaehler+" / "+nenner+" = "+off;
		
		pxDelta -= off;

		var revPos = (i - this.config.ySegments[this.config.ySegments.length - 1]) * this.config.tileHeight * this.currentZoomFactor + pxDelta;
		var y = (this.pxExtent[1] * this.currentZoomFactor) - revPos;
		
		return new Array(Math.round(x),Math.round(y));
	},
	index2lat : function(index) {
		if (index >= 0 && index < this.idConvArr.length) return this.idConvArr[index];
		else return -1;
	},
	lat2index : function(lat) {
		var i = this.config.ySegments[this.config.ySegments.length - 1];
		while (this.index2lat(i) < lat && i < this.idConvArr.length) i++;
		i--;
		return i;
	},
	remove : function() {
		Event.stopObserving(this.tileMatrix.container,"mousemove",this.mouseObserver,true);
		Event.stopObserving(document,"mousemove",this.mouseObserver,true);
		
		this.mouseObserver = null;
		
		this.tileMatrix = null;
		this.config = null;
		this.mouseObserver = null;
	}
}



AGS_Coordinates = Class.create();
AGS_Coordinates.prototype = Object.extend(new Coordinates(),{
	initialize: function(config,tileMatrix) {
		this.config = config;
		this.tileMatrix = tileMatrix;
		
		this.mouseX = 0;
		this.mouseY = 0;
	},
	prepare: function() {
		this.currentZoomFactor = this.config.agsScale[0] / this.config.agsScale[this.config.zoomLevel];		
//		//debug.writeline("ags_coordinates: currentZoomFactor is "+this.currentZoomFactor);

		this.UL = this.config.agsTileOrigin;
		this.mouseObserver = this.getMouseCoords.bindAsEventListener(this);

		this.attachMouseCoordsCalc();
	},
	getCurrentResolution: function() {
		return this.config.agsResolution[this.getCurrentZoomLevel()];
	},
	getCurrentScale: function() {
		return this.config.agsScale[this.getCurrentZoomLevel()];
	},
	px2rw : function(x,y) {
		var lon = this.UL[0] + this.getCurrentResolution() * x;
		var lat = this.UL[1] - this.getCurrentResolution() * y;

		return new Array(lon, lat);
	},
	rw2px : function(lon, lat) {
		var x = (lon - this.UL[0]) / this.getCurrentResolution();
		var y = (this.UL[1] - lat) / this.getCurrentResolution();

		return new Array(Math.round(x),Math.round(y));
	}
});

/*
 * 
 * Adding copyright information to the map, drawing them on the bottom right
 * 
 */
CopyrightInfo = Class.create();
CopyrightInfo.prototype = {
	initialize: function(config,container) {
		this.config = config;
		this.container = container;
		this.div = null;
		this.items = new Array();
	},
	paint: function() {
		if (this.div == null) {
			this.div = document.createElement("div");

			Element.addClassName(this.div,"copyrightInfo");
	
			for (var item = 0 ; item < this.config.copyrightInfos.length ; item++) {
				this.items.push(document.createElement("a"));
				this.items[this.items.length - 1].href = this.config.copyrightInfos[item][1];
				this.items[this.items.length - 1].target = "_blank";
				this.items[this.items.length - 1].id = this.config.copyrightInfos[item][2];				
							
				this.items[this.items.length - 1].innerHTML = this.config.copyrightInfos[item][0];
				
				this.div.appendChild(this.items[this.items.length - 1]);
				
				this.container.appendChild(this.div);
	
				if (item < (this.config.copyrightInfos.length - 1)) {
					var txt_between = document.createTextNode(" | ");
					this.div.appendChild(txt_between);
				}
			}
		}
		this.position();
	},
	position: function() {
		Element.setStyle(this.div,{position:"absolute",top:(this.config.height - this.config.copyrightInfosOffsetY)+"px",left:this.config.copyrightInfosOffsetX+"px"});
	},
	remove: function() {
		this.config = null;
		if (this.div != null) {
			while(this.div.hasChildNodes()) {
				this.div.removeChild(this.div.firstChild);
			}
			this.container.removeChild(this.div);
		}
		this.container = null;
		this.items = null;
//		//debug.writeline("copyrightInfo removed");
	}
}


DebugConsole = Class.create();
DebugConsole.prototype = {
	initialize: function(container,x,y,width,height) {
		this.container = container;
		this.debugBox = null;
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		this.painted = false;
	},
	paint: function() {
		if (this.debugBox == null) {
			this.debugBox = document.createElement("textarea");
			this.debugBox.cols = this.width;
			this.debugBox.rows = this.height;
			Element.setStyle(this.debugBox,{zIndex:10000000,position:"absolute",top:this.y+"px",left:this.x+"px"});
			this.container.appendChild(this.debugBox);
		}
	},
	writeline: function(txt) {
		if (this.painted) {
			var time = new Date();
			this.debugBox.value = time.getTime()+": "+txt + "\r" + this.debugBox.value;
		}
	},
	clear: function() {
		this.debugBox.value = "";
	}
}



/* EventLogger
 * 
 * Logs all map actions like drawing, moving, zooming to the alta4gis logger
 * 
 */
EventLogger = Class.create();
EventLogger.prototype = {
	initialize: function(url, userId, project) {
		this.url = url;
		this.userId = userId;
		this.project = project;
		this.active = false;
	},
	toggle: function(doLog) {
		this.active = doLog;
	},
	log: function(type) {
		if (this.active) {
			var parameter = "userid="+this.userId+"&projekt="+this.project+"&kategorie="+type+"&rand="+Math.random();
			var aRequest = new Ajax.Request(
				this.url, 
				{
					method: "get",
					parameters: parameter
				}
			);
		}
	},
	remove: function() {
	}
}

/*	Helper
 * 
 * Helper functions, providing extended abilities for the basic types
 * 
 */
 
var loadedScripts = {
	callFunction: function(){
	}
};
var observer = {
	name: "alta4 observer",
	/**
	 * diese Methode wird aufgerufen, sobald der noticeCoordinates-Request abgearbeitet wurde. Überschreiben um sich dort reinzuhängen
	 * 
	 */
	update: function(){
		//alert(this.name + " : "+$A(arguments).join(', '));
	}
};

function loadClasses() {
	loadedScripts.callFunction();
};
 
String.prototype.delPx = function() {
	var str = this.valueOf();
	var len = this.length;
	if (str.substring((len-2),len) == "px") {
		str = str.substring(0, (len-2));
		str = parseInt(str, 10);
	}
	return str;
}

/*	ImageCache

Provides ability to cache images and retrieve them, therefore optimizing page load time

*/
ImageCache = Class.create();
ImageCache.prototype = {
	initialize: function(config,poiManager) {
		this.config = config;
		this.poiManager = poiManager;
		this.cache = new Array();
	},
	cache: function(src) {
		if (typeof(this.cache[src]) != "object") {
			this.cache[src] = document.createElement("img");
			this.cache[src].src = src;
		}
	},
	get: function(src) {
		if (typeof(this.cache[src]) != "object") this.cache(src);
		return this.cache[src];
	},
	deleteCache: function() {
		this.cache = new Array();
	}
}

/*	Interactions
 * 
 * Interactions will hold any HID events and dispense them to the defined event handler.
 * Implemented are:
 * 
 * * panning the map with the direction keys
 * * zooming with the mouse wheel
 * 
 */
Interactions = Class.create();
Interactions.prototype = {
	initialize: function(mapContainer, mapManager, CompassRose) {
		this.container = mapContainer;
		this.mapManager = mapManager;
		this.compassRose = CompassRose;
		this.keyboardMoving = false;
		this.keyboardDownObserver = null;
		this.keyboardUpObserver = null;
	},
	keyboard: function() {
		//ES: keybord-Unterstützung rausgenommen, da sonst Key-Navi im Fels-Edit-Formular nicht funktionieren (0001985)
		//this.keyboardDownObserver = this.keyboardDown.bindAsEventListener(this);
		//this.keyboardUpObserver = this.keyboardUp.bindAsEventListener(this);
		//Event.observe(document,'keydown', this.keyboardDownObserver);
		//Event.observe(document,'keyup', this.keyboardUpObserver);
	},
	mouse: function() {
		this.mouseWheelObserver = this.mousewheelActions.bindAsEventListener(this);
		Event.observe(this.container,"mousewheel",this.mouseWheelObserver,true);
		Event.observe(this.container,"DOMMouseScroll",this.mouseWheelObserver,true);
	},
	keyboardDown: function(e) {
		if (!this.keyboardMoving) {
			if(e.keyCode == Event.KEY_UP) this.compassRose.down_n(e);
			if(e.keyCode == Event.KEY_DOWN) this.compassRose.down_s(e);
			if(e.keyCode == Event.KEY_LEFT) this.compassRose.down_e(e);
			if(e.keyCode == Event.KEY_RIGHT) this.compassRose.down_w(e);
		}
	},
	keyboardUp: function(e) {
		if (this.compassRose) this.compassRose.stopMoving(e);
		this.keyboardMoving = false;
	},
	mousewheelActions: function(e) {
		Event.stop(e);
		(e.wheelDelta) ? amount = e.wheelDelta / -120 : amount = e.detail / 3;
		this.mapManager.zoom(-amount, true);
	},
	remove: function() {
		//Event.stopObserving(document,'keydown',this.keyboardDownObserver,true);
		//Event.stopObserving(document,'keyup',this.keyboardUpObserver,true);
		Event.stopObserving(this.container,"mousewheel",this.mouseWheelObserver,true);
		Event.stopObserving(this.container,"DOMMouseScroll",this.mouseWheelObserver,true);
//		//debug.writeline("interactions removed");
	}
}

/*	JSONParser
 * 
 * Handles the server communication for POI retrieval. All communications based on JSON and AJAX.
 * 
 */
	
JSONParser = Class.create();
JSONParser.prototype = {
	initialize: function(servletAddress,POIManager,coordinates) {
		this.POIManager = POIManager;
		this.servletAddress = servletAddress;
		this.coordinates = coordinates;
		this.working = false;
	},
	load: function(minX,minY,maxX,maxY) {
		this.working = true;
		var isAggreagtion = false;
		if(this.coordinates.getCurrentScale()>=3000000 && this.coordinates.getCurrentScale()<=5000000){
			isAggreagtion = true;
		} 
		this.parameter = "minX="+minX+"&minY="+minY+"&maxX="+maxX+"&maxY="+maxY+"&mapScale="+this.coordinates.getCurrentScale()+"&sessionId="+this.POIManager.config.sessionId+"&topics="+this.POIManager.config.activeTopics+"&areas="+this.POIManager.config.activeAreas+"&targetGroups="+this.POIManager.config.activeTargetGroups+"&isAggregation="+isAggreagtion;
		//debug.writeline(this.servletAddress+this.parameter);
		this.request = new Ajax.Request(
			this.servletAddress, 
			{
				method: "get",
				parameters: this.parameter,
				onComplete:this.processResult.bind(this)
			}
		);
	},
	processResult: function(response) {
		if (this.working) {
//			alert("extent? evaluating JSON: "+response.responseText);
			var json = eval("("+response.responseText+")");
			var count = 0;
			for (var i in json) {
				try{			
					var type = json[i]["type"];
					if (type == "meta") {
						var pxCoord = this.coordinates.rw2px(json[i]["lon"],json[i]["lat"]);
						json[i]["lon"]=pxCoord[0];
						json[i]["lat"]=pxCoord[1];
						this.POIManager.addMetaPOIInfo(i,json[i]); 
					}else if(type == "gebiet"){
						//felsCount: nur bei Gebieten vorhanden
						this.POIManager.addPOIGebiet(json[i]["lon"],json[i]["lat"],i,type,json[i]["name"], json[i]["felsCount"]);
						
					}else{
						
						this.POIManager.addPOI(json[i]["lon"],json[i]["lat"],i,type,json[i]["name"]);

					}
					count++;
				}catch(e){
					//alert(i)
					//alert(e)
				}
			}
			//debug.writeline("extent?  JSON syntax OK, "+count+" points added");
		}
	},
	stop: function() {
		this.working = false;
	},
	remove: function() {
		this.POIManager = null;
		this.request = null;
		this.coordinates = null;
	}
}

	
/*	ListenerDispenser
 * 
 * Listener Dispenser enables a class to define events which are then dispersed to other classes methods.
 * Other classes can attach "listeners" to the events specified. 
 * 
 */

ListenerDispenser = Class.create();
ListenerDispenser.prototype = {
	initialize: function() {
		this.listener = new Array();
	},
	attachListener: function(action,callbackMethod) {
		if (typeof(this.listener[action]) != "object") this.listener[action] = new Array();
		this.listener[action].push(callbackMethod);
	},
	trigger: function(action) {
		// if action exists loop through all attached listeners and execute the callback functions
		if (typeof(this.listener[action]) == "object") {
			for (var i = 0 ; i < this.listener[action].length ; i++) {
				this.listener[action][i]();
			}
		}
	},
	remove: function() {
		this.listener = null;
	}
}
/*	Map
Map is the wrapper class, which is exposed to the user.
*/
Map = Class.create();
Map.prototype = {
	/**
	 * 
	 * @param {Object} DOMParent	der Container in dem die Anwendung läuft
	 * @param {Object} width	breite der sichtbaren Karte
	 * @param {Object} height 	Höhe der sichtbaren Karte
	 */
	initialize: function(DOMParent, width, height) {
		this.mapContainer = DOMParent;
		this.width = width;
		this.height = height;
		this.doAggregation = true;
		this.poiObservers = {};
		
		if (!this.doAggregation){
			mapOptions.isAggregation=false
			overviewMapOptions.isAggregation=false
		}else{
			mapOptions.isAggregation=true
			overviewMapOptions.isAggregation=true
		}
		
		this.hasOverviewMap = true;
		if (width<=300 && height <= 300){
			this.hasOverviewMap = false;
		}			
		
		this.map = null;
		this.overviewMap = null;
		this.overviewMapContainer = null;
		
		this.map = new MapIntern(this.mapContainer, this.width, this.height, mapOptions, this);

		if (this.hasOverviewMap) {
			this.createOverviewMap(120,120);
		}

	},
	setAggregation: function(isAggregation) {
		this.map.setAggregation(isAggregation);
		if (this.overviewMap !== null) {
			this.overviewMap.setAggregation(isAggregation);
		}
	},
	createOverviewMap: function(width, height) {
		if (! $("overviewMapContainer")) {
			this.overviewMapContainer = document.createElement("div");
			this.overviewMapContainer.id = "overviewMapContainer";
			Element.setStyle(this.overviewMapContainer, { position:"relative" , top:"0px" , left:"0px", zIndex:20000, border:overviewMapOptions.overviewMapBorderStyle , width:width+"px", height:height+"px" });
			this.mapContainer.appendChild(this.overviewMapContainer);
		}
		else this.overviewMapContainer = document.getElementById("overviewMapContainer");
		
		if (this.overviewMap === null) {
			this.overviewMap = new MapIntern(this.overviewMapContainer, width, height, overviewMapOptions, this);

			this.map.connectMap(this.overviewMap);
			
			this.overviewMap.setMoveObserver(this.map,this.map.observeMoves);
			this.overviewMap.setCenterObserver(this.map, this.map.observeCenter);
		}
	},
	destroyOverviewMap: function() {
		if (this.overviewMap !== null) {
			this.overviewMap.remove();
			this.overviewMap.shutdown();
		}
		if (this.overviewMapContainer !== null) {
			this.mapContainer.removeChild(this.overviewMapContainer);
		}
	},
	/*
	 * drawing
	 */
	paint: function() {
		this.map.paint();
		if (this.overviewMap !== null) this.overviewMap.paint();
	},
	repaint: function() {
		this.map.repaint();
		if (this.overviewMap !== null) this.overviewMap.repaint();
	},
	suspendRedraw: function(suspendState){
		this.map.suspendRedraw(suspendState);
		if (this.overviewMap !== null) this.overviewMap.suspendRedraw(suspendState);		
	},
	remove: function() {
		this.map.remove();
		if (this.overviewMap !== null) this.overviewMap.remove();		
	},
	shutdown: function() {
		this.map.shutdown();
		if (this.overviewMap !== null) this.destroyOverviewMap();		
	},
	/*
	 * map manipulation
	 */
	setCenter: function(pos) {
		this.map.setCenter(pos);
		if (this.overviewMap !== null) this.overviewMap.setCenter(pos);		
	},
	setZoomLevel: function(level) {
		this.map.setZoomLevel(level);
	},
	setZoomFactor: function(factor) {
		this.map.setZoomFactor(factor);
	},
	setMapScale: function(scale) {
		this.map.setMapScale(scale);
	},
	setFullWidth: function() {
		this.map.setFullWidth();
	},
	setFullHeight: function() {
		this.map.setFullHeight();
	},
	setWidth: function(width) {
		this.map.setWidth(width);
	},
	setHeight: function(height) {
		this.map.setHeight(height);
	},
	zoomTo: function(level) {
		this.map.zoomTo(level);
	},
	hasLayerSelector: function(hasLayerSelector){
	},
	hasZoomBar: function(hasZoomBar){
		this.map.hasZoomBar(hasZoomBar);
	},
	hasCompassRose: function(hasCompassRose){
		this.map.hasCompassRose(hasCompassRose);
	},

	getServiceIndex: function() {
		return this.map.getServiceIndex();
	},
	isLayerVisible: function(layer){
		return this.map.isLayerVisible(layer);
	},
	
	/*
	 * content manipulation
	 */
	hideLayer: function(layers) {
		this.map.hideLayer(layers);
	},
	showLayer: function(layers) {
		this.map.showLayer(layers);
	},
	showPOI: function(poiId){
		this.map.showPOI(poiId);
	},
	/*
	 * environment
	 */
	getBrowserDimensions: function() {
		return this.map.getBrowserDimensions();
	},
	getBrowserLang: function() {
		return this.map.getBrowserLang();
	},
	getDOMParentPosition: function() {
		return this.getDOMParentPosition();
	},
	/*
	 * session handling
	 */
	/*
	 * helper
	 */
	// this function will check all the set*** inputs, if they are a valid list
	checkInput: function(input) {
		return this.map.checkInput(input);
	},
	notifyObservers: function(){
		var data = $A(arguments);
		if (data[0]=="poiId"){
			this.activePoiId = this.map.activePoiId;
			this.activePoiType = this.map.activePoiType;
			this.activePoi = this.map.activePoi;
			for (o in this.poiObservers){
				this.poiObservers[o].update(this,data[0]);
			}
		}else if(data[0]=="mehrLink"){
			var poi = data[1];
			for (o in this.poiObservers){
				this.poiObservers[o].update(this,data[0],poi);
			}
		}else if(data[0]=="addPOI"){
			var poi = data[1];
			for (o in this.poiObservers){
				this.poiObservers[o].update(this,data[0],poi);
			}
		}else if(data[0]=="zoom"){
			for (o in this.poiObservers){
				this.poiObservers[o].update(this,data[0]);
			}
		}
	},
	addObserver: function(name, observer){
		this.poiObservers[name]=observer;
	}
}
/*	MapIntern

MapIntern is the base class, which is self contained and holds everything.

*/
MapIntern = Class.create();
MapIntern.prototype = {
	/**
	 * 
	 * @param {Object} DOMParent	der Container in dem die Anwendung läuft
	 * @param {Object} width	breite der sichtbaren Karte
	 * @param {Object} height 	Höhe der sichtbaren Karte
	 */
	initialize: function(DOMParent, width, height, options, theMap) {
		this.config = new cloneObject(options);	
		this.DOMParent = DOMParent;
		this.map = theMap;
		this.urlHandler = new URLHandler();
		this.serviceIndex = new ServiceIndex();
		this.urlHandler.parse(window.location.search);
		this.checkConfig();
		this.setWidth(width);
		this.setHeight(height);
					
		if (width<=300 && height <= 300){
			this.config.minimumAllowedZoomLevel	= 0;
			this.config.zoomLevel = 0;
		}		
		
		// will be the visible map object
		this.mapManager = null;

		// compass rose
		this.compassRose = null;
		// zoom bar
		this.zoomBar = null;	
		// interactions are all things like mouse gestures, key strokes etc.
		this.interactions = null;
		// draws the copyright infos
		this.copyrightInfo = null;
		//poi spezifisch
		this.activePoiId=null;
		this.activePoiIdTash=null;
		this.activePoi = null;
		
		// log map drawing
		this.eventLogger = new EventLogger(this.config.loggingURL, this.config.loggingUserId, this.config.loggingProject);
		if (this.config.loggingActive) this.eventLogger.toggle(true);

		this.painted = false;
	},
	/**
	 * interne Methode
	 */
	createMapCon: function() {
		if (this.container == null) {
			this.container = document.createElement("div");
		
			// should be unique, to avoid confusion if more than one map is displayed on the page
			this.container.id = "map"+Math.random();

			Element.setStyle(this.container, { position:"absolute" , left:this.config.left+"px" , top:this.config.top+"px" , width:this.config.width+"px" , height:this.config.height+"px" });
			Element.addClassName(this.container,"mapCon");
	
			// linking the whole thing to the page
			this.DOMParent.appendChild(this.container);
		}
	},
	/**
	 * interne Methode
	 */
	checkConfig: function() {
		if (this.config.width == "full") this.setFullWidth();
		if (this.config.height == "full") this.setFullHeight();
		
		// backup: if no lang tag is defined, use browser information
		this.getBrowserLang();

		// update container offset
		this.getDOMParentPosition();

		this.urlHandler.handle("lang",this.setLang.bind(this));
		this.urlHandler.handle("center",this.setCenter.bind(this));
		this.urlHandler.handle("zoomLevel",this.setZoomLevel.bind(this));
		this.urlHandler.handle("zoomFactor",this.setZoomFactor.bind(this));
		this.urlHandler.handle("mapScale",this.setMapScale.bind(this));
		this.urlHandler.handle("edit",this.setEditSession.bind(this));
		this.urlHandler.handle("recode",this.setEditRecode.bind(this));
		this.urlHandler.handle("id",this.setEditID.bind(this));
		this.urlHandler.handle("type",this.setEditType.bind(this));
		this.urlHandler.handle("name",this.setEditName.bind(this));
		this.urlHandler.handle("showNeverGeocodePois",this.setEditPassThrough_showNeverGeocodePOIs.bind(this));
		this.urlHandler.handle("geocodeStatus",this.setEditPassThrough_geocodeStatus.bind(this));
		this.urlHandler.handle("list",this.setEditPassThrough_list.bind(this));
		this.urlHandler.handle("searchResults",this.setSearchResultIds.bind(this));
				
		// update position information for edit point
		if (this.config.editMode) this.checkEditPOIPosition();
	},
	/**
	 * 
	 * @param {Object} content
	 */
	setLang: function(content) {
		if (content.indexOf("fr") != -1) this.config.usedLang = 0;
		if (content.indexOf("de") != -1) this.config.usedLang = 1;
		if (content.indexOf("en") != -1) this.config.usedLang = 2;
	},
	setCenter: function(pos) {
		this.config.lon = pos[0];
		this.config.lat = pos[1];
	},
	setZoomLevel: function(level) {
		if (this.config.minimumAllowedZoomLevel > level){
			this.config.zoomLevel = this.config.minimumAllowedZoomLevel;
		}else{
			this.config.zoomLevel = parseInt(level);
		}
	},
	setZoomFactor: function(factor) {
		for (var item = 0; item < this.config.agsResolution.length ; item++) {
			if (this.config.agsResolution[item] == factor) this.config.zoomLevel = item;
		}
	},
	setMapScale: function(scale) {
		for (var item = 0; item < this.config.agsScale.length ; item++) {
			if (this.config.agsScale[item] == scale) this.config.zoomLevel = item;
		}
	},
	setFullWidth: function() {
		var dim = this.getBrowserDimensions();
		this.config.width = dim[0] - this.config.left;
	},
	setFullHeight: function() {
		var dim = this.getBrowserDimensions();
		this.config.height = dim[1] - this.config.top;
	},
	setWidth: function(width) {
		this.config.width = width;
	},
	setHeight: function(height) {
		this.config.height = height;
	},
	setEditSession: function(bool) {
		if (bool == "true") this.config.editMode = true;
	},
	setEditRecode: function(bool) {
		if (bool == "true") this.config.editObject.recode = true;
	},
	setEditID: function(id) {
		this.config.editObject.id = id;
	},
	setEditType: function(type) {
		this.config.editObject.type = type;
	},
	setEditName: function(name) {
		this.config.editObject.name = decodeURIComponent(name);
	},
	setEditPassThrough_geocodeStatus: function(content) {
		this.config.editPassThrough += "&geocodeStatus="+content;
	},
	setEditPassThrough_showNeverGeocodePOIs: function(content) {
		this.config.editPassThrough += "&showNeverGeocodePois="+content;
	},
	setEditPassThrough_list: function(content) {
		this.config.editPassThrough += "&list="+content;
	},
	setSearchResultIds: function(content) {
		// if parameter ids is set, then only search results are displayed. Therefore we override the normal extent queries by
		// changing "..../extent?" to "..../searchExtent?ids=123,456,789&" so the rest is the same for this session.
		// Advantage: No code change necessary...
		this.config.showOnlySearchResults = true;
		this.config.searchResultIds = content.split(",");
		this.config.poiServletAddress = this.config.searchResultServletAddress+"ids="+content;
	},
	setOverviewMap: function() {
		this.config.isOverviewMap = true;
	},
	connectMap: function(map) {
		this.config.connectedMap = map;
		this.config.hasConnectedMap = true;
	},
	getState: function() {
		var state = null;
		if (this.mapManager != null) {
			var center = this.mapManager.getCenter();
			var si = this.serviceIndex;
			var exportSI = new ServiceIndex();
			for (l in si.idx){
				exportSI.idx[l].isVisible = si.idx[l].isVisible
			}
			state = new State(center[1], center[0], this.mapManager.getCurrentZoomLevel(), exportSI);
		}
		else {
			// if we haven't drawn the map yet assume the config is right...
			state = new State(this.config.lat, this.config.lon, this.config.zoomLevel, null);
		}
		return state;
	},
	setState: function(state) {
		if (state.zoomLevel != null) this.setZoomLevel(state.zoomLevel);
		var center = new Array(state.lon, state.lat);
		this.serviceIndex=state.serviceIndex;
		this.setCenter(center);
		
		// STATE REPAINT OR NOT?
		this.repaint();
	},
	setMoveObserver: function(obj, callBack) {
		this.config.moveObserver = callBack.bindAsEventListener(obj);
	},
	setCenterObserver: function(obj, callBack) {
		this.config.centerObserver = callBack.bindAsEventListener(obj);
	},
	observeMoves: function(center) {
		this.setCenter(center);
		this.repaint();
	},
	observeCenter: function() {
		return this.mapManager.getCenter();
	},
	zoomTo: function(level) {
		this.mapManager.zoomTo(level);
	},
	checkEditPOIPosition: function() {
		this.config.editObject.lon = this.config.lon;
		this.config.editObject.lat = this.config.lat;
	},
	getBrowserDimensions: function() {
		var winWidth, winHeight, d=document;
		if (typeof(window.innerWidth) !='undefined') {
			winWidth = window.innerWidth;
			winHeight = window.innerHeight;
		}
		else {
			if (d.documentElement && (typeof(d.documentElement.clientWidth) !='undefined') && (d.documentElement.clientWidth != 0)) {
				winWidth = d.documentElement.clientWidth;
				winHeight = d.documentElement.clientHeight;
			}
			else {
				if (d.body && (typeof(d.body.clientWidth) != 'undefined')) {
					winWidth = d.body.clientWidth;
					winHeight = d.body.clientHeight;
				}
			}
		}
		return new Array(winWidth,winHeight);
	},
	getBrowserLang: function() {
		// all based on lang array, defined in config -> 0 = fr, 1 = de, 2 = en
		// default to german!
		this.config.usedLang = 1;

		var testLang = "de";
		if (typeof(navigator.browserLanguage) != "undefined") testLang = navigator.browserLanguage; // works in IE
		if (typeof(navigator.language) != "undefined") testLang = navigator.language; // works in FF, Opera, Netscape
			
		if (testLang.indexOf("en") != -1) this.config.usedLang = 2;
		if (testLang.indexOf("fr") != -1) this.config.usedLang = 0;
	},
	getDOMParentPosition: function() {
       var off = Position.cumulativeOffset(this.DOMParent);
       this.config.DOMParentOffsetX = off[0];
       this.config.DOMParentOffsetY = off[1];
    },
	paint: function() {
		if (this.config.suspendRedraw) return;
			
		this.createMapCon();

		if (this.mapManager == null){
			if (this.serviceIndex==null) this.serviceIndex = new ServiceIndex()
			this.mapManager = new MapManager(this,this.container,this.config, this.serviceIndex, this.eventLogger);
		}

		// compass rose
		if (this.compassRose == null) this.compassRose = new CompassRose(this.mapManager,this.container,this.config);
		// zoom bar
		if (this.zoomBar == null) this.zoomBar = new ZoomBar(this.mapManager,this.container,this.config);	
		// interactions are all things like mouse gestures, key strokes etc.
		if (this.interactions == null) {
			if (!this.config.isOverviewMap || (this.config.isOverviewMap && this.config.hasInteractions)) {
				this.interactions = new Interactions(this.container,this.mapManager,this.compassRose);
			}
		}
		// draws the copyright infos
		if (this.copyrightInfo == null) this.copyrightInfo = new CopyrightInfo(this.config,this.container);

		this.mapManager.paint();
		if (this.config.hasCompassRose) this.compassRose.paint();
		if (this.config.hasZoomBar) this.zoomBar.paint();
		if (this.config.hasCopyrightInfo) this.copyrightInfo.paint();

		if (this.interactions != null) this.interactions.keyboard();
		if (!this.config.isOverviewMap) this.interactions.mouse();
		
		// also paint the overview map if necessary
		if (this.config.hasConnectedMap && !this.config.isOverviewMap) {
			this.config.connectedMap.paint();
		}

		this.painted = true;
	},
	repaint: function() {
		if (this.config.suspendRedraw) return;

//		this.remove();
		this.painted = false;
		if (this.config.hasConnectedMap && !this.config.isOverviewMap){
//			//debug.writeline("Overview map found - removing")
			this.config.connectedMap.remove();
//			//debug.writeline("Overview map removed");
		}
		if (this.compassRose != null) this.compassRose.remove();
		if (this.mapManager != null) this.mapManager.remove();
		if (this.zoomBar != null) this.zoomBar.remove();
		if (this.copyrightInfo != null) this.copyrightInfo.remove();
		if (this.interactions != null) this.interactions.remove();

		try {
			this.container.parentNode.removeChild(this.container);
		}
		catch(e) {
			//debug.writeline("removing map container failed");
		}

		this.compassRose = null;
		this.mapManager = null;
		this.zoomBar = null;
		this.copyrightInfo = null;
		this.interactions = null;
		this.container = null;
		this.paint();
		

		this.painted = true;
	},
	suspendRedraw: function(susp){
		this.config.suspendRedraw = susp;
	},
	
	
// here comes the official API for the TASH project:
 
	
	// this function will check all the set*** inputs, if they are a valid list
	checkInput: function(input) {
		var outArray = new Array();
		for (var node = 0 ; node < input.length ; node++) {
			var asNum = parseInt(input[node]);
			if (!isNaN(asNum)) {
				outArray.push(asNum);	
			}
		}
		var outString = "0";
		if (outArray.length > 0) outString = outArray.join(",");
		return outString;
	},
	
	/*
	layer aus service index unsichtbar machen
	*/
	hideLayer: function(layers) {		
			for(var i=0;i<layers.length;i++){
				var tmp = layers[i];
				this.serviceIndex.idx[this.config.typeMapMapping[tmp]].isVisible=false;
				if (this.mapManager != null){
					if(this.isRWLayers(tmp)){
						this.mapManager.currentTileMatrix.hide(this.config.typeMapMapping[tmp]);
					}else{
						this.mapManager.currentTileMatrix.poiBox.hidePOIs(this.config.typeMapMapping[tmp]);
					}
					
				}
			}
		
		//debug.writeline("hideLayer");
	},
	/*
	layer aus service index sichtbar machen
	*/
	showLayer: function(layers) {	
			for(var i=0;i<layers.length;i++){
				var tmp = layers[i];
				this.serviceIndex.idx[this.config.typeMapMapping[tmp]].isVisible=true;
				if (this.mapManager != null){
					if(this.isRWLayers(tmp)){
						this.mapManager.currentTileMatrix.show(this.config.typeMapMapping[tmp]);
					}else{
						this.mapManager.currentTileMatrix.poiBox.showPOIs(this.config.typeMapMapping[tmp]);
					}
				}
			}	
		//debug.writeline("showLayer");
	},

	/*
		Gibt den ServiceIndex zurück
	*/
	getServiceIndex: function() {
		return this.serviceIndex;
	},
	isLayerVisible: function(layer){
		return this.serviceIndex.idx[this.config.typeMapMapping[layer]].isVisible;
	},
	
	resetConfig: function() {
		// here we will reset the configuration object which we will have saved at the beginning
	},
	
	remove: function() {
//		//debug.writeline("--- remove ---");

		this.painted = false;

		if (this.config.hasConnectedMap && !this.config.isOverviewMap){
//			//debug.writeline("Overview map found - removing")
			this.config.connectedMap.remove();
//			//debug.writeline("Overview map removed");
		}
		if (this.compassRose != null) this.compassRose.remove();
		if (this.mapManager != null) this.mapManager.remove();
		if (this.zoomBar != null) this.zoomBar.remove();
		if (this.copyrightInfo != null) this.copyrightInfo.remove();
		if (this.interactions != null) this.interactions.remove();

		try {
			this.container.parentNode.removeChild(this.container);
		}
		catch(e) {
			//debug.writeline("removing map container failed");
		}
//		//debug.writeline("--- remove successfull ---");
		this.compassRose = null;
		this.mapManager = null;
		this.zoomBar = null;
		this.copyrightInfo = null;
		this.interactions = null;
		this.container = null;
	},
	shutdown: function() {
		//debug.writeline("--- full shutdown ---");
		//this.remove();
		
		//debug.writeline("--- map is down, bye bye! ---");
	},
	
	hasZoomBar: function(hasZoomBar){
		this.config.hasZoomBar = hasZoomBar;
	},
	
	hasCompassRose: function(hasCompassRose){
		this.config.hasCompassRose = hasCompassRose;
	},
	
	setAggregation: function(isAggregation) {
		this.config.isAggregation=isAggregation;
	}

}
	

/*	MapManager
 * 
 * Handles all "external" map actions, abstract, no real image shuffling, no map src calcs
 * 
 */
MapManager = Class.create();
MapManager.prototype = {
	initialize: function(map,parent,config, serviceIndex, eventLogger) {
		this.map = map;
		this.container = parent;
		this.config = config;
		this.serviceIndex = serviceIndex;

		this.currentZoomLevel = this.config.zoomLevel;

		// used to prevent race conditions while zooming / panning
		this.moving = false;
		this.zooming = false;
				
		// predefine what should happen when the user interacts with the map
		// ! should be moved to interactions, shouldn't it?
		this.handDownObserver = this.down.bindAsEventListener(this);	
		this.handMoveObserver = this.handMove.bindAsEventListener(this);
		this.handUpObserver = this.up.bindAsEventListener(this);
		this.dblClickObserver = this.zoomIn.bindAsEventListener(this);

		// enables other objects to hook up to map events like panning and zooming
		this.listenerDispenser = new ListenerDispenser();
		
		// logs all actions to the alta4 clickCounter
		this.eventLogger = eventLogger;
		
		this.currentTileMatrix = null;
		
		this.minimizer = null;
	},
	paint: function() {
		// 0 is a4log "paint" event
		// 1 is a4log "move" event
		// 2 is a4log "zoom" event
		// 3 is a4log "identify" event
		this.eventLogger.log(0);

		if (this.currentTileMatrix == null) {
	
			// attach a new tile matrix and create the tiles. Position and zoomlevel is defined in config
			this.currentTileMatrix = new TileMatrix(this,this.config,this.container);
			this.currentTileMatrix.paint();
	
			if (this.config.hasPOIs) this.currentTileMatrix.startCaching();
			
			if (this.config.hasMinimizer) {
				this.minimizer = new Minimizer(this);
				this.minimizer.paint();
			}
			
			this.attachEventHandlers();
			this.map.map.notifyObservers("paint");	
		}
	},
	attachEventHandlers: function() {
		if (this.config.hasInteractions) {
			Event.observe(this.container,"mousedown",this.handDownObserver,false);
			Event.observe(this.container,"mousemove",this.handMoveObserver,false);
			Event.observe(this.container,"mouseup",this.handUpObserver,false);
		}
		if (!this.config.isOverviewMap) Event.observe(this.container,"dblclick",this.dblClickObserver,false);
	},
	detachEventHandlers: function() {
		if (this.config.hasInteractions) {
			Event.stopObserving(this.container,"mousedown",this.handDownObserver,false);
			Event.stopObserving(this.container,"mousemove",this.handMoveObserver,false);
			Event.stopObserving(this.container,"mouseup",this.handUpObserver,false);
		}
		if (!this.config.isOverviewMap) Event.stopObserving(this.container,"dblclick",this.dblClickObserver,false);
	},
	zoom: function(amount) {
		// see if we got a flag for using mouse coords rather then map center
		var data =  $A(arguments);
		var doZoomOnMousePos = false;
		if (data.length > 1) doZoomOnMousePos = data[1];
		
		// update container offset
		this.map.getDOMParentPosition();

		// get center coordinates to create new tile matrix centered there
		var oldCenter = this.currentTileMatrix.coordinates.getCenter();
		// if it should be an mouse triggered event, use the cursor position to zoom to...
		if (doZoomOnMousePos) oldCenter = this.currentTileMatrix.coordinates.getMousePosInRW();

		if (!this.zooming && this.checkZoomAmount(amount)) {

			// log map drawing		
			// 0 is a4log "paint" event
			// 1 is a4log "move" event
			// 2 is a4log "zoom" event
			// 3 is a4log "identify" event
			this.eventLogger.log(2);
			this.listenerDispenser.trigger("preZoom");

			this.zooming = true;

			this.currentTileMatrix.removePOIs();
						
			// update zoom level
			this.currentZoomLevel += amount;

			// if we have a connected overview map, update its zoomlevel too...
			if (this.config.hasConnectedMap && !this.config.isOverviewMap) {
				var ovMapLevel = this.currentZoomLevel - this.config.overviewMapZoomlevelDifference;
				if (ovMapLevel < 0) ovMapLevel = 0;
				this.config.connectedMap.zoomTo(ovMapLevel);
			}

			// prepare the new tile matrix config
			this.config.zoomLevel = this.currentZoomLevel;
			this.config.lon = oldCenter[0];
			this.config.lat = oldCenter[1];
			
			// save the old tile matrix into a temporary variable
			this.tempTileMatrix = this.currentTileMatrix;

			// create the new one
			this.currentTileMatrix = new TileMatrix(this,this.config,this.container);
			this.currentTileMatrix.prepare();

			//defines that the tile matrix is invisible when created
			this.currentTileMatrix.setHidden();
			this.currentTileMatrix.paint();
	
			this.zoom2();
		}
	},
	zoom2: function() {
		this.currentTileMatrix.setVisible();
		this.currentTileMatrix.startCaching();

		if (this.tempTileMatrix) {
			this.tempTileMatrix.remove();
			this.tempTileMatrix = null;
		}

		this.zooming = false;

		this.listenerDispenser.trigger("postZoom");
		this.map.map.notifyObservers("zoom");	
	},
	checkZoomAmount: function(amount) {
		if (this.currentZoomLevel + amount >= this.config.agsResolution.length) return false;
		if (this.currentZoomLevel + amount < this.config.minimumAllowedZoomLevel) return false;
		return true;
	},
	zoomIn : function(e) {
		// check if we are triggered by an mouse event (then zoom on mouse pos)
		if (typeof(e) == "object") {
			Event.stop(e);
			this.zoom(1,true);
		}
		else this.zoom(1);
	},
	zoomOut : function(e) {
		this.zoom(-1);
	},
	zoomTo: function(level) {
		var amount = level - this.currentZoomLevel;
		if (amount != 0) {
			this.zoom(amount);
		}
	},
	zoomToAndCenter: function(level, point){
		this.config.lon = point.lon;
		this.config.lat = point.lat;
		var amount = level - this.currentZoomLevel;
		//this.zoom(amount);
		// see if we got a flag for using mouse coords rather then map center
		//var data =  $A(arguments);
		//var doZoomOnMousePos = false;
		//if (data.length > 1) doZoomOnMousePos = data[1];
		
		// update container offset
		this.map.getDOMParentPosition();

		// get center coordinates to create new tile matrix centered there
		//var oldCenter = this.currentTileMatrix.coordinates.getCenter();
		// if it should be an mouse triggered event, use the cursor position to zoom to...
		//if (doZoomOnMousePos) oldCenter = this.currentTileMatrix.coordinates.getMousePosInRW();

		if (!this.zooming && this.checkZoomAmount(amount)) {

			// log map drawing
			// 0 is a4log "paint" event
			// 1 is a4log "move" event
			// 2 is a4log "zoom" event
			// 3 is a4log "identify" event
			this.eventLogger.log(2);
			this.listenerDispenser.trigger("preZoom");

			this.zooming = true;

			this.currentTileMatrix.removePOIs();
						
			// update zoom level
			this.currentZoomLevel += amount;

			// if we have a connected overview map, update its zoomlevel too...
			if (this.config.hasConnectedMap && !this.config.isOverviewMap) {
				var ovMapLevel = this.currentZoomLevel - this.config.overviewMapZoomlevelDifference;
				if (ovMapLevel < 0) ovMapLevel = 0;
				
				//es: wenn nur zoomTo: bei Pulldown-Reg-Selektion nicht synchron
				this.config.connectedMap.setCenter([point.lon,point.lat]);
				this.config.connectedMap.repaint();
			}

			// prepare the new tile matrix config
			this.config.zoomLevel = this.currentZoomLevel;
			//this.config.lon = oldCenter[0];
			//this.config.lat = oldCenter[1];
			
			// save the old tile matrix into a temporary variable
			this.tempTileMatrix = this.currentTileMatrix;

			// create the new one
			this.currentTileMatrix = new TileMatrix(this,this.config,this.container);
			this.currentTileMatrix.prepare();

			//defines that the tile matrix is invisible when created
			this.currentTileMatrix.setHidden();
			this.currentTileMatrix.paint();
	
			this.zoom2();
		}
	},
	getCurrentZoomLevel: function() {
		return this.currentZoomLevel;
	},
	getCurrentZoomFactor: function() {
//		return this.config.zoomFactors[this.currentZoomLevel];
		return this.config.agsScale[0] / this.config.agsScale[this.currentZoomLevel];
	},
	getCurrentPxOff: function(dir) {
		if (this.currentTileMatrix) return this.currentTileMatrix.mapSource.getPxOff(dir);
	},
	// Event handler: Panning
	down: function(e){
		// log map move
		// 0 is a4log "paint" event
		// 1 is a4log "move" event
		// 2 is a4log "zoom" event
		// 3 is a4log "identify" event
		this.eventLogger.log(1);
		this.moving = true;
		this.oldMouseX = Event.pointerX(e);
		this.oldMouseY = Event.pointerY(e);
		Event.observe(document,"mousemove",this.handMoveObserver,true);
		Event.observe(document,"mouseup",this.handUpObserver,true);
		Event.stop(e);
	},
	up: function(e){
		this.moving = false;
		Event.stopObserving(document,"mousemove",this.handMoveObserver,true);
		Event.stopObserving(document,"mouseup",this.handUpObserver,true);
		
		var center = this.getCenter();
		this.config.lon = center[0];
		this.config.lat = center[1];

		if (this.config.hasConnectedMap) {
			this.config.connectedMap.setCenter(center);
			this.config.connectedMap.repaint();
		}
		if (this.config.moveObserver !== null) {
			this.config.moveObserver(center);
		}

		Event.stop(e);		
	},
	handMove: function(e){
		if(this.moving) {
			this.move(Event.pointerX(e) - this.oldMouseX, Event.pointerY(e) - this.oldMouseY);
			this.oldMouseX = Event.pointerX(e);
			this.oldMouseY = Event.pointerY(e);
		}
		Event.stop(e);
	},
	getCenter: function() {
		return this.currentTileMatrix.coordinates.getCenter();
	},
	centerAt: function(rwX,rwY){
		if (typeof(this.currentTileMatrix) == "object") {
			// log map move
			// 0 is a4log "paint" event
			// 1 is a4log "move" event
			// 2 is a4log "zoom" event
			// 3 is a4log "identify" event
			this.eventLogger.log(1);

			var currentCenter = this.getCenter();

			var currentCenterPx = this.currentTileMatrix.coordinates.rw2px(currentCenter[0],currentCenter[1]);
			
			var targetCenterPx = this.currentTileMatrix.coordinates.rw2px(rwX,rwY);
			
			var pxDeltaX = currentCenterPx[0] - targetCenterPx[0];
			var pxDeltaY = currentCenterPx[1] - targetCenterPx[1];

			//this.moveBy(pxDeltaX,pxDeltaY);
			// we start at
			this.alreadyMovedX = 0;
			this.alreadyMovedY = 0;

			// we want to
			this.moveX = pxDeltaX;
			this.moveY = pxDeltaY;

			// this means direction
			(pxDeltaX != 0) ? this.moveDirX = this.moveX/Math.abs(this.moveX) : this.moveDirX = 1;
			(pxDeltaY != 0) ? this.moveDirY = this.moveY/Math.abs(this.moveY) : this.moveDirY = 1;
			
			// and step size
			this.moveStepX = this.moveX;
			this.moveStepY = this.moveY;
			
			this.move(this.moveStepX,this.moveStepY);

		}
	},
	moveTo: function(rwX,rwY) {
		if (typeof(this.currentTileMatrix) == "object") {
			// log map move
			// 0 is a4log "paint" event
			// 1 is a4log "move" event
			// 2 is a4log "zoom" event
			// 3 is a4log "identify" event
			this.eventLogger.log(1);

			var currentCenter = this.getCenter();

			var currentCenterPx = this.currentTileMatrix.coordinates.rw2px(currentCenter[0],currentCenter[1]);
			
			var targetCenterPx = this.currentTileMatrix.coordinates.rw2px(rwX,rwY);
			
			var pxDeltaX = currentCenterPx[0] - targetCenterPx[0];
			var pxDeltaY = currentCenterPx[1] - targetCenterPx[1];

			this.moveBy(pxDeltaX,pxDeltaY);
		}
	},
	moveBy: function(deltaX,deltaY) {
		if (!this.moving) {
			// log map move
			// 0 is a4log "paint" event
			// 1 is a4log "move" event
			// 2 is a4log "zoom" event
			// 3 is a4log "identify" event
			this.eventLogger.log(1);

			// we start at
			this.alreadyMovedX = 0;
			this.alreadyMovedY = 0;

			// we want to
			this.moveX = deltaX;
			this.moveY = deltaY;

			// this means direction
			(deltaX != 0) ? this.moveDirX = this.moveX/Math.abs(this.moveX) : this.moveDirX = 1;
			(deltaY != 0) ? this.moveDirY = this.moveY/Math.abs(this.moveY) : this.moveDirY = 1;
			
			// and step size
			this.moveStepX = this.moveX / this.config.moveSpeed;
			this.moveStepY = this.moveY / this.config.moveSpeed;

			// now trigger
			var that = this;
			new PeriodicalExecuter(function(pe) {
 				if ((that.alreadyMovedX+Math.abs(that.moveStepX)) <= Math.abs(that.moveX) || (that.alreadyMovedY+Math.abs(that.moveStepY)) <= Math.abs(that.moveY)) {
    				that.move(that.moveStepX,that.moveStepY);
					that.alreadyMovedX += Math.abs(that.moveStepX);
					that.alreadyMovedY += Math.abs(that.moveStepY);
				}else{
					pe.stop();
				}
			}, 0.05);
//			this.numberMove();
		}
	},
	numberMove: function() {
		if (this.moving) {
			if ((this.alreadyMovedX+Math.abs(this.moveStepX)) <= Math.abs(this.moveX) || (this.alreadyMovedY+Math.abs(this.moveStepY)) <= Math.abs(this.moveY)) {

				this.move(this.moveStepX,this.moveStepY);

				this.alreadyMovedX += Math.abs(this.moveStepX);
				this.alreadyMovedY += Math.abs(this.moveStepY);

				setTimeout(this.numberMove.bind(this),20);
			}
			else this.moving = false;
		}
	},
	moveToAndCall: function(rwX,rwY, func) {
		if (typeof(this.currentTileMatrix) == "object") {
			// log map move
			// 0 is a4log "paint" event
			// 1 is a4log "move" event
			// 2 is a4log "zoom" event
			// 3 is a4log "identify" event
			this.eventLogger.log(1);

			var currentCenter = this.getCenter();

			var currentCenterPx = this.currentTileMatrix.coordinates.rw2px(currentCenter[0],currentCenter[1]);
			
			var targetCenterPx = this.currentTileMatrix.coordinates.rw2px(rwX,rwY);
			
			var pxDeltaX = currentCenterPx[0] - targetCenterPx[0];
			var pxDeltaY = currentCenterPx[1] - targetCenterPx[1];
			this.moveByAndCall(pxDeltaX,pxDeltaY,func);
		}
	},
	moveByAndCall: function(deltaX,deltaY,func) {
		if (!this.moving) {
			// log map move
			// 0 is a4log "paint" event
			// 1 is a4log "move" event
			// 2 is a4log "zoom" event
			// 3 is a4log "identify" event
			this.eventLogger.log(1);

			// we start at
			this.alreadyMovedX = 0;
			this.alreadyMovedY = 0;

			// we want to
			this.moveX = deltaX;
			this.moveY = deltaY;

			// this means direction
			(deltaX != 0) ? this.moveDirX = this.moveX/Math.abs(this.moveX) : this.moveDirX = 1;
			(deltaY != 0) ? this.moveDirY = this.moveY/Math.abs(this.moveY) : this.moveDirY = 1;
			
			// and step size
			this.moveStepX = this.moveX / this.config.moveSpeed;
			this.moveStepY = this.moveY / this.config.moveSpeed;

			// now trigger
			var that = this;
			new PeriodicalExecuter(function(pe) {
 				if ((that.alreadyMovedX+Math.abs(that.moveStepX)) <= Math.abs(that.moveX) || (that.alreadyMovedY+Math.abs(that.moveStepY)) <= Math.abs(that.moveY)) {
    				that.move(that.moveStepX,that.moveStepY);
					that.alreadyMovedX += Math.abs(that.moveStepX);
					that.alreadyMovedY += Math.abs(that.moveStepY);
				}else{
					pe.stop();
					func();
				}
			}, 0.05);
		}
	},
	numberMoveAndCall: function(func) {
		if (this.moving) {
			if ((this.alreadyMovedX+Math.abs(this.moveStepX)) <= Math.abs(this.moveX) || (this.alreadyMovedY+Math.abs(this.moveStepY)) <= Math.abs(this.moveY)) {

				this.move(this.moveStepX,this.moveStepY);

				this.alreadyMovedX += Math.abs(this.moveStepX);
				this.alreadyMovedY += Math.abs(this.moveStepY);

				setTimeout(this.numberMoveAndCall.bind(this, func),20);
			}
			else {
				this.moving = false;
				func();
			}
		}
	},
	
	move: function(deltaX,deltaY) {
		// some more plausibility checks on the deltas:
		if (isNaN(deltaX)) deltaX = 0;
		if (isNaN(deltaY)) deltaY = 0;
		
		this.currentTileMatrix.move(deltaX,deltaY);
		this.listenerDispenser.trigger("move");	
	},
	setSize: function(width, height) {
		this.container.style.height = height+"px";
		this.container.style.width = width+"px";
	},
	remove: function() {
		Event.stopObserving(document,"mousemove",this.handMoveObserver,true);
		Event.stopObserving(document,"mouseup",this.handUpObserver,true);
		Event.stopObserving(this.container,"mousedown",this.handDownObserver,false);
		Event.stopObserving(this.container,"mousemove",this.handMoveObserver,false);
		Event.stopObserving(this.container,"mouseup",this.handUpObserver,false);
		Event.stopObserving(this.container,"dblclick",this.dblClickObserver,false);

		this.handDownObserver = this.down.bindAsEventListener(this);	
		this.handMoveObserver = this.handMove.bindAsEventListener(this);
		this.handUpObserver = this.up.bindAsEventListener(this);
		this.dblClickObserver = this.zoomIn.bindAsEventListener(this);
		
		if (this.currentTileMatrix != null) {
			this.currentTileMatrix.remove();
			this.currentTileMatrix = null;
		}
		if (this.listenerDispenser != null) {
			this.listenerDispenser.remove();
			this.listenerDispenser = null;
		}
		if (this.tempTileMatrix != null) {
			this.tempTileMatrix.remove();
			this.tempTileMatrix = null;
		}
		if (this.eventLogger != null) {
			this.eventLogger.remove();
			this.eventLogger = null;
		}
//		//debug.writeline("mapManager removed");
	}
}

/*	MapSource
 * 
 * MapSource is responsible for the map displayed on the tiles. This is the connection to the map server.
 * MapSource does nothing else as to connect a tile offset with a src from the map server
 * 
 */
MapSource = Class.create();
MapSource.prototype = {
	initialize: function(config, coordinates, url) {
		this.coordinates = coordinates;
		this.config = config;
		this.pxXoff = 0;
		this.pxYoff = 0;
		this.tileXoff = 0;
		this.tileYoff = 0;
		this.pxDelta = 0;
		this.url = url;
	},
	prepare: function() {
		this.currentZoomLevel = this.config.zoomLevel;
//		this.currentZoomFactor = this.config.zoomFactors[this.currentZoomLevel];
		this.currentZoomFactor = this.config.agsScale[0] / this.config.agsScale[this.currentZoomLevel];

		this.pxDelta = this.coordinates.rw2px(this.config.lon,this.config.lat);
//		//debug.writeline("using center: "+this.config.lat+","+this.config.lon+" creates "+pxDelta[0]+"x"+pxDelta[1]);

		// adding half of a viewport width as we want to center the map.
		this.pxXoff = this.pxDelta[0] - Math.round(this.config.width / 2);
		this.pxYoff = this.pxDelta[1] - Math.round(this.config.height / 2);
//		alert("creates px off: "+this.pxXoff+","+this.pxYoff);
	},
	getSrc: function(xIndex,yIndex) {
		xIndex += this.tileXoff;
		var xDirArrIndex = Math.floor( xIndex / this.currentZoomFactor);
		var goodX = xIndex - xDirArrIndex * this.currentZoomFactor;

		yIndex += this.tileYoff;
		var yDirArrIndex = Math.floor( yIndex / this.currentZoomFactor);
		var goodY = yIndex - yDirArrIndex * this.currentZoomFactor;
		
		if ( ((xDirArrIndex >= 0) && (xDirArrIndex < this.config.xSegments.length)) && ((yDirArrIndex >= 0) && (yDirArrIndex < this.config.ySegments.length))) {
			if (goodX >= 0 && goodX < this.currentZoomFactor && goodY >= 0 && goodY < this.currentZoomFactor) {
				return this.config.mapTileDir + this.url + this.config.xSegments[xDirArrIndex] + "-" + (this.config.xSegments[xDirArrIndex] + 1) + "_" + this.config.ySegments[yDirArrIndex] + "-" + (this.config.ySegments[yDirArrIndex] + 1) + "/" + this.currentZoomFactor + "x" + this.currentZoomFactor + "/" + ((this.currentZoomFactor - 1) - (goodY - this.currentZoomFactor * Math.floor(goodY / this.currentZoomFactor))) + "x" + (goodX - this.currentZoomFactor * Math.floor(goodX / this.currentZoomFactor)) +  "." + this.config.mapTileImgFormat;
			}
		}
		return this.config.spacerImg;
	},
	addPxOff: function(x,y) {
		//window.defaultStatus = "adding px off "+x+", "+y+" --> "+this.pxXoff+", "+this.pxYoff;
		this.pxXoff += x;
		this.pxYoff += y;
	},
	getPxOff: function(dir) {
		if (dir == "y") return this.pxYoff;
		else return this.pxXoff;
	},
	getTileOff: function(dir) {
		if (dir == "y") return Math.floor(this.pxYoff / this.config.tileHeight);
		else return Math.floor(this.pxXoff / this.config.tileHeight);
	},
	remove: function() {
		this.config = null;
		this.coordinates = null;
	}
}


AGS_MapSource = Class.create();
AGS_MapSource.prototype = Object.extend(new MapSource(), {
	getSrc: function(xIndex,yIndex) {

		var zoomLevel = this.currentZoomLevel.toString();
		while (zoomLevel.length < 2) zoomLevel = "0"+zoomLevel;

		var folder = this.config.mapTileDir+this.url+zoomLevel;
		
		xIndex += this.tileXoff;
		yIndex += this.tileYoff;

		var subFolder = this.dez2hex(yIndex);
		while (subFolder.length < 8) subFolder = "0" + subFolder;
	
		var imgNr = this.dez2hex(xIndex);
		while (imgNr.length < 8) imgNr = "0"+imgNr;

		return folder + "/R" + subFolder+ "/C"+ imgNr + "." + this.config.mapTileImgFormat;
	},
	hex2dez: function(hex) {
		return parseInt(hex,16);
	},
	dez2hex: function(dez) {
		return dez.toString(16);
	}
});
Minimizer = Class.create();
Minimizer.prototype = {
	initialize: function(mapManager) {
		this.mapManager = mapManager;
		this.config = this.mapManager.config;
		this.img = null;
		
		this.maximized = true;

		this.clickHandler = this.toggle.bindAsEventListener(this);
	},
	paint: function() {
		this.img = document.createElement("img");
		this.img.src = this.config.minimizerMinimizeImageSrc;

		this.img.width = this.config.minimizerImageSize[0];
		this.img.height = this.config.minimizerImageSize[1];
				
		this.positionIcon(this.config.height - this.config.minimizerImageSize[0], this.config.width - this.config.minimizerImageSize[1]);

		this.mapManager.container.appendChild(this.img);
		
		Event.observe(this.img,"mousedown",this.clickHandler,true);
	},
	toggle: function(e) {
		if (this.maximized) {
			this.mapManager.setSize(this.config.minimizerImageSize[0],this.config.minimizerImageSize[1]);
			this.mapManager.detachEventHandlers();

			this.positionIcon(0,0);
			this.img.src = this.config.minimizerMaximizeImageSrc;
			
			Element.setStyle(this.mapManager.map.DOMParent, { width:this.config.minimizerImageSize[0]+"px" , height:this.config.minimizerImageSize[1]+"px" });

			this.maximized = false;
			this.config.suspendRedraw = true;
		}
		else {
			var newCenter = this.config.centerObserver();
			this.mapManager.centerAt(newCenter[0], newCenter[1]);
		
			this.mapManager.setSize(this.config.width, this.config.height);
			this.mapManager.attachEventHandlers();

			this.img.src = this.config.minimizerMinimizeImageSrc;
			this.positionIcon(this.config.height - this.config.minimizerImageSize[0], this.config.width - this.config.minimizerImageSize[1]);

			Element.setStyle(this.mapManager.map.DOMParent, { width:this.config.width+"px" , height:this.config.height+"px" });

			this.maximized = true;
			this.config.suspendRedraw = false;
		}
		Event.stop(e);
	},
	positionIcon: function(top, left) {
		this.img.style.position="absolute";
		this.img.style.zIndex = 20000;
		this.img.style.top = top+"px";
		this.img.style.left = left+"px";
	},
	remove: function() {
		if (this.img !== null) {
			Event.stopObserving(this.img,"click",this.clickHandler,false);
			this.mapManager.container.removeChild(this.img);
		}
	},
	dispose: function() {
		this.remove();
		this.img = null;
	}
};
/*	POI
 * 
 * One single point. All actions. NameTag, PopUp, Data loading.
 * 
 */
POI = Class.create();
POI.prototype = {
	initialize: function(poiManager,parent,container,coordinates,point,id,type,name,config,logger) {
		this.poiManager = poiManager;
		this.parent = parent; // Map Container
		this.container = container; // POICon
		this.coordinates = coordinates;
		this.config = config;
		this.type = type;
		this.point = point;
		this.id = id;
		this.name = name;
		this.isMoveable = false;
		this.data = null;
		this.nameTag = null;
		this.nameDiv = null; 
		this.hideCountdown = null;
		this.popupIsVisible = false;
		this.poiObservers = {};
		this.iconInfo = null;
		this.upObserver = this.up.bindAsEventListener(this);
		
		this.felsCount = null; //nur bei Gebieten
		
		// preparation for possible edit mode, when points can be moved
		this.editBar = null;
		this.eventLogger = logger;
	},
	setFelsCount: function(count){
		this.felsCount = count;
	},
	getFelsCount: function(){
		return this.felsCount;
	},
	notifyObservers: function() {
		var data = $(arguments)
		for (o in this.poiObservers){
			this.poiObservers[o].update(this,data[0]);
		}
	},
	addObserver: function(name, observer){
		this.poiObservers[name]=observer;
	},
	getIcon: function(type) {
		if (this.iconInfo==null){
			if (typeof(this.config.usedIcons[type]) == "object") this.iconInfo = this.config.usedIcons[type];
			else this.iconInfo = this.config.defaultIcon;
		}
		return this.iconInfo;
	},
	getIconActive: function(type) {

		if (typeof(this.config.usedIconsActive[type]) == "object") this.iconInfo = this.config.usedIconsActive[type];
		else this.iconInfo = this.config.defaultIcon;

		return this.iconInfo;
	},
	
	paint: function() {
		if (this.icon == null) {
			this.div = document.createElement("div");
			this.div.style.zIndex = 10; //standard-wert -> bezogen auf eigenen GrouPayer (neuer Stack) 
			this.icon = document.createElement("img");
			this.icon.src = this.getIcon(this.type)[0];
			this.sizeX = this.getIcon(this.type)[1];
			this.sizeY = this.getIcon(this.type)[2];

			Element.setStyle(this.icon, { display:"inline", width:this.sizeX+"px", height:this.sizeY+"px"});
			
			
 
	
			//Element.addClassName(this.div,"poi");
			if(this.type == "gebiet"){
				this.span = document.createElement("span");
				this.div.style.backgroundImage="url('/viewer/symbols/dav/blue.png')";
				
				var txt = document.createTextNode(this.felsCount);
				this.span.appendChild(txt);
						Element.setStyle(this.span,{
						position: 'relative',
					    float: 'left',
		 				width: '21px',
					    lineHeight: '21px ',
						verticalAlign: 'middle',
					    textAlign:'center',
					    color:'White',
					    fontWeight:'bold',
					    fontSize:'11px',
						fontFamily:'Arial',
						margin: '0px',		
						padding: '0px',
	   			 		cursor:'pointer'
					}); 
				this.div.appendChild(this.span);
			} else{
				this.div.appendChild(this.icon);
			}
			
			
			var delayClose = true;
			if (this.type == "meta") delayClose = false;
			this.popUp = new PopUp(this,this.div,Math.ceil(( this.sizeX / 2) + 1), -Math.ceil(this.sizeY / 2) - 1,delayClose);

				this.showNameObserver = this.showName.bindAsEventListener(this);
				this.hideObserver = this.hide.bindAsEventListener(this);
			if (!this.config.editMode && this.type != "gebiet") {
				Event.observe(this.icon,"mouseover",this.showNameObserver,false);
				Event.observe(this.icon,"mouseout",this.hideObserver,false);
			} else if (this.type == "gebiet") {
				//Gebiete haben kein Icon
				Event.observe(this.span,"mouseover",this.showNameObserver,false);
				Event.observe(this.span,"mouseout",this.hideObserver,false);
			}		
			if (this.config.width > this.config.showPopUpMinWidth && this.config.height > this.config.showPopUpMinHeight) {						
				// aggregated points do not have a popup.
				if (this.type != "meta" && this.type != "gebiet") {
					//popup öffnen
					this.downObserver = this.down.bindAsEventListener(this);
					this.upObserver = this.up.bindAsEventListener(this);
					Event.observe(this.icon,"mousedown",this.downObserver,false);
					Event.observe(this.icon,"mouseup",this.upObserver,false);
				} else if(this.type == "gebiet"){
					//zoomen
					this.downObserver = this.gebietsPoiClickHandler.bindAsEventListener(this);
					Event.observe(this.span, 'mousedown', this.downObserver);
				}				
			}			
			this.update();
			if (this.type != "meta") this.container.appendChild(this.div);
		}
	},
	showBubble: function(){
		this.overlay = document.createElement("div");
				this.textNode = document.createTextNode(""+this.idx);
				this.textContainer = document.createElement("span");
				this.textContainer.appendChild(this.textNode);
				this.overlay.appendChild(this.textContainer);
				Element.addClassName(this.overlay,"bubblesOverlay");
				
				// TASH specific switch:
				switch (this.type) {
					  case "Tourismusinformation":		  	
					  case "Bahnhof":
					  	Element.addClassName(this.overlay,"bubblesOverlayGray");
					  	break;
					  case "HotelPension":
					  case "FerienwohnungHaus":
					  case "Campingplatz":
					  case "Jugendherberge":
					  case "Bauernhof":
					  case "BettBike":
					  case "WellnesshotelBeautyfarm":
					  case "Gastronomie":
					  case "MuseumDenkmal":
					  case "Theater":
					  case "SchlossGarten":
					  case "KlosterKirche":
					  case "Freizeitpark":
					  case "TierparkZoo":
					  case "Event":
					  	Element.addClassName(this.overlay,"bubblesOverlayOrange");
					  	break;
					  case "Naturerlebnis":
					  case "Article":
					  case "StrandBaden":
					  case "Segeln":
					  	Element.addClassName(this.overlay,"bubblesOverlayBlue");
					  	break;
					  case "FahrradverleihReparaturservice":
					  case "AbschliessbareFahrradbox":
					  case "RastplatzSchutzhuette":
					  	Element.addClassName(this.overlay,"bubblesOverlayGreen");
					  	break;			    
					  default:
					  	Element.addClassName(this.overlay,"bubblesOverlayGreen");
					    break;
				}
				
				this.div.appendChild(this.overlay);
	},
	update: function() {
		var pixelPos = this.getPxCoords();
		Element.setStyle(this.div,{position:"absolute",left: pixelPos[0] - Math.round(this.sizeX/2) + "px",top: pixelPos[1] - Math.round(this.sizeY/2) + "px"});
		if (this.editBar != null) this.editBar.update();
	},
	remove: function() {
		if (this.popUp != null) this.popUp.close();
		if (this.icon != null) {
			try {
				this.container.removeChild(this.icon);
			}
			catch(e) {
				//debug.writeline("removing POI icon failed");
			}
		}
		if (this.editBar != null) this.editBar.remove();
	},
	deletePopup: function(){
		//andres Icon für aktives POI
		var newImg = this.config.usedIcons[this.type][0];
		this.icon.src = newImg; //andres Icon für aktives POI
		
		//soll den standard-z-wert bekommen (damit es kein popup überdeckt)
		//this.div.style.zIndex = null; //ES 200910 breaks IE8 on POI-click
		this.div.style.zIndex = ''; 
			
		if (this.popUp != null){
			this.popupIsVisible = false;
			this.popUp.close();
		}
	},
	showName: function() {
		if(!this.popupIsVisible){ //ES: Name-Div anzeigen nur wenn PopUp des Pois nicht offen ist (da in Felsinfo nicht gleich lang)
			this.openName();
			if (this.editBar != null) this.editBar.show();
		}
	},
	openName: function() {
		//Name
		if (this.nameTag == null) {
			this.nameTag = document.createElement("p");
			Element.addClassName(this.nameTag,"nameTag");
		}
		this.nameTag.innerHTML = this.name;
		
		//Zusatzinfos [es]
		if (this.nameTagInfo == null) {
			this.nameTagInfo = document.createElement("div");
			Element.setStyle(this.nameTagInfo,{fontWeight:"normal"}); //wäre sonst fett wg. ubergeordnert Klasse 
		}
		
		
		if (this.nameDiv == null) {
			this.nameDiv = document.createElement("div");
			this.nameDiv.id = this.id+"_popUp";
			Element.addClassName(this.nameDiv,"popUp");
			var pixelPos = this.getPxCoords();
			var offsetY = Math.ceil((this.sizeY/2));
			var offsetX = Math.ceil(this.sizeX/2);
			Element.setStyle(this.nameDiv,{position:"absolute",top:pixelPos[1]-offsetY+"px",left:pixelPos[0]+offsetX+"px"});
			
		}

		//Name PopUp soll über allen POI's erscheinen 
		//-> GroupLayer mit aus dem der Name PopUp "kommt" soll über allen andren GroupLayern angezeigt werden
		/*PH: Bugfix
		(Zeile auskommentiert: verhindert POIs vor PopUp beim darüberhowern)
		this.poiManager.resetZindexAllTypes();
		//this.container.style.zIndex=(parseInt(this.container.style.zIndex)+1000);	//diesen GroupLayer hochsezen  
		*/
		this.container.style.position="absolute"; //ohne 'absolute greift der Z-Index nicht'
		//this.div.style.zIndex = 2000;
		
		if(this.type == "gebiet"){
			this.nameTagInfo.innerHTML = this.getFelsCount()+" Felsen";
			
			//Zusatzinfos unter Namen anzeigen
			this.nameTag.appendChild(this.nameTagInfo);
		}
		
		this.nameDiv.appendChild(this.nameTag);
		this.container.appendChild(this.nameDiv);
	},
	hide: function() {
		this.triggerClose();
		if (this.editBar != null) this.editBar.triggerHide();
	},
	triggerClose: function(){		
		if (this.hideCountdown == null) {
			this.hideCountdown = setTimeout(this.close.bind(this),this.config.popUpHideDelay);
		}
	},
	close: function() {
		try {this.container.removeChild(this.nameDiv);}
		catch(e){}

		
		this.hideCountdown = null;
	},
	down: function(e){
		Event.stop(e);
		
		// log identifies
		// 0 is a4log "paint" event
		// 1 is a4log "move" event
		// 2 is a4log "zoom" event
		// 3 is a4log "identify" event
		this.eventLogger.log(3);
		
		if (this.config.editMode) {
			this.isMoveable = true;
			this.moveObserver = this.move.bindAsEventListener(this);
			Event.observe(this.parent,"mousemove",this.moveObserver,true);
		}
		else{
			//coordinate ermitteln und falls POI zu nah am Rand Karte zentrieren
			this.showPopUp();
		} 
		Event.observe(document,"mouseup",this.upObserver,false);
	},
	showPopUp: function(){
			this.poiManager.resetZindexAllTypes(); //PH
			this.notifyObservers("showPopUp");
			//PH bugfix/workaround 0003957 
			this.div.style.zIndex = this.div.style.zIndex + 11000;  //PH: damit PopUp über allen POIs 
			this.container.style.zIndex = 2000; //PH: damit PopUp-Grouplayer nicht überdeckt wird
			
			if(!this.popupIsVisible){			
				this.popupIsVisible = true;
				if(this.data == null){
					this.loadDataFromServlet();			
				}else{
					this.popUpEvent();
				}
			}else{
				this.closePopUp();
			}
	},
	closePopUp: function() {
		this.popupIsVisible = false;
		this.popUp.close();
		
		//beim Zoomout darf das popup nicht wieder offen sein (da hiermit geschlossen)
		this.poiManager.tileMatrix.mapManager.map.activePoi = null;
		this.poiManager.tileMatrix.mapManager.map.activePoiId = null;
		this.poiManager.tileMatrix.mapManager.map.activePoiType= null;
		
	},
	up: function(e){
		if (this.config.editMode) {
			this.isMoveable = false;

			// save the coordinates in the persistent edit object in config to retrieve them again after zoom
			this.config.editObject.lon = this.point.getX();
			this.config.editObject.lat = this.point.getY();

			// update the server session to reflect new coordinates
			this.noticeCoords();

			Event.stopObserving(this.parent,"mousemove",this.moveObserver,true);
		}

		Event.stopObserving(document,"mouseup",this.upObserver,false);
		Event.stop(e);
	},
	move: function(e){
		if(this.isMoveable){
			var pxXoff = this.coordinates.tileMatrix.mapSource.pxDelta[0] - Math.round(this.config.width / 2);
			var pxYoff = this.coordinates.tileMatrix.mapSource.pxDelta[1] - Math.round(this.config.height / 2);
			var tX = this.coordinates.viewPortMouseX() + pxXoff;
			var tY = this.coordinates.viewPortMouseY() + pxYoff;
			
			var rwPos = this.coordinates.px2rw(tX,tY);
			this.point.setX(rwPos[0]);
			this.point.setY(rwPos[1]);
			
			this.update();
			Event.stop(e);
		}
	},
	getPxCoords: function() {
		return this.coordinates.rw2px(this.point.getX(),this.point.getY());
	},
	setPxCoords: function(newCoords) {
		var rwCoords = this.coordinates.px2rw(newCoords[0],newCoords[1]);
		this.point.setXY(rwCoords[0],rwCoords[1]);
	},	
	noticeCoords: function() {
		//debug.writeline(this.config.editServletAddress+"id="+this.id+"&lat="+this.point.getY()+"&lon="+this.point.getX()+"&sessionId="+this.config.sessionId);
		this.request = new Ajax.Request(
			this.config.editServletAddress, 
			{ 
				method: "get",
				parameters: "id="+this.id+"&lat="+this.point.getY()+"&lon="+this.point.getX()+"&sessionId="+this.config.sessionId,
				onComplete: this.evalNoticeCoords.bind(this)
			}
		);
	},
	evalNoticeCoords: function(rawData) {
		//alert(rawData.responseText)
		var evaluatedData = eval( "("+rawData.responseText+")" );		
		var successful = evaluatedData.successful;
		if(successful){
			observer.update({lat:evaluatedData.lat,lon:evaluatedData.lon});
		}
		//debug.writeline("noticeCoordinates? (id "+this.id+" ("+this.point.getX()+", "+this.point.getY()+")): "+rawData.responseText);		
	},
	getData: function() {
		return this.data;
	},
	loadDataFromServlet: function() {
		if (this.data == null) {
			//debug.writeline(this.config.poiDataServletAddress+"id="+this.id+"&lang="+this.config.langs[this.config.usedLang]+"&sessionId="+this.config.sessionId);
			var param="type=poi";
			if (this.type=="fels"){
				param="type=fels";
			}
			this.request = new Ajax.Request(
				this.config.poiDataServletAddress, 
				{ 
					method: "get",
					parameters: "id="+this.id+"&"+param,
					onComplete:this.addDataFromServlet.bind(this)
				}
			);
		}
	},
	addDataFromServlet: function(rawData){
		//alert(rawData.responseText);
		var evaluatedData = eval( "("+rawData.responseText+")" );
		this.data = evaluatedData[this.id];
		this.popUpEvent();
	},
	popUpEvent: function() {
		this.createLUXaddOns();	
						
		// TASH specific switch:
		switch (this.type) {
		  case "Tourismusinformation":
		  	this.popUp.color = "gray10";
		  	break;
		  case "Bahnhof":
		  	this.popUp.color = "gray20";
		  	break;
		  case "HotelPension":
		  case "FerienwohnungHaus":
		  case "Campingplatz":
		  case "Jugendherberge":
		  case "Bauernhof":
		  case "BettBike":
		  case "WellnesshotelBeautyfarm":
		  case "Gastronomie":
		  case "MuseumDenkmal":
		  case "Theater":
		  case "SchlossGarten":
		  case "KlosterKirche":
		  case "Freizeitpark":
		  case "TierparkZoo":
		  case "Event":
		  	this.popUp.color = "orange";
		  	break;
		  case "Naturerlebnis":
		  case "Article":
		  	this.popUp.color = "blue";
		  	break;
		  case "StrandBaden":
		  	this.popUp.color = "blue60";
		  	break;
		  case "Segeln":
		  	this.popUp.color = "blue80";
		  	break;
		  case "FahrradverleihReparaturservice":
		  	this.popUp.color = "green91";
		  	break;
		  case "AbschliessbareFahrradbox":
		  	this.popUp.color = "green92";
		  	break;
		  case "RastplatzSchutzhuette":
		  	this.popUp.color = "green92";
		  	break;
		  case "rw1":
		  case "rw2":
		  case "rw3":
		  case "rw4":
		  case "rw5":
		  case "rw6":
		  case "rw7":
		  case "rw8":
		  case "rw9":
		  case "rw10":
		  case "rw11":
		  case "rw12":
		  	this.popUp.color = "rw";
		  	break;	    
		  default:
		  	this.popUp.color = "green";
		    break;
		}
		
		this.popUp.create();
		this.popUp.fill();
		var w = this.popUp.width;
		var h = this.popUp.height;
		
		var rwCenter = this.coordinates.getCenter();
		var center = this.coordinates.rw2px(rwCenter[0],rwCenter[1]);
		var popupWidth = w;
		var popupHeight = h;
		var coord = this.getPxCoords();
		
		var newX = this.config.width/2.0 + center[0];
		var newY = this.config.height/2.0 + center[1];
		
		var popUpMaxY = coord[1] + popupHeight;
		var popUpMaxX = coord[0] + popupWidth;
		var isInMovePosition = false;
		var newCenterX = center[0]+1;
		var newCenterY = center[1]+1;
		if ((popUpMaxX > newX)){
			newCenterX = Math.round(newCenterX+(popUpMaxX - newX + 10));
			isInMovePosition = true;
		}
		if((popUpMaxY > newY)){
			newCenterY = Math.round(newCenterY+(popUpMaxY - newY - 10));
			isInMovePosition = true;
		}
		if(isInMovePosition){
			var moveToPoint = this.coordinates.px2rw(newCenterX,newCenterY);
			this.poiManager.tileMatrix.mapManager.moveTo(moveToPoint[0],moveToPoint[1]);			
		}
		//PopUp soll über allen POI's erscheinen 
		//-> GroupLayer mit aus dem der PopUp "kommt" soll über allen andren GroupLayern angezeigt werden
		this.poiManager.resetZindexAllTypes();
		this.container.style.zIndex=(parseInt(this.container.style.zIndex)+1000);	//diesen GroupLayer hochsezen
		this.div.style.zIndex = 2000;
		
		var newImg = this.getIconActive(this.type)[0];
		this.icon.src = newImg; //andres Icon für aktives POI	
	},
	openWindow: function() {
		this.dbChildJustOpenened = true;
		this.dbChild = window.open(this.config.luxInterfaceAddress+""+this.data.idLux,"");
	},
	createLUXaddOns: function() {
		if (this.data.webSite) {
			if (this.data.webSite.length > 18) this.data.niceWebSite = this.data.webSite.substring(0,18)+"...";
			else this.data.niceWebSite = this.data.webSite;
			
			if (this.data.webSite.substr(0,4) != "http") this.data.webSite = "http://"+this.data.webSite;
			// check for empty links
			if (this.data.niceWebSite == "/" || this.data.niceWebSite == "" || this.data.niceWebSite == "http://") {
				this.data.webSite = "";
				this.data.niceWebSite = "";
			}
		}
		if (this.data.email) {
			if (this.data.email.length > 18) this.data.niceEmail = this.data.email.substring(0,18)+"...";
			else this.data.niceEmail = this.data.email;
		}
	},
	gebietsPoiClickHandler: function(e){
			Event.stop(e);
//			var d = $A(arguments);
//			var data = d[1];
//			var id = d[2];	//david
//			this.clickedId = id;
			
			this.notifyObservers("gebietClicked");	 
			
			var center = {lat:this.point.y, lon:this.point.x};
			this.poiManager.tileMatrix.mapManager.map.activePoiId = null; //kein popup öffnen!
			//this.poiManager.tileMatrix.mapManager.zoomToAndCenter(6,center);
		}
		
}


/* POICache
 * 
 * Handles the caching of the POIs. Is based on a zoom factor aware raster.
 * 
 */
POICache = Class.create();
POICache.prototype = {
	initialize: function(poiManager,tMWidth,tMHeight) {
		this.poiManager = poiManager;
		this.coordinates = poiManager.coordinates;
		this.config = poiManager.config;
		this.tMWidth = tMWidth;
		this.tMHeight = tMHeight;
		
		this.isCached = new Array();
		
		this.caching = false;
		
		this.parser = new JSONParser(this.config.poiServletAddress,this.poiManager,this.coordinates);
	},
	calcStepSize: function() {
		var res = this.coordinates.getCurrentResolution();
		var width = this.config.width;
		var height = this.config.height;
		
		var extent = new Array(this.config.width * this.coordinates.getCurrentResolution(),this.config.height * this.coordinates.getCurrentResolution());
		// The stepsize should be a little bit smaller than the shorter side of the viewport
		// wenn ich diese zeile ändere funktionierts auch mit dem Laden der POIs
		//this.stepSize = extent.min() - Math.round(extent.min()/100);
		this.stepSize = extent.min();
		//debug.writeline("POI Caching raster is "+this.stepSize);
	},
	startCaching: function(listenerDispenser) {
		this.listenerDispenser = listenerDispenser;
		if (this.coordinates.getCurrentZoomLevel() >= this.config.poiMinimumZoomLevel) {
			this.caching = true;
			this.calcStepSize();
			this.listenerDispenser.attachListener("move",this.cachePOIs.bind(this));
			this.cachePOIs();
		}
		else this.stopCaching();
	},
	stopCaching: function() {
		this.caching = false;
		this.parser.stop();
		this.isCached = new Array();
	},
	cachePOIs: function() {
		if (this.caching) {
			var bb = this.getRasterBB(this.coordinates.getBB());
			
			//CK if it works the server load is reduced by 3 quarters
			//ES  -> alt: 4 Request für TASH-Extent -> neu: nur 1 Request
			this.checkAndLoad(bb[0], bb[1], bb[2] + (bb[2]-bb[0]),bb[3] + (bb[3]-bb[1]) );
			
			/*this.checkAndLoad(bb[0],bb[1],bb[2],bb[3]);
			this.checkAndLoad(bb[0],bb[3],bb[2],bb[3] + (bb[3]-bb[1]));
			this.checkAndLoad(bb[2],bb[1],bb[2] + (bb[2]-bb[0]),bb[3]);
			this.checkAndLoad(bb[2],bb[3],bb[2] + (bb[2]-bb[0]),bb[3] + (bb[3]-bb[1]));*/
		}
	},
	checkAndLoad: function(minX,minY,maxX,maxY) {
		if (typeof(this.isCached[ minX + minY * 10000 + "_tile"]) != "string") {
			this.parser.load(minX , minY , maxX , maxY);
			this.isCached[ minX + minY * 10000 + "_tile"] = "cached";
		}
	},
	cleanAddStep: function(val) {
		// MAGIC NUMBER: As JS is not able to calculate floats in the way I want ( 5.1 + 0.1 != 5.2 but == 5.20000000000000000005) it gets some "buffer".
		var num = val + 1.2*this.stepSize;
		return this.getRaster(num);
	},
	getRaster: function(val) {
		var num = val - (val % this.stepSize);
		num = Math.floor(num * Math.pow(10,this.config.poiMaxPrecision)) / Math.pow(10,this.config.poiMaxPrecision);
		return num;
//		return val;
	},
	getRasterBB: function(bb) {
		var minX = this.getRaster(bb[0][0]);
		var minY = this.getRaster(bb[1][1]);
		var maxX = this.getRaster(bb[1][0]);
		var maxY = this.getRaster(bb[0][1]);

		return new Array(minX,minY,maxX,maxY);
	},
	remove: function() {
		this.stopCaching();
		this.parser.remove();
		this.config = null;
		this.coordinates = null;
		this.poiManager = null;
		this.listenerDispenser = null;
	}
}

/**
 * @author agutheil
 */
	PoiContainer = Class.create();
	PoiContainer.prototype = {
		initialize: function(poiManager,parent,coordinates, config){
			this.lat=0;
			this.lon=0;
			this.name="";
			this.type="";
			this.parent=parent;
			this.poiManager = poiManager;
			this.serviceIndex = poiManager.serviceIndex;
			this.coordinates = coordinates;
			this.config = config;
			this.content=null;
			this.container = null;
			this.poilistContainer = null;
			this.allePoisAggrContainer = null;
			this.types = null;
			this.poiObservers = {};
			this.poiAggrObservers = {};
			this.singlePoiObservers = {};
			this.poiContainerObservers = {};
			this.clickedType="";
			this.clickedSubType="";
			this.clickedId=null;
			this.id = null;
			this.dataModell = {};
			this.actualClickedPoiAggrItem=null;
			this.zIndex = poiManager.inc();
		},
		addData: function(id,data){
			this.id = id;
			this.lat = data.lat;
			this.lon = data.lon;
			this.name = data.name;
			this.type = data.type;
			this.content = data.content;
			this.checkBaseTypes(this.content);			
		},
		checkBaseTypes: function(content){
			var baseTypes = {
				Gebiete:0
			};
			var typeArrays = {
				Gebiete:[]
			};
			var i = 0;
			for (var region in content){				
				var d ={
					type : "gebiet",
					imgUrl : typesConf["Gebiete"].img,
					txt : region,
					cnt : content[region].felsCount,
					id : content[region].id
				};
				typeArrays.Gebiete[i]=d
				baseTypes.Gebiete = baseTypes.Gebiete+1;
				i++;
			}
			this.types = {baseTypes:baseTypes,typeArrays:typeArrays};
		},
		dataForType: function(t, c){
			var d ={
				type : t,
				imgUrl : typesConf[t].img,
				txt : typesConf[t].txt,
				cnt : c	
			};
			return d;
		},
		rw2px: function(lat,lon){
			return {top:lat+"px", left:lon+"px"};
		},
		paint: function(){
			this.paintPoiContainer();
		},
		repaintPoiContainer: function(){
			this.checkBaseTypes(this.content);
			this.paintPoiContainer();	
		},
		//erste ebene
		paintPoiContainer: function(){
			this.removePoiContainer();
			var poicontainer = createElement("div");
			var pxCoord = this.rw2px(this.lat,this.lon)
			Element.setStyle(poicontainer,{
  				position:'absolute',
                top:pxCoord.top,
                left:pxCoord.left,
				zIndex:this.zIndex,			
				fontSize: '0.9em',  
    			margin:'0px',
   			 	padding:'0px'
			});
			var pois = createElement("div");			
			poicontainer.appendChild(pois);
			var anzPoi = 0;
			for (var t in this.types.baseTypes){				
				var cnt = this.types.baseTypes[t];		
				if (cnt > 0){
					anzPoi++;		
					var span = createElement("span");
					Element.setStyle(span,{
						position: 'absolute',
					    textAlign:'center',
					    color:'Gray',
					    fontWeight:'bold',
						fontFamily:'Arial',
					    fontSize:'12px',
						width: '23px',
					    lineHeight: '23px ',
						verticalAlign: 'middle'
						
					});
					var txt = document.createTextNode(cnt);
					span.appendChild(txt);
					this.dataModell[t] = {cnt:this.types.baseTypes[t], container:txt};
					var poi = createElement("div");
					Element.setStyle(poi,{
						position: 'relative',
					    float: 'left',
		 				width: '23px ',
					    height: '23px ',
						margin: '0px',		
						padding: '0px',
	   			 		cursor:'pointer'
					});
					var style = this.styleForType(t);
					poi.style.backgroundImage=style.backgroundImage;
					span.style.color=style.color;
					poi.appendChild(span);
					pois.appendChild(poi);				
					this.poiObservers[poi]= this.clickHandler.bindAsEventListener(this,t);
					Event.observe(poi, 'mousedown', this.poiObservers[poi]);
				}				
			}			
			var w = '23px';
			var h = '23px';
			if (anzPoi == 2){
				w = '46px';
				h = '23px';
			}else if (anzPoi > 2){
				w = '46px';
				h = '46px';
			}
			Element.setStyle(pois,{
				position:'relative',
    			width:w,
    			height:h,
    			lineHeight:'100%',
    			fontSize:'10px',
    			margin:'0px',
   			 	padding:'0px'
			});
			var clearBr = createElement("br");
			clearBr.style.clear="both";
			pois.appendChild(clearBr);
			this.parent.appendChild(poicontainer);
			this.container = poicontainer;
			
		},
		regionHeadingClickHandler: function(e){
			Event.stop(e);
			this.notifyObservers("regionHeadingClicked");
		},
		gebietHeadingClickHandler: function(e){
			Event.stop(e);
			this.notifyObservers("gebietHeadingClicked");
		},	
		paintPoilistContainerPaging: function(e){
			Event.stop(e);
			var d = $A(arguments);
			this.paintPoilistContainer(d[1],d[2],d[3],d[4]); //daten weiterreichen
		},	
		//zweite ebene
		paintPoilistContainer: function(){
			
			var d = $A(arguments);
			var type = d[0];
			var data = d[1];
			var typesArray = data;
			var tmpContainer = null;
			
			//dev: hardcoded ?
			var pagingStart;
			var pagingNum;
			
			
			if (d[2] != undefined | d[3] != undefined) { //workaround -> in clickHandler mitgeben?
				pagingStart = d[2];
				pagingNum = d[3];
			}else{
				//default nehemen (startposition beim Blätten) 
				pagingStart = 0;
				pagingNum = 10;
			}
			
			this.removePoilistContainer();
			var poilistContainer = createElement("div");	
			poilistContainer.makePositioned();					
			Element.setStyle(poilistContainer,{
  				zIndex:'100',
				display:'block',
			    backgroundColor:'White', 
			    width:'150px',
				margin: '0px',		
				padding: '0px'
			});
			//poilistContainer.addClassName('poi_list_container');		
			var heading = createElement("div");
			//heading.addClassName('',');
			this.regionHeadingObserver = this.regionHeadingClickHandler.bindAsEventListener(this);
			Event.observe(heading, 'mousedown', this.regionHeadingObserver);
			Element.setStyle(heading,{
  				fontSize:'12px',
    			color:'White',
    			fontWeight:'bold',
    			backgroundColor:headingBGcolor[type],
    			padding:'2px',
				margin: '0px',
				cursor:'pointer'	
			});
			
			heading.appendChild(document.createTextNode(this.name));
			poilistContainer.appendChild(heading);
			//die poi_aggr_list
			var poiAggrList = createElement("div");
			//poiAggrList.addClassName('poi_aggr_list');
			Element.setStyle(poiAggrList,{
				position:'relative'
			});
				

			var anzahlEintraege = typesArray.length;
			//Anlegen der Einträge (hier Gebiete)
			for (var i=0;i<typesArray.length ;i++){
				
				//Paging: nur den gewünschten Teil der Liste anzeigen
				if(i >= pagingStart && i< (pagingStart + pagingNum)){
					//check if Type is visible in serviceIndex				
					var poiAggr = createElement("div");
						Element.setStyle(poiAggr,{
						position:'relative',
						margin: '0px',		
						padding: '0px',
	   			 		cursor:'pointer',
	   			 		backgroundImage:"url('"+aggrPoiBGImg[type]+"')",
	  					backgroundRepeat:'no-repeat',
	  					backgroundPosition:'right bottom'
					});
					//var isLayerVisible = this.serviceIndex.idx[typesArray[i].type].isVisible;
					//if (!isLayerVisible) Element.hide(poiAggr);
					var poiAggrImg = createElement("img");
					
					//Beim DAV in Aggreg-Liste keine Icons für Gebiete
					/*poiAggrImg.setAttribute('alt',typesArray[i].type);
					poiAggrImg.setAttribute('src',typesArray[i].imgUrl);
					Element.setStyle(poiAggrImg,{
						margin: '0px',
	    				padding : '0px',
	    				borderWidth: '0px',
						width:'20px ',
						height:'20px '			
					});*/
					var poiAggrSpan = createElement("span");
					poiAggrSpan.appendChild(document.createTextNode(typesArray[i].txt));
					Element.setStyle(poiAggrSpan,{
						margin: '3px',
						padding : '0px'  								
					});
					var poiAggrSpan2 = createElement("span");
					poiAggrSpan2.appendChild(document.createTextNode("("+typesArray[i].cnt+")"));
					Element.setStyle(poiAggrSpan2,{
						margin: '3px',
						padding : '0px'   								
					});
					/*poiAggr.appendChild(poiAggrImg);*/
					poiAggr.appendChild(poiAggrSpan); 
					poiAggr.appendChild(poiAggrSpan2);
					//alt: öffnet Felsliste
					//this.poiAggrObservers[poiAggr] = this.poiAggrClickHandler.bindAsEventListener(this,typesArray[i].type,0,10,poiAggr, typesArray[i].txt,typesArray[i].id )
					//Event.observe(poiAggr, 'mousedown', this.poiAggrObservers[poiAggr]);
					 
					 //neu: zoomt in Gebiet
					this.poiAggrObservers[poiAggr] = this.foo.bindAsEventListener(this,typesArray[i].id );
					Event.observe(poiAggr, 'mousedown', this.poiAggrObservers[poiAggr]);
					  
					poiAggrList.appendChild(poiAggr);
					var tmpType = typesArray[i].type;
					if (this.clickedSubType == tmpType){
						//dritte ebene zeichnen
						tmpContainer = poiAggr;					
					}
				}				
			}//ende for-loop
			
				//Paging-Navi anzeigen
				var paging = createElement("div");
				Element.setStyle(paging,{
					margin:'3px',
	                textAlign:'center',
					padding : '0px' 							
				});
				if (pagingStart!=0){
					var p1 = createElement("span");		
					Element.setStyle(p1,{
						margin:'3px',           
						padding : '0px', 	
						cursor:'pointer'			
					});	
					//Funktion ruft sich quasi selbst auf, nur mit anderem Paging-Werten
					var obs1 = this.paintPoilistContainerPaging.bindAsEventListener(this, d[0], d[1], (pagingStart-pagingNum), pagingNum);						
					Event.observe(p1, 'mousedown', obs1);
					var txt = document.createTextNode("<");
					p1.appendChild(txt);
					paging.appendChild(p1);
				}
				if (anzahlEintraege > (pagingStart+pagingNum)){ //wenn noch mehr POIS da sind
					var p2 = createElement("span");
					Element.setStyle(p2,{
						margin:'3px',           
						padding : '0px',
						cursor:'pointer' 		 				
					});	
					var obs2 = this.paintPoilistContainerPaging.bindAsEventListener(this, d[0], d[1], (pagingStart+pagingNum), pagingNum);
					Event.observe(p2, 'mousedown', obs2);
					var txt2 = document.createTextNode(">");
					p2.appendChild(txt2);
					paging.appendChild(p2);
				}
				poiAggrList.appendChild(paging);
				//ENDE: Paging-Navi anzeigen
			
			poilistContainer.appendChild(poiAggrList);
			this.container.appendChild(poilistContainer);
			this.poilistContainer = poilistContainer;
			Element.setStyle(this.container,{
				zIndex:this.zIndex + this.poiManager.max() + 1
			});					
			var cumPosThis = Position.cumulativeOffset(this.container);
			var dimsThis = this.container.getDimensions();
			var cumPosThisMax = {
				left: cumPosThis[0] + (dimsThis.width + 10), 
				top: cumPosThis[1] + (dimsThis.height + 10)
			};
			var cumPosMapCon = Position.cumulativeOffset(this.poiManager.tileMatrix.parent);
			var dimsMapCon = this.poiManager.tileMatrix.parent.getDimensions();
			var cumPosMapConMax = {
				left: cumPosMapCon[0] + dimsMapCon.width, 
				top: cumPosMapCon[1] + dimsMapCon.height
			};
			var isMoveX = false;
			var isMoveY = false;
			var moveX = 1;
			var moveY = 1;
			if (cumPosThisMax.left > cumPosMapConMax.left){
				moveX = cumPosMapConMax.left - cumPosThisMax.left;
				isMoveX = true;
			}
			if (cumPosThisMax.top > cumPosMapConMax.top){
				moveY = cumPosMapConMax.top - cumPosThisMax.top;
				isMoveY = true;
			}
			if(isMoveX || isMoveY){
				this.poiManager.tileMatrix.mapManager.moveBy(moveX,moveY);		
			}
			if(tmpContainer!=null){
				this.repaintAllePoisAggr(tmpContainer, this.clickedSubType);
			}					
		},
		repaintAllePoisAggr: function(parent, type){
			if(this.actualClickedPoiAggrItem!=null){
				this.paintAllePoisAggr(
				parent,type,
				this.actualClickedPoiAggrItem.data,
				this.actualClickedPoiAggrItem.start,
				this.actualClickedPoiAggrItem.pages,
				this.actualClickedPoiAggrItem.parentType
				);
			}
		},
		foo: function(e,id){
			this.actualClickedPoiAggrItem={};
			this.actualClickedPoiAggrItem.id=id;
			Event.stop(e);
			this.notifyObservers("gebietHeadingClicked");
		},
		//dritte ebene
		paintAllePoisAggr: function(parent,type, data,start,pages,parentType,id){
			this.clickedSubType = type;
			this.actualClickedPoiAggrItem={};
			this.actualClickedPoiAggrItem.id=id;
			this.actualClickedPoiAggrItem.data=data;
			this.actualClickedPoiAggrItem.start=start;
			this.actualClickedPoiAggrItem.pages=pages;
			this.actualClickedPoiAggrItem.parentType=parentType;
			this.removeAllePoisAggr();
			var anzahlEintraege=0;
			var allePoisAggr = createElement("div");
			allePoisAggr.zIndex='1001';
			Element.setStyle(allePoisAggr,{
				zIndex:this.zIndex + this.poiManager.max() + 1,
				position:'absolute',
    			top:'0px',
    			left:'150px',
    			width:'150px',
   				backgroundColor:'White',
   				margin: '0px',
				padding : '0px' 
			});

			var heading = createElement("div");
			Element.setStyle(heading,{
  				fontSize:'12px',
    			color:'White',
    			fontWeight:'bold',
    			backgroundColor:"Gray",
   				margin: '0px',
				padding : '3px' 
			});
			heading.appendChild(document.createTextNode(parentType));	
			allePoisAggr.appendChild(heading);
			this.gebietHeadingObserver = this.gebietHeadingClickHandler.bindAsEventListener(this);
			Event.observe(heading, 'mousedown', this.gebietHeadingObserver);
			var poiList = createElement("div");
			//poiList.addClassName('poi_list');
			Element.setStyle(poiList,{
				position:'relative',
    			backgroundColor:'White',
   				margin: '0px',
				padding : '0px' 
			});
			allePoisAggr.appendChild(poiList);
			var normaleEinrueckung = '3px';
			var weiterEinrueckung = '9px';
			var einrueckung = normaleEinrueckung;		
			for (var p in data){
				if (data[p].type=="fels"){				
					anzahlEintraege = anzahlEintraege + 1;
					var singlePoi = createElement("div");
					Element.setStyle(singlePoi,{
							margin:'3px',					
							padding : '0px',
		   					cursor:'pointer' 
						});										
					singlePoi.appendChild(document.createTextNode(data[p].name));				
					poiList.appendChild(singlePoi);
					this.singlePoiObservers[singlePoi] = this.singlePoiClickHandler.bindAsEventListener(this,data[p],p)
					Event.observe(singlePoi, 'mousedown', this.singlePoiObservers[singlePoi]);					
				}
			}
			Element.setStyle(allePoisAggr,{
				position:'absolute',
   				margin: '0px',
				padding : '0px' 				
			});
			/*
			.paging {
                margin:3px;
                text-align:center;
            }
            .paging span{
                margin:3px;
            }  
			*/
			var paging = createElement("div");
			Element.setStyle(paging,{
				margin:'3px',
                textAlign:'center',
				padding : '0px' 							
			});
			if (start!=0){
				var p1 = createElement("span");		
				Element.setStyle(p1,{
					margin:'3px',           
					padding : '0px', 	
					cursor:'pointer'			
				});	
				var obs1 = this.poiAggrClickHandler.bindAsEventListener(this,type,start-pages,pages,parent, parentType,id);						
				Event.observe(p1, 'mousedown', obs1);
				var txt = document.createTextNode("<");
				p1.appendChild(txt);
				paging.appendChild(p1);
			}
			if (anzahlEintraege==pages){
				var p2 = createElement("span");
				Element.setStyle(p2,{
					margin:'3px',           
					padding : '0px',
					cursor:'pointer' 		 				
				});	
				var obs2 = this.poiAggrClickHandler.bindAsEventListener(this,type,start+pages,pages,parent, parentType,id);
				Event.observe(p2, 'mousedown', obs2);
				var txt2 = document.createTextNode(">");
				p2.appendChild(txt2);
				paging.appendChild(p2);
			}
			
			allePoisAggr.appendChild(paging);
			parent.appendChild(allePoisAggr);
			
			this.allePoisAggrContainer = allePoisAggr;
			
			var dim_w = this.container.getDimensions();
			var p_w = Position.cumulativeOffset(this.container);
			var p_w_max = {
				left: p_w[0] + dim_w.width,
				top: p_w[1] + dim_w.height
			};
			var dim_c = allePoisAggr.getDimensions();
			var p_c = Position.cumulativeOffset(allePoisAggr);
			var p_c_max = {
				left: p_c[0] + dim_c.width,
				top: p_c[1] + dim_c.height
			};
			var cumPosThisMax = {
				left: Math.max(p_w_max.left, p_c_max.left) + 10,
				top: Math.max(p_w_max.top, p_c_max.top) + 10
			};
			var dim_container = {
				width: cumPosThisMax.left - p_w[0],
				height: cumPosThisMax.top - p_w[1]
			};	
			
			var cumPosMapCon = Position.cumulativeOffset(this.poiManager.tileMatrix.parent);
			var dimsMapCon = this.poiManager.tileMatrix.parent.getDimensions();
			var cumPosMapConMax = {
				left: cumPosMapCon[0] + dimsMapCon.width, 
				top: cumPosMapCon[1] + dimsMapCon.height
			};
			var isMoveX = false;
			var isMoveY = false;
			var moveX = 1;
			var moveY = 1;	
			if (cumPosThisMax.left > cumPosMapConMax.left){
				moveX = cumPosMapConMax.left - cumPosThisMax.left;
				isMoveX = true;
			}
			if (cumPosThisMax.top > cumPosMapConMax.top){
				moveY = cumPosMapConMax.top - cumPosThisMax.top;
				isMoveY = true;
			}
			if(isMoveX || isMoveY){
				this.poiManager.tileMatrix.mapManager.moveBy(moveX,moveY);					
			}				
			
		},
		
		clickHandler: function(e){
			Event.stop(e);
			var data = $A(arguments);
			var t = data[1];			
			var pagingStart = data[2];	//testit		
			var pagingSize = data[3];			
			this.actualClickedPoiAggrItem=null;
			this.notifyObservers("clickHandler");
			if (this.poilistContainer != null && this.clickedType == t){ //also popup1 sichtbar
				this.clickedType = "";				
				this.deletePopup();
			}else{	//kein Popup sichtbar
				this.clickedType = t;
				this.paintPoilistContainer(this.clickedType, this.types.typeArrays[this.clickedType], pagingStart, pagingSize);
			}			
			
		},
		mouseHandler: function(e){
			var data = $A(arguments);
			var t = data[1];
			this.notifyObservers("mouseHandler");
			var caller = $(Event.element(e));
			var m =  this.poiObservers[caller];
			Event.stopObserving(caller, 'mouseover', m);
			this.poiObservers[caller]=null;
			this.clickedType = t;
			var url = 'http://localhost:8080/tash/testdata/req2.txt';
			new Ajax.Request(url, {
			  	method: 'get',
			 	onSuccess: this.req2success.bind(this,this.clickedType)
			});
			
			/*
			 * this.poiObservers[poi]= this.mouseHandler.bindAsEventListener(this,t);
				Event.observe(poi, 'mouseover', this.poiObservers[poi]);
			 */	
			
		},
		notifyObservers: function() {
			var data = $A(arguments);
			for (o in this.poiContainerObservers){
				this.poiContainerObservers[o].update(this,data[0]);
			}
		},
		addObserver: function(name, observer){
			this.poiContainerObservers[name]=observer;
		},
		/**
		 * funktion muss type kennen und html-container, um Inhalt ranzuhaengen
		 */
		poiAggrClickHandler: function(e){
			var data = $A(arguments);			
			var type = data[1];
			var start = data[2]; 
			var pages = data[3];
			var parent = data[4];
			var parentType = data[5];
			var id = data[6];
			Event.stop(e);
			var url = "/viewer/jsonviewer/leaves?";
			new Ajax.Request(url, {
			  	method: 'get',
				parameters: "id="+id+"&start="+start+"&count="+pages,
			 	onSuccess: this.poiAggrSuccess.bind(this,parent,type,start,pages,parentType,id)
			});
		},
		poiAggrSuccess: function(parent,type,start,pages,parentType,id,data){	
			var leavesData = eval('('+data.responseText+')');
			this.paintAllePoisAggr(parent,type, leavesData,start,pages,parentType,id);
		},
		singlePoiClickHandler: function(e){
			var d = $A(arguments);
			var data = d[1];
			var id = d[2];	//david
			this.clickedId = id;
			this.notifyObservers("felsClickHandler");	
			Event.stop(e);
			var center = {lat:data.lat, lon:data.lon};
			this.poiManager.tileMatrix.mapManager.zoomToAndCenter(6,center);
					
		},
		styleForType: function(type){
			var style = {};
			switch(type){
				case 'blue':
					style.backgroundImage="url('"+typesConf[type].img+"')";
					style.color="Blue";
					break;
				case 'green':
					style.backgroundImage="url('"+typesConf[type].img+"')";
					style.color="Green";
					break;
				case 'orange':
					style.backgroundImage="url('"+typesConf[type].img+"')";
					style.color="Orange";
					break;
				case 'gray':
					style.backgroundImage="url('"+typesConf[type].img+"')";
					style.color="Gray";
					break;
				default:
					style.backgroundImage="url('/viewer/symbols/dav/orange.png')";
					style.color="White";
					break;
			}
			return style;
		},
		remove: function(){
			this.actualClickedPoiAggrItem=null;
			this.removePoiContainer();
		},
		removePoiContainer: function(){
			if (this.container != null){
				this.removePoilistContainer();
				Element.remove(this.container);				
				this.container = null;
			} 
		},
		removePoilistContainer: function(){
			if (this.poilistContainer != null){
				this.removeAllePoisAggr();
				Element.remove(this.poilistContainer);
				this.poilistContainer = null;
			} 
		},
		removeAllePoisAggr: function(){
			if (this.allePoisAggrContainer != null) {
				Element.remove(this.allePoisAggrContainer);
				this.allePoisAggrContainer = null;
			}
		},			
		deletePopup: function(){
			/*this.container.setStyle({
				zIndex:this.zIndex
			});*/
			this.removePoilistContainer();
		}
	};
	var createElement = function(elemName){
		var elem = document.createElement(elemName);
		return $(elem);
	};
	
	var typesConf = {
		Tourismusinformation:{txt:"Tourismusinformation", img:"10_icon_info.png"}, 	
		Bahnhof:{txt:"Bahnhof", img:"20_icon_train.png"},					
	  	HotelPension:{txt:"Hotels, Pension", img:"31_icon_bed.png"},
  		FerienwohnungHaus:{txt:"Ferienwohnung", img:"32_icon_house.png"},
  		Campingplatz:{txt:"Campingplatz", img:"33_icon_camping.png"},
		Jugendherberge:{txt:"Jugendherberge", img:"34_icon_hostel.png"},
  		Bauernhof:{txt:"Bauernhof", img:"35_icon_farm.png"},
  		BettBike:{txt:"Bett & Bike", img:"36_icon_bedbike.png"},
  		WellnesshotelBeautyfarm:{txt:"WellnesshotelBeautyfarm", img:"37_icon_beauty.png"},
  		Gastronomie:{txt:"Gastronomie", img:"40_icon_food.png"},
  		MuseumDenkmal:{txt:"MuseumDenkmal", img:"51_icon_museum.png"},
  		Theater:{txt:"Theater", img:"52_icon_theatre.png"},
  		SchlossGarten:{txt:"Schloss & Garten", img:"53_icon_castle.png"},
  		KlosterKirche:{txt:"Kloster & Kirche", img:"54_icon_church.png"},
  		Freizeitpark:{txt:"Freizeitpark", img:"55_icon_themepark.png"},
  		TierparkZoo:{txt:"Tierpark & Zoo", img:"56_icon_zoo.png"},
  		Event:{txt:"Event", img:"event.png"},
  		Naturerlebnis:{txt:"Naturerlebnis", img:"70_icon_nature.png"},
  		Article:{txt:"Article", img:"article.png"},
 	 	StrandBaden:{txt:"Strand & Baden", img:"60_icon_beach.png"},
  		Segeln:{txt:"Segeln", img:"80_icon_sailing.png"},
  		FahrradverleihReparaturservice:{txt:"Fahrradverleih & Reparaturservice", img:"91_icon_bikerepair.png"},
  		AbschliessbareFahrradbox:{txt:"Abschliessbare Fahrradbox", img:"92_icon_bikelock.png"},
  		RastplatzSchutzhuette:{txt:"Rastplatz & Schutzhuette", img:"93_icon_bikeshed.png"},
  		Reiten:{txt:"Reiten", img:"110_icon_riding.png"},
  		Golfen:{txt:"Golfen", img:"100_icon_golf.png"},
		gray:{txt:"Infrastruktur", img:"symbols/dav/meta/meta-grau.png"},
		orange:{txt:"Übernachten/Essen/Kultur", img:"symbols/dav/meta/meta-orange.png"},
		blue:{txt:"Meer & Natur", img:"symbols/dav/meta/meta-blau.png"},
		green:{txt:"Rad, Golf, Reiten", img:"symbols/dav/meta/meta-gruen.png"},
		Gebiete:{txt:"Gebiete", img:"symbols/dav/blue_s.png"}	
	}
	
	var headingBGcolor = {
		blue:"#008fc7",
		gray:"#8b8b8b",
		green:"#2b9f12",
		orange:"#e8882f",
		Gebiete:"#8b8b8b"
	}
	var aggrPoiBGImg = {
		blue:"/tash/symbols/tash/olc_blue.gif",
		gray:"/tash/symbols/tash/olc_gray.gif",
		green:"/tash/symbols/tash/olc_green.gif",
		orange:"/tash/symbols/tash/olc_orange.gif"
	}
/*	POIManager
 * 
 * Handles all things POI. Creating, moving, icons...
 * 
 */
POIManager = Class.create();
POIManager.prototype = {
	initialize: function(tileMatrix) {
		this.tileMatrix = tileMatrix;
		this.container = tileMatrix.container;
		this.coordinates = tileMatrix.coordinates;
		this.config = tileMatrix.config;
		this.pointBox = new Array();
		this.poiConBox = new Array();
		this.zIndexCount=0;
		this.poiCache = null;
		this.poiContainerList=new Array();
		this.eventLogger = this.tileMatrix.mapManager.eventLogger;
		this.serviceIndex = this.tileMatrix.serviceIndex;
		
		this.createPOICon(-1);		
		this.pb_counter = 1;
	},
	setPoiCache: function(poiCache){
		this.poiCache = poiCache;
	},
	startCaching: function(listenerDispenser){
		this.poiCache.startCaching(listenerDispenser);
	},
	stopCaching: function() {
		this.poiCache.stopCaching();
	},
	inc:function(){
		this.zIndexCount++;
		return this.zIndexCount;
	},
	max:function(){
		return this.zIndexCount;
	},
	addNewPoiContainer: function(container,coordinates, config,id,data){
		var poiContainer = new PoiContainer(this,container,coordinates, config);
		poiContainer.addObserver("clickObserver",this);
		this.poiContainerList.push(poiContainer);
		poiContainer.addData(id,data);
		return poiContainer;
	},											
	addNewPoi: function(poiManager,parent,poiConBox,coordinates,point,id,type,name,config,logger){
		var poi = new POI(poiManager,parent,poiConBox,coordinates,point,id,type,name,config,logger);
		poi.addObserver("clickObserver",this);
		this.poiContainerList.push(poi);
		return poi;
	},
	addNewPoiGebiet: function(poiManager,parent,poiConBox,coordinates,point,id,type,name,config,logger, felsCount){
		var poi = new POI(poiManager,parent,poiConBox,coordinates,point,id,type,name,config,logger);
		poi.setFelsCount(felsCount);
		poi.addObserver("clickObserver",this);
		this.poiContainerList.push(poi);
		return poi;
	},
	update: function(){
		var data = $A(arguments);
		var caller = data[0];
		var method = data[1];	
		if (method == "singlePoiClickHandler"){
			this.tileMatrix.mapManager.map.activePoiId=caller.clickedId;
		}else if (method == "showPopUp"){
			this.tileMatrix.mapManager.map.activePoiId=caller.id;
			this.tileMatrix.mapManager.map.activePoiType=caller.type;	
			this.tileMatrix.mapManager.map.activePoi = caller;
			//this.tileMatrix.mapManager.map.map.notifyObservers("poiId"); 
			for (var i = 0; i < this.poiContainerList.length; i++){
				if (caller != this.poiContainerList[i]) this.poiContainerList[i].deletePopup();
			}
		}else if (method == "mehrLink"){
			this.tileMatrix.mapManager.map.activePoiId=caller.id;
			this.tileMatrix.mapManager.map.activePoiType=caller.type;	
			this.tileMatrix.mapManager.map.activePoi = caller;
			this.tileMatrix.mapManager.map.map.notifyObservers("poiId");
			/*for (var i = 0; i < this.poiContainerList.length; i++){
				if (caller != this.poiContainerList[i]) this.poiContainerList[i].deletePopup();
			}*/
		}else if (method == "regionHeadingClicked"){
			this.tileMatrix.mapManager.map.activePoiId=caller.id;
			this.tileMatrix.mapManager.map.activePoiType="region";
			this.tileMatrix.mapManager.map.activePoi = caller;
			this.tileMatrix.mapManager.map.map.notifyObservers("poiId");
		}else if (method == "gebietHeadingClicked"){
			this.tileMatrix.mapManager.map.activePoiId=caller.actualClickedPoiAggrItem.id;
			this.tileMatrix.mapManager.map.activePoiType="gebiet";
			this.tileMatrix.mapManager.map.activePoi = caller;
			this.tileMatrix.mapManager.map.map.notifyObservers("poiId");
		}else if (method == "gebietClicked"){
			this.tileMatrix.mapManager.map.activePoiId=caller.id;
			this.tileMatrix.mapManager.map.activePoiType="gebiet";
			this.tileMatrix.mapManager.map.activePoi = caller;
			this.tileMatrix.mapManager.map.map.notifyObservers("poiId");
		}else if (method == "felsClickHandler"){
			this.tileMatrix.mapManager.map.activePoiId=caller.clickedId;
			this.tileMatrix.mapManager.map.activePoiType="fels";
			this.tileMatrix.mapManager.map.activePoi = caller;
			//es: war auskommentiert(?)
			//this.tileMatrix.mapManager.map.map.notifyObservers("poiId");
		}
	},
	removeAll: function(){
		for (var i = 0; i < this.poiContainerList.length; i++){
			this.poiContainerList[i].remove();
		}
		this.zIndexCount=0;
	},
	createPOICon: function(type) {
		if (typeof(this.poiCon) != "object") {
			this.poiCon = document.createElement("div");
			this.poiCon.id = "poiCon_"+Math.random();

			Element.addClassName(this.poiCon,"poiCon");
			Element.setStyle(this.poiCon,{position:"absolute",top:"0px",left:"0px"});

			this.container.appendChild(this.poiCon);
			this.tileMatrix.addToMoveList(this.poiCon);
		}
		if (typeof(this.poiConBox[type]) != "object") {
			//ES Bugfix crash IE8! 0003957
			if(isNaN(this.pb_counter)) this.pb_counter = '';
			
			this.pb_counter++;
			this.poiConBox[type] = document.createElement("div");
			this.poiConBox[type].id = type+"_poiCon_"+Math.random();
			
			//jeder GroupLayer kriegt einen expliziten zIndex (nix mit 'auto')
			this.poiConBox[type].style.zIndex=this.pb_counter;
			/*ACHTUNG: position:relative bringt beim pannen im IE7 die POI's zum verschwinden!!!
			this.poiConBox[type].style.position='relative';	//sonst ignoriert der IE6 & IE7 den Z-Index ! -> u.U. PopUp durch POI verdeckt
			*/
			this.poiConBox[type].origZ=this.pb_counter; //merken

			this.poiCon.appendChild(this.poiConBox[type]);		
		}
	},	
	resetZindexAllTypes: function() {
			//Z-Index bei allen GroupLayern auf initalwert setzen
				
			for (var type in  this.poiConBox){
					 
					 if (typeof(this.poiConBox[type]) == "object" && type!="-1" // nur bei "richtigen" GroupLayern
					 	&& this. tileMatrix.mapManager.map.activePoiType != type){ //GroupLayer mit aktive PopUp nicht zurücksetzen; soll weiterhin oben erscheinen 
						 
						 	var newZ = this.poiConBox[type].origZ;
						 	this.poiConBox[type].style.zIndex = newZ; // Original-Index zuweisen
					}
			}
	},	
	isNonToggleType: function(type){
		for (var index = 0; index < this.config.nonToggleTypes.length; ++index) {
   			var item = this.config.nonToggleTypes[index];
			if (item == type){
				return true;
			} 
  		}
		return false;
	},
	isRadweg: function(type){
		var check = false;
		var rwX = ['rw1','rw2','rw3','rw4','rw5','rw6','rw7','rw8','rw9','rw10','rw11','rw12'];
		for (var index = 0, len = rwX.length; index < len; ++index) {
  			var item = rwX[index];
  			if(type==item){
  				check = true;
  			}
		}
		return check;
	},
	/*
	x lon
	y lat
	id id
	type type
	name name
	*/
	addPOI: function(x,y,id,type,name) {
			if (typeof(this.pointBox[id]) != "object") {
				var theVisibility = "hidden";
				var isLayerVisible = false;
				type = this.config.typeMapMapping[type];	
				this.createPOICon(type);
				var isLayerVisible = this.serviceIndex.idx[type].isVisible;				
				if (isLayerVisible || this.config.editMode){
					theVisibility = "visible";
					isLayerVisible=true;
				}				
				this.pointBox[id] = this.addNewPoi(this,this.container,this.poiConBox[type],this.coordinates,new Point(x,y),id,type,name,this.config,this.eventLogger);	
				if (typeof(this.poiConBox[type]) == "object"){
					if(isLayerVisible){
				 		Element.show(this.poiConBox[type]);
				 	}else{
				 		Element.hide(this.poiConBox[type]);
				 	}
				} 
				this.pointBox[id].paint();
				if (id == this.tileMatrix.mapManager.map.activePoiId){
					this.pointBox[id].showPopUp();
				}
				this.tileMatrix.mapManager.map.map.notifyObservers("addPOI", this.pointBox[id]);						
			}			
	},
	addPOIGebiet: function(x,y,id,type,name, felsCount) {
			if (typeof(this.pointBox[id]) != "object") {
				var theVisibility = "hidden";
				var isLayerVisible = false;
				type = this.config.typeMapMapping[type];	
				this.createPOICon(type);
				var isLayerVisible = this.serviceIndex.idx[type].isVisible;				
				if (isLayerVisible || this.config.editMode){
					theVisibility = "visible";
					isLayerVisible=true;
				}				
				this.pointBox[id] = this.addNewPoiGebiet(this,this.container,this.poiConBox[type],this.coordinates,new Point(x,y),id,type,name,this.config,this.eventLogger, felsCount);	
				if (typeof(this.poiConBox[type]) == "object"){
					if(isLayerVisible){
				 		Element.show(this.poiConBox[type]);
				 	}else{
				 		Element.hide(this.poiConBox[type]);
				 	}
				} 
				this.pointBox[id].paint();
				if (id == this.tileMatrix.mapManager.map.activePoiId){
					this.pointBox[id].showPopUp();
				}
				this.tileMatrix.mapManager.map.map.notifyObservers("addPOI", this.pointBox[id]);						
			}			
	},
	
	addMetaPOIInfo: function(id,data) {
		if (typeof(this.pointBox[id]) != "object") {
			var type = "meta"
			this.createPOICon(type);
			//if (typeof(this.poiConBox[type]) == "object") Element.setStyle(this.poiConBox[type],{visibility:this.serviceIndex.idx[type].getStyle()});
			this.pointBox[id] = this.addNewPoiContainer(this.poiConBox["meta"],this.coordinates, this.config,id,data)					
			this.pointBox[id].paint();
		}
	},

	hidePOIs: function(type) {
		var o = this.serviceIndex.idx[type];
			if (o != undefined && typeof(o) == "object"){
				this.serviceIndex.idx[type].isVisible=false;
				var check = +this.serviceIndex.idx[type].isVisible;
				var o2 = this.poiConBox[type];
				if (typeof o2 != 'undefined' && typeof o2 == "object") {	//einzelpoistufe
					Element.hide(this.poiConBox[type]);
				}else if (this.config.isAggregation) {
					for (var i = 0; i < this.poiContainerList.length; i++){
						try{
						this.poiContainerList[i].repaintPoiContainer();
						}catch(e){}
					}
					try
					{
						this.tileMatrix.mapManager.map.activePoi.paintPoilistContainer(this.tileMatrix.mapManager.map.activePoi.clickedType, this.tileMatrix.mapManager.map.activePoi.types.typeArrays[this.tileMatrix.mapManager.map.activePoi.clickedType], 0, 10);
					}catch(e){}
				}
			}
	},
	showPOIs: function(type) {		
			var o = this.serviceIndex.idx[type];
			if (o != undefined && typeof(o) == "object"){
				this.serviceIndex.idx[type].isVisible=true;
				var check = +this.serviceIndex.idx[type].isVisible;
				var o2 = this.poiConBox[type];
				if (typeof o2 != 'undefined' && typeof o2 == "object") {
					Element.show(this.poiConBox[type]);
				}else if (this.config.isAggregation) {	//aggregated pois
					for (var i = 0; i < this.poiContainerList.length; i++){
						try{
						this.poiContainerList[i].repaintPoiContainer();
						}catch(e){}
					}
					try
					{
						this.tileMatrix.mapManager.map.activePoi.paintPoilistContainer(this.tileMatrix.mapManager.map.activePoi.clickedType, this.tileMatrix.mapManager.map.activePoi.types.typeArrays[this.tileMatrix.mapManager.map.activePoi.clickedType], 0,10);
					}catch(e){}
				}
			}
	},
	updatePOIs: function() {
		for (var one in this.pointBox) {
		//for (var i = 0; i < this.pointBox.length; i++){
			if (typeof(this.pointBox[one]) == "object"){
				 this.pointBox[one].update();
			}
		}
	},
	checkPoiId: function(theId){
		var isChosen = false;
		if (typeof(this.config.tashIds) == "object"){
			this.config.tashIds.each(function(id){
					if(id==theId){
						isChosen=true;
					}
				});
		}
		return isChosen;
	},
	checkBubbleId: function(theId){
		var isChosen = false;
		if (typeof(this.config.tashIds) == "object"){
			this.config.tashIds.each(function(id){
					if(id==theId){
						isChosen=true;
					}
				});
		}
		return isChosen; 
	},
	removePOIs: function() {

		for (var one in this.pointBox) {
			if (typeof(this.pointBox[one]) == "object") this.pointBox[one].remove();
		}
		this.pointBox = new Array();
		
		if (typeof(this.poiCon) == "object" && typeof(this.container) == "object") this.container.removeChild(this.poiCon);
	},
	remove: function() {
		this.tileMatrix = null;
		this.coordinates = null;
		this.container = null;
		this.config = null;
	}
};

/*	Point
 * 
 * Point is the most advanced class in the whole code.
 * 
 */
Point = Class.create();
Point.prototype = {
	initialize: function(x,y) {
		this.x = x;
		this.y = y;
	},
	setX: function(x) {
		this.x = x;
	},
	setY: function(y) {
		this.y = y;
	},
	getX: function() {
		return this.x;
	},
	getY: function() {
		return this.y;
	},
	setXY: function (x,y) {
		this.x = x;
		this.y = y;
	},
	getXY: function() {
		return new Array(this.x,this.y);
	}
}

/*	PopUp
 * 
 * The PopUp object itself. All things displaying. Filling the template.
 * 
 */
PopUp = Class.create();
PopUp.prototype = {
	initialize: function(poi,parent,offsetX,offsetY,hasDelayClose) {
		this.poi = poi;
		this.container = parent;
		this.offsetX = offsetX;
		this.offsetY = offsetY;
		this.height=0;
		this.width=0;
		this.hideCountdown = null;
		this.hasDelayClose = hasDelayClose;
		this.cancelCloseObserver = this.cancelClose.bindAsEventListener(this);
		this.triggerCloseObserver = this.triggerClose.bindAsEventListener(this);
		this.clickObserver = this.close.bindAsEventListener(this);
		this.preventOtherActionsObserver = this.preventOtherActions.bindAsEventListener(this);
		
		this.items = new Array();
		
		this.color;
	},
	setOffsetX: function(newOff) {
		this.offsetX = parseInt(newOff);
	},
	open: function() {
		this.cancelClose();
		this.create();
		if (this.poi.data == null) this.poi.loadDataFromServlet();
		else this.fill();
	},
	triggerClose: function() {
		if (this.hideCountdown == null) {
			this.hideCountdown = setTimeout(this.close.bind(this),2000);
		}
	},
	cancelClose: function() {
		if (this.hideCountdown != null) {
			clearTimeout(this.hideCountdown);
			this.hideCountdown = null;
		}
	},
	/*es: solle nie direkt, sondern vom POI aufgerufen werden (damit poi seinen status angleichen kann -> TODO: refactor: popup-open/close-stati aus POI hierhin verschieben)*/
	close: function() {
		try {
			this.container.removeChild(this.div);
		}
		catch(e){}
		this.hideCountdown = null;
	},
	/* es: weiterleitung an POI (damit auf inaktiv setzen kann)*/
	closeOverPoi: function() {
		this.poi.closePopUp();
	},
	clear: function() {
		if (typeof(this.div) == "object") {
			while ( this.div.hasChildNodes() ) { 
				this.div.removeChild(this.div.firstChild);
			}
		}
	},
	create: function() {
		if (this.div == null) {
			this.div = document.createElement("div");
			this.div.id = this.poi.id+"_popUp";
		
			Element.addClassName(this.div,"popUp");
		}
		this.position();

		this.container.appendChild(this.div);
	},
	position: function() {
		var pixelPos = this.poi.getPxCoords();
		Element.setStyle(this.div,{position:"absolute",top:"0px",left:"22px"});
	},
	fill: function() {		
//		alert("we are beginnning to fill the thing");
		// uses predefined html template and replaces all tags with the values for this poi.
		//var template = this.poi.config.popUpTemplate;
				
		// TASH specific switch:
		switch (this.color) {
		  case "gray10":
		   	var template = this.poi.config.popUpTemplateGray10;
		  	break;
		  case "gray20":
		   	var template = this.poi.config.popUpTemplateGray20;
		  	break;
		  case "orange":
		  	var template = this.poi.config.popUpTemplateOrange;
		  	break;
		  case "blue":
		  	var template = this.poi.config.popUpTemplateBlue;
		  	break;
		  case "blue60":
		  	var template = this.poi.config.popUpTemplateBlue60;
		  	break;
		  case "blue80":
		  	var template = this.poi.config.popUpTemplateBlue80;
		  	break;
		  case "green91":
		  	var template = this.poi.config.popUpTemplateGreen91;
		  	break;
		  case "green92":
		  	var template = this.poi.config.popUpTemplateGreen92;
		  	break;		
		  case "rw":
		  	var template = this.poi.config.popUpTemplateRW;
		  	break;	    
		  default:
		    var template = this.poi.config.popUpTemplateDavpoi;
		    break;
		}

		// creates descD, descF, descE ...
		var descKey = "desc"+this.translateLangSign();

		for (var key in this.poi.data) {
			var usedKey = key;
			if (key == descKey) usedKey = "desc";
			
			var replaceWith = this.poi.data[key];
			
			if (replaceWith != "") {
				switch (usedKey) {
					case "previewImage":
						replaceWith = "<img src=\""+replaceWith+"\" alt=\"\" class=\"thumbnail\" />";
						break;
					case "phone1":
						replaceWith = "Telefon: "+replaceWith+"<br />";
						break;
					case "address1Street":
						replaceWith = replaceWith+"<br />";
						break;
					case "address1City":
						replaceWith = replaceWith+"<br />";
						break;
					case "streetNumber":
						replaceWith = replaceWith+"<br />";
						break;
					case "city":
						replaceWith = replaceWith+"<br />";
						break;
					case "shortDescription":
						if(replaceWith.match(/Länge/gi)) {
							replaceWith = replaceWith.replace(/Länge/gi,"<br />Länge");
						}
						
						//Beim DAV steht in "shortDescription" escaptes HTML -> unescapen damit es gerendert wird (sonst erscheint der HTML-Code als Text im PopUp)
						replaceWith = replaceWith.unescapeHTML();
						 
						break;
					case "name": 
						if (replaceWith.length > this.poi.config.maxNameLength) {
							//Name soll komplett erscheinen
							//replaceWith = replaceWith.substring(0,this.poi.config.maxNameLength)+"...";
							
						}
						break;							
				}
			}
			else {
				// wenn kein Bild vorhanden ist, dann kleben wir die
				// Adresse an die linke Seite
				if (usedKey == "previewImage") {
					var changeClassExpr = new RegExp('<p class="address">',"g");
					template = template.replace(changeClassExpr,'<p class="address-left">');
				}
			}
			var toReplace = new RegExp("_"+usedKey+"_","g");
			template = template.replace(toReplace,replaceWith);
		}
		// now clean up every tag which is not used
		for (var i = 0; i < this.poi.config.popUpTagNames.length ; i++) {
			var toReplace = new RegExp("_"+this.poi.config.popUpTagNames[i]+"_","g");
			template = template.replace(toReplace,"");
		}
		this.div.innerHTML = template;
		
		var c = this.div.getElementsByClassName('overlay-window')[0];
		if (this.poi.type=="fels"){
			var mehr = document.createElement("span");
			c.getElementsByClassName('body')[0].appendChild(mehr);
			var mehrTxt = document.createTextNode("mehr >");
			mehr.appendChild(mehrTxt);
			mehr.style.cursor = 'pointer';
			mehr.style.textDecoration = 'underline';
			this.downObserver = this.mehrLinkClickHandler.bindAsEventListener(this);
			Event.observe(mehr,"mousedown",this.downObserver,false);
		}
		
		//CLOSE-LINK 
		var closeLink = document.createElement("span"); //Hintergrundbild: s. CSS-Datei
		c.getElementsByClassName('davpoi-titlebar')[0].appendChild(closeLink); 
		var closeText = document.createTextNode(" ");
		
		closeLink.appendChild(closeText);
		closeLink.style.cursor = 'pointer';	
		
		var attrClass = document.createAttribute('class');
		attrClass.value = 'popup_close';
		closeLink.setAttributeNode(attrClass);
		
		this.downObserverClose = this.closeOverPoi.bindAsEventListener(this);
		Event.observe(closeLink,"mousedown",this.downObserverClose,false);
		
		
		var dims = c.getDimensions();
		var popupWidth = dims.width;
		var popupHeight = dims.height;
		this.width=popupWidth;
		this.height=popupHeight;
	},
	mehrLinkClickHandler: function(e){
		Event.stop(e);
		this.poi.poiManager.update(this.poi,"mehrLink");			
	},
	translateLangSign: function() {
		if (this.poi.config.usedLang == 0) return "F";
		if (this.poi.config.usedLang == 2) return "E";
		else return "D";
	},
	preventOtherActions: function(e) {
		Event.stop(e);
	}
}

ServiceIndex = Class.create();
ServiceIndex.prototype = {
	initialize: function(){
		this.idx= { 
			meta:{isVisible:true},
			schilder: {isVisible:true},
			p_platz: {isVisible:true},
			halte_s: {isVisible:true},
			info: {isVisible:true},
			bahn:{isVisible:true},
			davhneu: {isVisible:true},
			anlagedav: {isVisible:true},
			bergsportl: {isVisible:true},
			anlagekomm: {isVisible:true},
			essen: {isVisible:true},
			touripunkt: {isVisible:true},
			bett: {isVisible:true},
			zeltplatz: {isVisible:true},
			fels: {isVisible:true},
			gebiet: {isVisible:true},
			Radweg:{isVisible:true},
			fels:{isVisible:true},
			gebiet:{isVisible:true}
		};
	}
};
State = Class.create();
State.prototype = {
	initialize: function(lat, lon, zoomLevel, serviceIndex) {
		this.lat = lat;
		this.lon = lon;
		this.zoomLevel = zoomLevel;
		this.serviceIndex = serviceIndex;
	}
}

Tile = Class.create();
Tile.prototype = {
	initialize: function(container,config,mapSource,tMWidth,tMHeight) {
		this.drawn = false;

		this.container = container;
		
		this.config = config;
		
		this.tMWidth = tMWidth;
		this.tMHeight = tMHeight;
		
		this.mapSource = mapSource;

		this.img = document.createElement("img");
		Element.addClassName(this.img,"tile");
	},
	define: function(xIndex,yIndex) {
		this.xIndex = xIndex;
		this.yIndex = yIndex;
		
		this.lowerXBound = -this.config.tileWidth*this.config.borderCache;
		this.upperXBound = this.config.width + this.config.tileWidth*this.config.borderCache;
		this.lowerYBound = -this.config.tileHeight*this.config.borderCache;
		this.upperYBound = this.config.height + this.config.tileHeight*this.config.borderCache;
		
		this.img.src = this.mapSource.getSrc(this.xIndex,this.yIndex);
		
		this.position();
	},
	getIndex: function() {
		return new Array(this.xIndex,this.yIndex);
	},
	position: function() {
		this.x = this.xIndex * this.config.tileWidth;
		this.y = this.yIndex * this.config.tileHeight;

		if (typeof(this.img) == "object") {
			Element.setStyle(this.img,{top:this.y+"px",left:this.x+"px"});
			Element.setStyle(this.img,{width:this.config.tileWidth+"px",height:this.config.tileHeight+"px"});
		}
	},
	check: function(pxXoff,pxYoff) {
		if ((this.x - pxXoff) < this.lowerXBound) {
			this.define(this.xIndex + this.tMWidth,this.yIndex);
			return;
		}
		if ((this.x - pxXoff) > this.upperXBound) {
			this.define(this.xIndex - this.tMWidth,this.yIndex);
			return;
		}
		if ((this.y - pxYoff) < this.lowerYBound) {
			this.define(this.xIndex,this.yIndex + this.tMHeight);
			return;
		}
		if ((this.y - pxYoff) > this.upperYBound) {
			this.define(this.xIndex,this.yIndex - this.tMHeight);
			return;
		}
	},
	paint: function() {
		if (!this.drawn) this.container.appendChild(this.img);
		this.drawn = true;
	},
	remove: function() {
		//this.img.src = "";
		try {
			this.container.removeChild(this.img);
		}
		catch(e) {}
		this.img = null;
		this.config = null;
		this.mapSource = null;
	}
}
/**
 * @author agutheil
 */
Tilecontainer = Class.create();
Tilecontainer.prototype = {
	initialize: function(mapSource, parent, config, name) {
		this.name = name
		this.mapSource = mapSource;
		this.container = parent;
		this.config = config;
		this.coordinates = this.mapSource.coordinates;
		this.tileBox = new Array();
		this.tileCon=null;
		this.tileCon = document.createElement("div");
	},
	addTile: function(xIndex,yIndex) {
 		var tile = new Tile(this.tileCon,this.config,this.mapSource,this.tMWidth,this.tMHeight);

		tile.define(xIndex,yIndex);
		tile.paint();
		
		this.tileBox.push(tile);
	},
	createTileCon: function() {
		
		if (this.hidden) this.setHidden();
		
		Element.setStyle(this.tileCon,{position:"absolute",top:-this.mapSource.pxYoff+"px",left:-this.mapSource.pxXoff+"px"});		
		Element.addClassName(this.tileCon,"tileCon");
		
		this.container.appendChild(this.tileCon);
	},
	paint: function() {
		this.createTileCon();

		this.tMWidth = Math.floor(this.config.width / this.config.tileWidth) + 2*this.config.borderCache;
		this.tMHeight = Math.floor(this.config.height / this.config.tileHeight) + 2*this.config.borderCache;

		var tileOff = this.getCurrentTileOffset();

		for (var yTile = 0 ; yTile < this.tMHeight ; yTile++) {
			for (var xTile = 0 ; xTile < this.tMWidth ; xTile++) {
				this.addTile(xTile - this.config.borderCache + tileOff[0],yTile - this.config.borderCache + tileOff[1]);
			}
		}
		//this.startCheckTiles();
	},
	checkTiles: function() {
		var pxXoff = this.mapSource.pxXoff;
		var pxYoff = this.mapSource.pxYoff;
		for (var i = 0; i < this.tileBox.length ; i++) this.tileBox[i].check(pxXoff,pxYoff);
	},
	getCurrentTileOffset: function() {
		var pxOff = this.coordinates.rw2px(this.config.lon,this.config.lat);

//		//debug.writeline("pxoff used: "+pxOff);

		return new Array( Math.floor(pxOff[0] / this.config.tileWidth), Math.floor(pxOff[1] / this.config.tileHeight));
	},
	show: function(){
		Element.show(this.tileCon);
	},
	hide: function(){
		Element.hide(this.tileCon);
	},
	remove: function(){
		for (var i = 0; i < this.tileBox.length ; i++){
			 this.tileBox[i].remove();
		}
	}
}
/*
 * 
 * TileMatrix is responsible for all kinds of tile shuffling, how the tiles will be draw,
 * when they will load, what happens when zooming (scaling animation)
 * 
 */
TileMatrix = Class.create();
TileMatrix.prototype = {
	initialize: function(mapManager,config,parent) {
		this.parent = parent;
		this.serviceIndex = mapManager.serviceIndex;
		this.container  = document.createElement("div");
		this.parent.appendChild(this.container);
		this.poiBox = null;
		this.config = config;
		this.mapManager = mapManager;
		
		this.coordinates = new AGS_Coordinates(this.config,this);
		this.mapSource = new AGS_MapSource(this.config,this.coordinates, "davfels/Layer/_alllayers/L");
		

		Element.setStyle(this.container,{position:"absolute",top:-this.mapSource.pxYoff+"px",left:-this.mapSource.pxXoff+"px"});	

		// used for objects that shall be moved with the map, like POI or PopUp container
		this.moveList = new Array();
		this.tileContainer = new Array();
	   
	    var tc = new Tilecontainer(this.mapSource, this.container, this.config,"base");
		this.tileContainer.push(tc);
		
		this.tMWidth = 0;
		this.tMHeight = 0;
		this.hidden = false;
		
		this.checkTilesObserver = null;
		this.prepare();
		tc.show();
		
	},
	prepare: function() {
		this.coordinates.prepare();
		for (var i = 0; i < this.tileContainer.length ; i++)this.tileContainer[i].mapSource.prepare();
	},
	setHidden: function() {
		this.hidden = true;
		if (this.container) Element.setStyle(this.container,{visibility:"hidden"});
	},
	setVisible: function() {
		this.hidden = false;
		if (this.container) Element.setStyle(this.container,{visibility:"visible"});
	},
	show: function(tcName){
		for (var i = 0; i < this.tileContainer.length ; i++){
			if (this.tileContainer[i].name == tcName){
				this.tileContainer[i].show();
			}
		}
	},
	hide: function(tcName){
		for (var i = 0; i < this.tileContainer.length ; i++){
			if (this.tileContainer[i].name == tcName){
				this.tileContainer[i].hide();
			}
		}
	},
	paint: function() {
		for (var i = 0; i < this.tileContainer.length ; i++) this.tileContainer[i].paint();
		// if we enable POIs, we create a new POI manager and attach it to the map (internal)
		if (this.config.hasPOIs) {
			this.poiBox = new POIManager(this);
		}
		this.startCheckTiles();
	},
	startCheckTiles: function() {
		this.checkTilesObserver = this.checkTiles.bind(this);
		Event.observe(this.container,"mousemove",this.checkTilesObserver,false);
	},
	stopCheckTiles: function() {
		if (this.checkTilesObserver) Event.stopObserving(document,"mousemove",this.checkTilesObserver,false);
	},
	checkTiles: function() {
		for (var i = 0; i < this.tileContainer.length ; i++) this.tileContainer[i].checkTiles();
	},
	startCaching: function() {
		if (this.config.hasPOIs) {
			// create a new POI Cache to draw POIs, if edit session, use edit cache
			if (this.config.editMode) this.poiBox.setPoiCache(new EditPOICache(this.poiBox));
			else this.poiBox.setPoiCache(new POICache(this.poiBox,this.tMWidth,this.tMHeight));
			// start caching and provide listener dispenser for move listening
			this.poiBox.startCaching(this.mapManager.listenerDispenser);
		}
	},
	removePOIs: function() {
		if (this.config.hasPOIs) {
			this.poiBox.stopCaching();
			this.poiBox.removePOIs();
		}
	},
	move: function(deltaX,deltaY) {
		//window.defaultStatus = "Begin: ";
		
		// last check for ugly deltas:
		deltaX = Math.round(deltaX);
		deltaY = Math.round(deltaY);

		this.container.style.left = this.container.style.left.delPx() + deltaX + "px";
		this.container.style.top = this.container.style.top.delPx() + deltaY + "px";
				
		for (var i = 0; i < this.tileContainer.length ; i++) this.tileContainer[i].mapSource.addPxOff(-deltaX,-deltaY);
		
		//this.updateMoveListPositions();	//rausgenommen, da die POIs jetzt gemeinsame mit der Kacheln unter einem Container hängen, der voschoben wird
	},
	addToMoveList: function(obj) {
		this.moveList.push(obj);
		this.updateMoveListPositions();
	},
	removeFromMoveList: function(id) {
		for (var i = 0 ; i < this.moveList.length ; i++) {
			if (this.moveList[i].id == id) this.moveList.splice(i,1);
		}
	},
	updateMoveListPositions: function() {
		var xOff = -this.mapSource.getPxOff("x");
		var yOff = -this.mapSource.getPxOff("y");
		this.moveList.each( function(node) {
			node.style.left = xOff + "px";
			node.style.top = yOff + "px";
		});
	},
	remove: function() {
		this.stopCheckTiles();
		this.mapSource.remove();
		//this.mapSource2.remove();
		//this.mapSource3.remove();
		//this.mapSource4.remove();
		if (this.config.hasPOIs) this.poiBox.remove();
	
		/*this.tileBox.each( function(node) {
			if(typeof(node) == "object") node.remove();
		});*/
		
/*		this.tileContainer.each( function(node) {
			if(typeof(node) == "object") node.remove();
		});*/
		for (var i = 0; i < this.tileContainer.length ; i++){
			this.tileContainer[i].remove();
		}

		if (typeof(this.tileCon) == "object") {
			this.container.removeChild(this.tileCon);
		}
		
		this.coordinates.remove();
		this.tileContainer = null;
		this.tileBox = null;
		this.mapSource = null;
		this.coordinates = null;
		this.poiBox = null;
		this.parent.removeChild(this.container);
		this.container = null;
	}
}

/*
 * 
 * URLHandler is invoked when the user adds HTTP GET Strings to the called URL.
 * Each action must have a return function as a parameter, which will then be called
 * if the parameter is set in the URL.
 * This function is given the parameters found in the GET string.
 * The only action taken is a .split("_")
 * 
 */
URLHandler = Class.create();
URLHandler.prototype = {
	initialize: function() {
		this.searchString = "";
		this.searchArguments = new Array();
	},
	parse: function(searchString) {
		this.searchString = searchString;
		if (this.searchString != "") {
			// remove possible leading #
			var toReplace = new RegExp("#","");
			var processingString = this.searchString.replace(toReplace,"");
			// remove leading ?
			processingString = processingString.substring(1,processingString.length);
			// if we have more than one argument
			if (processingString.indexOf("&") != -1) {
				// the whole key=value pairs
				var rawSearchArguments = processingString.split("&");
				// for each pair, divide by "=" and create array ( array(key,value) , array(key,value) ...)
				for (var i = 0 ; i < rawSearchArguments.length ; i++) this.searchArguments[i] = rawSearchArguments[i].split("=");
			}
			else this.searchArguments[0] = processingString.split("=");
		}
	},
	handle: function(argument,returnFunction) {
		for (var i = 0; i < this.searchArguments.length ; i++ ) {
			if (this.searchArguments[i][0] == argument) {
				// check if the parameter has "_" and create array if necessary. Call return function with the values.
				var parameter = this.searchArguments[i][1];
				if (parameter.indexOf("_") != -1) parameter = parameter.split("_");
				returnFunction(parameter);
			}
		}
	}
}


/*	ZoomBar
 * 
 * The zoom bar object.
 * 
 */
ZoomBar = Class.create();
ZoomBar.prototype = {
	initialize: function(mapManager,container,config) {
		this.mapManager = mapManager;
		this.container = container;
		this.config = config;
		this.triggerZoomInObserver = null;
		this.triggerZoomOutObserver = null;
		this.knobDownObserver = null;
		this.zoomBarCon = null;
	},
	paint: function() {
		if (this.zoomBarCon == null) {
			
			this.zoomBarCon = document.createElement("div");
			Element.addClassName(this.zoomBarCon,"zoomBarCon");
			
			this.zoomPlus = document.createElement("img");
			Element.addClassName(this.zoomPlus,"zoomPlus");
			this.zoomPlus.src = this.config.plusImg;
			
			this.zoomMinus = document.createElement("img");
			Element.addClassName(this.zoomMinus,"zoomMinus");
			this.zoomMinus.src = this.config.minusImg;
			
			this.zoomKnob = document.createElement("img");
			Element.addClassName(this.zoomKnob,"zoomKnob");
			this.zoomKnob.src = this.config.knobImg;
			
			this.zoomStepImgs = new Array();
			
			this.oneZoomLevelHeight = this.config.knobImgHeight;
			
			this.stepMultiplier = Math.ceil(this.oneZoomLevelHeight / this.config.stepImgHeight);
			this.zoomBarHeight = 0;
			
			this.zoomBarCon.appendChild(this.zoomPlus);
			this.zoomBarHeight += this.config.zoomBarPlusImgHeight;
			
			if (this.config.zoomBarStyle == "full") {
				for (var i = 0; i < ( (this.config.agsResolution.length - this.config.minimumAllowedZoomLevel) * this.stepMultiplier ) ; i ++) {
					var zoomStepImg = document.createElement("img");
					Element.addClassName(zoomStepImg,"zoomStepImg");
					this.zoomStepImgs.push(zoomStepImg);
					this.zoomStepImgs[this.zoomStepImgs.length - 1].src = this.config.stepImg;
					this.zoomStepImgs[this.zoomStepImgs.length - 1].height = this.config.stepImgHeight;
					this.zoomBarCon.appendChild(this.zoomStepImgs[this.zoomStepImgs.length - 1]);
					this.zoomBarHeight += this.config.stepImgHeight;
				}
			}
			this.zoomBarCon.appendChild(this.zoomMinus);
			this.zoomBarHeight += this.config.minusImgHeight;
					
			// MAGIC NUMBER: Zoombar Image is not symmetric horizontally, therefore, the knob offset is less then "center" -> -4
			// TODO: Positionierungsberechnungen unabhängig von bestimmter Positionierung implementieren!
			//var knobLeft = Math.round( ( this.config.stepImgWidth - this.config.knobImgWidth ) / 2) - 4;
			var knobLeft = 0;
			// workaround for IE 6, zoomKnob displaced..
			if (navigator.userAgent.match(/MSIE 6/gi)) {
				//knobLeft = 3; //macht im IE6 probleme
				knobLeft = 0;
			}
			//Element.setStyle(this.zoomKnob,{position:"absolute",top:"60px",left: knobLeft+"px"});
			Element.setStyle(this.zoomKnob,{position:"absolute",top: ( this.container.style.height.delPx() - this.config.zoomBarPaddingHeight + this.config.minusImgHeight + this.config.knobImgHeight) + "px",left: knobLeft+"px"});
			
			if (this.config.zoomBarStyle == "full") this.zoomBarCon.appendChild(this.zoomKnob);
			
			//Element.setStyle(this.zoomBarCon,{position:"absolute",top: ( this.container.style.height.delPx() - this.config.zoomBarPaddingBottom - this.zoomBarHeight ) + "px", left:"0px",width: this.config.stepImgWidth+"px" });
			var ie6Displacing = 0;
			// workaround for IE 6, zoomBar displaced..
			if (navigator.userAgent.match(/MSIE 6/gi)) {
				//ie6Displacing = 3; //macht im IE6 probleme
				ie6Displacing = 0;
			}
			
			Element.setStyle(this.zoomBarCon,{position:"absolute",top: ( this.config.zoomBarPaddingHeight ) + "px", left: ( this.container.style.width.delPx() - 46 - ie6Displacing) + "px" ,width: this.config.stepImgWidth+"px" });
			//Element.addClassName(this.zoomBarCon,"zoomBarCon");
			
			this.container.appendChild(this.zoomBarCon);
			
			this.addEventHandler();
			
			this.mapManager.listenerDispenser.attachListener("postZoom",this.positionKnob.bind(this));
		}		
		this.positionKnob(this.mapManager.getCurrentZoomLevel());
	},
	addEventHandler: function() {
		this.knobDownObserver = this.knobDown.bindAsEventListener(this);
		this.triggerZoomInObserver = this.triggerZoomIn.bindAsEventListener(this);
		this.triggerZoomOutObserver = this.triggerZoomOut.bindAsEventListener(this);
		
		Event.observe(this.zoomKnob,'mousedown',this.knobDownObserver,false);
		Event.observe(this.zoomPlus,'click',this.triggerZoomInObserver,false);
		Event.observe(this.zoomMinus,'click',this.triggerZoomOutObserver,false);
	},
	triggerZoomIn: function() {
		this.mapManager.zoomIn();
	},
	triggerZoomOut: function() {
		this.mapManager.zoomOut();
	},
	positionKnob: function(zoomLevel) {
		if (this.config.zoomBarStyle == "full") {
			if (typeof(zoomLevel) != "number") zoomLevel = this.mapManager.getCurrentZoomLevel();
			var knobTop = this.config.zoomBarPlusImgHeight;
			var levelCount = this.config.agsResolution.length - this.config.minimumAllowedZoomLevel - zoomLevel;
		
			knobTop += this.stepMultiplier * levelCount * this.config.stepImgHeight;
			Element.setStyle(this.zoomKnob,{top:knobTop+"px"});
		}
	},
	calcKnobZoomLevel: function() {
		var zoomLevel = Math.round((this.zoomKnob.style.top.delPx() - this.config.zoomBarPlusImgHeight) / this.config.knobImgHeight);
		zoomLevel = this.config.agsResolution.length - zoomLevel - 1;
		this.mapManager.zoomTo(zoomLevel);
	},
	knobDown: function(e) {
		Event.stop(e);
		this.knobObserver = this.knobMove.bindAsEventListener(this);
		this.knobUpObserver = this.knobUp.bindAsEventListener(this);
		Event.observe(this.container,'mousemove',this.knobObserver,false);
		Event.observe(this.container,'mouseup',this.knobUpObserver,false);
	},
	knobUp: function(e) {
		
		Event.stop(e);
		Event.stopObserving(this.container,'mousemove',this.knobObserver,false);
		Event.stopObserving(this.container,'mouseup',this.knobUpObserver,false);
		this.calcKnobZoomLevel();
	},
	knobMove: function(e) {
		
		Event.stop(e);
		var startX = Event.pointerX(e);
		var startY = Event.pointerY(e);
		var elementToDrag = this.zoomKnob;
		var ktop = elementToDrag.style.top;
		
		ktop = ktop.substring(0, ktop.length-2);
		//window.defaultStatus = ktop+"";
		ktop = parseInt(ktop);
		var orig = Position.cumulativeOffset(elementToDrag);
		var deltaX = startX - orig[0];
		var deltaY = startY - orig[1];
		ktop = ktop+deltaY;
		
		var dimensions = elementToDrag.getDimensions();
		var w = dimensions.width;
		var h = dimensions.height;
		ktop = ktop-h/2; 
		
		var ktopmin = this.config.zoomBarPlusImgHeight;
		var ktopmax = this.zoomBarHeight - this.config.zoomBarPlusImgHeight - h;
		
		if (ktop < ktopmin) ktop = ktopmin;
		if (ktop > ktopmax) ktop = ktopmax;
		
		Element.setStyle(elementToDrag,{top:ktop+"px"});
	},
	remove: function() {
		// classical remove routine
		// 1. Event Handler
		Event.stopObserving(this.container,'mousemove',this.knobObserver,false);
		Event.stopObserving(this.container,'mouseup',this.knobUpObserver,false);
		
		if (this.zoomBarCon != null) {

			Event.stopObserving(this.zoomKnob,'mousedown',this.knobDownObserver,false);
			Event.stopObserving(this.zoomPlus,'click',this.triggerZoomInObserver,false);
			Event.stopObserving(this.zoomMinus,'click',this.triggerZoomOutObserver,false);

			// 2. DOM Objects
			try {
				this.zoomBarCon.removeChild(this.zoomKnob);
				this.zoomBarCon.removeChild(this.zoomMinus);
				this.zoomBarCon.removeChild(this.zoomPlus);
				for (var i = 0; i < this.zoomStepImgs.length ; i++) {
					try {
						this.zoomBarCon.removeChild(this.zoomStepImgs[i]);
					} catch(e) {
						//debug.writeline("removing child from zoombar icon failed");
					}
				}
				this.container.removeChild(this.zoomBarCon);
			} catch(e) {
				//debug.writeline("removing zoombarcon failed");
			}
		}

		// 3. Nullify
		this.knobDownObserver = null;
		this.knobObserver = null;
		this.knobUpObserver = null;
		this.triggerZoomInObserver = null;
		this.triggerZoomOutObserver = null;

		this.config = null;
		this.container = null;
//		//debug.writeline("zoomBar removed");
	}
}
// used to remember the last open popup,
// is used for the state object, when
// transferring the state of the smaller
// map to the bigger map...
lastOpenPopUp=-1;
function setLastOpenPopUp(id) {
	lastOpenPopUp = id;
}

function getLastOpenPopUp() {
	return lastOpenPopUp;
}

// code from http://www.irt.org/script/878.htm
function cloneObject(what) {
    for (i in what) {
        this[i] = what[i];
    }
}

mapOptions = {
	//  ++++++++++ Base Map +++++++++++
	width : 648,//"full", // height and width of the displayed map view in px
	height : 394,//"full",
	top : 0, // offset of this map view from browser window top, left in px
	left : 0,

	/*
	mapTileDir : "http://tash.alta4gis.de/cache/_alllayers/L",
	mapTileImgFormat : "png",
	tileWidth : 256, 
	tileHeight : 256,
	agsTileXOffset : 25,
	agsTileYOffset : 17,
	agsResolution: [661.459656252645,396.875793751587,132.29193125053,66.145965625264878,26.458386250105598,13.229193125053678,6.614596562526839],
	agsScale: [2500000,1500000,500000,250000,100000,50000,25000],
	agsTileOrigin: [3347029.35624691,6199383.27838854],
	*/
	mapTileDir : 'http://dav.alta4cloud.com/cache/', // 'http://www.dav-felsinfo.de/cache/',
	mapTileImgFormat : "jpg",
	tileWidth : 256, // width and height of one map tile in px
	tileHeight : 256,
	agsTileXOffset : 0,
	agsTileYOffset : 0,

	agsResolution: [2645.83862501058,1322.91931250529,793.751587503175,396.875793751588,132.291931250529,66.1459656252646,26.4583862501058,13.2291931250529,6.61459656252646,2.64583862501058,1.32291931250529],
	agsScale: [10000000,5000000,3000000,1500000,500000,250000,100000,50000,25000,10000,5000],
	agsTileOrigin: [3000000,6200000],
	spacerImg : "http://dav.alta4cloud.com/spacer_white.gif", //spacerImg : "http://tash.alta4gis.de/design/spacer_white.gif", // is used for display when no map tile is defined for the requested position		
	borderCache: 2, // count of tiles by which the tilematrix is bigger than the visible extent
	
	minimumAllowedZoomLevel: 1,

	//  ++++++++++ Initial loading cnfiguration +++++++++++
	// will be overridden by the URL parameters! (when implemented)
	zoomLevel : 1, // zoomLevel is the index of the zoomFactor array (in [1,4,8,16] 2 would resemble zoomFactor "8")
	lon : 3542670, // map center on start
	lat : 5368875,
			

	//  ++++++++++ Coordinates +++++++++++
	magicNumberRW_Y: 0.0, // ugly magic numbers to account for points offset in the coordinate calculation
	magicNumberPX_Y: 0.4,
	
	//  ++++++++++ Compass Rose +++++++++++
	hasCompassRose : true,
	compassRoseSize : 54, // size in px, is assumed to be quadratic
	compassRoseSrc: "/viewer/a4viewer/design/e7/windrose.gif",
	compassRoseSpeed : 5, // in something like px / s
	compassRoseGamestyleSpeed: 0.25, // factor for the gamestyle mover
	compassRosePadding: 10, // distance from map border
	compassRosePosition : "top-left", // possible: "bottom-left","bottom-right","top-left"

	//  ++++++++++ ZoomBar +++++++++++
	hasZoomBar : true,
	zoomBarStyle : "full", // if "full", the zoomBar gets a slider to select zoomLevel, otherwise only plus and minus sign is displayed
	zoomBarTop : 0,
	zoomBarLeft: 0,

	plusImg: "/viewer/a4viewer/design/e7/zoomplus.gif", // design elements of the zoomBar
	plusImgWidth: 18,
	zoomBarPlusImgHeight: 18,
	minusImg: "/viewer/a4viewer/design/e7/zoomminus.gif",
	minusImgWidth: 18,
	minusImgHeight: 18,
	stepImg: "/viewer/a4viewer/design/e7/scrollbar.gif",
	stepImgWidth: 18,
	stepImgHeight: 11,
	knobImg: "/viewer/a4viewer/design/e7/schieber.gif",
	knobImgWidth: 18,
	knobImgHeight: 11,

	zoomBarPaddingBottom: 0, // if the zoom bar shall not align to map view bottom, add offset here
	zoomBarPaddingHeight: 74,

	//  ++++++++++ POIs +++++++++++
	hasPOIs : true,
	poiMinimumZoomLevel : 0,
	poiMaxPrecision: 4, // defines the maximum accuracy of the poiCache. Should be ok for all normal use cases
	
	poiSearchExtentAddress: "/dav/controller/searchExtent?", //URL zum searchExtent-Servlet
	poiServletAddress: "/viewer/jsonviewer/extent?", // List of POIs in the Extent (minX,minY,maxX,maxY)
	leavesServletAddress: "/viewer/jsonviewer/leaves?", // 
	poiTypeServletAddress: "/viewer/jsonviewer/type?", // all subpoints of a given point of a given type (id,type)
	poiDataServletAddress: "/viewer/jsonviewer/poi?", // all attributes of one poi (id)
		
	defaultIcon: ["/dav/symbols/dav/meta.png",22,22], // default icon to be used if no icon is defined
	usedIcons: {
		schilder: ["/viewer/a4viewer/images/poi_noncomm/schilder/index.gif",16,16],
		p_platz:  ["/viewer/a4viewer/images/poi_noncomm/p_platz/index.gif",16,16],
		halte_s:  ["/viewer/a4viewer/images/poi_noncomm/halte_s/index.gif",16,16],
		info:  ["/viewer/a4viewer/images/poi_noncomm/info/index.gif",16,16],
		bahn: ["/viewer/a4viewer/images/poi_noncomm/bahn/index.gif",16,16],
		davhneu:  ["/viewer/a4viewer/images/poi_noncomm/davhneu/index.gif",16,16],
		anlagedav:  ["/viewer/a4viewer/images/poi_noncomm/anlagedav/index.gif",16,16],
		bergsportl:  ["/viewer/a4viewer/images/poi_comm/bergsportl/index.gif",16,16],
		anlagekomm:  ["/viewer/a4viewer/images/poi_comm/anlagekomm/index.gif",16,16],
		essen:  ["/viewer/a4viewer/images/poi_comm/essen/index.gif",16,16],
		touripunkt:  ["/viewer/a4viewer/images/poi_comm/touripunkt/index.gif",16,16],
		bett:  ["/viewer/a4viewer/images/poi_comm/bett/index.gif",16,16],
		zeltplatz:  ["/viewer/a4viewer/images/poi_comm/zeltplatz/index.gif",16,16],
		fels: ["/viewer/a4viewer/images/fels/index.gif",16,16],
		gebiet: ["/viewer/a4viewer/images/gebiet/index.gif",20,20],
		meta: ["/dav/symbols/dav/meta.png",22,22]

	},
	usedIconsActive: {
		schilder: ["/viewer/a4viewer/images/poi_noncomm/schilder/index_f2.gif",16,16],
		p_platz:  ["/viewer/a4viewer/images/poi_noncomm/p_platz/index_f2.gif",16,16],
		halte_s:  ["/viewer/a4viewer/images/poi_noncomm/halte_s/index_f2.gif",16,16],
		info:  ["/viewer/a4viewer/images/poi_noncomm/info/index_f2.gif",16,16],
		bahn: ["/viewer/a4viewer/images/poi_noncomm/bahn/index_f2.gif",16,16],
		davhneu:  ["/viewer/a4viewer/images/poi_noncomm/davhneu/index_f2.gif",16,16],
		anlagedav:  ["/viewer/a4viewer/images/poi_noncomm/anlagedav/index_f2.gif",16,16],
		bergsportl:  ["/viewer/a4viewer/images/poi_comm/bergsportl/index_f2.gif",16,16],
		anlagekomm:  ["/viewer/a4viewer/images/poi_comm/anlagekomm/index_f2.gif",16,16],
		essen:  ["/viewer/a4viewer/images/poi_comm/essen/index_f2.gif",16,16],
		touripunkt:  ["/viewer/a4viewer/images/poi_comm/touripunkt/index_f2.gif",16,16],
		bett:  ["/viewer/a4viewer/images/poi_comm/bett/index_f2.gif",16,16],
		zeltplatz:  ["/viewer/a4viewer/images/poi_comm/zeltplatz/index_f2.gif",16,16],
		fels: ["/viewer/a4viewer/images/fels/index_f2.gif",16,16],
		gebiet: ["/viewer/a4viewer/images/gebiet/index_f2.gif",20,20],
		meta: ["/dav/symbols/dav/meta.png",22,22]

	},	
	typeMap: { // defines translations for the POI types, order: FR,DE,EN
		schilder: ["Artikel","Artikel","Artikel"],
		p_platz: ["Offer","Offer","Offer"],
		halte_s: ["Event","Event","Event"],
		info: ["Tourismusinformation","Tourismusinformation","Tourismusinformation"],
		bahn: ["Bahnhöfe","Bahnhöfe","Bahnhöfe"],
		davhneu: ["Hotels, Pensionen","Hotels, Pensionen","Hotels, Pensionen"],
		anlagedav: ["Ferienwohnungen/-häuser","Ferienwohnungen/-häuser","Ferienwohnungen/-häuser"],
		bergsportl: ["Campingplätze","Campingplätze","Campingplätze"],
		anlagekomm: ["Jugendherbergen","Jugendherbergen","Jugendherbergen"],
		essen: ["Bauernhöfe","Bauernhöfe","Bauernhöfe"],
		touripunkt: ["Bett & Bike","Bett & Bike","Bett & Bike"],
		bett: ["Wellnesshotels & Beautyfarmen","Wellnesshotels & Beautyfarmen","Wellnesshotels & Beautyfarmen"],
		zeltplatz: ["Gastronomie","Gastronomie","Gastronomie"],
		fels: ["Museen & Denkmäler","Museen & Denkmäler","Museen & Denkmäler"],
		gebiet: ["Theater","Theater","Theater"]
	},
	
	typeMapMapping: {
		4:"schilder",
		5:"p_platz",
		7:"halte_s",
		8:"info",
		9:"bahn",
		11:"davhneu",
		12:"anlagedav",
		101:"bergsportl",
		102:"anlagekomm",
		103:"essen",
		104:"touripunkt",
		105:"bett",
		106:"zeltplatz",
		fels:"fels",
		gebiet:"gebiet"
	},
	
	rw_mapping: {
		
	},


	langs: ["fr","de","en"],

	metaPointXOff : 0,
	metaPointYOff : 0,
	subPointXOff: 0,
	subPointYOff: 0,
	
	// search results:
	
	showOnlySearchResults: false, // defines if only search result pois are displayed, the searchResult servlet is used...
	searchResultIds: [], // containes the IDs
	searchResultServletAddress: "http://luxembourg2007.alta4gis.de/luxembourg2007/dav/controller/searchExtent?", // List of search results in the Extent (replaces normal servlet)

	editObject: { // prepares the object to be edited
		recode: false,
		id: 0,
		lon: 0,
		lat: 0,
		origLon: 0,
		origLat: 0,
		type: null,
		name: ""
	},
	
	//  ++++++++++ PopUps +++++++++++
	showPopUpMinWidth: 300,
	showPopUpMinHeight: 300,
	
	// using this template the popups can be customized, just add the fields in the DB at the position where they should be displayed,
	// all HTML content is parsed into a div by using innerHTML
	
	//TASH
	popUpTagNames: ["name","street","streetNumber","zipCode","zip","city","address1Street","address1ZipCode","address1City","contactName","email","homepage","phone1","phone","fax","previewImage","imageUrl","shortDescription","detailLink","region","lonWGS84","latWGS84"],
	
	popUpTemplateGray10: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="gray-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_gray.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_street_ _streetNumber__zipCode_ _city__phone1_</p><p class="copy">_shortDescription_</p></div></div>',
	popUpTemplateGray20: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="gray-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_gray.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_street_ _streetNumber__zipCode_ _city_</p></div></div>',
	
	//mit Fax: popUpTemplateOrange: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="orange-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_orange.gif" alt="" /><h3>_name_</h3></div><div class="body"><img src="_previewImage_" alt="" class="thumbnail" /><p class="address">_address1Street__address1ZipCode_ _address1City__phone1_</p><p class="copy">_shortDescription_</p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div>',
	popUpTemplateOrange: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="orange-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_orange.gif" alt="" /><h3>_name_</h3></div><div class="body">_previewImage_<p class="address">_address1Street__address1ZipCode_ _address1City__phone1_</p><p class="copy">_shortDescription_</p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div>',
	
	popUpTemplateBlue: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="blue-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_blue.gif" alt="" /><h3>_name_</h3></div><div class="body">_previewImage_<p class="address">_address1Street__address1ZipCode_ _address1City__phone1_</p><p class="copy">_shortDescription_</p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div>',
	popUpTemplateBlue60: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="blue-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_blue.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_address1Street__zipCode_ _address1City_</p></div></div>',
	popUpTemplateBlue80: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="blue-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_blue.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_address1Street__address1ZipCode_ _address1City_</p><p class="copy">Region(en): _region_<br />Koordinaten: _lonWGS84_, _latWGS84_<br/>_shortDescription_</p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div>',
	
	//Fels + POI
	popUpTemplateDavpoi: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="davpoi-titlebar"><h3>_name_</h3></div><div class="body"><div>_previewImage_<p class="address">_address1Street__address1ZipCode_ _address1City__phone1_</p><p class="copy">_shortDescription_</p><p class="morelink"></p></div></div></div>',
	
	popUpTemplateGreen91: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="green-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_green.gif" alt="" /><h3>_name_</h3></div><div class="body">_previewImage_<p class="address">_address1Street__address1ZipCode_ _address1City__phone1_</p><p class="morelink"></p></div></div>',
	popUpTemplateGreen92: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="green-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_green.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_address1Street__address1ZipCode_ _address1City_</p><div></div>',
	popUpTemplateRW: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="gray-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_gray.gif" alt="" /><h3>_name_</h3></div><div class="body"><img src="_imageUrl_" alt="" class="thumbnail" /><p class="address">_contactName_<br />_email_<br />_homepage_<br />_street_ _streetNumber_<br />_zip_ _city_<br /></p><p class="copy">_shortDescription_</p></div></div>',		
	popUpHideDelay: 250, // how long should the popup stay open after leaving (in ms)
	linesVisibleInAggregatedPopUp:10, // after x lines, the popup is cut and a scrollbar is drawn.
	maxLinkLength: 30, // after 30 letters, "..." will be displayed and the link is cut.
	maxNameLength: 30, // after 30 letters, "..." will be displayed and the name (_name_) is cut.
	
	
	weatherStationsTemplate : '<span>#{name}<span><img src="#{img}">#{minTmp}°/#{maxTmp}°',
	
	//  ++++++++++ MapManager +++++++++++
	zoomSpeed: 75, // speed of the zoom animation
	moveSpeed: 10, // step size of the move animation (translate the tiles by ?)
	
	//  ++++++++++ Copyright +++++++++++
	hasCopyrightInfo: true,
	copyrightInfos: [["&copy; Landesvermessungs&auml;mter","http://www.dav-felsinfo.de/viewer/copyright_lvermessung.html", "copyright_data"], ["powered by alta4","http://www.alta4.com", "copyright_sitelink"]],
	copyrightInfosOffsetX: 20,
	copyrightInfosOffsetY: 20,

	//  ++++++++++ Logging +++++++++++
	loggingUserId: 1,
	loggingProject: 1,
	loggingURL: "/viewer/logging/index.jsp",
	loggingActive: true,
	googleAnalyticsLoggingActive: false,
	
	//  ++++++++++ OverviewMap +++++++++++
	overviewMapZoomlevelDifference: 4, // number of zoomlevels between overview map and main map
	hasInteractions: true,
	isOverviewMap: false,
	hasConnectedMap: false,
	connectedMap: null,
	moveObserver: null,
	centerObserver: null,
	overviewMapBorderStyle: "1px solid white",
	
	//  ++++++++++ Authentification +++++++++++
	sessionId: 0, // will be used to store the current sessionId
	//editGetPOIServlet: "/alta4_servlets/getPOI?",
	//editSaveServlet: "/alta4_servlets/savePOI?",
//	closeSessionServlet: "/alta4_servlets/closeSession",
	
	//  ++++++++++ Minimizeability +++++++++++
	hasMinimizer: false,
	minimizerMinimizeImageSrc: "/viewer/a4viewer/design/ov_map_close.png",
	minimizerMaximizeImageSrc: "/viewer/a4viewer/design/ov_map_open.png",
	minimizerImageSize: [15,15],

	// no map updates when true!!!	
	suspendRedraw: false
};


overviewMapOptions = {	
	//  ++++++++++ Base Map +++++++++++
	width : 250,//"full", // height and width of the displayed map view in px
	height : 250,//"full",
	top : 0, // offset of this map view from browser window top, left in px
	left : 0,
	
	//  ++++++++++ Initial loading cnfiguration +++++++++++
	// will be overridden by the URL parameters! (when implemented)
	
	mapTileDir : 'http://dav.alta4cloud.com/cache/', // 'http://www.dav-felsinfo.de/cache/',
	mapTileImgFormat : "jpg",
	tileWidth : 256, // width and height of one map tile in px
	tileHeight : 256,
	agsTileXOffset : 0,
	agsTileYOffset : 0,

	agsResolution: [2645.83862501058,1322.91931250529,793.751587503175,396.875793751588,132.291931250529,66.1459656252646,26.4583862501058,13.2291931250529,6.61459656252646,2.64583862501058,1.32291931250529],
	agsScale: [10000000,5000000,3000000,1500000,500000,250000,100000,50000,25000,10000,5000],
	agsTileOrigin: [3000000,6200000],
	spacerImg : "http://dav.alta4cloud.com/spacer_white.gif", //spacerImg : "http://tash.alta4gis.de/design/spacer_white.gif", // is used for display when no map tile is defined for the requested position		
	borderCache: 2, // count of tiles by which the tilematrix is bigger than the visible extent
	minimumAllowedZoomLevel: 1,

	//  ++++++++++ Initial loading cnfiguration +++++++++++
	// will be overridden by the URL parameters! (when implemented)
	zoomLevel : 1, // zoomLevel is the index of the zoomFactor array (in [1,4,8,16] 2 would resemble zoomFactor "8")
	lon : 3542670, // map center on start
	lat : 5368875,
	//  ++++++++++ Coordinates +++++++++++
	magicNumberRW_Y: 0.0, // ugly magic numbers to account for points offset in the coordinate calculation
	magicNumberPX_Y: 0.4,
	
	//  ++++++++++ Compass Rose +++++++++++
	hasCompassRose : false,
	compassRoseSize : 54, // size in px, is assumed to be quadratic
	compassRoseSrc: "/viewer/a4viewer/design/e7/windrose.gif",
	compassRoseSpeed : 5, // in something like px / s
	compassRoseGamestyleSpeed: 0.25, // factor for the gamestyle mover
	compassRosePadding: 10, // distance from map border
	compassRosePosition : "top-left", // possible: "bottom-left","bottom-right","top-left"

	//  ++++++++++ ZoomBar +++++++++++
	hasZoomBar : false,
	zoomBarStyle : "full", // if "full", the zoomBar gets a slider to select zoomLevel, otherwise only plus and minus sign is displayed
	zoomBarTop : 0,
	zoomBarLeft: 0,

	plusImg: "/viewer/a4viewer/design/e7/zoomplus.gif", // design elements of the zoomBar
	plusImgWidth: 18,
	zoomBarPlusImgHeight: 18,
	minusImg: "/viewer/a4viewer/design/e7/zoomminus.gif",
	minusImgWidth: 18,
	minusImgHeight: 18,
	stepImg: "/viewer/a4viewer/design/e7/scrollbar.gif",
	stepImgWidth: 18,
	stepImgHeight: 11,
	knobImg: "/viewer/a4viewer/design/e7/schieber.gif",
	knobImgWidth: 18,
	knobImgHeight: 11,

	zoomBarPaddingBottom: 0, // if the zoom bar shall not align to map view bottom, add offset here
	zoomBarPaddingHeight: 74,

	//  ++++++++++ POIs +++++++++++
	hasPOIs : false,
	poiMinimumZoomLevel : 0,
	poiMaxPrecision: 4, // defines the maximum accuracy of the poiCache. Should be ok for all normal use cases
	
	poiSearchExtentAddress: "/dav/controller/searchExtent?", //URL zum searchExtent-Servlet
	poiServletAddress: "/dav/controller/extent?", // List of POIs in the Extent (minX,minY,maxX,maxY)
	leavesServletAddress: "/dav/controller/leaves?", // 
	poiTypeServletAddress: "/dav/controller/type?", // all subpoints of a given point of a given type (id,type)
	poiDataServletAddress: "/dav/controller/poi?", // all attributes of one poi (id)
		
	defaultIcon: ["/dav/symbols/dav/meta.png",22,22], // default icon to be used if no icon is defined
	usedIcons: {
		Article: ["/dav/symbols/dav/article.png",22,22],
		Offer: ["/dav/symbols/dav/offer.png",22,22],
		Event: ["/dav/symbols/dav/event.png",22,22],
		Tourismusinformation: ["/dav/symbols/dav/10_icon_info.png",24,24],
		Bahnhof: ["/dav/symbols/dav/20_icon_train.png",24,24],
		HotelPension: ["/dav/symbols/dav/31_icon_bed.png",24,24],
		FerienwohnungHaus: ["/dav/symbols/dav/32_icon_house.png",24,24],
		Campingplatz: ["/dav/symbols/dav/33_icon_camping.png",24,24],
		Jugendherberge: ["/dav/symbols/dav/34_icon_hostel.png",24,24],
		Bauernhof: ["/dav/symbols/dav/35_icon_farm.png",24,24],
		BettBike: ["/dav/symbols/dav/36_icon_bedbike.png",24,24],
		WellnesshotelBeautyfarm: ["/dav/symbols/dav/37_icon_beauty.png",24,24],
		Gastronomie: ["/dav/symbols/dav/40_icon_food.png",24,24],
		MuseumDenkmal: ["/dav/symbols/dav/51_icon_museum.png",24,24],
		Theater: ["/dav/symbols/dav/52_icon_theatre.png",24,24],
		SchlossGarten: ["/dav/symbols/dav/53_icon_castle.png",24,24],
		KlosterKirche: ["/dav/symbols/dav/54_icon_church.png",24,24],
		Freizeitpark: ["/dav/symbols/dav/55_icon_themepark.png",24,24],
		TierparkZoo: ["/dav/symbols/dav/56_icon_zoo.png",24,24],
		StrandBaden: ["/dav/symbols/dav/60_icon_beach.png",24,24],
		Naturerlebnis: ["/dav/symbols/dav/70_icon_nature.png",24,24],
		Segeln: ["/dav/symbols/dav/80_icon_sailing.png",24,24],
		FahrradverleihReparaturservice: ["/dav/symbols/dav/91_icon_bikerepair.png",24,24],
		AbschliessbareFahrradbox: ["/dav/symbols/dav/92_icon_bikelock.png",24,24],
		RastplatzSchutzhuette: ["/dav/symbols/dav/93_icon_bikeshed.png",24,24],
		Golfen: ["/dav/symbols/dav/100_icon_golf.png",24,24],
		Reiten: ["/dav/symbols/dav/110_icon_riding.png",24,24],
		meta: ["/dav/symbols/dav/meta.png",22,22]
	},	
	typeMap: { // defines translations for the POI types, order: FR,DE,EN
		Article: ["Artikel","Artikel","Artikel"],
		Offer: ["Offer","Offer","Offer"],
		Event: ["Event","Event","Event"],
		Tourismusinformation: ["Tourismusinformation","Tourismusinformation","Tourismusinformation"],
		Bahnhof: ["Bahnhöfe","Bahnhöfe","Bahnhöfe"],
		HotelPension: ["Hotels, Pensionen","Hotels, Pensionen","Hotels, Pensionen"],
		FerienwohnungHaus: ["Ferienwohnungen/-häuser","Ferienwohnungen/-häuser","Ferienwohnungen/-häuser"],
		Campingplatz: ["Campingplätze","Campingplätze","Campingplätze"],
		Jugendherberge: ["Jugendherbergen","Jugendherbergen","Jugendherbergen"],
		Bauernhof: ["Bauernhöfe","Bauernhöfe","Bauernhöfe"],
		BettBike: ["Bett & Bike","Bett & Bike","Bett & Bike"],
		WellnesshotelBeautyfarm: ["Wellnesshotels & Beautyfarmen","Wellnesshotels & Beautyfarmen","Wellnesshotels & Beautyfarmen"],
		Gastronomie: ["Gastronomie","Gastronomie","Gastronomie"],
		MuseumDenkmal: ["Museen & Denkmäler","Museen & Denkmäler","Museen & Denkmäler"],
		Theater: ["Theater","Theater","Theater"],
		SchlossGarten: ["Schlösser & Gärten","Schlösser & Gärten","Schlösser & Gärten"],
		KlosterKirche: ["Klöster & Kirchen","Klöster & Kirchen","Klöster & Kirchen"],
		Freizeitpark: ["Freizeitparks","Freizeitparks","Freizeitparks"],
		TierparkZoo: ["Tierparks/Zoos","Tierparks/Zoos","Tierparks/Zoos"],
		StrandBaden: ["Strand & Baden","Strand & Baden","Strand & Baden"],
		Naturerlebnis: ["Naturerlebnis","Naturerlebnis","Naturerlebnis"],
		Segeln: ["Segeln","Segeln","Segeln"],
		FahrradverleihReparaturservice: ["Fahrradverleih & Reparaturservice","Fahrradverleih & Reparaturservice","Fahrradverleih & Reparaturservice"],
		AbschliessbareFahrradbox: ["abschließbare Fahrradboxen","abschließbare Fahrradboxen","abschließbare Fahrradboxen"],
		RastplatzSchutzhuette: ["Rastplätze & Schutzhütten","Rastplätze & Schutzhütten","Rastplätze & Schutzhütten"],
		Golfen: ["Golfen","Golfen","Golfen"],
		Reiten: ["Reiten","Reiten","Reiten"],
		Wetter: ["Wetter","Wetter","Wetter"],
		meta: ["meta","meta","meta"]
	},
	typeMapMapping: { 
		1:"Article",
		2:"Offer",
		3:"Event",
		3:"Tourismusinformation",
		4:"Bahnhof",
		5:"HotelPension",
		6:"FerienwohnungHaus",
		7:"Campingplatz",
		8:"Jugendherberge",
		9:"Bauernhof",
		10:"BettBike",
		11:"WellnesshotelBeautyfarm",
		12:"Gastronomie",
		13:"MuseumDenkmal",
		14:"Theater"
	},

	
	nonToggleTypes: ["meta","Wetter"],
	
	langs: ["fr","de","en"],

	metaPointXOff : 0,
	metaPointYOff : 0,
	subPointXOff: 0,
	subPointYOff: 0,
	
	luxInterfaceAddress: "http://www.luxembourg2007.org/manif.php?map=", // jump back address for the event types
	
	// search results:
	
	showOnlySearchResults: false, // defines if only search result pois are displayed, the searchResult servlet is used...
	searchResultIds: [], // containes the IDs
	searchResultServletAddress: "http://luxembourg2007.alta4gis.de/luxembourg2007/dav/controller/searchExtent?", // List of search results in the Extent (replaces normal servlet)

	// edit
	
	editMode: false, // defines if points can be edited, so the edit bar will be displayed and they can be moved
	//editServletAddress: "/alta4_servlets/noticeCoordinates?", // send edit commands to
	//deletePOIServletAddress: "/alta4_servlets/deletePOI?", // deleting POIs from DB
	//resetCoordinatesServletAddress: "/alta4_servlets/resetCoordinates?", // resetting coordinates to "before login" state

	editServletAddress: "/dav/controller/noticeCoordinates?", // send edit commands to
	deletePOIServletAddress: "/dav/controller/deletePOI?", // deleting POIs from DB
	resetCoordinatesServletAddress: "/dav/controller/resetCoordinates?", // resetting coordinates to "before login" state


	editObject: { // prepares the object to be edited
		recode: false,
		id: 0,
		lon: 0,
		lat: 0,
		origLon: 0,
		origLat: 0,
		type: null,
		name: ""
	},
	
	// contains the parameters which are passed through the geocoding process
	editPassThrough: "",
			
	//  ++++++++++ PopUps +++++++++++
	showPopUpMinWidth: 300,
	showPopUpMinHeight: 300,
	
	// using this template the popups can be customized, just add the fields in the DB at the position where they should be displayed,
	// all HTML content is parsed into a div by using innerHTML
	
	//Lux07
	//popUpTagNames: ["name","street","streetNumber","countryCode","zipCode","city","tel","fax","email","webSite","desc","niceWebSite","niceEmail"],
	//popUpTemplate: '<table class="popUpContent"><tr><td colspan="2" class="title">_name_</td></tr><tr><td class="addressCell">_street_ _streetNumber_<br />_countryCode_-_zipCode_ _city_<br />_tel_<br />_fax_<br /><a href="mailto:_email_">_niceEmail_</a><br /><a href="_webSite_" target="blank">_niceWebSite_</a><br /></td><td class="descCell"><div class="desc">_desc_</div></td></tr></table>',
	
	//TASH
	popUpTagNames: ["name","street","streetNumber","zipCode","city","address1Street","address1ZipCode","address1City","phone1","fax","previewImage","shortDescription","detailLink","region","lonWGS84","latWGS84"],
	
	popUpTemplateGray10: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="gray-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_gray.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_street_ _streetNumber_<br />_zipCode_ _city_<br />Telefon: _phone1_<br /></p><p class="copy">_shortDescription_</p></div></div>',
	popUpTemplateGray20: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="gray-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_gray.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_street_ _streetNumber_<br />_zipCode_ _city_<br /></p></div></div>',
	
	//mit Fax: popUpTemplateOrange: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="orange-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_orange.gif" alt="" /><h3>_name_</h3></div><div class="body"><img src="_previewImage_" alt="" class="thumbnail" /><p class="address">_address1Street_<br />_address1ZipCode_ _address1City_<br />Telefon: _phone1_<br />Fax: _fax_<br /></p><p class="copy">_shortDescription_</p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div>',
	popUpTemplateOrange: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="orange-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_orange.gif" alt="" /><h3>_name_</h3></div><div class="body"><img src="_previewImage_" alt="" class="thumbnail" /><p class="address">_address1Street_<br />_address1ZipCode_ _address1City_<br />Telefon: _phone1_<br /></p><p class="copy">_shortDescription_</p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div>',
	
	popUpTemplateBlue: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="blue-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_blue.gif" alt="" /><h3>_name_</h3></div><div class="body"><img src="_previewImage_" alt="" class="thumbnail" /><p class="address">_address1Street_<br />_address1ZipCode_ _address1City_<br />Telefon: _phone1_<br />Fax: _fax_<br /></p><p class="copy">_shortDescription_</p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div>',
	popUpTemplateBlue60: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="blue-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_blue.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_address1Street_<br />_zipCode_ _address1City_<br /></p></div></div>',
	popUpTemplateBlue80: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="blue-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_blue.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_address1Street_<br />_address1ZipCode_ _address1City_<br /></p><p class="copy">Region(en): _region_<br />Koordinaten: _lonWGS84_, _latWGS84_<br/>_shortDescription_</p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div>',
	
	//felsen + poi
	popUpTemplateDavpoi: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="davpoi-titlebar"><h3>_name_</h3></div><div class="body"><div><img src="_previewImage_" alt="" class="thumbnail" /><p class="address">_address1Street_<br />_address1ZipCode_ _address1City_<br />Telefon: _phone1_<br />Fax: _fax_<br /></p><p class="copy">_shortDescription_</p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div></div>',
	
	popUpTemplateGreen91: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="green-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_green.gif" alt="" /><h3>_name_</h3></div><div class="body"><img src="_previewImage_" alt="" class="thumbnail" /><p class="address">_address1Street_<br />_address1ZipCode_ _address1City_<br />Telefon: _phone1_<br /></p><p class="morelink"><a href="_detailLink_">&gt; Mehr</a></p></div></div>',
	popUpTemplateGreen92: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="green-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_green.gif" alt="" /><h3>_name_</h3></div><div class="body"><p class="address-left">_address1Street_<br />_address1ZipCode_ _address1City_<br /></p><div></div>',
	
	popUpTemplateRW: '<div class="overlay-window" style="left: 0px; top: 0px;"><div class="gray-titlebar"><img src="/viewer/a4viewer/design/e7/overlay_corner_gray.gif" alt="" /><h3>_name_</h3></div><div class="body"><img src="_imageUrl_" alt="" class="thumbnail" /><p class="address">_contactName_<br />_email_<br />_homepage_<br />_street_ _streetNumber_<br />_zip_ _city_<br /></p><p class="copy">_shortDescription_</p></div></div>',
		
	popUpHideDelay: 250, // how long should the popup stay open after leaving (in ms)
	linesVisibleInAggregatedPopUp:10, // after x lines, the popup is cut and a scrollbar is drawn.
	maxLinkLength: 30, // after 30 letters, "..." will be displayed and the link is cut.
	maxNameLength: 30, // after 30 letters, "..." will be displayed and the name (_name_) is cut.
	
	weatherStationsTemplate : '<span>#{name}<span><img src="#{img}">#{minTmp}°/#{maxTmp}°',
		
	//  ++++++++++ LayerSelector +++++++++++
	hasLayerSelector:true,
	layerSelectorPosX: 0, // offset of the layer selector from window border in px
	layerSelectorPosY: 0,
	
	//  ++++++++++ MapManager +++++++++++
	zoomSpeed: 75, // speed of the zoom animation
	moveSpeed: 10, // step size of the move animation (translate the tiles by ?)
	
	//  ++++++++++ Copyright +++++++++++
	hasCopyrightInfo: false,
	copyrightInfos: [["powered by alta4","http://www.alta4.com"],["&copy; 2007 alta4","disclaimer.html"]],
	copyrightInfosOffsetX: 50,
	copyrightInfosOffsetY: 30,

	//  ++++++++++ Logging +++++++++++
	projectName: "tash",
	loggingURL: "http://www.alta4gis.de/clickCounter.php",
	loggingActive: false,
	googleAnalyticsLoggingActive: false,
	
	//  ++++++++++ OverviewMap +++++++++++
	overviewMapZoomlevelDifference: 3, // number of zoomlevels between overview map and main map
	hasInteractions: true,
	isOverviewMap: true,
	hasConnectedMap: false,
	connectedMap: null,
	moveObserver: null,
	centerObserver: null,
	overviewMapBorderStyle: "2px solid white",
	
	//  ++++++++++ Authentification +++++++++++
	sessionId: 0, // will be used to store the current sessionId
	//editGetPOIServlet: "/alta4_servlets/getPOI?",
	//editSaveServlet: "/alta4_servlets/savePOI?",
//	closeSessionServlet: "/alta4_servlets/closeSession",
	
	editGetPOIServlet: "/dav/controller/getPOI?",
	editSaveServlet: "/dav/controller/savePOI?",
	closeSessionServlet: "/dav/controller/closeSession",
	editResetCoordinatesServlet: "/dav/controller/resetCoordinates?",
	
	bestViewServlet: "/dav/controller/getBestView?",
	
	//  ++++++++++ TASH specific +++++++++++
	isArea:false,
	activeAreas:"0",
	activeTopics:"0",
	activeTargetGroups:"0",
	
	wetterInfos: {
	},
	wetterMapping:{
		0:'/viewer/a4viewer/design/weather/d_0_b.gif',
		1:'/viewer/a4viewer/design/weather/d_1_b.gif',
		2:'/viewer/a4viewer/design/weather/d_2_b.gif',
		3:'/viewer/a4viewer/design/weather/d_3_b.gif',
		4:'/viewer/a4viewer/design/weather/d_4_b.gif',
		5:'/viewer/a4viewer/design/weather/d_5_b.gif',
		6:'/viewer/a4viewer/design/weather/d_6_b.gif',
		7:'/viewer/a4viewer/design/weather/d_7_b.gif',
		8:'/viewer/a4viewer/design/weather/d_8_b.gif',
		9:'/viewer/a4viewer/design/weather/d_9_b.gif',
		10:'/viewer/a4viewer/design/weather/d_nodata_b.gif'	
	},
	
	//  ++++++++++ Minimizeability +++++++++++
	hasMinimizer: true,
	minimizerMinimizeImageSrc: "/viewer/a4viewer/design/ov_map_close.png",
	minimizerMaximizeImageSrc: "/viewer/a4viewer/design/ov_map_open.png",
	minimizerImageSize: [15,15],

	// no map updates when true!!!	
	suspendRedraw: false
};
