//
// (C)  Bernhard Zwischenbrugger
// http://datenkueche.com
// bz(at)datenkueche(dot)com
//

//
//  Todo
//  this.clientX is a bit buggy. Firefox 2.0 does not give evt.clientX on mousewheel -> lots of workaround
//  slider sometimes is not at the correct position
//  Pos1, End support like in google map
//

//
// mapTypeControl
//

function mapTypeControl(){
	this.map=null;
        this.controlDiv=document.createElement("div");
        this.controlDiv.style.top="5px";
        this.controlDiv.style.left="100px";
        this.controlDiv.style.position="absolute";

/*
	var button=this.createButton("Mapnik");
	Event.attach(button,"mousedown",this.map.mapType("mapnik"),this,false);
	button.style.top="0px";
	button.style.left="00px";
	this.controlDiv.appendChild(button);

	var button=this.createButton("t@h");
	button.style.top="0px";
	button.style.left="120px";
	Event.attach(button,"mousedown",this.map.mapType("osma"),this,false);
	this.controlDiv.appendChild(button);

	var button=this.createButton("bikemap");
	button.style.top="0px";
	button.style.left="240px";
	Event.attach(button,"mousedown",this.map.mapType("bikenet"),this,false);
	this.controlDiv.appendChild(button);
*/
}

mapTypeControl.prototype.initialize=function(map){
	this.map=map;
	map.control.appendChild(this.controlDiv);

}

mapTypeControl.prototype.mapnik=function(){
	this.map.makeURL=this.map.mapnikURL;
	this.map.clearMap();
        this.map.maxZoom=18;
	this.map.draw();
}
mapTypeControl.prototype.osma=function(){
	this.map.server="http://b.tah.openstreetmap.org/Tiles/tile/";
	this.map.clearMap();
       this.map.maxZoom=17;
	this.map.draw();
}
mapTypeControl.prototype.bikemap=function(){
	this.map.server="http://a.andy.sandbox.cloudmade.com/tiles/cycle/";
	this.map.clearMap();
	this.map.maxZoom=18;
	this.map.draw();
}

mapTypeControl.prototype.createButton=function(text){
	var button=document.createElement("div");
	button.style.width="100px";
	button.style.height="20px";
	button.style.backgroundColor="lightgrey";
	button.style.border="1px solid white";
	//button.style.opacity=0.8;
	button.style.position="absolute";
	var textNode=document.createTextNode(text);
	button.appendChild(textNode);
	button.style.textAlign="center";
	button.style.color="white";
	button.style.cursor="pointer";
	return button;
}


//
// mapControl
//

function mapControl(){
	this.map=null;
        this.controlDiv=document.createElement("div");
        this.controlDiv.style.top="5px";
        this.controlDiv.style.left="5px";
        this.controlDiv.style.position="absolute";
	this.moveSpeed=1;
	this.sliding=false;
}


mapControl.prototype.initialize=function(map){
	this.map=map;
	this.iconServer="http://www.khtml.org/"

	//keyboard
	Event.attach(document,"keydown",this.keyDown,this,false);
	Event.attach(document,"keyup",this.keyUp,this,false);
	

	//zoommoveeleemnt

	map.control.appendChild(this.controlDiv);

	Event.attach(map.control,"mouseup",this.stopMove,this,false);
	var img=this.appendImg(this.iconServer+"images/west.png",0,20);
	Event.attach(img,"mousedown",this.startMoveWest,this,false);

	var img=this.appendImg(this.iconServer+"images/east.png",40,20);
	Event.attach(img,"mousedown",this.startMoveEast,this,false);

	var img=this.appendImg(this.iconServer+"images/north.png",20,0);
	Event.attach(img,"mousedown",this.startMoveNorth,this,false);

	var img=this.appendImg(this.iconServer+"images/south.png",20,40);
	Event.attach(img,"mousedown",this.startMoveSouth,this,false);
	var img=this.appendImg(this.iconServer+"images/zoom-plus.png",20,63);
	Event.attach(img,"mousedown",this.startZoomIn,this,false);
	var img=this.appendImg(this.iconServer+"images/zoom-minus.png",20,278);
	Event.attach(img,"mousedown",this.startZoomOut,this,false);
	var div=document.createElement("div");
	this.slider=div;
	div.style.position="absolute";
	div.style.top="0px";
	div.style.left="0px";
	this.controlDiv.appendChild(div);
	var img=this.appendImg(this.iconServer+"images/zoombar.png",20,80);
	div.appendChild(img);
	var img=this.appendImg(this.iconServer+"images/slider.png",19,100);
	div.appendChild(img);
	Event.attach(div,"mousedown",this.startSlider,this,false);
	Event.attach(div.parentNode.parentNode.parentNode,"mousemove",this.moveSlider,this,false);
	Event.attach(div.parentNode.parentNode.parentNode,"mouseup",this.endSlider,this,false);

}

mapControl.prototype.keyDown=function(evt){
	//if(evt.preventDefault){ evt.preventDefault( ); }else{ evt.returnValue = false;}
	switch(evt.keyCode){
		case 39:this.startMoveEast();
			break; 
		case 37:this.startMoveWest();
			break; 
		case 38:this.startMoveNorth();
			break; 
		case 40:this.startMoveSouth();
			break; 
		case 107:
		case 187: //chrome
		case 61:this.startZoomIn();
			break; 
		case 189: //chrome
		case 109:this.startZoomOut();
			break; 
		case 13:this.map.snap();
			break; 
		default:
		//	alert(evt.keyCode);
	}
}

mapControl.prototype.keyUp=function(evt){
	this.stopMove();
}

mapControl.prototype.startSlider=function(evt){
	if(evt.preventDefault){ evt.preventDefault( ); }else{ evt.returnValue = false;}
	this.sliding=true;
	var z=(-evt.pageY + this.map.origworld.offsetTop +80 +200)/11.12 ;
	if(z < 0)z=0;
	if(z > this.map.maxZoom)z=this.map.maxZoom;
	this.map.zoom=z;
	this.map.draw();
}

mapControl.prototype.moveSlider=function(evt){
	if(evt.preventDefault){ evt.preventDefault( ); }else{ evt.returnValue = false;}
	if(this.sliding){
		var z=(-evt.pageY + this.map.origworld.offsetTop +80 +200)/11.12 ;
		if(z < 0)z=0;
		if(z > this.map.maxZoom)z=this.map.maxZoom;
		this.map.zoom=z;
		this.map.draw();
	}
}

mapControl.prototype.callback=function(){
	var mover=this.slider.childNodes.item(1);
	var top=-this.map.zoom*11.11 + this.map.origworld.offsetTop  +260;
	mover.style.top=top+"px";
}

mapControl.prototype.endSlider=function(evt){
	if(evt.preventDefault){ evt.preventDefault( ); }else{ evt.returnValue = false;}
	this.sliding=false;
}

mapControl.prototype.startMoveEast=function(){
	this.move(1,0);
	this.moveSpeed=this.moveSpeed+0.2;
	thisObject=this;
	thisObject.moveTimer=setTimeout("thisObject.startMoveEast()",20);
}
mapControl.prototype.startMoveWest=function(){
	this.move(-1,0);
	this.moveSpeed=this.moveSpeed+0.2;
	thisObject=this;
	thisObject.moveTimer=setTimeout("thisObject.startMoveWest()",20);
}
mapControl.prototype.startMoveSouth=function(){
	this.move(0,1);
	this.moveSpeed=this.moveSpeed+0.2;
	thisObject=this;
	thisObject.moveTimer=setTimeout("thisObject.startMoveSouth()",20);
}
mapControl.prototype.startMoveNorth=function(){
	this.move(0,-1);
	this.moveSpeed=this.moveSpeed+0.2;
	thisObject=this;
	thisObject.moveTimer=setTimeout("thisObject.startMoveNorth()",20);
}

mapControl.prototype.startZoomIn=function(){
	//this.move(0,-1);
	this.zoom(+1);
	this.moveSpeed=this.moveSpeed+0.2;
	thisObject=this;
	thisObject.moveTimer=setTimeout("thisObject.startZoomIn()",20);
}
mapControl.prototype.startZoomOut=function(){
	//this.move(0,-1);
	this.zoom(-1);
	this.moveSpeed=this.moveSpeed+0.2;
	thisObject=this;
	thisObject.moveTimer=setTimeout("thisObject.startZoomOut()",20);
}




mapControl.prototype.stopMove=function(){
	this.moveSpeed=1;
	clearTimeout(this.moveTimer);
}

mapControl.prototype.move=function(x,y){
	this.map.x=this.map.x+x*this.moveSpeed;	
	this.map.y=this.map.y+y*this.moveSpeed;	
	this.map.latlng();
	this.map.draw();
}

mapControl.prototype.zoom=function(z){
	z=this.map.zoom+0.01*z*this.moveSpeed/4+z*0.01;
	if(z<0)z=0;
	if(z>this.map.maxZoom)z=this.map.maxZoom;
	this.map.zoom=z;
	this.map.draw();
}

mapControl.prototype.appendImg=function(src,left,top){
	var img=document.createElement("img");
	img.setAttribute("src",src);
	img.style.top=top+"px";
	img.style.left=left+"px";
	img.style.position="absolute";
	this.controlDiv.appendChild(img);
	return img;
}







//
//
//  THIS IS THE MAIN OBJECT
//
//






function kMap(world){
	this.origworld=world;

	this.getSize(world);
	this.overlayArray = new Array();
	
	

	this.eventtarget=document.createElement("div");
	this.eventtarget.style.position="absolute";
	this.eventtarget.style.top="0px";
	this.eventtarget.style.left="0px";
	this.eventtarget.style.width=this.origworld.offsetWidth+"px";
	this.eventtarget.style.height=this.origworld.offsetHeight+"px";
	this.origworld.appendChild(this.eventtarget);


	this.world=document.createElement("div");
	this.world.style.position="absolute";
	this.world.style.top=0+"px";
	this.world.style.left=0+"px";
	this.eventtarget.appendChild(this.world);

	this.control=document.createElement("div");
	this.control.style.position="absolute";
	this.control.style.top="0px";
	this.control.style.left="0px";
	world.appendChild(this.control);

	this.copyrightDiv=this.origworld.appendChild(this.copyright());


	this.worldOverlay=document.createElement("div");
	this.worldOverlay.style.zIndex=2000;
	this.worldOverlay.style.position="absolute";
	this.worldOverlay.style.top=this.height/2+"px";
	this.worldOverlay.style.left=this.width/2+"px";
	this.worldOverlay.setAttribute("name","overlay");
	this.world.appendChild(this.worldOverlay);

	//constants
	this.server="http://b.tile.openstreetmap.org/";
	//this.server="http://b.tah.openstreetmap.org/Tiles/tile/";
	this.maxZoom=18;
	this.minZoom=0;
	this.wx=256;
	this.wy=256;
	//variables
	this.makeURL=this.mapnikURL;
	this.snaping=true;
	this.clicked=false;
	this.visibleLayer=null;
	this.downX=0;
	this.downY=0;
	this.overlayDX=0;
	this.overlayDY=0;
	this.clientX=this.width/2+ this.left;  //middle of map
	this.clientY=this.height/2+ this.top;
	this.dx=0;
	this.dy=0;
	this.x=0;
	this.y=0;
	this.zoomspeed=0.01;
	this.loadStatArray=new Array(this.maxZoom - this.minZoom);
	for(var i=this.minZoom; i <= this.maxZoom;i++){this.loadStatArray[i]=0}
	this.frames=0;
	this.xydrawBlocked=false;
	this.moveBlocked=false;
	this.doubleClick=false;
	this.snapTimer=null;
	this.selectArea=null;
	//this.world=world;
	world.style.overflow="hidden";
	/*
        window.attach(this,"resize",this.getSize,false);
        this.world.attach(this,"DOMMouseScroll",this.wheel,false);
	*/
	Event.attach(window,"resize",this.getSize,this,false);
	Event.attach(this.eventtarget,"DOMMouseScroll",this.wheel,this,false);
	Event.attach(this.eventtarget,"mousedown",this.down,this,false);
	Event.attach(this.eventtarget,"dblclick",this.doubledown,this,false);
	Event.attach(document,"mouseup",this.up,this,false);
	Event.attach(document,"mousemove",this.move,this,false);

//	while(this.world.firstChild){this.world.removeChild(this.world.firstChild)}
	//this.xydraw(1000,800,3,3);


        //callback handlers;
        this.callbackHandlers=new Array();
}

//
//  Overlay handling
//

kMap.prototype.addOverlay=function(lng,lat,el){
	el.style.position="absolute";
	this.worldOverlay.appendChild(el);

	var x= (lng / 360 ) *this.wx; 
	var y=  (this.lat2y(-lat) / 360 ) *this.wy ;

	var overlay=new Array(x,y,el);
 	this.overlayArray.push(overlay);	
}

kMap.prototype.drawOverlays=function(middleX,middleY,zoom,intZoom){
	var faktor=Math.pow(2,zoom );
	for(var i=0; i < this.overlayArray.length;i++){
		var x=this.overlayArray[i][0];	
		var y=this.overlayArray[i][1];	
		var el=this.overlayArray[i][2];	
		el.style.left=(x+128)*faktor-this.x+"px";
		el.style.top=(y+128)*faktor-this.y+"px";
	}
}

//
//  GET Browser Window size - important if you resize the window
//

kMap.prototype.getSize=function(){
	this.width=this.origworld.offsetWidth;
	this.height=this.origworld.offsetHeight;
	this.top=this.origworld.offsetTop;
	this.left=this.origworld.offsetLeft;
	this.clientX=this.width/2;
	this.clientY=this.height/2;
	//alert(this.width);
}

//
//  Mouse Down Handler
//

kMap.prototype.down=function(evt){
	if(evt.preventDefault){ evt.preventDefault( ); }else{ evt.returnValue = false;}
	clearTimeout(this.snapTimer);
	this.downX=parseInt(evt.pageX);
	this.downY=parseInt(evt.pageY);
	if(this.doubleClick){
		this.zoomIn(evt);	
	}else{
		if(evt.shiftKey){
			this.selectArea=document.createElement("div");
			this.eventtarget.appendChild(this.selectArea);
			this.selectArea.style.position="absolute";
			/*
			this.selectArea.style.top=parseFloat(evt.pageY)-this.top+"px";
			this.selectArea.style.left=parseFloat(evt.pageX)-this.left+"px";
			*/
			if(!document.all){
				this.selectArea.style.backgroundColor="white";
			}
			this.selectArea.style.border="1px solid grey";
			this.selectArea.style.opacity=0.4;
			//this.selectArea.style.filter="alpha(opacity=20)";
		}else{
			this.doubleClick=true;
			thisObject=this;
			setTimeout("thisObject.doubleClick=false",300);	
			this.clicked=true;
			this.moving=true;
		}
	}
}

//
//  Doubleclick
//

kMap.prototype.doubledown=function(evt){
	if(evt.preventDefault){ evt.preventDefault( ); }else{ evt.returnValue = false;}
	this.downX=parseInt(evt.pageX);
	this.downY=parseInt(evt.pageY);
	this.zoomIn(evt);	
}
	

kMap.prototype.zoomIn=function(evt){
	var steps=20;
	var force="no";
	//var ddx=this.downX-evt.pageX;
	//var ddy=this.downY-evt.pageY;
	ddx=this.clientX - this.left - this.width/2;
	ddy=this.clientY - this.top - this.height/2;
	for(var i=0;i<=steps;i++){
		var dx=ddx*(1 - i / steps);
		var dy=ddy*(1 - i / steps);
		var z=this.zoom+(Math.floor(this.zoom+1) - this.zoom)*( i / steps);
		thisObject=this;
		if(i==steps){
			//alert(z);
			z=Math.round(z) 
			force="yes";
		}
		if(z>this.maxZoom)z=this.maxZoom;
		var cmd="thisObject.dx="+dx+";thisObject.dy="+dy+";thisObject.zoom="+z+";thisObject.draw('"+force+"')";
		//console.log(cmd);
		setTimeout(cmd,20*i);	
		//var cmd="thisObject.dx="+dx+";thisObject.dy="+dy+";thisObject.zoom="+Math.round(z)+";thisObject.draw()";
	}
	
}

//
//   Snap Functions:
//   The tiles are 256x256 pixels. If the zoomlevel is not an integer, the tiles have a size that is different to
//   256x256. Because the Browser has to zoom the images the quality is not so good. 
//
//   Snap zooms to an integer zoomlevel
//

kMap.prototype.snap=function(){
	var z=Math.round(this.zoom);
	this.zoom=z;
	this.draw("yes");
}

kMap.prototype.autosnap=function(){
	if(this.snaping){
		thisObject=this;
		window.clearTimeout(this.snapTimer);
		if(typeof(thisObject.snap)=="function"){ //now idea but keyboard action ...
			thisObject.snapTimer=setTimeout( function () {thisObject.snap();},500);
		}
	}
}

kMap.prototype.snapZoom=function(sn){
	this.snaping=sn;
}

//
//   If a tile finished loading this function is called
//


kMap.prototype.imgLoaded=function(evt,intZoom){
	try{
		evt.srcElement.style.visibility="";
	}catch(e){}
	this.loadStatArray[intZoom]--;	
	if(this.loadStatArray[intZoom]==0){
		if(intZoom > this.visibleLayer){
			this.visibleLayer=intZoom;
		}
		this.draw();
	}
}

//
//   Mouse Move
//

kMap.prototype.move=function(evt){
	if(evt.preventDefault){ evt.preventDefault( );
	}else{ evt.returnValue = false;}
	this.doubleClick=false;
	this.clientX=parseInt(evt.pageX);
	this.clientY=parseInt(evt.pageY);
	if(this.moving){
		if(this.moveBlocked){
			return;
		}
		var dx=this.downX-this.clientX;
		var dy=this.downY-this.clientY;
		var x=this.x+dx;
		var y=this.y+dy;
		/*
		this.visualDiv.style.top=y;
		this.visualDiv.style.left=x;
		*/

		this.moveBlocked=true;
		this.world.style.top=-dy+"px";	
		this.world.style.left=-dx+"px";
		thisObject=this;	
		setTimeout( function () {thisObject.moveBlocked=false;},1);
		if(Math.abs(dx) > 100 || Math.abs(dy) > 100 ){
			this.world.style.top=0+"px";	
			this.world.style.left=0+"px";
			this.x=x;
			this.y=y;
			this.downX=parseInt(evt.pageX);
			this.downY=parseInt(evt.pageY);
			this.latlng();
			this.xydraw(x,y,this.zoom,Math.floor(this.zoom));
		}
	}
	if(this.selectArea!=null){
		/*
		this.selectArea.style.width=parseFloat(evt.pageX) - this.left - parseFloat(this.selectArea.style.left)+"px";
		this.selectArea.style.height=parseFloat(evt.pageY) - this.top - parseFloat(this.selectArea.style.top)+"px";
		*/
		var width=parseFloat(evt.pageX) -  this.downX;
		var height=parseFloat(evt.pageY) -  this.downY;
		if(width > 0){
			var left=this.downX - this.left;
		}else{
			var left=evt.pageX - this.left;
			width=width*-1;
		}
		if(height > 0){
			var top=this.downY - this.top;
		}else{
			var top=evt.pageY - this.top;
			height=height*-1;
		}
		this.selectArea.style.top=top+"px";
		this.selectArea.style.left=left+"px";
		this.selectArea.style.width=width+"px";
		this.selectArea.style.height=height+"px";
	}
}

//
//  Mouse up
//

kMap.prototype.up=function(evt){
	if(this.moving){
		this.x=this.x+this.downX-parseInt(evt.pageX);	
		this.y=this.y+this.downY-parseInt(evt.pageY);
		this.moving=false;

		//GPS (WGS84) Koordinaten berechnen

		this.world.style.top=0+"px";	
		this.world.style.left=0+"px";
	//	this.lng=(this.x/Math.pow(2,this.zoom) /this.wx -0.5)*(360);
	//	this.lat=this.y2lat((this.y/Math.pow(2,this.zoom) /this.wy -0.5)*(-360));
		this.latlng();
		this.draw();
	}
	if(this.selectArea!=null){
		this.areaSelect();
		this.selectArea.parentNode.removeChild(this.selectArea);
		this.selectArea=null;
	}
}

//
//  If Shift-Key is holded you can select an area with the mouse....
//

kMap.prototype.areaSelect=function(){
	
	var top=parseFloat(this.selectArea.style.top);
	var left=parseFloat(this.selectArea.style.left);
	var width=parseFloat(this.selectArea.style.width);
	var height=parseFloat(this.selectArea.style.height);
	if(isNaN(width)){
		return;
	}
	if(isNaN(height)){
		return;
	}
	x=left+width/2;
	y=top+height/2;
	this.latlng();
	var deltaz1=parseFloat(this.width) /width;
	var deltaz2=parseFloat(this.height) /height;
	if(deltaz1 < deltaz2){
		deltaz=deltaz1;
	}else{
		deltaz=deltaz2;
	}
	deltazoom=Math.log(deltaz)/Math.log(2);
	this.clientX=x +this.left;
	this.clientY=y +this.top;
	//console.log(this.clientX+":"+this.clientY);
	this.latlng();
	
	
	this.zoom=Math.floor(this.zoom+deltazoom);
	this.dx=0;
	this.dy=0;
	this.draw();
}


//
//  This is an important funktion for WGS84 calculations.
//  It's used in many places
//  Please hands of it you don't exactly know what you do.
//


//show lat, lng
kMap.prototype.latlng=function(){
	this.dx=this.clientX - this.left - this.width/2;
	this.dy=this.clientY - this.top - this.height/2;
//	this.lng=((this.x+this.dx)/Math.pow(2,this.zoom) /this.wx -0.5)*(360);
//	this.lat=this.y2lat(((this.y+this.dy)/Math.pow(2,this.zoom) /this.wy -0.5)*(-360));


	this.lng=((this.x)/Math.pow(2,this.zoom) /this.wx -0.5)*(360);
	this.lat=this.y2lat(((this.y)/Math.pow(2,this.zoom) /this.wy -0.5)*(-360));

	this.x=this.x+this.dx;
	this.y=this.y+this.dy;

	this.setCenter(this.lat,this.lng,this.zoom);
/*
	document.getElementById("lat").firstChild.nodeValue=this.dx;
	document.getElementById("lng").firstChild.nodeValue=this.lng+" : "+this.lat+" : "+this.zoom;
*/

}

//
//  Set the map Center and zoom level
//

kMap.prototype.setCenter=function(lat,lng,z){
	this.zoom=z;
	this.lat=lat;
	this.lng=lng;
	//this.xydraw(x,y,z,Math.floor(z));
	if(this.visibleLayer==null){
		this.visibleLayer=Math.floor(this.zoom)
	}
	this.dx=0;
	this.dy=0;
	this.draw("yes");
}

//
//   Mouse wheel support for zoom
//

kMap.prototype.wheel=function(evt){

	if(evt.preventDefault){ evt.preventDefault( ); }else{ evt.returnValue = false;}
	this.latlng();
        var delta = 0;
        if (!evt) /* For IE. */
                evt = window.event;
        if (evt.wheelDelta) { /* IE/Opera. */
                delta = evt.wheelDelta/120;
                if (window.opera){
                        delta = delta*2;
		}
	} else if (evt.detail) { /** Mozilla case. */
		delta = -evt.detail/3;
	}
	 // var normal = e.detail ? e.detail * -1 : e.wheelDelta / 120;
	if(evt.altKey){
		extraspeed=1;
	}else{
		extraspeed=10;
	}

	this.zoom=this.zoom+this.zoomspeed*delta*extraspeed;

	this.zoomspeed=this.zoomspeed+0.01;
	if(this.zoom >this.maxZoom) this.zoom=this.maxZoom;
	if(this.zoom <0) this.zoom=0;
	var cmd="this.zoomspeed=this.zoomspeed -0.01";
	thisObject=this;
            setTimeout( function () {thisObject.zoomspeed=thisObject.zoomspeed -0.01;},100);




//	this.xydraw(this.x,this.y,this.zoom,Math.floor(this.zoom));
	this.draw();
}

//
//  Oops, this function is dublicated
//


kMap.prototype.imgLoaded=function(evt,intZoom){
	/*
	if(evt.target){
	//	evt.target.style.visibility="";
	}else{
		try{
		evt.srcElement.style.visibility="";
		}catch(e){}
	}
	*/
		try{
		evt.srcElement.style.visibility="";
		}catch(e){}
	//alert("hi ie");
	//console.log("z: "+intZoom);
	this.loadStatArray[intZoom]--;	
	if(this.loadStatArray[intZoom]==0){
		this.visibleLayer=intZoom;
		this.draw();
	}
}

//
//    This function draws the map and uses "xydraw" and "realXYdraw"
//    Hands off please!!
//


kMap.prototype.draw=function(force){
//	console.log(this.dx,this.dy,this.clientX,this.clientY);
	this.x= (this.lng / 360 +0.5) *Math.pow(2,this.zoom) *this.wx - this.dx ;
	this.y=  (this.lat2y(-this.lat) / 360 +0.5) *Math.pow(2,this.zoom) *this.wy -this.dy ;
/*	
	document.getElementById("lat").firstChild.nodeValue=this.lng+":"+this.zoom+":"+this.wy+":"+this.dx;
	document.getElementById("lng").firstChild.nodeValue=this.y;
*/

	//console.log("--"+this.x+":"+this.y);
	this.xydraw(this.x,this.y,this.zoom,Math.floor(this.zoom),force);
}


//
//   Same parameters as realXYdraw
//   Here some timeouts guaranties SPEED
//   Hands OFF!!!
//

kMap.prototype.xydraw=function(middleX,middleY,zoom,intZoom,force){
	if(this.xydrawBlocked && (force!='yes')){
		return;
	}
	this.xydrawBlocked=true;

	this.realXYdraw(middleX,middleY,zoom,intZoom);
	this.drawOverlays(middleX,middleY,zoom,intZoom);

	if(this.loadStatArray[intZoom]==0){
		this.visibleLayer=intZoom;	
	}else{
		//debug(this.loadStatArray,intZoom,this.visibleLayer);

		if(this.loadStatArray[intZoom]!=0){
			if(intZoom > this.visibleLayer){
				this.realXYdraw(middleX,middleY,zoom,this.visibleLayer);
			}
		}
	}

	var thisObject=this;
            setTimeout( function () {thisObject.xydrawBlocked=false;},20);
	this.autosnap();
}

//
//   deletes all layers
//

kMap.prototype.clearMap=function(){
	var divs=this.world.childNodes;
	for(var i=0; i < divs.length; i++){
		if(divs.item(i).getAttribute("name")!="overlay"){
			this.world.removeChild(divs.item(i));
		}
	}
	for(var i=this.minZoom; i <= this.maxZoom;i++){this.loadStatArray[i]=0}
}


//
// This function is a bit chaotic - it's opimized for speed and not for reading
// Please don't touch !!!!!!!
//

kMap.prototype.realXYdraw=function(middleX,middleY,zoom,intZoom){

	this.frames++;
	var faktor=Math.pow(2,(zoom-intZoom));

	//this.world.style.border="3px solid red";
	var divs=this.world.childNodes;
	for(var i=0; i < divs.length; i++){
                if(divs.item(i).getAttribute("zoomlevel") != intZoom){
			if(divs.item(i).getAttribute("name") != "overlay"){
				divs.item(i).style.display="none";
			}
                }
		if(divs.item(i).getAttribute("zoomlevel")==intZoom){
			var div=divs.item(i);
		}
	}	
	//create div for zoomlevel if not existing
	if(!div){
		var div=document.createElement("div");
                div.setAttribute("zoomlevel",intZoom);
                //div.style.opacity=0.2;
                div.style.position="absolute";
                this.world.appendChild(div);
        }
	this.visualDiv=div;
	div.setAttribute("notLoaded",0);
	//set div coordinates to the middle of the world
        div.style.top=this.height/2+"px";
        div.style.left=this.width/2+"px";
        div.style.display="none";

        var startX=Math.floor(middleX/this.wx/faktor)-Math.ceil(this.width/this.wx/2/faktor);
        var startY=Math.floor(middleY/this.wy/faktor)-Math.ceil(this.height/this.wy/2/faktor);

        var endX=Math.floor(middleX/this.wx/faktor)+Math.ceil(this.width/this.wx/2/faktor)+1;
        var endY=Math.floor(middleY/this.wy/faktor)+Math.ceil(this.height/this.wy/2/faktor)+1;

	//document.getElementById("zoom").firstChild.nodeValue=this.frames+" : "+startX+" : "+endX+" : "+intZoom+":"+this.zoom;
        var anz=Math.pow(2,intZoom);  //available openstreetmap images for zoomlevel
	//console.log(startX);
        for(var x=startX;x<endX;x++){
                for(var y=startY;y<endY;y++){
                        //make world recursive
                        var imgX=x % anz;
                        var imgY=y % anz;
                        if(imgX < 0) imgX=imgX+anz;  //modulo function gives negative value for negative numbers
                        if(imgY < 0) imgY=imgY+anz;
                        //place the images
                        //var url=this.server+intZoom+"/"+imgX+"/"+imgY+".png";
			var url=this.makeURL(imgX,imgY,intZoom);
                        var id="img"+intZoom+":"+x+":"+y;
                        //if(document.getElementById(id)){
                        //      var img=document.getElementById(id);
                        var img=null;
			
                        var imgs=div.getElementsByTagName("img");
                        for(var i=0;i < imgs.length;i++){
                                if(imgs.item(i).getAttribute("theid")==id){
                                        img=imgs.item(i);
                                        break;
                                }

                        }
			

			//img=document.getElementById(id);

                        if(img==null){
				//console.log("new pic");
                                var img=document.createElement("img");
				//load events handling
				Event.attach(img,"load",this.imgLoaded,this,false,intZoom);
				this.loadStatArray[intZoom]++;


                                img.setAttribute("theid",id);
                                img.setAttribute("onload","this.style.visibility=''");

                                img.style.visibility="hidden";

				img.style.position="absolute";

				img.setAttribute("src",url);
                                div.appendChild(img);
                        }
			img.setAttribute("keep","yes");
                        var top=(y*this.wy*faktor -middleY);
                        var left=(x *this.wx*faktor -middleX);
                        img.style.top=Math.round(top)+"px";
                        img.style.left=Math.round(left)+"px";
                        img.style.width=Math.round(this.wx *faktor +left)- Math.round(left)+"px";
                        img.style.height=Math.round(this.wx *faktor +top)- Math.round(top)+"px";
                }
        }
	//if(this.clicked){
		//console.log("loadstat: "+this.loadStatArray[intZoom]);
		var imgs=div.getElementsByTagName("img");
		for(var i=imgs.length-1;i >=0;i--){
			var img=imgs.item(i);		
			if(img.getAttribute("keep")!="yes"){
				if(img.complete==false){
					if(window.opera){
					this.loadStatArray[intZoom]--;
					}
				}
				img.parentNode.removeChild(img);
				
			}
			img.setAttribute("keep","no");
		}
		this.clicked=false;
	//}
//	if(intZoom==this.visibleLayer){
		div.style.display="";
//	}

	// call all callback things
	for(var i=0; i <  this.callbackHandlers.length;i++){
		this.callbackHandlers[i].callback();
	}	
}


//
//  Map Types
//  Should be relative easy to understand
//




kMap.prototype.mapType=function(type) {
	this.clearMap();
	switch(type){
		case "mapnik":this.makeURL=this.mapnikURL;
			this.maxZoom=17;
			break;
		case "osma":this.makeURL=this.osmaURL;
			this.maxZoom=17;
			break;
		case "bikemap":this.makeURL=this.bikemapURL;
			this.maxZoom=18;
			break;
		default:
			alert("unknown maptype");
	}
	this.draw();
}

kMap.prototype.osmaURL=function(imgX,imgY,intZoom) {
	var serverNum=(imgX+imgY) % 3 +1;
	switch(serverNum){
		case 1: server="a";break;
		case 2: server="b";break;
		case 3: server="c";break;
	}
	var url="http://"+server+".tah.openstreetmap.org/Tiles/tile/"+intZoom+"/"+imgX+"/"+imgY+".png";
	return(url);
}

kMap.prototype.mapnikURL=function(imgX,imgY,intZoom) {
        var serverNum=(imgX+imgY) % 3 +1;
        switch(serverNum){
                case 1: server="a";break;
                case 2: server="b";break;
                case 3: server="c";break;
        }
	var url="http://"+server+".tile.openstreetmap.org/"+intZoom+"/"+imgX+"/"+imgY+".png";
	return(url);
}

kMap.prototype.bikemapURL=function(imgX,imgY,intZoom) {
        var serverNum=(imgX+imgY) % 3 +1;
        switch(serverNum){
                case 1: server="a";break;
                case 2: server="b";break;
                case 3: server="c";break;
        }
	var url="http://"+server+".andy.sandbox.cloudmade.com/tiles/cycle/"+intZoom+"/"+imgX+"/"+imgY+".png";
	return(url);
}

kMap.prototype.y2lat=function(a) { return 180/Math.PI * (2 * Math.atan(Math.exp(a*Math.PI/180)) - Math.PI/2); }
kMap.prototype.lat2y=function(a) { return 180/Math.PI * Math.log(Math.tan(Math.PI/4+a*(Math.PI/180)/2)); }


kMap.prototype.addControl=function(control){
	control.initialize(this);
	if((typeof control.callback)=="function"){
		this.callbackHandlers.push(control);
	}
}

//
//  Displays the Copyright
//  Could be done with innerHTML - but innerHTML is evil ;-)
//  But here it would not be a problem
//

kMap.prototype.showCopyright=function(bool){
	if(this.copyright){
		if(bool){
			this.copyrightDiv.style.display="";
		}else{
			this.copyrightDiv.style.display="none";
		}
	}
}

kMap.prototype.copyright=function(control){
	copyright=document.createElement("p");

	var img=document.createElement("img");
	var cca=document.createElement("a");
	cca.setAttribute("href","http://creativecommons.org/licenses/by-sa/2.0/");	
	img.setAttribute("src","http://i.creativecommons.org/l/by-sa/2.0/80x15.png");
	img.setAttribute("align","top");
	img.style.border="0px";
	cca.appendChild(img);
	copyright.appendChild(cca);


	var osma=document.createElement("a");
	osma.setAttribute("href","http://www.openstreetmap.org");	
	var t1=document.createTextNode(" openstreetmap ");
	osma.appendChild(t1);
	copyright.appendChild(osma);


	var khtmla=document.createElement("a");
	khtmla.setAttribute("href","http://www.khtml.org");	
	var t1=document.createTextNode(" khtml.org ");
	khtmla.appendChild(t1);
	copyright.appendChild(khtmla);



	copyright.style.position="absolute";
	copyright.style.top=this.height-40;
	copyright.style.left=5
	copyright.style.backgroundColor="white";
	copyright.style.opacity=0.6;
	copyright.style.fontSize="11px";
	return copyright;
	
}
//
//
//
//   Attach - Function
//
//
// Rest of Code ist a Copy from
// http://hiveminds.org.hiveminds.co.uk/phpBB/viewtopic6b81.html?t=2930
//
// This is for events in Object Oriented Programming
// Ab hier wird nur die Methode "attach" definiert. Am besten einfach ignorieren.
// 
//
//


/***
<member name="$a" type="global static method">
   <summary>Loops through each argument in the supplied argument object and puts it into an array.</summary>
   <param name="a">Argument object to loop through.</param>
   <returns>Array</returns>
</member>
***/
function $a(a) {
   var r = new Array();
   for (var i = 0, l = a.length; i < l; i++) {
      r.push(a[i]);
   }
   return r;
} 

// Creates an object called "Event" if one doesn't already exist (IE).
if (!Event) {
   var Event = {};
}

/***
<member name="Event.attach" type="static method">
   <summary>Attach an event listener to an object.</summary>
   <param name="o">Object whose event to attach.</param>
   <param name="t">Type of event.</param>
   <param name="f">Method to fire when event is raised.</param>
   <param name="fc">Context of called method f. Defaults to object o.</param>
   <param name="c">Use capture (not available in IE).</param>
   <param name="*">Arguments to pass to function f.</param>
</member>
***/

Event.attach = function (o, t, f, fc, c) {
   var a = (arguments.length > 5 ? $a(arguments).slice(5, arguments.length) : new Array());
   var fn = function(e) {
      a.unshift(e || window.event);
      return f.apply((fc ? fc : o), a);
   }
   if (o.addEventListener) {
        if (navigator.appName.indexOf("Netscape") == -1){
                if(t=="DOMMouseScroll"){
                        t="mousewheel";
                }
        }
        if (navigator.userAgent.indexOf("Safari")!=-1 ){
                if(t=="DOMMouseScroll"){
                        o.onmousewheel=fn;
                }else{
                        o.addEventListener(t, fn, c);
                }
        }else{
                o.addEventListener(t, fn, c);
        }
   } else {
        if(t=="DOMMouseScroll"){
                o.attachEvent("onmousewheel",fn);
        }else{
              o.attachEvent("on" + t, fn);
        }
   }

};


