/**
 * Header: tgMap API Documentation
 * 
 * Author: Thomas Fruetel
 */

var MAX_ZOOMLEVEL = 15;
var API_SERVER    = "api.klicktel.de";
var NMAP_WORLD_X  = 23592960;
var NMAP_WORLD_Z  = 87162880;
var NMAP_WORLD_X2 = 86507520;
var NMAP_WORLD_Z2 = 24248320;

var TG_ANCHOR_TOP_RIGHT    = 'top_right';
var TG_ANCHOR_TOP_LEFT     = 'top_left';
var TG_ANCHOR_BOTTOM_RIGHT = 'bottom_right';
var TG_ANCHOR_BOTTOM_LEFT  = 'bottom_left';

var TG_ANCHOR_RIGHT = 'right';
var TG_ANCHOR_LEFT  = 'left';

var _METER_PER_PIXEL = Array (0.25,0.5,1,2,4,8,16,32,64,128,256,512,1024,2048,4096);
var _TG_MAPS         = [];

/**
 * Class: tgMap
 * Central class for creating and managing dynamic maps
 * 
 * Parameters:
 * 	parent - <string> DOM ID of a DIV-Block that will contain the map.
 * 	apiKey - <string> The API key.
 */
var tgMap = Class.create ({
		
	 // Public properties	
	errorHandler: null,
	panes: {
		stretch: null,
		map: null,
		markerShadow: null,
		marker: null,
		floaterShadow: null,
		markerMouseTarget: null,
		floater: null
	},
	tileManager: null,
	routeId: -1,
	trafficjam: 0,
	isMiniMap: false,
	
	 //Private properties
	_staticPoi: [],
	_apiKey: '',
	_container: null,
	_containerWidth: 0,
	_containerHeight: 0,
	_longitude: 0,
	_latitude: 0,
	_savedLongitude: 0,
	_savedLatitude: 0,
	_zoomlevel: 4,
	_observers: [],
	_mapType: 'map',
	_mapTypes: '',
	_panel: null, //Div Element to hold all the panes
	
	_x: 0,
	_y: 0,
	_clickX: 0,
	_clickY: 0,
	_irqManager: null,
	_mouseX: 0,
	_mouseY: 0,
	_dragging: false,
	_wasDragged: false,
	_eventObserver: null,
	
	_optionDblClickZoom: true,
	_optionScrollZoom: true,
	_optionContinuousZoom: true,
	_optionDragging: true,
	_optionInfoWindow: true,
	_optionClickZoom: true,
	
	
	_infoWindow: null,
	
	_overlays: [],
	_controls: [],
	_sidebar: null,
	_centerMarker: null,
	_copyright: null,
	
	_eventsEnabled: true,
	
	
	initialize: function (parent, apiKey, opts) {

		this.errorHandler = new tgMapErrorHandler();
		
		this._apiKey = apiKey;
		
		var container   = document.createElement ("div");
		this._container = container;
		this._parent    = parent;
				
		this.panes = {};
		
		parent.appendChild (this._container);
						
		this._panel = document.createElement ("div");
		this._container.appendChild (this._panel);
				
		Element.setStyle (this._panel, {position:"absolute", top:"0px", left:"0px"});
		
		//Create DIV Layers for all required panes
		this.panes.stretch = document.createElement("div");
		Element.addClassName (this.panes.stretch, "tgMapPaneStretch");
		this._panel.appendChild (this.panes.stretch);
		Element.setStyle(this.panes.stretch, {position:"absolute",top:"0px", left:"0px",zIndex:50});
		
		this.panes.map = document.createElement("div");
		Element.addClassName (this.panes.map, "tgMapPaneMap");
		this._panel.appendChild (this.panes.map);
		Element.setStyle(this.panes.map, {position:"absolute",top:"0px", left:"0px",zIndex:100});
		
		this.panes.markerShadow = document.createElement("div");
		Element.addClassName (this.panes.markerShadow,"tgMapPaneMarkerShadow");
		this._panel.appendChild (this.panes.markerShadow);
		Element.setStyle(this.panes.markerShadow, {position:"absolute",top:"0px", left:"0px",zIndex:200});
		
		this.panes.marker = document.createElement("div");
		Element.addClassName (this.panes.marker, "tgMapPaneMarker");
		this._panel.appendChild (this.panes.marker);
		Element.setStyle(this.panes.marker, {position:"absolute",top:"0px", left:"0px",zIndex:300});
		
		this.panes.floaterShadow = document.createElement("div");
		Element.addClassName (this.panes.floaterShadow, "tgMapPaneFloaterShadow");
		this._panel.appendChild (this.panes.floaterShadow);
		Element.setStyle(this.panes.floaterShadow, {position:"absolute",top:"0px", left:"0px",zIndex:400});
		
		this.panes.markerMouseTarget = document.createElement("div");
		Element.addClassName (this.panes.markerMouseTarget, "tgMapPaneMarkerMouseTarget");
		this._panel.appendChild (this.panes.markerMouseTarget);
		Element.setStyle(this.panes.markerMouseTarget, {position:"absolute",top:"0px", left:"0px", zIndex:500});		

		this.panes.floater = document.createElement("div");
		Element.addClassName (this.panes.floater, "tgMapPaneFloater");
		this._panel.appendChild (this.panes.floater);
		Element.setStyle(this.panes.floater, {position:"absolute",top:"0px", left:"0px",zIndex:600});
		
		this._eventObserver = this._handleEvent.bindAsEventListener(this);
		
		Event.observe (this.panes.map, "mousedown", this._eventObserver);
		Event.observe (this.panes.map, "mouseup", this._eventObserver);
		Event.observe (this.panes.map, "mousemove", this._eventObserver);
		Event.observe (this.panes.map, "dblclick", this._eventObserver);
		Event.observe (this.panes.map, "click", this._eventObserver);
		
		Event.observe (this.panes.map, "mousewheel", this._eventObserver, false);
		Event.observe (this.panes.map, "DOMMouseScroll", this._eventObserver, false); // Firefox
		
		this.tileManager = new tgTileManager(this);
		this._irqManager = new tgIrqManager (this);
		this._infoWindow = new tgInfoWindow (this);
		
		// (2011-Dez-22): these lines should persist here - if the api is requested by external pages !
		if (typeof tgEagleView != "undefined" 
			&& _TG_MAPS.length == 0) { //Eagleview only for the first map
			this.eagleView = new tgEagleView(this);
		}
						
		_TG_MAPS.push (this);
		this.checkResize();
		this._checkApiKey ();
		this._fireEvent('initialize');
	},
			
	/**
	 * Function: isInitialized
	 * 
	 * Returns:
	 * <bool> true if center has been set with setCenter, 
	 * false otherwise
	 */
	isInitialized: function () {
		if (this.longitude != 0 && this.latitude != 0) {
			return true;
		} else {
			return false;
		}
	},
	
	/**
	 * Function: destroy
	 * Should be called before removing a tgMap Object. 
	 * Cleans up event observers.
	 * 
	 * Events: 
	 * destroy
	 */
	destroy: function () {
		Event.stopObserving (this._container, "mousedown", this._eventObserver);
		Event.stopObserving (this._container, "mouseup", this._eventObserver);
		Event.stopObserving (this._container, "mousemove", this._eventObserver);
		Event.stopObserving (this._container, "dblclick", this._eventObserver);
		Event.stopObserving (this._container, "click", this._eventObserver);
		
		this._controls.each (function (control) {
			control.destroy();	
		});
		
		this._fireEvent('destroy');
	},
	
	 //Public methods
	
	/**
	 * Function: enableDragging
	 * Enables mouse dragging for the map
	 */
	enableDragging: function () {
		this._optionDragging = true;
	},
	
	/**
	 * Function: disableDragging
	 * Disables mouse dragging for the map
	 */
	disableDragging: function () {
		this._optionDragging = false;
	},
	
	/**
	 * Function: draggingEnabled
	 * 
	 * Returns: 
	 * <bool> true if mouse dragging is currently enabled, false otherwise
	 */
	draggingEnabled: function () {
		return this._optionDragging;
	},
	
	/**
	 * Function: enableInfoWindow
	 * Allows the infoWindow to shop up
	 */
	enableInfoWindow: function () {
		this._infoWindowEnabled = true;
	},
	
	/**
	 * Function: disableInfoWindow
	 * Prevents the infoWindow from showing up
	 */
	disableInfoWindow: function () {
		this._infoWindowEnabled = false;		
	},
	
	/**
	 * Function: infoWindowEnabled
	 * 
	 * Returns:
	 * <bool> true if infoWindow is currently enabled,
	 * false otherwise
	 */
	infoWindowEnabled: function () {
		return this._infoWindowEnabled;		
	},
	
	/** 
	 * Function: enableDoubleClickZoom
	 * Makes the map zoom in when user double clicks
	 */
	enableDoubleClickZoom: function () {
		this._optionDblClickZoom = true;		
	},
	
	/**
	 * Function: disableDoubleClickZoom 
	 * Keeps the map from zooming in when user double clicks
	 */
	disableDoubleClickZoom: function () {
		this._optionDblClickZoom = false;
	},
	
	/** 
	 * Function: doubleClickZoomEnabled
	 * 
	 * Returns: 
	 * <bool> true, if double click zooming is enabled,
	 * false otherwise
	 */
	doubleClickZoomEnabled: function () {
		return _optionDblClickZoom;
	},
	
	/**
	 * Function: enableContinuousZoom
	 * Enables the continuous zoom feature, where
	 * tiles are dynamically resized in the browser
	 * when zooming in or out
	 */
	enableContinuousZoom: function () {
		this._optionContinuousZoom = true;
	},
	
	/**
	 * Function: disableContinuousZoom
	 * Disables the continuous zoom feature, where
	 * tiles are dynamically resized in the browser
	 * when zooming in or out
	 */
	disableContinuousZoom: function () {
		this._optionContinuousZoom = false;
	},
	
	/**
	 * Function: continuousZoomEnabled
	 * 
	 * Returns:
	 * true when continuous zoom feature is enabled,
	 * false otherwise
	 */
	continuousZoomEnabled: function () {
		return this._optionContinuousZoom;
	},
	
	/**
	 * Function: enableScrollWheelZoom
	 * Activates zooming with the scroll wheel
	 * of the mouse
	 */
	enableScrollWheelZoom: function () {
		this._optionScrollZoom = true;
	},

	/**
	 * Function: disableScrollWheelZoom
	 * Turns off zooming with the scroll wheel
	 * of the mouse
	 */	
	disableScrollWheelZoom: function () {
		this._optionScrollZoom = false;
	},
	
	/**
	 * Function: scrollWheelZoomEnabled
	 * 
	 * Returns:
	 * <bool> true when zooming with the scroll wheel is enabled
	 * false otherwise
	 */
	scrollWheelZoomEnabled: function () {
		return this._optionScrollZoom;
	},
	
	/**
	 * Function: addControl
	 * Adds a given control to the map at given position
	 * 
	 * Parameters:
	 * control - <tgMapControl> The control to be added
	 * position - <object> JSON Object defining {x,y}
	 */
	addControl: function (control, position) {
		control.addToMap(this);
		control.draw();
		
		this._controls.push (control);
		this._fireEvent("addcontrol", control);

	},
	
	/**
	 * Function: removeControl
	 * Removes a control element from the map
	 */
	removeControl: function (control) {
		this._fireEvent("controlremoved", control);
	},
	
	/**
	 * Function: getContainer
	 * 
	 * Returns:
	 * <node> A reference to the DOM node that contains the map
	 */
	getContainer: function () {
		return this._container;
	},
	
	/**
	 * Function: getMapTypes
	 * Deprecated since december 2011, not longer supported in Version 1.1 
	 * 
	 * Returns:
	 * <array> A list of supported map types
	 */
	getMapTypes: function () {
		return this._mapTypes;
	},
	
		
	/**
	 * Function: getCurrentMapType
	 * Deprecated since december 2011, not longer supported in Version 1.1. 
	 * No more eagleview/aerial. Only type 'map' will be returned by default
	 * 
	 * Returns:
	 * <string> 'map' 
	 * The currently active map type
	 */
	getCurrentMapType: function () {
		return 'map';		
	},
	
	
	/**
	 * Function: setMapType
	 * Deprecated since december 2011. Not longer supported in Version 1.1. 
	 * Only type 'map' available. (No more eagleview or aerial)
	 * 
	 * Parameters:
	 * type - <string> 'map'
	 * 
	 * Event:
	 * setmaptype
	 */
	setMapType: function (type) {
		this._fireEvent("beforesetmaptype");

		if (type != this._mapType) {
			this._mapType = 'map';
			this._draw();
			// change Map Type Control Font Weight
			this._fireEvent('setmaptype');
		}
	},
	
	/**
	 * Function: getCenterPixel
	 * Calculates the pixel coordinates of the
	 * geographical center of the map relative to top left corner
	 * of the map on screen
	 * 
	 *  Returns:
	 *  <object> {x,y}
	 */
	getCenterPixel: function () {
		var size    = this.getSize(false);
		var y       = Math.floor(size.height/2);
		var sidebar = this.getSidebar();
		if (sidebar != null) {
			var x = Math.floor ((size.width+sidebar.getWidth())/2);
		} else {
			var x = Math.floor (size.width/2);
		}
		
		return {x:x, y:y};
	},
	
	/**
	 * Function: getCenter
	 * 
	 * Returns:
	 * <tgLngLat>  The coordinates of the current geographical 
	 * center of the map
	 */	
	getCenter: function () {		
		var center = new tgLngLat (this._longitude, this._latitude);
		return center;
	},
	
	/**
	 * Function: getBounds
	 * Determine longitude(east,west)/latitude(north,south) of each edge.
	 * 
	 * Parameters:
	 * sidebar - <bool> If set to false, whole screen width will
	 * go into calculation, ignoring sidebar
	 * 
	 * Returns:
	 * <object> {north, east, south, west}
	 */	
	getBounds: function (sidebar) {
		var upp = this.getUnitsPerPixel();
		
		var size        = this.getSize(false);
		var centerPixel = this.getCenterPixel();
		
		var cX = centerPixel.x*upp;
		var cY = centerPixel.y*upp;
		
		if (this.getSidebar() == null) {
			var n = this._latitude + cY;
			var s = this._latitude - cY;
			var e = this._longitude + cX;
			var w = this._longitude - cX;
		} else {
			var n = this._latitude + cY;
			var s = this._latitude - cY;
			
			var sbWidth = this.getSidebar().getWidth();
					
			if (sidebar == null || sidebar == true) {				
				var e = this._longitude + (size.width-centerPixel.x)*upp;
				var w = this._longitude - (centerPixel.x-sbWidth)*upp;
				
			} else {			
				var e = this._longitude + (size.width-centerPixel.x)*upp;
				var w = this._longitude - cX;
			}
		}
		
 		return {north:n, east:e, south:s, west:w};		
	},
	
	/**
	 * Function: getEdges
	 * Determine Pixel coordinates of each edge 
	 *
	 * Returns:
	 * <object> {top, right, bottom, left}
	 */
	getEdges: function (sidebar) {
		var pos = Element.cumulativeOffset (this._container);
		//var dims = Element.getDimensions (this._container);
		var dims = {
			width: this._containerWidth,
			height: this._containerHeight
		}
		
		if ((sidebar == null || sidebar == true) && this.getSidebar() != null) {
			var sbWidth = this.getSidebar().getWidth();
			return {top:pos[1], right:pos[0]+dims.width, bottom: pos[1]+dims.height, left:pos[0]+sbWidth}; 
		} else {
			return {top:pos[1], right:pos[0]+dims.width, bottom: pos[1]+dims.height, left:pos[0]};
		}
	},

		
	/**
	 * Function: getBoundsZoomLevel
	 * Determins the best zoomlevel for a map that shows
	 * a recatangle as defined by the bounds parameter
	 * 
	 * Parameters:
	 * bounds - <object> {north, east, south, west}
	 */
	getBoundsZoomLevel: function (bounds, sidebar) {
		if (sidebar == null) {
			sidebar = true;
		}
		var size = this.getSize(sidebar);
		
		var height = size.height;
		var width  = size.width;
		
		var boundsWidth  = bounds.east - bounds.west;
		var boundsHeight = bounds.north - bounds.south;
		
		for (var i = 15; i >= 1; i--) {
			var units = this.getUnitsPerPixel(i);
			
			var tmpWidth  = Math.floor(boundsWidth/units);
			var tmpHeight = Math.floor(boundsHeight/units);
			
			if (tmpWidth > width || tmpHeight > height) {
				break;
			}
		}
		if (i>15) {
			var zoom = 15;
		} else {
			var zoom = i + 1;
		}
		return zoom;
	},

	/**
	 * Function: fitViewToMarkerlist
	 * Sets the map center and the zoom, so that all markers are displayed
	 * 
	 * Parameters:
	 * markerlist - <array>
	 * sidebar - <bool> If set to false, whole screen width will
	 * go into calculation, ignoring sidebar. Optional, defaults to true
	 */
	fitViewToMarkerlist: function( p_markerlist, sidebar ) {
		if (sidebar == null) {
			sidebar = true;
		}
		var maxLng;
		var minLng;
		var maxLat;
		var minLat;
		var centerLng = null;
		var centerLat = null;
		
		p_markerlist.each( function( l_marker ) {
			if (l_marker.longitude != null && l_marker.latitude != null && l_marker.longitude > 0 && l_marker.latitude > 0) {  
				if (minLng == null || l_marker.longitude < minLng) {
					minLng = l_marker.longitude;
				}
	
				if (maxLng == null || l_marker.longitude > maxLng) {
					maxLng = l_marker.longitude;
				}
	
				if (minLat == null || l_marker.latitude < minLat) {
					minLat = l_marker.latitude;
				}
	
				if (maxLat == null || l_marker.latitude > maxLat) {
					maxLat = l_marker.latitude;
				}
			}
				
//			var lat = this._overlays[i]._latitude;
//			var lng = this._overlays[i]._longitude;
		});

		centerLng = Math.floor((minLng + maxLng) / 2);
		centerLat = Math.floor((minLat + maxLat) / 2);
		
		var center = new tgLngLat(centerLng, centerLat);
		var bounds = {north:maxLat, east:maxLng, south:minLat, west:minLng};
		if (p_markerlist.length > 1) {
			var zoom = this.getBoundsZoomLevel( bounds, sidebar );
		} else {
			var zoom = 4;
		}
		
		this.setCenter(center, zoom);
	},
	
	/**
	 * Function: getSize
	 * Calculates the actual pixel size of the maps container
	 * 
	 * Parameters:
	 * sidebar - <bool> If set to false, whole screen width will
	 * go into calculation, ignoring sidebar
	 * 
	 * Returns:
	 * <object> {width, height}
	 */
	getSize: function (sidebar) {
		var dimensions = Element.getDimensions (this._container);
				
		if (sidebar == null || sidebar == true) {
			dimensions.width = dimensions.width - this._sidebar.getWidth();
		}
		
		return dimensions;
	},
	
	/**
	 * Function: getWidth
	 * Calculates the actual pixel width of the maps container
	 * 
	 * Parameters:
	 * sidebar - <bool> If set to false, whole screen width will
	 * go into calculation, ignoring sidebar
	 * 
	 * Returns:
	 * <integer>
	 */
	getWidth: function (sidebar) {
		var dimensions = Element.getDimensions (this._container);
		
		var w = dimensions.width;
		
		if (this._sidebar != null && (sidebar == null || sidebar == true)) {
			w = w - this._sidebar.getWidth();
		}
		
		return w;
	},
	
	/**
	 * Function: getHeight
	 * Calculates the actual pixel height of the maps container
	 * 
	 * Returns:
	 * <integer>
	 */
	getHeight: function () {
		var dimensions = Element.getDimensions (this._container);
		return dimensions.height;
		return this._containerHeight;
	},
	
	/**
	 * Function: getUnitsPerPixel
	 * Calculates how many units of 10cm on physical earth a single 
	 * pixel in the map represents at given zoomlevel
	 * 
	 * Parameters:
	 * zoomlevel - <integer> Optional, if no zoomlevel is provided,
	 * current zoomlevel of the map will be used 
	 * 
	 * Returns:
	 * <integer> - Number of 10cm units per pixel.
	 */
	getUnitsPerPixel: function (zoomlevel) {
		if (zoomlevel == null) {
			zoomlevel = this._zoomlevel;
		}
			return _METER_PER_PIXEL[zoomlevel-1]*10;
	},
	
	/**
	 * Function: getZoom
	 * 
	 * Returns:
	 * <integer> - Current zoomlevel
	 */
	getZoom: function () {
		return this._zoomlevel;
	},

	/**
	 * Function: getOverlays
	 * 
	 * Returns:
	 * <array> - List of current overlays
	 */
	getOverlays: function () {
		return this._overlays;
	},
	
	/**
	 * Function: setCenter
	 * Centers the map at the given position
	 * 
	 * Parameters:
	 * center - <tgLngLat> The maps new center coordinates
	 * zoom - <integer> zoomlevel, optional
	 * type - <string> [map|aerial|hybrid], optional
	 * skipevent - <bool> If set to true, no events will be fired
	 * 
	 * Events:
	 * beforesetcenter, implausiblecoordinates, zoom, setcenter
	 * 
	 */	
	setCenter: function (center, zoom, type, skipevent) {
		if (zoom > MAX_ZOOMLEVEL) {
			zoom = MAX_ZOOMLEVEL;
		}
		
		if (zoom < 1) {
			zoom = 1;
		}
		
		if (skipevent == null) {
			skipevent = false;
		}
		if (!skipevent) {
			this._fireEvent ("beforesetcenter", {center: center, zoom: zoom});
		}
		if (center == null) {
			
		}
		
		var latLong = center.getTg();
		
		if (latLong.longitude == 0 && latLong.latitude == 0) {
			this._fireEvent ("implausiblecoordinates");
			return false;
		}
		
		this._longitude = latLong.longitude;
		this._latitude  = latLong.latitude;
		
		this.savePosition();
		
		if (zoom != null) {
			if (this._zoomlevel != zoom)
			{
				this._zoomlevel = zoom;
				if (!skipevent) {
					this._fireEvent("zoom");
				}
			}
		}

		if (type != null) {
			this._mapType = 'map'; //set to default - other types aren't supported since December 2011
		}
		
		this._draw();
						
		if (!skipevent) {
			this._fireEvent ("setcenter", {center: center, zoom: zoom});
		}
	},
	
	/**
	 * Function: panTo
	 * Pans the map to a given position
	 * 
	 * Parameters:
	 * center - <tgLngLat> The position to center on
	 * 
	 * Events:
	 * pan
	 */	
	panTo: function (center) {
		//@todo: Determine if the new center is in reasonable distance
		//to the current center. If it is, do a panByPixel. Otherwise, just setCenter
		this.setCenter(center);
		this._fireEvent("pan");
	},
		
	/**
	 * Function: panByPixel
	 * Pans the map by given pixel values
	 * 
	 * Parameters:
	 * x - <integer> Number of pixels to pan in x direction
	 * y - <integer> Number of pixels to pan in y direction
	 * 
	 * Events:
	 * pan
	 */	
	panByPixel: function (x,y) {
		if (typeof Scriptaculous != 'undefined' && typeof Effect != 'undefined') {
			this._eventsEnabled = false;
			new Effect.Move (this._panel, {
				x: x,
				y: y,
				mode: 'relative',
				duration: 0.2,
				afterFinish: function () {
					this._x                     -= x;
					this._y                     += y;
					this._longitude             -= x*this.getUnitsPerPixel();
					this._latitude              += y*this.getUnitsPerPixel();
					this.tileManager.forceUpdate = true;
					this._eventsEnabled          = true;
					this._fireEvent("pan");
				}.bind(this)
			})
		} else {
			this._x               -= x;
			this._panel.style.left = (parseInt(this._panel.style.left)+x)+"px";		
			this._longitude       -= x*this.getUnitsPerPixel();
			
			this._y                     += y;
			this._panel.style.top        = (parseInt(this._panel.style.top)+y)+"px";	
			this._latitude              += y*this.getUnitsPerPixel();
			this.tileManager.forceUpdate = true;
			this._fireEvent("pan");
		}
		
	},
	
	/**
	 * Function: setZoom
	 * Sets the maps zoomlevel
	 * 
	 * Parameters:
	 * level - <integer> Desired zoomlevel
	 * 
	 * Events:
	 * zoomin, zoomout, zoom, draw
	 */
	setZoom: function (level) {
		if (level > MAX_ZOOMLEVEL) {
			level = MAX_ZOOMLEVEL;
		}
		
		if (level < 1) {
			level = 1;
		}
		var oldLevel    = this._zoomlevel;
		this._zoomlevel = level;
		
		if (oldLevel < level) {
			this._fireEvent("zoomout");
		}
		if (oldLevel > level) {
			this._fireEvent("zoomin");
		}

		if (oldLevel != level) {
			this._draw();
			this._fireEvent("zoom");
		}		
	},
	
	/**
	 * Function: zoomIn
	 * Zooms in by one level
	 * 
	 * Events:
	 * zoomin, zoom, draw
	 */
	zoomIn: function () {
		if (this._zoomlevel > 1) {
			this.setZoom(this._zoomlevel - 1);
		}
	},
	
	/**
	 * Function: zoomOut
	 * 
	 * Events:
	 * zoomout, zoom, draw
	 */
	zoomOut: function () {
		if (this._zoomlevel < MAX_ZOOMLEVEL) {
			this.setZoom(this._zoomlevel + 1);
		}
	},
	
	/**
	 * Function: savePosition
	 * Saves current position of the map to return to later
	 * with <returnToSavedPosition>
	 */
	savePosition: function () {
		this._savedLatitude  = this._latitude;
		this._savedLongitude = this._longitude;
	},
	
	/**
	 * Function: returnToSavedPosition
	 * Returns the map to the position
	 * saved earlyer by <savePosition>
	 * 
	 * Events:
	 * draw
	 */
	returnToSavedPosition: function () {
		this._latitude  = this._savedLatitude;
		this._longitude = this._savedLongitude;
		
		this._draw();
	},
	
	/**
	 * Function: checkResize
	 * Forces the map to adjust itself to a
	 * change in size of its container.
	 * Should be called whenever container size
	 * has changed.
	 * 
	 * Events:
	 * resize
	 */
	checkResize: function () {
		if (this.isInitialized()) {
			var dims = Element.getDimensions (this._parent);
			
			Element.setStyle (this._container, {width:"0px", height: "0px"});
			var dims = Element.getDimensions (this._parent);
			var ua   = navigator.userAgent;
			
			if(ua.indexOf("opera")<0 && (ua.indexOf("MSIE 8")>-1 || ua.indexOf("MSIE 7")>-1)){
				dims.height -= 2;	
			}
			 
			
			Element.setStyle (this._container, {overflow:"hidden", position:"relative", width:dims.width+"px", height: dims.height+"px"});
			if (this._sidebar != null) {
				this._sidebar.checkResize();
			}
			this.tileManager.forceUpdate = true;
			this.tileManager.positionTiles();
			if (this._overlays.length > 0 ) {
				this._overlays.each(function(overlay) {
					overlay.position();
				})
			}
						
			var dims              = Element.getDimensions(this._container);
			this._containerWidth  = dims.width;
			this._containerHeight = dims.height;
			
			this._fireEvent("resize");
		}	
	},
	
	/**
	 * Function: addOverlay
	 * Adds an overlay to the map
	 * 
	 * Parameters:
	 * overlay - <tgOverlay>
	 * 
	 * Events:
	 * addoverlay
	 */
	addOverlay: function (overlay) {
		overlay.addToMap (this);
		overlay.draw ();
		this._overlays = this._overlays.reject (function (o) {
			return (o.id == overlay.id);
		});
		this._overlays.push (overlay);
		this._fireEvent("addoverlay");
	},
	
	/**
	 * Function: removeOverlay
	 * Removes an overlay from the map
	 * 
	 * Parameters: 
	 * overlay - <tgOverlay> The overlay to be removed
	 * 
	 * Events: 
	 * removeoverlay
	 */	
	removeOverlay: function (overlay) {
		overlay.remove();
		this._overlays = this._overlays.reject (function (o) {
			return (o.id == overlay.id);
		});
		this._fireEvent("removeoverlay");
	},
	
	/**
	 * Function: clearOverlays
	 * Removes all overlays from the map
	 * 
	 * Events:
	 * clearoverlays
	 */
	clearOverlays: function () {
		this._overlays.each( function(l_value) {
			l_value.remove();
		}.bind(this));
		this._overlays = [];		

		this._fireEvent("clearoverlays");
	},
	
	/**
	 * Function: openInfoWindow
	 * Opens an infowindow at the given point.
	 * Map will be panned so that infowindow is fully visible.
	 * Infowindow will be filled according to given options.
	 * 
	 * Parameters:
	 * lnglat - <tgLngLat> Position the infoWindow points to
	 * opts - <tgInfoWindowOptions> Optional options
	 * 
	 * Events:
	 * openinfowindow, pan
	 */	
	openInfoWindow: function (lnglat, opts) {
		if (opts == null) {
			opts = {};
		}
		
		//Set options for the infoWindow instance
		if (opts.tipX == null) {
			this._infoWindow.opts.tipX = 5;
		} else {
			this._infoWindow.opts.tipX = opts.tipX;
		}
		
		if (opts.tipY == null) {
			this._infoWindow.opts.tipY = 144;
		} else {
			this._infoWindow.opts.tipY = opts.tipY;
		}
		
		
		if (opts.shadowUrl == null) {
			this._infoWindow.opts.shadowUrl = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/shadow.png";
		} else {
			this._infoWindow.opts.shadowUrl = opts.shadowUrl;
		}
		
		if (opts.shadowOffsetX == null) {
			this._infoWindow.opts.shadowOffsetX = 0;
		} else {
			this._infoWindow.opts.shadowOffsetX = opts.shadowOffsetX;
		}
		
		if (opts.shadowOffsetY == null) {
			this._infoWindow.opts.shadowOffsetY = 106;
		} else {
			this._infoWindow.opts.shadowOffsetY = opts.shadowOffsetY;
		}
		
		if (opts.shadowwidth == null) {
			this._infoWindow.opts.shadowwidth = 420;
		} else {
			this._infoWindow.opts.shadowwidth = opts.shadowwidth;
		}
		
		if (opts.shadowheight == null) {
			this._infoWindow.opts.shadowheight = 40;
		} else {
			this._infoWindow.opts.shadowheight = opts.shadowheight;
		}
		
		if (opts.classes == null) {
			this._infoWindow.opts.classes = ["tgInfoWindow"];
		} else {
			this._infoWindow.opts.classes = opts.classes;
		}
		
		if (opts.marker == null) {
			this._infoWindow.opts.marker = null;
		} else {
			this._infoWindow.opts.marker = opts.marker;
		}
		
		if (opts.html != null) {
			this._infoWindow.update (opts.html);
		}
				
		this._infoWindow.open(lnglat);
		this._fireEvent("openinfowindow");
	},
	
	/**
	 * Function: openInfoWindowAjax
	 * Opens an infowindow at the given point.
	 * Map will be panned so that infowindow is fully visible.
	 * Infowindow will be filled with result of ajax call to url.
	 * 
	 * Parameters:
	 * lnglat - <tgLngLat> Position the infoWindow points to
	 * url - <string> URL for the Ajax call
	 * parameters - <string> Parameter string for ajax call
	 * method - <string> HTTP Method for Ajax call (Should be either "get" or "post"
	 * opts - <object> Options given as JSON Object
	 * 
	 * Events:
	 * openinfowindow, pan
	 */	
	openInfoWindowAjax: function (lnglat, url, parameters, method, opts) {
		var aj = new Ajax.Request(url, {
			method: method,
			parameters: parameters,
			onComplete: function (r) {
				opts.html = r.responseText;
				this.openInfoWindow (lnglat, opts);
				this._fireEvent("infowindowload");
			}.bind(this)
		})
	},
	
	/**
	 * Function: closeInfoWindow
	 * Closes the infowindow
	 * 
	 * Events:
	 * closeinfowindow
	 */		
	closeInfoWindow: function () {
		this._infoWindow.close();
	},
	
	/**
	 * Function: getInfoWindow
	 * Returns the infowindow object of the map
	 * 
	 * Returns:
	 * <tgInfoWindow>
	 */
	getInfoWindow: function () {
		return this._infoWindow;
	},
	
	/**
	 * Function: getPixel
	 * Calculates the pixel offset of the
	 * given location from the top left
	 * corner of the map
	 * 
	 * Parameters:
	 * lnglat - <tgLngLat> 
	 * 
	 * Returns:
	 * <object> {x,y}
	 */
	getPixel: function (lnglat) {
		var tg     = lnglat.getTg();
		var bounds = this.getBounds(false);
		var upp    = this.getUnitsPerPixel();
		
		var x = Math.floor((tg.longitude-bounds.west)/upp);
		var y = Math.floor((bounds.north - tg.latitude)/upp);
		return {x:x, y:y};
	},
	
	getStaticPoi: function () {
		return this._staticPoi;
	},
	
	addStaticPoi: function (poi, doDraw) {
		this._staticPoi.push(poi);
		this._staticPoi = this._staticPoi.uniq();
		if (doDraw == null || doDraw == true) {
			this._draw(2);
		}
	},
	
	removeStaticPoi: function (poi, doDraw) {
		this._staticPoi = this._staticPoi.without(poi);
		if (doDraw == null || doDraw == true) {
			this._draw(2);
		}
	},
	
	/**
	 * Function: getGeo
	 * Calculates the geographical
	 * position of a given pixel
	 * 
	 * Parameters:
	 * x - <integer>
	 * y - <integer>
	 * 
	 * Returns:
	 * <tgLngLat>
	 */
	getGeo: function (x,y) {
		var upp    = this.getUnitsPerPixel();
		var bounds = this.getBounds(false);
		var lo     = Math.round(bounds.west+x*upp);
		var la     = Math.round(bounds.north-y*upp);
		var lnglat = new tgLngLat (lo, la);
		return lnglat;
	},
	
	/**
	 * Function: getPanOffsets
	 * Returns the number of pixels the map
	 * has been panned since it was created
	 * 
	 * Returns:
	 * <object> {x,y}
	 */
	getPanOffsets: function () {
		return {x:this._x, y:this._y}
	},
	
	/**
	 * Function getParent
	 * Returns:
	 * <object> Parent DOM-Node of the map object
	 */
	getParent: function () {
		return this._parent;
	},
	
	/**
	 * Function: observe
	 * Sets an observer on a tgMap event
	 * 
	 * Paramters:
	 * event - <string>
	 * callback - <function> The function to be executed when the event fires
	 */
	observe: function (event, callback) {
		Event.observe(this._container, "tgMap::"+event, callback);
		//Create an Object to remember the parameters, for later deletion of the event
		var obs = {
			_event: event,
			_callback: callback
		};
		this._observers.push (obs);
	},
	
	/**
	 * Function: stopObserving
	 * Removes an observer from a tgMap event
	 * 
	 * Paramters:
	 * event - <string>
	 * callback - <function> The function to be executed when the event fires
	 */
	stopObserving: function (event, callback) {
		if (callback != null) { //Remove one specific observer
			Event.stopObserving(this._container, "tgMap::" + event, callback);
		} else if (event != null) { //Remove all observers for given event
			this._observers.each (function (obs) {
				if (obs._event == event) {
					Event.stopObserving(this._container, "tgMap::" + obs._event, obs._callback);
				}
			}.bind(this));
		} else { //Remove every observer
			this._observers.each (function (obs) {
				Event.stopObserving(this._container, "tgMap::" + obs._event, obs._callback);
			}.bind(this));
			this._observers = [];
		}
	},
	
	registerSidebar: function () {
		this._sidebar = new tgSidebar (this);
		this.changeSidebarStatus();
	},
	
	getSidebar: function () {
		return this._sidebar;
	},
	
	changeSidebarStatus: function () {
		if (this._sidebar == null) {
			return;
		}
		this._fireEvent( 'beforesidebarchanged' );
		var width = this._sidebar.getWidth();
		for (var i = 0; i < this._controls.length; ++i) {
			if (this._controls[i].position == TG_ANCHOR_BOTTOM_LEFT || this._controls[i].position == TG_ANCHOR_TOP_LEFT) {
				this._controls[i].outerdiv.style.left = width + 8  + 'px';
			} else if (this._controls[i].position == TG_ANCHOR_LEFT) {
				this._controls[i].outerdiv.style.left = width + 33  + 'px';
			}
		}
		this._fireEvent( 'sidebarchanged' );
	},
	
	unregisterSidebar: function () {
		
	},
	
	 // Private methods
	
	_checkApiKey: function () {
		//var aj = new Ajax.Request ("");
		if (false) {
			this._irqManager.destroy();
			this._container.style.display = "none";
			alert ("Ungültiger API Key!");
		} else {
			
		}
	},
	
	/**
	 * @private
	 * @description Removes all tiles from the viewport and draws the map
	 */
	_draw: function (layer) { 
		var tiles = this._container.getElementsBySelector("img.tgMapTile");
		tiles.each (function (tile) {
			
			var lay = ((tile.style.zIndex/10)) - 1;
			if (layer == null || layer == lay) {
				//Minimap workaround for IE6
				tile.onload = function () {
					if (tile.parentNode != null) { //Seems to be buggy otherwise
						Element.remove(tile);
					}
				}
				tile.src = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/spacer.gif";
				if (tile.style.filter != null) {
					tile.style.filter = "";
				}
			}
		});
		this.tileManager.forceUpdate = true;
		this.tileManager.update();
		if (this._overlays.length > 0 ) {
			this._overlays.each(function(overlay) {
				overlay.position();
			})
		}

		this._updateCopyright();
		if (layer == null && this.isMiniMap == false) {
			this._fireEvent("draw");
		}
	},

	/**
	 * @private
	 * @description Call prototypes Event.fire with the given event
	 * @param {Event} e
	 */
	_fireEvent: function (e, memo) {
		if (typeof (memo) == undefined || memo == null) {
			this._container.fire ("tgMap::"+e);
		} else {
			this._container.fire ("tgMap::"+e, memo);
		}
	},
	
	/**
	 * @private
	 * @description Takes a given event and takes appropriate action
	 * @param {Event} e
	 */
	_handleEvent: function (e) {
		if (this._eventsEnabled == false) {
			return;
		}
		var x = Event.pointerX(e)*1;
		var y = Event.pointerY(e)*1;
		
		switch (e.type) {
			case 'mousedown':
				if ( this._optionDragging && Event.isLeftClick(e)) {
					this._clickX   = x;
					this._clickY   = y;
					this._dragging = true;
				}
				if (Event.element(e).tagName == "IMG" &&  e.preventDefault) {
					e.preventDefault();
				}
				this._fireEvent ("mousedown", e);
			break;
			
			case 'click': 
				this._fireEvent ("click", e);
			break;
			
			case 'DOMMouseScroll':
			case 'mousewheel':
				if ( this.scrollWheelZoomEnabled() ) {
					var delta = 0;
					var event = e;
					
					if (!event) { /* For IE. */
						event = window.event;
					}
			                        
	
					if (event.wheelDelta) { /* IE/Opera. */
						delta = event.wheelDelta/120; /** In Opera 9, delta differs in sign as compared to IE. */
						if (window.opera) {
							delta = -delta;
						}
							
					} else if (event.detail) { /** Mozilla case. In Mozilla, sign of delta is different than in IE. Also, delta is multiple of 3. */
						delta = -event.detail/3;
					}
					
					/** If delta is nonzero, handle it. Basically, delta is now positive if wheel was scrolled up, and negative, if wheel was scrolled down. */
					if (delta) {
						if (delta == -1) {
							this.zoomOut();
						} else if (delta == 1) {
							this.zoomIn();
						}
					}
						
					/** Prevent default actions caused by mouse wheel. That might be ugly, but we handle scrolls somehow anyway, so don't bother here..      */
					if (event.preventDefault) {
						event.preventDefault();
					}
				}
				this._fireEvent ("mousewheel", e);
			break;
			
			case 'dblclick':
				if (this._optionDblClickZoom) {
					
					var offset = this._container.cumulativeOffset();
					
					var left = offset.left;
					var top  = offset.top;
					
					var clickx = x - offset.left;
					var clicky = y - top;
					
					var geoPos = this.getGeo(clickx,clicky);
					
					this._longitude = geoPos._longitude;
					this._latitude  = geoPos._latitude;
					
					this.zoomIn();
				}
				this._fireEvent ("dblclick", e);
			break;
							
			case 'mouseup':
				if ( this._optionDragging ) {
					this._dragging = false;

					if (this._wasDragged == true) {
						this._fireEvent("stopdrag");
					}
					this._wasDragged = false;
				}
				this._fireEvent ("mouseup", e);
			break;
			
			case 'mousemove':
				if ( this._optionDragging ) {
					if (this._dragging) {
						this._wasDragged             = true;
						this.tileManager.forceUpdate = true;
						
						var diffX = x - this._clickX;
						var diffY = y - this._clickY;
						
						this._panel.style.left = (parseInt(this._panel.style.left)+diffX)+"px";	
						this._clickX          += diffX;
						this._x               -= diffX;
						this._longitude       -= diffX*this.getUnitsPerPixel();
						
						this._panel.style.top = (parseInt(this._panel.style.top)+diffY)+"px";	
						this._clickY         += diffY;
						this._y              += diffY;
						this._latitude       += diffY*this.getUnitsPerPixel();
						
						this._fireEvent("drag");
					} else {
						return false;
					}
					Event.stop(e);
				}
				this._fireEvent("mousemove", e);
			break;
		}
	},
	
	_markCenter: function () {
		if (this._centerMarker == null) {
			this._centerMarker = document.createElement("div");
			this._container.appendChild (this._centerMarker);
		}
		
		var centerPixel = this.getCenterPixel();
		this._centerMarker.setStyle ({
			position: "absolute",
			zIndex: 10000000,
			left: (centerPixel.x-2)+"px",
			top: (centerPixel.y-2)+"px",
			width: "4px",
			height: "4px",
			backgroundColor: "#00ff00",
			border: "1px solid #ff0000"
		})
	},
	
	_updateCopyright: function () {
		if (this._copyright == null) {
			this._copyright = new tgCopyright();
			this.addControl(this._copyright);
		}
		this._copyright.draw();
	}
	
});

/** 
 * Class: tgSidebar
 * Instanciated by tgMap. Used to hold information on
 * the sidebar that has been registered with the
 * registerSidebar Method of tgMap.
 * 
 * Parameters:
 * map - <tgMap> The map the sidebar is used for
 * div - <node> Dom-Node to be used as sidebar
 * handle - <node> Optional Dom-Node of the handle to minimize or
 * maximize the sidebar
 *  
 */

var tgSidebar = Class.create({
	div: null,
	handle: null,
	content: null,
	resultlistPadding: 30,
	_map: null,
	_minimized: false,
	_handleObserver: null,
	_width: null,
	
	initialize: function (map) {
		if (map.getSidebar() != null) {
			return map.getSidebar();
		} 
		this._map = map;
		
		// Create the sidebar div
		this.div = new Element('div');
		this.div.addClassName('sidebar');
		this.div.style.cursor = "auto";
		
		// Create the content div
		this.content = new Element('div');
		this.content.addClassName('sidebarContent');
		
		// Create the sidebarhandle div
		this.handle = new Element('div');
		this.handle.addClassName('sidebarHandle');
		
		// Append the sidebar to the map
		this.div.appendChild( this.content );
		this.div.appendChild( this.handle );
		this._map.getContainer().appendChild( this.div );

		this.checkResize();
		
		// Create the observer for the sidebarHandle
		this._handleObserver = function (e) {
			if (this._minimized) {
				this.maximize();
			} else {
				this.minimize();
			}
		}.bindAsEventListener(this);
		Event.observe(this.handle, "click", this._handleObserver);
		
		// resize the handle
		var dims                 = Element.getDimensions(this.div);
		this.handle.style.height = dims.height+"px";
	},

	getWidth: function () {
		var pos    = Element.cumulativeOffset(this.div);
		var mapPos = Element.cumulativeOffset(this._map.getContainer());
		
		var x = pos[0]-mapPos[0];
		
		if (this._width == null) {
			var dims    = Element.getDimensions(this.div);
			this._width = dims.width;
		}
		
		return this._width + x;
		
	},
	
	minimize: function () {
		var dims = Element.getDimensions(this.div);
		
		var handleWidth = 0;
		
		if (this.handle != null) {
			handleWidth = Element.getDimensions(this.handle).width;
		} 
		
		this._map._longitude = this._map._longitude + ((handleWidth-dims.width)/2)*this._map.getUnitsPerPixel();
		
		if (typeof Scriptaculous != 'undefined' && typeof Effect != 'undefined') {
			new Effect.Move (this.div, {
				x: handleWidth-dims.width,
				y: 0,
				mode: 'relative',
				duration:0.5,
				afterFinish: function () {
					this._minimized = true;
					this._map.tileManager.update();
		
					this._map.changeSidebarStatus();		
				}.bind(this)
			});
		} else {
			this.div.style.left = handleWidth-dims.width+"px";
			this._minimized     = true;
			this._map.tileManager.update();
		
			this._map.changeSidebarStatus();
		}		
	},
			
	maximize: function () {
		var dims = Element.getDimensions(this.div);
				
		var handleWidth = 0;
		
		if (this.handle != null) {
			handleWidth = Element.getDimensions(this.handle).width;
		} 
		
		this._map._longitude = this._map._longitude + ((dims.width-handleWidth)/2)*this._map.getUnitsPerPixel();
		if (typeof Scriptaculous != 'undefined' && typeof Effect != 'undefined') {
			new Effect.Move (this.div, {
				x: dims.width-handleWidth,
				y: 0,
				mode: 'relative',
				duration:0.5,
				afterFinish: function () {
					this._minimized = false;
					this._map.tileManager.update();
					this._map.changeSidebarStatus()
				}.bind(this)
			});
		} else {
			this.div.style.left = "0px";
			this._minimized     = false;
			this._map.tileManager.update();
			this._map.changeSidebarStatus();
		}
		
	},
	
	checkResize: function () {
		var dims              = Element.getDimensions(this._map.getContainer());
		this.div.style.height = dims.height+"px";
		if (this.handle != null) {
			this.handle.style.height = dims.height+"px";
		}
		
		var resultLists = this.div.getElementsBySelector("div.resultlist");
		if (resultLists.length > 0 != null) {
			var resultList = resultLists[0];
			if (resultList != null) {
				var containerPos = Position.cumulativeOffset(this._map.getContainer());
				var pos          = Position.cumulativeOffset(resultList);
				
				var h                   = dims.height - (pos[1]-containerPos[1]);
				resultList.style.height = (h-this.resultlistPadding)+"px";
				
			}
		}
		
		var dims    = Element.getDimensions (this.div);
		this._width = dims.width;
		
	},
	
	update: function (html)
	{
		this.content.update(html);
		this.checkResize();
		this._map._fireEvent("sidebarupdated");
	},
	
	destroy: function () {
		
	}
	
});

/**
 * Class: tgLngLat
 * Universal object for defining a geographical point
 *  
 * Parameters:
 * lng - <float> Longitude of the location
 * lat - <float> Latitude of the location 
 */
var tgLngLat = Class.create ({
 	//Public properties
	
	//Private properties
	_longitude: 0,
	_latitude: 0,

	initialize: function (lng, lat) {
		if (lng == 0 && lat == 0) {
			this._longitude = 0;
			this._latitude  = 0;
			return false;
		}
		
		if (lng > 360 && lat > 360) { 
			this.setTg (lng, lat);
		} else {
			this.setWGS84 (lng, lat);
		}
	},
	
	//Public methods
	/**
	 * Function: setWGS84
	 * Sets location by WGS-84 coordinates
	 * 
	 * Parameters:
	 * lng - <float> longitude
	 * lat - <float> latitude
	 */
	setWGS84: function (lng, lat) {
		var kt          = this._WGS2Tg (lng*1.0, lat*1.0);
		this._longitude = kt[0];
		this._latitude  = kt[1];
	},
	
	/**
	 * Function: setTg
	 * Sets location by Telegate proprietary coordinates
	 * 
	 * Parameters:
	 * lng - <integer> longitude
	 * lat - <integer> latitude
	 */
	setTg: function (lng, lat) {
		this._longitude = lng*1;
		this._latitude  = lat*1;
	},
	
	/**
	 * Deprecated, don't use for new projects 
	 */
	getWGS84: function () {
		console.warn ("getWGS84 is deprecated. Change to getWGS.");
		var wgs = this._tG2WGS84 (this._longitude, this._latitude);
		return {longitude: wgs[0], latitude:wgs[1]};
	},
	
	/**
	 * Function: getWGS
	 * 
	 * Returns:
	 * <object> {longitude, latitude} The WGS-84 coordinates of the point
	 */
	getWGS: function () {
		var wgs = this._tG2WGS84 (this._longitude, this._latitude);
		return {longitude: wgs[1], latitude:wgs[0]};
	},
	
	/**
	 * Function: getTg
	 * 
	 * Returns:
	 * <object> {longiutde, latitude} The Telegate proprietary coordinates of the point
	 */
	getTg: function () {
		return ({longitude: this._longitude, latitude: this._latitude});
	},
	
	//Private methods
	_WGS2Tg: function (longitude, latitude) {
            var a  = 6378137;     // earth radius
            var e  = 0.081819191; // eccentricity
            var p0 = 46.5;        // centre of projection
            var p1 = 40;          // first parallel in degrees
            var p2 = 55;          // second parallel in degrees
            var l  = 6;           // centre meridian in degrees

            var phi0    = ( p0 * Math.PI ) / 180;
            var phi1    = ( p1 * Math.PI ) / 180;
            var phi2    = ( p2 * Math.PI ) / 180;
            var lambda0 = ( l * Math.PI ) / 180;

            // Temporary variables
            var n = Math.log( Math.cos(phi1 ) / Math.cos(phi2 ) ) / Math.log( Math.tan( ( Math.PI / 4 ) + ( phi2 / 2 ) ) / Math.tan( ( Math.PI / 4 ) + ( phi1 / 2 ) ) );

            var f = ( Math.cos( phi1 ) * Math.pow( Math.tan( ( Math.PI / 4 ) + ( phi1 / 2 ) ), n ) ) / n;

            var rho0 = ( a * f ) / Math.pow( Math.tan( ( Math.PI / 4 ) + ( phi0 / 2 ) ), n );

            // Transform 84 to Telegate internal
            var phi    = ( latitude * Math.PI ) / 180;
            var lambda = ( longitude * Math.PI ) / 180;
            var rho    = ( a * f ) / Math.pow( Math.tan( ( Math.PI / 4 ) + ( phi / 2 ) ), n );
            var ny     = n * ( lambda - lambda0 );

            var x = 50000000 + Math.round( ( 10 * rho * Math.sin( ny ) ));
            var y = 50000000 + Math.round( ( 10 * ( rho0 - ( rho * Math.cos( ny ) ) ) ));
            return [x, y];
	},
	
	_tG2WGS84: function (longitude, latitude) {
	   		var a  = 6378137;     // earth radius
            var e  = 0.081819191; // eccentricity
            var p0 = 46.5;        // centre of projection
            var p1 = 40;          // first parallel in degrees
            var p2 = 55;          // second parallel in degrees
            var l  = 6;           // centre meridian in degrees

            var phi0    = ( p0 * Math.PI ) / 180;
            var phi1    = ( p1 * Math.PI ) / 180;
            var phi2    = ( p2 * Math.PI ) / 180;
            var lambda0 = ( l * Math.PI ) / 180;

            // Temporary variables
            var n = Math.log( Math.cos(phi1 ) / Math.cos(phi2 ) ) / Math.log( Math.tan( ( Math.PI / 4 ) + ( phi2 / 2 ) ) / Math.tan( ( Math.PI / 4 ) + ( phi1 / 2 ) ) );

            var f = ( Math.cos( phi1 ) * Math.pow( Math.tan( ( Math.PI / 4 ) + ( phi1 / 2 ) ), n ) ) / n;

            var rho0 = ( a * f ) / Math.pow( Math.tan( ( Math.PI / 4 ) + ( phi0 / 2 ) ), n );

            var x      = (longitude - 50000000)/10;
            var y      = (latitude - 50000000)/10;
            var my     = Math.atan(x / (rho0 - y));
            var lambda = my/n + lambda0;
            var rho    = Math.sqrt(Math.pow(x,2) + Math.pow((rho0 - y),2));
            if (n < 0) {
                    rho = -rho;
            }

            var phi            = 2*Math.atan(Math.pow((a*f/rho),(1/n))) - Math.PI/2;
            var LongitudeWGS84 = (phi*180/Math.PI);
            var LatitudeWGS84  = (lambda*180/Math.PI);
            return [LongitudeWGS84, LatitudeWGS84];
	}	
});

/**
 * Class: tgOverlay
 * Class to define and handle overlays on the map.
 * An overlay has a fixed geographical position and
 * moves with the map
 * 
 * Parameters: 
 * id - <string> Unique ID for the overlay
 * position - <tgLngLat> Geographical position of the overlay
 */
var tgOverlay = Class.create ({
	name: '',
	
	id: '',
	_longitude: 0,
	_latitude: 0,
	_map: 0,
	
	initialize: function (id, position) {
		this.id         = id;
		var lnglat      = position.getTg();
		this._longitude = lnglat.longitude;
		this._latitude  = lnglat.latitude;
	},
	
	addToMap: function (map) {
		this._map = map;
	},
	
	/**
	 * Function: draw
	 * Show the overlay on the map.
	 * To be implemented by inheriting classes
	 */	
	draw: function () {  
		if ($(this.id) != null) {
			Element.remove($(this.id));
		}
	},
	
	/**
	 * Function: position
	 * Position the overlay on the map.
	 * To be implemented by inheriting classes
	 */
	position: function () {
		
	},
	
	getLngLat: function () {
		var lngLat = new tgLngLat(this._longitude, this._latitude);
		return lngLat;
	},
	
	setLngLat: function (position) {
		var lnglat      = position.getTg();
		this._longitude = lnglat.longitude;
		this._latitude  = lnglat.latitude;
		this.position();
	},
	
	remove: function () {
	}
});

/**
 * Class: tgMapMarker
 * 
 * Parameters: 
 * id - <string> Unique ID for the marker
 * position - <tgLngLat> Geographical position of the tip of the Marker
 * opts - <tgMapMarkerOptions> Optional options for the marker
 * 
 * Implements: <tgOverlay>
 * 
 */
var tgMapMarker = Class.create (tgOverlay, {
	//Public properties
	iconImg: null,
	shadowImg: null,
	clickImage: null,
	clickMap: null,
	onclick: null,
	priority: 5,
	type: null,
	
	//Private properties
	_iconUrl: "",
	_hoverUrl: "",
	_shadowUrl: "",
	_width: 0,
	_height: 0,
	//Offset of the tip of the marker from top left corner
	_tipX: 0,
	_tipY: 0,
	
	_map: null,
	_activeArea: "",
	_href: "",
	_title: "",
	_area: null,
	_clickobserver: null,
	_mouseoverobserver: null,
	_mouseoutobserver: null,
	
	//Constructor
	initialize: function ($super, id, position, opts) {
		$super (id, position);

		if (opts == null) {
			opts = {};
		}

		if (opts.icon == null) {
			this._iconUrl  = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/icon.png";
			this._hoverUrl = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/hovericon.png";
			
			this._shadowUrl    = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/iconshadow.png";			
			this._shadowwidth  = 36;
			this._shadowheight = 42;
			
			this._width  = 24;
			this._height = 42;
			
			
			this._tipX = 5;
			this._tipY = 39;
			
			this._activeArea = "1,1,22,1,22,22,14,22,6,39,5,39,5,22,1,22";
									
		} else {
			this._iconUrl = opts.icon.iconImg;
			this._width   = opts.icon.width;
			this._height  = opts.icon.height;
		
			if (opts.icon.shadowUrl == null) {
				this._shadowUrl = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/iconshadow.png";
			} else {
				this._shadowUrl = opts.icon.shadowUrl;			
			}
			
			if (opts.icon.shadowwidth == null) {
				this._shadowwidth = 36;
			} else {
				this._shadowwidth = opts.icon.shadowwidth;
			}
			
			if (opts.icon.shadowheight == null) {
				this._shadowheight = 42;
			} else {
				this._shadowheight = opts.icon.shadowheight;
			}
			
			this._tipX       = opts.icon.tipX;
			this._tipY       = opts.icon.tipY;
			this._activeArea = opts.icon.activeArea;
			this.priority    = opts.icon.priority;	
		}
						
		if (opts.href == null) {
			this._href = "#";
		} else {
			this._href = opts._href;
		}
		
		
		
				
		if (opts.title == null) {
			this._title = id;
		} else {
			this._title = opts.title;
		}
	},
		
	//Public methods
	draw: function ($super) {
		$super();

		if (this._map == null) {
			return;
		}
		if (this.iconImg == null) {
			
			this.iconImg = document.createElement("img");
			
			this.iconImg.style.width  = this._width+"px";
			this.iconImg.style.height = this._height+"px";
			
			if (Prototype.Browser.IE) {
				this.iconImg.src          = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/spacer.gif";
				this.iconImg.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this._iconUrl + "', sizingMethod='crop');"
			} else {
				this.iconImg.src = this._iconUrl;
			}
			this.iconImg.id = this.id;
			
			
			this._map.panes.marker.appendChild (this.iconImg);						
		}
		
		
		
		if (this.shadowImg == null && this._shadowUrl != "") {
						
			this.shadowImg = document.createElement("img");
			
			this.shadowImg.style.width  = this._shadowwidth+"px";
			this.shadowImg.style.height = this._shadowheight+"px";
			
			if (Prototype.Browser.IE) {
				this.shadowImg.src          = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/spacer.gif";
				this.shadowImg.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this._shadowUrl + "', sizingMethod='scale');"
			} else {
				this.shadowImg.src = this._shadowUrl;
			}
			this.shadowImg.id = this.id+"_shadow";
			this._map.panes.markerShadow.appendChild (this.shadowImg);						
		}
				
		
		if (this.clickImage == null) {
			
			var imageMap = document.createElement("map");
			this._map.panes.markerMouseTarget.appendChild (imageMap);
			imageMap.style.width  = this._width+"px";
			imageMap.style.height = this._height+"px";
			imageMap.setAttribute("id", this.id+"_clickmap");
			imageMap.setAttribute("name", this.id+"_clickmap");						
			this.clickMap = imageMap;
			
			this._clickobserver     = this._click.bindAsEventListener(this);
			this._mouseoverobserver = this._mouseover.bindAsEventListener(this);
			this._mouseoutobserver  = this._mouseout.bindAsEventListener(this);
			this._area              = document.createElement ("area");
			this._area.setAttribute("shape", "poly");
			this._area.setAttribute("coords", this._activeArea);
			this._area.setAttribute("href", this._href);
			this._area.setAttribute("title", this._title);
			this._area.setAttribute("id", this.id+"_clickarea");
			this.clickMap.appendChild(this._area);
			Event.observe (this._area, "click", this._clickobserver);
			Event.observe (this._area, "mouseover", this._mouseoverobserver);
			Event.observe (this._area, "mouseout", this._mouseoutobserver);
			
			this.clickImage              = document.createElement("img");
			this.clickImage.style.border = "none";
			this.clickImage.style.width  = this._width+"px";
			this.clickImage.style.height = this._height+"px";
			this.clickImage.src          = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/spacer.gif";
			this.clickImage.setAttribute("id", this.id+"_clickimage");
			this.clickImage.style.cursor = "pointer";
			this._map.panes.markerMouseTarget.appendChild (this.clickImage);						
			this.clickImage.setAttribute("useMap", "#"+this.id+"_clickmap");			
		}
				
		this.position();
			
	},
	
	getIconUrl: function () {
		return this._iconUrl;
	},
	
	position: function ($super) {
		$super();
		var lnglat = new tgLngLat(this._longitude, this._latitude);
		var pixel  = this._map.getPixel(lnglat);
		
		//These were used instead of _map._x and _map._y. 
		//Current implementation should offer better performance
		//var mapOffsetX = parseInt(this._map._panel.style.left);
		//var mapOffsetY = parseInt(this._map._panel.style.top);
				
		var st = {
			position: 'absolute',
			left: (pixel.x-this._tipX+this._map._x)+'px',
			top: (pixel.y-this._tipY-this._map._y)+'px',
			zIndex: this.priority*100000000-this._latitude
		}
		
		Element.setStyle (this.iconImg, st);
		if (this.shadowImg != null) {
			Element.setStyle (this.shadowImg, st);
		}
		Element.setStyle (this.clickImage, st);
		Element.setStyle (this._area, {
			zIndex: 10+this.priority*100000000-this._latitude
			});
	},
	
	remove: function ($super) {
		$super();
		if ($(this.id) != null) {
			Element.remove($(this.id));
		}
		if ($(this.id+"_shadow") != null) {
			Element.remove($(this.id+"_shadow"));
		}
		if ($(this.id+"_clickarea") != null) {
			Event.stopObserving (this._area, "click", this._clickobserver);
			Element.remove(this._area);
			Element.remove($(this.id+"_clickmap"));
			Element.remove($(this.id+"_clickimage"));
		}
	},
	
	hide: function () {
		this.iconImg.style.display = "none";
		if (this.shadowImg != null) {
			this.shadowImg.style.display = "none";
		}
		this.clickMap.style.display = "none";
	},
	
	show: function () {
		this.iconImg.style.display = "block";
		if (this.shadowImg != null) {
			this.shadowImg.style.display = "block";
		}
		this.clickMap.style.display = "block";
	},
		
	/**
	 * Fires the maps markerclick Event
	 */
	_click: function (e) {
		this._map._fireEvent("markerclick", {marker:this});
	},
	
	/**
	 * Fires the maps markermouseover Event
	 */
	_mouseover: function (e) {
		this._map._fireEvent("markermouseover", {marker:this});
	},
	
	/**
	 * Fires the maps markermouseout Event
	 */
	_mouseout: function (e) {
		this._map._fireEvent("markermouseout", {marker:this});
	},
	
	/**
	 * Currently not used, as clicks are detected via the
	 * tgMapPaneMarkerMouseTarget panel. Should probably
	 * become a public method, to allow the detection
	 * of overlaying icons. 
	 */		
	_within: function (x, y) {
		var base = Element.cumulativeOffset (this.iconImg);
		var n    = this._activeArea.length; //number of points in polygon
		var xnew,ynew,xold,yold,x1,y1,x2,y2,i;
		var inside = false;
		var xCheck = x-base[0];
		var yCheck = y-base[1];
		
		xold = this._activeArea[n - 2];
		yold = this._activeArea[n - 1];
		    
		for (var i = 0 ; i < n; i = i + 2) {
			xnew = this._activeArea[i];
			ynew = this._activeArea[i + 1];
		    if (xnew > xold) {
		    	x1 = xold;
		        x2 = xnew;
		        y1 = yold;
		        y2 = ynew;
		    } else {
		    	x1 = xnew;
		        x2 = xold;
		        y1 = ynew;
		        y2 = yold;
		    }
		    if ((xnew < xCheck) == (xCheck <= xold) && ((yCheck-y1)*(x2-x1) < (y2-y1)*(xCheck-x1))) {
		    	inside = !inside;
		    }
		    xold = xnew;
		    yold = ynew;
		}
		return inside;		
	}	
	
	//Private methods
		
});

/**
 * Class: tgMapMarkerOptions
 * Objects of this class can be given as options parameter to
 * the constructor of the <tgMapMarker> class to define
 * behavior of the marker.
 * 
 * This class is to be instanciated as a JSON Object
 * 
 * Properties:
 * icon - <tgMapIcon> The icon to be used to show the marker
 * title - <string> Title to be displayed in the tooltip when hovering
 * clickable - <bool> If set to true, the marker will fire the 
 * markerclickevent when clicked
 */


/**
 * Class: tgMapIcon
 * Helper Class to define the appearence of the icon of a <tgMapMarker>.
 * No constructor or methods, should be instantiated as JSON Object.
 * Can be used for the icon property of a <tgMapMarkerOptions> Object.
 * 
 * Properties:
 * iconImg - <string> URL of the image representing the icon.
 * hoverImg - <string> URL of the hover image.
 * shadowUrl - <string> URL of the image representing the icon's shadow.
 * activeArea - <array> Coordinates describing a polygon of the clickable area
 * of the icon.
 * 
 */

/**
 * Class: tgTileManager
 */
var tgTileManager = Class.create ({
	_map: null,
	_tiles: [],
	_loaded: 0,
	_loading: 0,
	_aborted: 0,
	_tileSize: 256,
	_tilesPerUpdate: 8,
	_tilesDrawn: 0,
	_browserstring: '',
	forceUpdate: true,
	
	//Constructor
	initialize: function(map){
		if (map.tileManager != null) {
			return map.tileManager;
		}
		this._map           = map;
		this._browserstring = this._getBrowserString();
	},
	
	positionTile: function (tile) {
			var lng    = tile.getAttribute("longitude");
			var lat    = tile.getAttribute("latitude");
			var center = this._map.getCenter().getTg();
			var mapLng = center.longitude;
			var mapLat = center.latitude;
			var upp    = this._map.getUnitsPerPixel();
			
			var offsetX = Math.floor((lng - mapLng) / upp);
			var offsetY = Math.floor((mapLat - lat) / upp);
			
			var size = this._map.getSize(false);
			
			
			var panOffsets  = this._map.getPanOffsets();
			var centerPixel = this._map.getCenterPixel();
					
			var left = Math.floor(centerPixel.x) + offsetX+panOffsets.x;
			var top  = Math.floor(centerPixel.y) + offsetY-panOffsets.y;	
			
			Element.setStyle (tile, {
				left: left+"px",
				top: top+"px"
			});
	},
	
	positionTiles: function () {
		var tiles = this._map.getContainer().getElementsBySelector ("img.tgMapTile");
		tiles.each (function (tile) {
			this.positionTile(tile);	
		}.bind(this));
	},
		
	/**
	 * Function: update
	 * Updates the tiles that are displayed on screen
	 */
	update: function (forcePosition) {
		if (this._map.isInitialized() == false) {
			return;
		}
		
		if (!this.forceUpdate && this._tilesDrawn == 0) {
			return;
		}
		
		this.forceUpdate = false;
		
		var upp = this._map.getUnitsPerPixel();
		var upt = this._tileSize * upp; //Units per Tile
		
		//Check for tiles that are outside the visible Area
		var tiles  = this._map.panes.map.getElementsBySelector ("img.tgMapTile");	
		var bounds = this._map.getBounds(false);
						
		var bodycount = 0;
		tiles.each (function (tile) {
			
						
			var destroyed = false;
			if (tile.getAttribute("longitude") > bounds.east) {
				Element.remove(tile);
				destroyed = true;
				bodycount++;
			} 
			
			if (!destroyed && tile.getAttribute("longitude")+upt < bounds.west) {
				Element.remove(tile);
				destroyed = true;
				bodycount++;
			} 
			
			if (!destroyed && tile.getAttribute("latitude") < bounds.south) {
				Element.remove(tile);
				destroyed = true;
				bodycount++;
			} 
			
			if (!destroyed && tile.getAttribute("latitude")-upt > bounds.north) {
				Element.remove(tile);
				destroyed = true;
				bodycount++;
			}
			
		}.bind(this));
						
		//Check for tiles that need to be created
		var center = this._map.getCenter().getTg();
		var lng    = center.longitude;
		var lat    = center.latitude;
		var extent = this._tileSize/2 * upp;
					
		//Draw center tile
		//Remember: Longitude and Latitude of a PPM Tile refer to the top left corner, not the tile's center!
		
		var centerTileLongitude = (upp*this._tileSize)*(Math.floor((lng-NMAP_WORLD_X)/(upp*this._tileSize)))+NMAP_WORLD_X;
		var centerTileLatitude  = (upp*this._tileSize)*(Math.ceil((lat-NMAP_WORLD_Z2)/(upp*this._tileSize)))+NMAP_WORLD_Z2;
		
		var tilesDrawn = 0;	
				
		for (var l = 0; l <= 2; l++) {
			this._drawTile(centerTileLongitude, centerTileLatitude, l, upp);
		}
		
		//Now draw the tiles around the center one
		var within = {};
		var d      = 0;
				
		for (var l = 0; l <= 2; l++) {
			within = {top:true, right:true, bottom: true, left: true};
			d      = 0;
			while ((within.top || within.right || within.bottom || within.left) && tilesDrawn < this._tilesPerUpdate) {
				d++;					
				if (bounds.north > (centerTileLatitude + (d-1) * upt)) {
					//Draw top row
					var topLatitude = centerTileLatitude + (d * upt);
					for (var x = (-1) * d; x <= d; x++) {
						if (tilesDrawn < this._tilesPerUpdate) {
							var topLongitude = centerTileLongitude + (x * upt);
							tilesDrawn      += this._drawTile(topLongitude, topLatitude, l, upp);
							
						}
					}
				}
				else {
					within.top = false;
				}

				if (bounds.south < (centerTileLatitude - (d) * upt)) {
					//Draw bottom row
					var bottomLatitude = centerTileLatitude - (d * upt);
					for (var x = (-1) * d; x <= d; x++) {
						if (tilesDrawn < this._tilesPerUpdate) {
							var bottomLongitude = centerTileLongitude + (x * this._tileSize * upp);
							tilesDrawn         += this._drawTile(bottomLongitude, bottomLatitude, l, upp);
						}
					}
				}
				else {
					within.bottom = false;
				}
				
				if (bounds.east > (centerTileLongitude + d * upt)) {
					//Draw right column
					var rightLongitude = centerTileLongitude + (d * upt);
					for (var y = (-1) * d; y <= d; y++) {
						if (tilesDrawn < this._tilesPerUpdate) {
							var rightLatitude = centerTileLatitude + (y * upt);
							tilesDrawn       += this._drawTile(rightLongitude, rightLatitude, l, upp);
						}
					}
				}
				else {
					within.right = false;
				}
				
				if (bounds.west < (centerTileLongitude - (d-1) * upt)) {
					//Draw left column
					var leftLongitude = centerTileLongitude - (d) * upt;
					for (var y = (-1) * d; y <= d; y++) {
						if (tilesDrawn < this._tilesPerUpdate) {
							var leftLatitude = centerTileLatitude - (y * upt);
							tilesDrawn      += this._drawTile(leftLongitude, leftLatitude, l, upp);
						}
					}
				}
				else {
					within.left = false;
				}
			}
			this._tilesDrawn = tilesDrawn;
			
		}
		
		
		//Check for tiles that have not yet loaded
	},
		
	_getBrowserString: function () {
		switch (navigator.appName) {
			case 'Netscape':
				var engine = 'mz';
			break;
			case 'Internet Explorer':
				var engine = 'ie';
			break;
			case 'Opera':
				var engine = 'op';
			break;
			default:
				var engine = 'unknown';
			break;
			
		}
		var version = navigator.appVersion.substr(0,3);
		
		return (engine+version);
	},
	
	_withinContainer: function (longitude, latitude) { //Returns true if the tile with the given container would be appear within the container
		var upp = this._map.getUnitsPerPixel();
		var upt = this._tileSize * upp; //Units per Tile
		
		var bounds = this._map.getBounds(false);
		
		if (longitude+upt < bounds.west) {
			return false;
		}
		
		if (longitude > bounds.east) {
			return false;
		}
		
		if (latitude - upt > bounds.north) {
			return false;
		}
		
		if (latitude < bounds.south) {
			return false;
		}
		
		return true;
	},		
			
	
	
	_drawTile: function (lng, lat, lay, upp) {
		var spalte = (lng-NMAP_WORLD_X)/(this._tileSize*upp);
		if (typeof pandoraData != 'undefined' && pandoraData != null) {
		
			var mapServers = pandoraData.mapServers.evalJSON(true);
			
			var serverCount    = mapServers.ppm.length;			
			var serverHostname = mapServers.ppm[Math.abs(spalte % serverCount)];
		} else {
			var serverId       = Math.abs(spalte % 4) + 1;
			var serverHostname = 'nmap0'+serverId+'.ppm-ng.de';
		}
		
		var layerCode = this._getLayerCode(lay);
		if (layerCode == 0) {
			return 0;
		}
		
		var routeId = -1;
		
		if (lay == 2) {
			routeId = this._map.routeId;
		}
		
		var trafficjam = 0;
		
		if (lay == 2) {
			trafficjam = this._map.trafficjam;
		} 
			
		//Avoid drawing top layer tiles which can not contain any data
		if (lay==2 && routeId == -1 && trafficjam == 0 && (this._map.getStaticPoi().length == 0 || this._map.getZoom() > 5)) {
			return 0;
		}
		
		var src = "http://"+serverHostname+"/scripts/image.dll?getxmapdirect&left="+lng+"&top="+lat+"&w=256&h=256&zoom="+upp*100+"&routeid="+routeId+"&trafficjam="+trafficjam+"&layer="+layerCode;
		
		if (lay == 2) {
			src += "&at="+this._browserstring;
			if (this._map.getStaticPoi().length > 0 && this._map.getZoom() < 6) {
				src      += "&poicat=";
				var count = 0;
				this._map.getStaticPoi().each (function (poi) {
					if (count > 0) {
						src += "|"
					};
					src     += poi;
					count++;
				}.bind(this));
			}
		}
		
		
		
		var tileExists = false;
		
		
		
		var tiles = this._map.getContainer().getElementsBySelector ("img.tgMapTile."+lng+"_"+lat);
		
		
					
		tiles.each (function (tile) {
			if (tile.src == src || (tile.altsrc != "" && tile.altsrc == src)) {
				if (this._map.isMiniMap || !Element.hasClassName (tile, "tgMiniMapTile")) {
					tileExists = true;
					return 0;
				}
			} 
		}.bind(this));
		
		if (!this._withinContainer (lng, lat)) {
			return 0;
		}
		
		
		//var tiles = $A(this._map.getContainer().getElementsByTagName ("img"));
		
		if (!tileExists) {
			//var img = document.createElement("img");
			
			var img = new Element("img", {
				altsrc: "",
				galleryimg: "false",
				longitude: lng,
				latitude: lat,
				upp: upp
			})
			
									
			this.positionTile(img);
			
			
			Element.setStyle(img, {
				position: "absolute",
				zIndex: (lay + 1) * 10,
				MozUserSelect: 'none',
				width:"256px",
				height:"256px",
				visibility:"hidden"
				
			});
			
			
			img.onload = function () {
				this.style.visibility = "visible";
			}.bind(img);
			
			this._map.panes.map.appendChild(img);

						
			if (Prototype.Browser.IE) {
				img.altsrc       = src;
				img.src          = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/spacer.gif";
				img.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+src+"', sizingMethod='scale');"
			}
			else {
				img.src = src;
			}
									
			
			
			Element.addClassName(img, "tgMapTile");
			
			if (this._map.isMiniMap) {
				Element.addClassName(img, "tgMiniMapTile");
			};
			
			Element.addClassName(img, lng+"_"+lat);
			
			if (lay > 0) {
				Element.addClassName(img, "printoff");
			}
					
			return 1;
		} else {
			return 0;
		}
	},
	
	_getLayerCode: function (layer) {
		// (modified 2011-Dez-20)
		if (this._map.getUnitsPerPixel() < 10240) {
			var baseLayer = 16;
			var topLayer  = 0;
			var userLayer = 128;
		} else {
			var baseLayer = 2;
			var topLayer  = 1;
			var userLayer = 128;
		}
		
		switch (layer) {
			case 0:
				return baseLayer;
			break;
			case 1:
				return topLayer;
			break;
			case 2:
				return userLayer;
			break;
		}
	}	
});

/**
 * Class tgIrqManager
 * Handles periodic tasks for the map object it belongs to
 */
var tgIrqManager = Class.create ({
	_frequency: 500, //Time in Miliseconds until next IRQ
	_map: null,
	_interval: null,
	_allowance: 3, 
	_running: false,
	
	//Public properties
	
	//Private properties
	
	//Constructor
	initialize: function (map) {
		if (map._irqManager != null) {
			return map._irqManager;
		}
		this._map = map;
		this._setInterval (this._frequency);
	},
	
	//Public Methods
	
	//Private Methods
	_doIrq: function () {
		if (this._running) {
			return false;
		}
		this._running = true;
		var d1        = new Date();
		var startIrq  = d1.getTime();
		if (this._map.isInitialized()) {
			this._map.tileManager.update();
		}
		var d2      = new Date();
		var endIrq  = d2.getTime();
		var irqTime = endIrq-startIrq;
		if ((this._frequency / irqTime) < this._allowance) {
			this._setInterval (this._frequency * 2);
		}
		
		if ((this._frequency / irqTime) > this._allowance * 1.5 && this._frequency > 100) {
			this._setInterval (this._frequency / 2);
		}
		this._running = false;
		
	},
	
	_setInterval: function (f) {
		if (this._interval != null) {
			window.clearInterval(this._interval);
			this._frequency = f;
			
		}
		this._interval = window.setInterval (function(){
			this._doIrq ();
		}.bind(this), this._frequency);
	},
	
	destroy: function () {
		if (this._interval != null) {
			window.clearInterval(this._interval);
		}
	}

});

/**
 * Class: tgMapErrorHandler
 * Used for error handling by tgMap Class
 * 
 */
var tgMapErrorHandler = Class.create ({
	defaultOutput: 'console',
	
	throwError: function (e, output) {
		if (output == null) {
			output = this.defaultOutput;
		}
		switch (output) {
			case 'alert': //Output as Alert-Dialog;
				alert (e);
			break;
			case 'layer': //Show a DHTML Layer with the Error Message
			break;
			case 'title': //Display error message in document title
				document.title = e;
			break;
			case 'location': //Displays error message in document's URL
				top.location.href = "#"+e;	
			break;
			default: //Log to Firebug Console
				if (console != null) {
					console.error (e);
				}
			break;
		}
	}
});

/**
 * Class: tgInfoWindow
 * Class for the infoWindow. Each map object has exactly
 * one instance of this class that is instanciated in 
 * the maps constructor
 * 
 * Parameters:
 * map - <tgMap> The map object this instance belongs to
 * 
 */
var tgInfoWindow = Class.create ({
	
	_marker: null,
	_map: null,
	_div: null,
	_content: null,
	_shadow: null,
	_closeOnEvent: null,
	_positionOnEvent: null,
	_position: null,
	
	opts: {
		tipX: 0,
		tipY: 0,
		shadowUrl: '',
		classes: [],
		marker: null,
		shadowOffsetX: 0,
		shadowOffsetY: 0,
		shadowwidth: 0,
		shadowheight: 0
	},
		
	initialize: function (map) {
				
		if (map._infoWindow != null) {
			return map._infoWindow;
		}
		this._map = map;
		var div   = document.createElement("div");
		this._map.panes.floater.appendChild(div);
		
		div.style.display = "none";
		
		Element.addClassName (div, "tgInfoWindow");
		
		this._div = div;
		
		
		var div = document.createElement("div");
		this._div.appendChild(div);
		Element.addClassName (div, "tgInfoWindowContent");
		this._content = div;
		
		//Shadow
		this._shadow     = document.createElement ("img");
		this._shadow.src = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/spacer.gif";
		this._map.panes.floaterShadow.appendChild (this._shadow);
		this._shadow.style.display = "none";
	},
	
	/**
	 * Function: open
	 * Displays the infoWindow at it's defined geographical
	 * position. Pans/Moves the map to show the full window if 
	 * necessary.
	 * 
	 * lnglat - <tgLngLat> Position the InfoWindow refers to
	 */
	open: function (lnglat) {
				
		//If there is an old marker associated to this infowindow,
		//make it visible again
		if (this._marker != null) {
			this._marker.show();
		}
		
		if (this.opts.marker != null) {
			this._marker = this.opts.marker;
			this._marker.hide();
		}
		
		//Reset classes
		$w(this._div.className).each(function (c) {
			Element.removeClassName (this._div, c);
		}.bind(this));
		this.opts.classes.each(function (c) {
			Element.addClassName(this._div, c);
		}.bind(this));
		
		this.position(lnglat);
		
		this._div.style.display    = "block";
		this._shadow.style.display = "block";
		
		this._closeOnEvent = function () {
			this.close();
		}.bindAsEventListener(this);
		
		this._positionOnEvent = function () {
			this.position(this._position);
		}.bindAsEventListener(this);
		
		this.fitInMap ();
		
		this._map.observe ("drag", this._closeOnEvent);
		this._map.observe ("zoom", this._positionOnEvent);
		this._map.observe ("resize", this._positionOnEvent);
				
		
	},
	
	/**
	 * Function getEdges
	 * Determines the absolute pixel coordinates of the 
	 * infowindows edges
	 * 
	 * Returns:
	 * <object> {top, right, bottom, left}
	 */
	getEdges: function () {
		var dims = Element.getDimensions(this._div);
		dims     = Element.getDimensions(this._div);
			
		var pos = Position.cumulativeOffset(this._div);
				
		var t = pos[1];
		var r = pos[0]+dims.width;
		var b = pos[1]+dims.height;
		var l = pos[0];
		
		return ({top:t, right:r, bottom:b, left: l})
	},
	
	getMarker: function () {
		return this._marker;
	},
	
	/**
	 * Function: fitInMap
	 * Tries to pan the map so that the open infowindow is not
	 * hidden by any map controls
	 * 
	 */
	fitInMap: function() {
		var padding = 15;
		
		var maxIterations = 1;
		
		var needToTest = true;
		var iterations = 0;
		
		var panX = 0;
		var panY = 0;
		
		//Variables for the infowindow's edges position
		var ti = 0;
		var ri = 0;
		var bi = 0;
		var li = 0;
		
		//Variables for the map's edges
		var mapEdges     = this._map.getEdges();
		mapEdges.top    += padding;
		mapEdges.bottom -= padding;
		mapEdges.left   += padding;
		mapEdges.right  -= padding;
		
		var iwEdges = this.getEdges();
		
		//Calculate Dimensions by edges that have been determined anyway
		//Should be more performance effective than calling Element.getDimensions
		var iwDims = {
			width: iwEdges.right - iwEdges.left,
			height: iwEdges.bottom - iwEdges.top
		}
		
		var offendingControl = null;
		var controls         = this._map._controls;
	
		while (needToTest == true && iterations < maxIterations) {

			//Calculate current edges of the infowindow div
			//considering corrections of the last iteration
			ti = iwEdges.top+panY;
			ri = iwEdges.right+panX;
			bi = iwEdges.bottom+panY;
			li = iwEdges.left+panX;

			
			//Make sure infowindow fits in map first
			if (ti < mapEdges.top) {
				panY += mapEdges.top - ti;
				ti    = iwEdges.top+panY;
				bi    = iwEdges.bottom+panY;
			}
						
			if (bi > mapEdges.bottom) {
				panY -= bi-mapEdges.bottom;
				ti    = iwEdges.top+panY;
				bi    = iwEdges.bottom+panY;
			}
			
			if (li < mapEdges.left) {
				panX += mapEdges.left -li;
				li    = iwEdges.left+panX;
				ri    = iwEdges.right+panX;
			}
						
			if (ri > mapEdges.right) {
				panX -= ri-mapEdges.right;
				li    = iwEdges.left+panX;
				ri    = iwEdges.right+panX;
			}
						
			//Now test for collision with controls. First colliding
			//Control will be chosen for fixing
			controls.each (function (control) {
				var controlEdges = control.getEdges();
															
				if (bi < controlEdges.top) {
					return;
				}
    			if (ti > controlEdges.bottom) {
    				return;
    			}

    			if (ri < controlEdges.left) { 
    				return;
    			}
    				
    			if (li > controlEdges.right) {
    				return;
    			}
    			
    			//Try to fix collision with first offending control
    			//found. Maybe there is a better way to determine
    			//the control to fix.
				offendingControl = control;
				return;
				
			});

			
			if (offendingControl != null) {				
				
				//Check possible for possible escape directions
				var controlEdges = offendingControl.getEdges();
				
				
				//Set overlap values to very high numbers,
				//so that non fitting directions won't be chosen
				//by the min edge detection
				var escape = [Infinity, Infinity, Infinity, Infinity];
												
				if (controlEdges.top - mapEdges.top > iwDims.height+padding) {			
					//Moving up is an option
					if (bi > controlEdges.top) {
						//Bottom Edge of infowindow below top of control
						escape[0] = controlEdges.top - bi;
					}
				}				
				if (mapEdges.right - controlEdges.right > iwDims.width+padding) {
					//Moving to the right is an option
					if (li < controlEdges.right) {
						//Right Edge of infowindow is within control
						escape[1] = controlEdges.right -li; 
					}
				}
				if (mapEdges.bottom - controlEdges.bottom > iwDims.height+padding) {
					//Moving down is an option
					if (ti < controlEdges.bottom) {
						//Top Edge of infowindow is within control
						escape[2] = controlEdges.bottom - ti;
					}
				}
				
				if (controlEdges.left - mapEdges.left > iwDims.width+padding) {
					//Moving left is an option
					if (ri > controlEdges.left) {
						escape[3] = controlEdges.left - ri;
					}
				}
								
				//Calculate how far infowindow has to be move in y-direction
				//to avoid collision
				//console.log(escape);
				
				var bestvalue = Infinity;
				var bestindex = -1;
				for (var index = 0; index < 4; index++) {
					if (Math.abs(escape[index]) < Math.abs(bestvalue)) {
						//We have a new best direction, remember it
						bestindex = index;
						bestvalue = escape[index];
					}
				}

				//Apply padding
				if (bestvalue > 0) {
					bestvalue += padding;	
				} else {
					bestvalue -= padding;
				}
				
				if (bestindex % 2 == 0) {
					//Move in y-direction
					panY += bestvalue;
				} else {
					//Move in x-direction
					panX += bestvalue;
				}
												
			}
									
			iterations++;
		}
		this._map.panByPixel (panX, panY);
	},
	
	/**
	 * Function: close
	 * Makes the infoWindow disappear.
	 */
	close: function () { 
		this._content.update ("");
		this._div.style.display    = "none";
		this._shadow.style.display = "none";
		if (this._closeOnEvent != null) {		
			this._map.stopObserving ("drag", this._closeOnEvent);
			this._closeOnEvent = null;
		}
		if (this._positionOnEvent != null) {
			this._map.stopObserving ("zoom", this._positionOnEvent);
			this._map.stopObserving ("resize", this._positionOnEvent);
			this._positionOnEvent = null;
		}
		if (this._marker != null) {
			this._marker.show();
		}
		this._marker = null;
		this._map._fireEvent("infowindowclose");
	},
	
	/**
	 * Function: update
	 * Updates the content HTML of the infoWindow. Can be
	 * used for closed as well as opened infoWindows.
	 * 
	 * Parameters:
	 * html - <string> HTML to put inside the infoWindow container
	 * open - <bool> Optional. If set to true, infoWindow will open
	 * if it is currently closed. 
	 */
	update: function (html, open) {
		this._content.update(html);
		if (open == true) {
			this.open();
		}
	},
	
	/**
	 * Function: position
	 * Sets the windows coordinates
	 * 
	 * Parameters:
	 * lnglat - <tgLngLat> Geographical position of the
	 * infoWindow
	 */
	position: function (lnglat) {
		var pixel      = this._map.getPixel(lnglat);
		var mapOffsetX = parseInt(this._map._panel.style.left);
		var mapOffsetY = parseInt(this._map._panel.style.top);
		
		var dims = Element.getDimensions(this._div);
		
		var style = {
			position: 'absolute',
			left: (pixel.x-this.opts.tipX-mapOffsetX)+'px',
			top: (pixel.y-this.opts.tipY-mapOffsetY)+'px'			
		}
		this._div.setStyle(style);
		
		
		if (Prototype.Browser.IE) {
			this._shadow.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+ this.opts.shadowUrl + "', sizingMethod='scale');"
			
		} else {
			this._shadow.src = this.opts.shadowUrl;
		}
		
		style = {
			position: 'absolute',
			left: (pixel.x+this.opts.shadowOffsetX-this.opts.tipX-mapOffsetX)+'px',
			top: (this.opts.shadowOffsetY+pixel.y-this.opts.tipY-mapOffsetY)+'px',
			width: this.opts.shadowwidth+"px",
			height: this.opts.shadowheight+"px"
		}
		Element.setStyle (this._shadow, style);
	
		this._position = lnglat;
	}
	
});



/**
 * Class: tgInfoWindowOptions
 * Objects of this class can be given as options parameter to
 * the constructor of the <tgInfoWindow> class to define
 * behavior of the window.
 * 
 * This class is to be instanciated as a JSON Object
 * 
 */



/**
 * Class: tgMapControl
 * Interface class implemented by various control types to be
 * added to the map, e.G. Zoomslider. A control is fixed and 
 * does not move when the map is moved.
 */
var tgMapControl = Class.create({
	
	x: null,
	y: null,
	outerdiv: null,
	/*
	 * @var tgMap
	 */
	_map: null,
	_url: '',

	_zoomLevels: {
		1:  '0',
		2: '11',
		3: '22',
		4: '33',
		5: '44',
		6: '55',
		7: '66',
		8: '77',
		9: '88',
		10: '99',
		11: '110',
		12: '121',
		13: '132',
		14: '143',
		15: '154'
	},

	initialize: function() {
	},
	
	/**
	 * Function: setMap
	 * Sets the map object the control will work for
	 * 
	 * Parameters:
	 * map - <tgMap>
	 */
	setMap: function (map) {
		this._map = map;
	},
	
	/**
	 * Function: getMap
	 * 
	 * Returns:
	 * The <tgMap> object the control works for
	 */
	getMap: function () {
		return this._map;
	},
	
	/**
	 * Function: getEdges
	 * Calculates the absolute pixel coordinates of the control's edges
	 * 
	 * Returns: 
	 * <object> {top,right,bottom,left}
	 */
	getEdges: function () {
		var dims = Element.getDimensions (this.outerdiv);
		var pos  = Element.cumulativeOffset (this.outerdiv);
		
		var left   = pos[0];
		var right  = pos[0]+dims.width;
		var top    = pos[1];
		var bottom = pos[1]+dims.height;
		
		return {top:top, right:right, bottom:bottom, left:left}
	}
	
});

var tgCopyright = Class.create(tgMapControl, {
	defaultPosition: TG_ANCHOR_BOTTOM_LEFT,
	position: null,
	_image: null,
	_map: null,
	
	initialize: function($super, position){
		$super();
		if (position == null) {
			this.position = this.defaultPosition;
		} else {
			this.position = position;
		}
	},
	
	addToMap: function (map) {
		this._map = map;
	},
	
	draw: function() { 
		if (this._image == null) {
			var div = new Element("div");
			var img = new Element("img");
			div.appendChild(img);
			this._map.getContainer().appendChild(div);
			this.outerdiv = div;
			
			div.addClassName ("copyright");
			this._image = img;
		}
		

		// (modified 2011-Dez-20: No more eagleview or aerial)
		var fuellsel = "";
		if (this._map.getUnitsPerPixel() >= 10240) {
			fuellsel = "_nasa";
		}
		 
		
		var hostname = location.hostname;
		
		if (hostname.match(/11880/)) {
			hostname = "www.11880.com";
		} else {
			hostname = "www.klicktel.de"; // changed for mini websites, e.g. http://www.cafe-wolf.com/
		}
		
		
		this._image.src = "http://"+hostname+"/dynamic/staticmap/copyright?ts="+Math.random();
		
		this._map.changeSidebarStatus();
	}
});

/**
 * Class: tgMapType
 * Implements <tgMapControl>
 * 
 * Deprecated since december 2011. Not longer supported in Version 1.1. 
 * (Only type 'map' available)
 * 
 */
var tgMapType = Class.create(tgMapControl,{
	defaultPosition: TG_ANCHOR_TOP_RIGHT,
	position: null,
	
	initialize: function($super, position){
		$super();
		if (position == null) {
			this.position = this.defaultPosition;
		} else {
			this.position = position;
		}
	},
	
	addToMap: function (map) {
		this._map = map;
	},
	
	draw: function() { 
		this._map.setMapType("map");
	}
	
});

/**
 * Class: tgMapScale
 * Implements <tgMapControl>
 */
var tgMapScale = Class.create(tgMapControl,{
	defaultPosition: TG_ANCHOR_BOTTOM_RIGHT,
	position: null,
	_image: null,

	initialize: function($super, position){
		$super();
		if (position == null) {
			this.position = this.defaultPosition;
		} else {
			this.position = position;
		}
	},
	
	addToMap: function (map) {
		this._map = map;
	},
	
	draw: function () {  

		var cssClass = '';
		if ( this.position == TG_ANCHOR_TOP_RIGHT ) {
			cssClass = 'scaleTopRight';
		} else if ( this.position == TG_ANCHOR_TOP_LEFT ) {
			cssClass = 'scaleTopLeft';
		} else if ( this.position == TG_ANCHOR_BOTTOM_RIGHT ) {
			cssClass = 'scaleBottomRight';
		} else if ( this.position == TG_ANCHOR_BOTTOM_LEFT ) {
			cssClass = 'scaleBottomLeft';
		}
		
		this.outerdiv = new Element( 'div');
		this.outerdiv.addClassName (cssClass);
		
		var zoomLevel = this._map.getZoom();
		var imagePath = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/scale_level_"+zoomLevel+".png";
		this._image   = new Element( 'img', { 'src': imagePath } );
		
		this.outerdiv.appendChild( this._image );
		this._map.getContainer().appendChild( this.outerdiv );
		
		this._changeImage = function () {
			var zoomLevel = this._map.getZoom();
			var imagePath = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/scale_level_"+zoomLevel+".png";
			
			this._image.src = imagePath;
		}.bindAsEventListener(this);
		
		this._map.observe( "zoomIn", this._changeImage );
		this._map.observe( "zoomOut", this._changeImage );
		this._map.observe( "zoom", this._changeImage );
	}	
});

/**
 * Class: tgMapLargeNavigation
 * Implements <tgMapControl>
 */
var tgMapLargeNavigation = Class.create(tgMapControl,{
	defaultPosition: TG_ANCHOR_TOP_LEFT,
	position: null,

	_eventObserver: null,
	_sliderTrackPos: null,
	_handleZoomIn: null,
	_handleZoomOut: null,
	_buttonZoomIn: null,
	_buttonZoomOut: null,
	_sliderTrack: null,
	_sliderHandler: null,

	_handlePanMapTop: null,
	_handlePanMapBottom: null,
	_handlePanMapLeft: null,
	_handlePanMapRight: null,
	_handlePanMapHome: null,
	
	_buttonDivTop: null,
	_buttonDivBottom: null,
	_buttonDivLeft: null,
	_buttonDivRight: null,
	_buttonDivHome: null,

	initialize: function($super, position){
		$super();
		if (position == null) {
			this.position = this.defaultPosition;
		} else {
			this.position = position;
		}
	},
	
	addToMap: function (map) {
		this._map = map;
	},

	_handleEvent: function (e) {
		var x = Event.pointerX(e)*1;
		var y = Event.pointerY(e)*1;


		switch (e.type) {
			case 'mousedown':
				this._dragging = true;

				var sliderTrackPos   =  Element.cumulativeOffset(this._sliderTrack);
				this._sliderTrackPos = sliderTrackPos.top;

				if ((Event.element(e).tagName == "DIV" || Event.element(e).tagName == "IMG") &&  e.preventDefault) {
					e.preventDefault();
				}

				break;

			case 'mouseup':
				this._dragging = false;
				break;

			case 'click':
				var sliderTrackPos =  Element.cumulativeOffset(this._sliderTrack);
				var newTop         = parseInt(y-sliderTrackPos.top);

				var zoomLevel = Math.ceil(newTop/11);
				if ( zoomLevel == 0  ) {
					zoomLevel = 1;
				} else if( newTop/11 == 14 ) {
					zoomLevel = 15;
				}

				this._map.setZoom( zoomLevel );
				this._sliderHandler.style.top = this._zoomLevels[zoomLevel]+"px";

				break;

			case 'mousemove':
				if (this._dragging) {
					var newTop = parseInt(y-this._sliderTrackPos);

					if (newTop < 0) {
						newTop = 0;
					} else if (newTop > parseInt(this._sliderTrack.getStyle("height")) - parseInt(this._sliderHandler.getStyle("height"))) {
						newTop = parseInt(this._sliderTrack.getStyle("height")) - parseInt(this._sliderHandler.getStyle("height"));
					}

					var zoomLevel = Math.ceil(newTop/11);
					if ( zoomLevel == 0  ) {
						zoomLevel = 1;
					} else if( newTop/11 == 14 ) {
						zoomLevel = 15;
					}

					this._map.setZoom( zoomLevel );
					this._sliderHandler.style.top = this._zoomLevels[zoomLevel]+"px";
				} else {
					return false;
				}
				Event.stop(e);
				break;
		}
	},

	draw: function () {
		var cssClass = '';
		if ( this.position == TG_ANCHOR_TOP_RIGHT ) {
			cssClass = 'mapLargePositionButtonsTopRight';
		} else if ( this.position == TG_ANCHOR_TOP_LEFT ) {
			cssClass = 'mapLargePositionButtonsTopLeft';
		} else if ( this.position == TG_ANCHOR_BOTTOM_RIGHT ) {
			cssClass = 'mapLargePositionButtonsBottomRight';
		} else if ( this.position == TG_ANCHOR_BOTTOM_LEFT ) {
			cssClass = 'mapLargePositionButtonsBottomLeft';
		}
		
		this.outerdiv = new Element( 'div');
		this.outerdiv.addClassName(cssClass);

		/**
		 * Create the Navigation Buttons
		 */
		this._handlePanMapTop = function () {
			this._map.panByPixel(0, Math.round(this._map.getHeight()/4));return false;
		}.bindAsEventListener(this);
		
		this._buttonDivTop = new Element( 'div' ).update( 'N' );
		this._buttonDivTop.addClassName('mapLargePositionButtonTop');
		Event.observe (this._buttonDivTop, "click", this._handlePanMapTop);
		this.outerdiv.appendChild( this._buttonDivTop );

		this._handlePanMapBottom = function () {
			this._map.panByPixel(0, -Math.round(this._map.getHeight()/4));return false;
		}.bindAsEventListener(this);
		
		this._buttonDivBottom = new Element( 'div' ).update( 'S' );
		this._buttonDivBottom.addClassName('mapLargePositionButtonBottom');
		Event.observe (this._buttonDivBottom, "click", this._handlePanMapBottom);
		this.outerdiv.appendChild( this._buttonDivBottom );

		this._handlePanMapLeft = function () {
			this._map.panByPixel(Math.round(this._map.getWidth()/4), 0);return false;
		}.bindAsEventListener(this);
		
		this._buttonDivLeft = new Element( 'div' ).update( 'W' );
		this._buttonDivLeft.addClassName('mapLargePositionButtonLeft');
		Event.observe (this._buttonDivLeft, "click", this._handlePanMapLeft);
		this.outerdiv.appendChild( this._buttonDivLeft );

		this._handlePanMapRight = function () {
			this._map.panByPixel(-Math.round(this._map.getWidth()/4),0);return false;
		}.bindAsEventListener(this);
		
		this._buttonDivRight = new Element( 'div' ).update( 'E' );
		this._buttonDivRight.addClassName('mapLargePositionButtonRight');
		Event.observe (this._buttonDivRight, "click", this._handlePanMapRight);
		this.outerdiv.appendChild( this._buttonDivRight );

		this._handlePanMapHome = function () {
			this._map.returnToSavedPosition();return false;
		}.bindAsEventListener(this);
		
		this._buttonDivHome = new Element( 'div' ).update( 'H' );
		this._buttonDivHome.addClassName('mapLargePositionButtonHome');
		Event.observe (this._buttonDivHome, "click", this._handlePanMapHome);
		this.outerdiv.appendChild( this._buttonDivHome );
		
		/**
		 * Create the Zoomslider
		 */
		this.zoomsliderDiv = new Element( 'div' );
		this.zoomsliderDiv.addClassName('mapLargeZoomSliderButtons');
		
		this._handleZoomIn = function () {
			this._map.zoomIn(-this._panPerPixel,0);return false;
		}.bindAsEventListener(this);

		this._buttonZoomIn = new Element( 'div' );
		this._buttonZoomIn.addClassName('mapZoomInButton');
		Event.observe (this._buttonZoomIn, "click", this._handleZoomIn);
		this.zoomsliderDiv.appendChild( this._buttonZoomIn );

		this._handleZoomOut = function () {
			this._map.zoomOut(-this._panPerPixel,0);return false;
		}.bindAsEventListener(this);

		this._buttonZoomOut = new Element( 'div' );
		this._buttonZoomOut.addClassName('mapZoomOutButton');
		Event.observe (this._buttonZoomOut, "click", this._handleZoomOut);
		this.zoomsliderDiv.appendChild( this._buttonZoomOut );

		this._sliderTrack = new Element( 'div' );
		this._sliderTrack.addClassName('mapZoomSliderTrack');

		this._sliderHandler = new Element( 'div' );
		this._sliderHandler.addClassName('zoomSliderHandle');
		this._sliderTrack.appendChild( this._sliderHandler );

		this.zoomsliderDiv.appendChild( this._sliderTrack );

		this.outerdiv.appendChild( this.zoomsliderDiv );
		
		/*
		if (this.zoomsliderDiv.style.filter != null) {
			this.zoomsliderDiv.style.backgroundImage = "";
			this.zoomsliderDiv.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://"+getApiDomain()+"/Atlas/gateway/1.0/images/zoomSliderBG_15steps.png', sizingMethod='scale');"
		}
		*/
		
		this._map.getContainer().appendChild( this.outerdiv );

		this._eventObserver = this._handleEvent.bindAsEventListener(this);

		Event.observe (this._sliderHandler, "mousedown", this._eventObserver);
		Event.observe (this._map.getContainer(), "mousemove", this._eventObserver);
		Event.observe (this._map.getContainer(), "mouseup", this._eventObserver);
		Event.observe (this._sliderTrack, "click", this._eventObserver);

		this._setZoomLevel = function () {
			var zoom                      = this._map.getZoom();
			this._sliderHandler.style.top = this._zoomLevels[zoom]+"px";
		}.bindAsEventListener(this);

		this._map.observe( "zoomIn", this._setZoomLevel );
		this._map.observe( "zoomOut", this._setZoomLevel );
		this._map.observe( "zoom", this._setZoomLevel );

	},
	
	destroy: function () {
		// Stop the Navigation Buttons Observing
		Event.stopObserving (this._buttonDivTop, "click", this._handlePanMapTop);
		Event.stopObserving (this._buttonDivBottom, "click", this._handlePanMapBottom);
		Event.stopObserving (this._buttonDivLeft, "click", this._handlePanMapLeft);
		Event.stopObserving (this._buttonDivRight, "click", this._handlePanMapRight);
		Event.stopObserving (this._buttonDivHome, "click", this._handlePanMapHome);

		// Destroy the ZoomSlider Observing
		Event.stopObserving (this._sliderHandler, "mousedown", this._eventObserver);
		Event.stopObserving (this._map.getContainer(), "mousemove", this._eventObserver);
		Event.stopObserving (this._map.getContainer(), "mouseup", this._eventObserver);
		Event.stopObserving (this._sliderTrack, "click", this._eventObserver);
		this._map.stopObserving( "zoomIn", this._setZoomLevel );
		this._map.stopObserving( "zoomOut", this._setZoomLevel );
		this._map.stopObserving( "zoom", this._setZoomLevel );
	}
});

/**
 * Class: tgMapSmallNavigation
 * Implements <tgMapControl>
 */
var tgMapSmallNavigation = Class.create(tgMapControl,{
	defaultPosition: TG_ANCHOR_TOP_LEFT,
	position: null,

	_eventObserver: null,
	_sliderTrackPos: null,
	_handleZoomIn: null,
	_handleZoomOut: null,
	_buttonZoomIn: null,
	_buttonZoomOut: null,
	_sliderTrack: null,
	_sliderHandler: null,

	_handlePanMapHome: null,

	_mapClickControl: null,
	_buttonDivTop: null,
	_buttonDivBottom: null,
	_buttonDivLeft: null,
	_buttonDivRight: null,
	_buttonDivHome: null,
	_area: null,
	_imageMap: null,
	clickMap: null,

	initialize: function($super, position){
		$super();
		if (position == null) {
			this.position = this.defaultPosition;
		} else {
			this.position = position;
		}
	},

	addToMap: function (map) {
		this._map = map;
	},

	_handleEvent: function (e) {
		var x = Event.pointerX(e)*1;
		var y = Event.pointerY(e)*1;


		switch (e.type) {
			case 'mousedown':
				this._dragging = true;

				var sliderTrackPos   =  Element.cumulativeOffset(this._sliderTrack);
				this._sliderTrackPos = sliderTrackPos.top;

				if ((Event.element(e).tagName == "DIV" || Event.element(e).tagName == "IMG") &&  e.preventDefault) {
					e.preventDefault();
				}

				break;

			case 'mouseup':
				this._dragging = false;
				break;

			case 'click':
				var sliderTrackPos =  Element.cumulativeOffset(this._sliderTrack);
				var newTop         = parseInt(y-sliderTrackPos.top);

				var zoomLevel = Math.ceil(newTop/11);
				if ( zoomLevel == 0  ) {
					zoomLevel = 1;
				} else if( newTop/11 == 14 ) {
					zoomLevel = 15;
				}

				this._map.setZoom( zoomLevel );
				this._sliderHandler.style.top = this._zoomLevels[zoomLevel]+"px";

				break;

			case 'mousemove':
				if (this._dragging) {
					var newTop = parseInt(y-this._sliderTrackPos);

					if (newTop < 0) {
						newTop = 0;
					} else if (newTop > parseInt(this._sliderTrack.getStyle("height")) - parseInt(this._sliderHandler.getStyle("height"))) {
						newTop = parseInt(this._sliderTrack.getStyle("height")) - parseInt(this._sliderHandler.getStyle("height"));
					}

					var zoomLevel = Math.ceil(newTop/11);
					if ( zoomLevel == 0  ) {
						zoomLevel = 1;
					} else if( newTop/11 == 14 ) {
						zoomLevel = 15;
					}

					this._map.setZoom( zoomLevel );
					this._sliderHandler.style.top = this._zoomLevels[zoomLevel]+"px";
				} else {
					return false;
				}
				Event.stop(e);
				break;
		}
	},

	draw: function () {
		var cssClass = '';
		if ( this.position == TG_ANCHOR_TOP_RIGHT ) {
			cssClass = 'mapSmallPositionButtonsTopRight';
		} else if ( this.position == TG_ANCHOR_TOP_LEFT ) {
			cssClass = 'mapSmallPositionButtonsTopLeft';
		} else if ( this.position == TG_ANCHOR_BOTTOM_RIGHT ) {
			cssClass = 'mapSmallPositionButtonsBottomRight';
		} else if ( this.position == TG_ANCHOR_BOTTOM_LEFT ) {
			cssClass = 'mapSmallPositionButtonsBottomLeft';
		}

		this.outerdiv = new Element( 'div');
		this.outerdiv.addClassName (cssClass);

		/**
		 * Create the Navigation Buttons
		 */
		
		console.log(getApiDomain());
		
		this.clickMap                = new Element( 'img' );
		this.clickMap.style.position = "absolute";
		this.clickMap.style.top      = "0px";
		this.clickMap.style.left     = "0px";
		this.clickMap.style.cursor   = "pointer";
		this.outerdiv.appendChild(this.clickMap);
		this.clickMap.src = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/navigationscontrol.gif";		
		// Pan Home
		this._handlePanMapHome = function () {
			this._map.returnToSavedPosition();return false;
		}.bindAsEventListener(this);		
		this._buttonDivHome                = new Element( 'img' );
		this._buttonDivHome.src            = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/homeControl.gif";
		this._buttonDivHome.style.position = "absolute";
		this._buttonDivHome.style.top      = "14px";
		this._buttonDivHome.style.left     = "14px";
		this._buttonDivHome.style.cursor   = "pointer";
		this.outerdiv.appendChild(this._buttonDivHome);
		Event.observe(this._buttonDivHome, "click", this._handlePanMapHome);
		
		// Pan East
		this._handlePanMapEast = function () {
			var panPixel = (this._map.getWidth()/2).round();
			this._map.panByPixel(-panPixel, 0);return false;
		}.bindAsEventListener(this);		
		this._buttonDivLeft                = new Element( 'img' );
		this._buttonDivLeft.src            = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/LeftControl.gif";
		this._buttonDivLeft.style.position = "absolute";
		this._buttonDivLeft.style.top      = "14px";
		this._buttonDivLeft.style.left     = "30px";
		this._buttonDivLeft.style.cursor   = "pointer";
		this.outerdiv.appendChild(this._buttonDivLeft);
		Event.observe(this._buttonDivLeft, "click", this._handlePanMapEast);
		
		// Pan North
		this._handlePanMapNorth = function () {
			var panPixel = (this._map.getHeight()/2).round();
			this._map.panByPixel(0, panPixel);return false;
		}.bindAsEventListener(this);		
		this._buttonDivTop                = new Element( 'img' );
		this._buttonDivTop.src            = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/NorthControl.gif";
		this._buttonDivTop.style.position = "absolute";
		this._buttonDivTop.style.top      = "0px";
		this._buttonDivTop.style.left     = "14px";
		this._buttonDivTop.style.cursor   = "pointer";
		this.outerdiv.appendChild(this._buttonDivTop);
		Event.observe(this._buttonDivTop, "click", this._handlePanMapNorth);
		
		// Pan West
		this._handlePanMapWest = function () {
			var panPixel = (this._map.getWidth()/2).round();
			this._map.panByPixel(panPixel, 0);return false;
		}.bindAsEventListener(this);
		this._buttonDivRight                = new Element( 'img' );
		this._buttonDivRight.src            = "http://"+getApiDomain()+"/Atlas/gateway/1.0/images/WestControl.gif";
		this._buttonDivRight.style.position = "absolute";
		this._buttonDivRight.style.top      = "14px";
		this._buttonDivRight.style.left     = "0px";
		this._buttonDivRight.style.cursor   = "pointer";
		this.outerdiv.appendChild(this._buttonDivRight);
		Event.observe(this._buttonDivRight, "click", this._handlePanMapWest);
		
		// Pan South
		this._handlePanMapSouth = function () {
			var panPixel = (this._map.getWidth()/2).round();
			this._map.panByPixel(0, -panPixel);return false;
		}.bindAsEventListener(this);
		this._buttonDivBottom                = new Element( 'img' );
		this._buttonDivBottom.src            ="http://"+getApiDomain()+"/Atlas/gateway/1.0/images/SouthControl.gif";
		this._buttonDivBottom.style.position = "absolute";
		this._buttonDivBottom.style.top      = "30px";
		this._buttonDivBottom.style.left     = "14px";
		this._buttonDivBottom.style.cursor   = "pointer";
		this.outerdiv.appendChild(this._buttonDivBottom);
		Event.observe(this._buttonDivBottom, "click", this._handlePanMapSouth);		
		
		/**
		 * Create the Zoomslider
		 */
		this.zoomsliderDiv = new Element( 'div');
		this.zoomsliderDiv.addClassName('mapSmallZoomSliderButtons');

		this._handleZoomIn = function () {
			this._map.zoomIn(-this._panPerPixel,0);return false;
		}.bindAsEventListener(this);

		this._buttonZoomIn = new Element( 'div' );
		this._buttonZoomIn.addClassName('mapZoomInButton');
		Event.observe (this._buttonZoomIn, "click", this._handleZoomIn);
		this.zoomsliderDiv.appendChild( this._buttonZoomIn );

		this._handleZoomOut = function () {
			this._map.zoomOut(-this._panPerPixel,0);return false;
		}.bindAsEventListener(this);

		this._buttonZoomOut = new Element( 'div' );
		this._buttonZoomOut.addClassName ('mapZoomOutButton');
		Event.observe (this._buttonZoomOut, "click", this._handleZoomOut);
		this.zoomsliderDiv.appendChild( this._buttonZoomOut );

		this._sliderTrack = new Element( 'div');
		this._sliderTrack.addClassName('mapZoomSliderTrack');

		this._sliderHandler = new Element( 'div' );
		this._sliderHandler.addClassName ('zoomSliderHandle');
		this._sliderTrack.appendChild( this._sliderHandler );

		this.zoomsliderDiv.appendChild( this._sliderTrack );

		this.outerdiv.appendChild( this.zoomsliderDiv );

		this._map.getContainer().appendChild( this.outerdiv );

		this._eventObserver = this._handleEvent.bindAsEventListener(this);

		Event.observe (this._sliderHandler, "mousedown", this._eventObserver);
		Event.observe (this._map.getContainer(), "mousemove", this._eventObserver);
		Event.observe (this._map.getContainer(), "mouseup", this._eventObserver);
		Event.observe (this._sliderTrack, "click", this._eventObserver);

		this._setZoomLevel = function () {
			var zoom                      = this._map.getZoom();
			this._sliderHandler.style.top = this._zoomLevels[zoom]+"px";
		}.bindAsEventListener(this);

		this._map.observe( "zoomIn", this._setZoomLevel );
		this._map.observe( "zoomOut", this._setZoomLevel );
		this._map.observe( "zoom", this._setZoomLevel );

	},

	destroy: function () {
		// Stop the Navigation Buttons Observing
		Event.stopObserving (this._buttonDivTop, "click", this._handlePanMapTop);
		Event.stopObserving (this._buttonDivBottom, "click", this._handlePanMapBottom);
		Event.stopObserving (this._buttonDivLeft, "click", this._handlePanMapLeft);
		Event.stopObserving (this._buttonDivRight, "click", this._handlePanMapRight);
		Event.stopObserving (this._buttonDivHome, "click", this._handlePanMapHome);

		// Destroy the ZoomSlider Observing
		Event.stopObserving (this._sliderHandler, "mousedown", this._eventObserver);
		Event.stopObserving (this._map.getContainer(), "mousemove", this._eventObserver);
		Event.stopObserving (this._map.getContainer(), "mouseup", this._eventObserver);
		Event.stopObserving (this._sliderTrack, "click", this._eventObserver);
		this._map.stopObserving( "zoomIn", this._setZoomLevel );
		this._map.stopObserving( "zoomOut", this._setZoomLevel );
		this._map.stopObserving( "zoom", this._setZoomLevel );
	}
});

/**
 * Class: tgZoomSlider
 * Implements <tgMapControl>
 */
var tgZoomSlider = Class.create(tgMapControl,{
	defaultPosition: TG_ANCHOR_LEFT,
	position: null,
	
	outerdiv: null,
	_eventObserver: null,
	_sliderTrackPos: null,
	_handleZoomIn: null,
	_handleZoomOut: null,
	_buttonZoomIn: null,
	_buttonZoomOut: null,
	_sliderTrack: null,
	_sliderHandler: null,
	
	_zoomLevels: {
		1:  '0',
		2: '11',
		3: '22',
		4: '33',
		5: '44',
		6: '55',
		7: '66',
		8: '77',
		9: '88',
		10: '99',
		11: '110',
		12: '121',
		13: '132',
		14: '143',
		15: '154'
	},
	
	initialize: function($super, position){
		$super();
		if (position == null) {
			this.position = this.defaultPosition;
		} else {
			this.position = position;
		}
	},
	
	addToMap: function (map) {
		this._map = map;
	},
	
	_handleEvent: function (e) {
		var x = Event.pointerX(e)*1;
		var y = Event.pointerY(e)*1;
		
				
		switch (e.type) {
			case 'mousedown':
				this._dragging = true;
				
				var sliderTrackPos   =  Element.cumulativeOffset(this._sliderTrack);
				this._sliderTrackPos = sliderTrackPos.top;
				
				if ((Event.element(e).tagName == "DIV" || Event.element(e).tagName == "IMG") &&  e.preventDefault) {
					e.preventDefault();
				}
				
				break;
			
			case 'mouseup':
				this._dragging = false;
				break;

			case 'click':
				var sliderTrackPos =  Element.cumulativeOffset(this._sliderTrack);
				var newTop         = parseInt(y-sliderTrackPos.top);
				
				var zoomLevel = Math.ceil(newTop/11);
				if ( zoomLevel == 0  ) {
					zoomLevel = 1;
				} else if( newTop/11 == 14 ) {
					zoomLevel = 15;
				}
				
				this._map.setZoom( zoomLevel );
				this._sliderHandler.style.top = this._zoomLevels[zoomLevel]+"px";
				
				break;
				
			case 'mousemove':
				if (this._dragging) {
					var newTop = parseInt(y-this._sliderTrackPos);
					
					if (newTop < 0) {
						newTop = 0;
					} else if (newTop > parseInt(this._sliderTrack.getStyle("height")) - parseInt(this._sliderHandler.getStyle("height"))) {
						newTop = parseInt(this._sliderTrack.getStyle("height")) - parseInt(this._sliderHandler.getStyle("height"));
					}
					
					var zoomLevel = Math.ceil(newTop/11);
					if ( zoomLevel == 0  ) {
						zoomLevel = 1;
					} else if( newTop/11 == 14 ) {
						zoomLevel = 15;
					}
					
					this._map.setZoom( zoomLevel );
					this._sliderHandler.style.top = this._zoomLevels[zoomLevel]+"px";
				} else {
					return false;
				}
				Event.stop(e);
				break;
		}
	},
	
	draw: function () {
		var cssClass = '';
		if ( this.position == TG_ANCHOR_RIGHT ) {
			cssClass = 'mapZoomSliderButtonsRight';
		} else if ( this.position == TG_ANCHOR_LEFT ) {
			cssClass = 'mapZoomSliderButtonsLeft';
		}
		
		this.outerdiv = new Element( 'div' );
		this.outerdiv.addClassName(cssClass);
		
		this._handleZoomIn = function () {
			this._map.zoomIn(-this._panPerPixel,0);return false;
		}.bindAsEventListener(this);
		
		this._buttonZoomIn = new Element( 'div' );
		this._buttonZoomIn.addClassName ('mapZoomInButton');
		Event.observe (this._buttonZoomIn, "click", this._handleZoomIn);
		this.outerdiv.appendChild( this._buttonZoomIn );

		this._handleZoomOut = function () {
			this._map.zoomOut(-this._panPerPixel,0);return false;
		}.bindAsEventListener(this);
		
		this._buttonZoomOut = new Element( 'div' );
		this._buttonZoomOut.addClassName('mapZoomOutButton');
		Event.observe (this._buttonZoomOut, "click", this._handleZoomOut);
		this.outerdiv.appendChild( this._buttonZoomOut );

		this._sliderTrack = new Element( 'div' );
		this._sliderTrack.addClassName('mapZoomSliderTrack');
		
		this._sliderHandler = new Element( 'div' );
		this._sliderHandler.addClassName('zoomSliderHandle');
		this._sliderTrack.appendChild( this._sliderHandler );
		
		this.outerdiv.appendChild( this._sliderTrack );
		
		this._map.getContainer().appendChild( this.outerdiv );
		
		this._eventObserver = this._handleEvent.bindAsEventListener(this);
		
		Event.observe (this._sliderHandler, "mousedown", this._eventObserver);
		Event.observe (this._map.getContainer(), "mousemove", this._eventObserver);
		Event.observe (this._map.getContainer(), "mouseup", this._eventObserver);
		Event.observe (this._sliderTrack, "click", this._eventObserver);
		
		this._setZoomLevel = function () {
			var zoom                      = this._map.getZoom();
			this._sliderHandler.style.top = this._zoomLevels[zoom]+"px";
		}.bindAsEventListener(this);
		
		this._map.observe( "zoomIn", this._setZoomLevel );
		this._map.observe( "zoomOut", this._setZoomLevel );
		this._map.observe( "zoom", this._setZoomLevel );
	},
	
	destroy: function () {
		
		Event.stopObserving (this._sliderHandler, "mousedown", this._eventObserver);
		Event.stopObserving (this._map.getContainer(), "mousemove", this._eventObserver);
		Event.stopObserving (this._map.getContainer(), "mouseup", this._eventObserver);
		Event.stopObserving (this._sliderTrack, "click", this._eventObserver);
		
		this._map.stopObserving( "zoomIn", this._setZoomLevel );
		this._map.stopObserving( "zoomOut", this._setZoomLevel );
		this._map.stopObserving( "zoom", this._setZoomLevel );
		
	}
	
});

/**
 * Class: tgMiniMap
 * Implements <tgMapControl>
 */
var tgMiniMap = Class.create(tgMapControl,{
	defaultPosition: TG_ANCHOR_BOTTOM_RIGHT,
	position: null,
		
	_miniMap: null,
	_marker: null,
	_markerPosX: null,
	_markerPosY:null,
	
	_minmax: null,
	_minimized: false,
	_memWidth: 0,
	_memHeight: 0,
	
	_repositionMap: null,
	_eventObserver: null,
	_dragged: false,
	
	initialize: function($super, position){
		$super();
		if (position == null) {
			this.position = this.defaultPosition;
		} else {
			this.position = position;
		}
	},
	
	addToMap: function (map) {
		this._map = map;
	},
	
	_minimize: function () {
		this._memWidth  = this.outerdiv.style.width;
		this._memHeight = this.outerdiv.style.height;
		
		
		var mindims                        = Element.getDimensions(this._minmax);
		this._minmax.style.backgroundImage = "url(http://"+getApiDomain()+"/Atlas/gateway/1.0/images/show-miniMap17.png)";
		
		
		this.outerdiv.style.width  = mindims.width+"px";
		this.outerdiv.style.height = mindims.height+"px";
		this._minimized            = true;
	},
	
	_maximize: function () {
		this._minmax.style.backgroundImage = "url(http://"+getApiDomain()+"/Atlas/gateway/1.0/images/hide-miniMap17.png)";
		this.outerdiv.style.width          = this._memWidth;
		this.outerdiv.style.height         = this._memHeight;
		this._minimized                    = false;
	},
	
	_handleEvent: function (e) {
		var x = Event.pointerX(e)*1;
		var y = Event.pointerY(e)*1;
		
				
		switch (e.type) {
			case 'mousedown':
				this._dragging = true;
				
				this._markerPosX = x;
				this._markerPosY = y;
				
				if ((Event.element(e).tagName == "DIV" || Event.element(e).tagName == "IMG") &&  e.preventDefault) {
					e.preventDefault();
				}
				
				break;
			
			case 'click':
				if (this._minmax != null) {
					var x = Event.pointerX (e);
					var y = Event.pointerY (e);
					
					if (Position.within(this._minmax, x, y)) {
						if (this._minimized) {
							this._maximize();
						} else {
							this._minimize();
						}
					}
				}
				break;
							
			case 'dblclick':
				break;
							
			case 'mouseup':
				this._dragging = false;
				if (this._dragged) {
					var l = parseInt(this._marker.style.left);
					var t = parseInt(this._marker.style.top);
					var w = parseInt(this._marker.style.width);
					var h = parseInt(this._marker.style.height);
					
					var x = Math.floor(l + (w / 2));
					var y = Math.floor(t + (h / 2));
					
					
					
					this._map.setCenter(this._miniMap.getGeo(x, y));
					this._repositionMap();
					this._dragged = false;
				}
				break;
			
			case 'mousemove':
				if (this._dragging) {
					var diffX = x - this._markerPosX;
					var diffY = y - this._markerPosY;
					
					this._markerPosX += diffX;
					this._markerPosY += diffY;
					
					this._marker.style.left = (parseInt(this._marker.style.left)+diffX)+"px";	
					this._marker.style.top  = (parseInt(this._marker.style.top)+diffY)+"px";
					this._dragged           = true;
				} else {
					return false;
				}
				Event.stop(e);
				break;
		}
	},
	
	draw: function () {
		var cssClass = '';
		if ( this.position == TG_ANCHOR_TOP_RIGHT ) {
			cssClass = 'miniMapTopRight';
		} else if ( this.position == TG_ANCHOR_TOP_LEFT ) {
			cssClass = 'miniMapTopLeft';
		} else if ( this.position == TG_ANCHOR_BOTTOM_RIGHT ) {
			cssClass = 'miniMapBottomRight';
		} else if ( this.position == TG_ANCHOR_BOTTOM_LEFT ) {
			cssClass = 'miniMapBottomLeft';
		}
		
		this.outerdiv = new Element( 'div', { 'id': 'miniMapDiv'});
		this.outerdiv.addClassName(cssClass);
		this._marker = new Element( 'div' );
		this._marker.addClassName('miniMapMarker');
		this.outerdiv.appendChild( this._marker );
		
		if (this.position == TG_ANCHOR_BOTTOM_RIGHT) {
			this._minmax = new Element( 'div');
			this._minmax.addClassName ("minmax");
			this._minmax.style.cursor = "pointer";
			this.outerdiv.appendChild(this._minmax);
		}
		
		this._map.getContainer().appendChild( this.outerdiv );
		this._miniMap           = new tgMap($("miniMapDiv"));
		this._miniMap.isMiniMap = true;
		
		var zoom = this._map.getZoom() + 4;
		if (zoom > 15) {
			zoom = 15;
		}
		
		this._miniMap.setCenter(this._map.getCenter(),zoom, null, true);
		this._miniMap.disableDoubleClickZoom();
		this._miniMap.disableDragging();
		
		this._repositionMap = function () {
			this._dragging = false;
			
			var zoom = this._map.getZoom() + 4;
			if (zoom > 15 ) {
				zoom = 15;
			}
			
			this._miniMap.setCenter(this._map.getCenter(),zoom, null, true);
			
			var bounds = this._map.getBounds();
			
			var topLeft     = new tgLngLat( bounds.west, bounds.north );
			var bottomRight = new tgLngLat( bounds.east, bounds.south );
			
			var pixelTopLeft     = this._miniMap.getPixel( topLeft );
			var pixelBottomRight = this._miniMap.getPixel( bottomRight );
			
			this._marker.style.top    = pixelTopLeft.y + 'px';
			this._marker.style.left   = pixelTopLeft.x + 'px';
			this._marker.style.width  = (pixelBottomRight.x - pixelTopLeft.x) + 'px';
			this._marker.style.height = (pixelBottomRight.y - pixelTopLeft.y) + 'px';		
			
		}.bindAsEventListener(this);
		
		this._repositionMap();
		
		this._map.observe( "stopdrag", this._repositionMap );

		this._map.observe( "zoomIn", this._repositionMap );
		this._map.observe( "zoomOut", this._repositionMap );

		this._map.observe( "sidebarchanged", this._repositionMap );
		this._map.observe( "resize", this._repositionMap );
		
		this._eventObserver = this._handleEvent.bindAsEventListener(this);
		
		Event.observe (this._marker, "mousedown", this._eventObserver);
		Event.observe (this.outerdiv, "mousemove", this._eventObserver);
		Event.observe (this.outerdiv, "click", this._eventObserver);
		Event.observe (this.outerdiv, "mouseup", this._eventObserver);
		
		//Event.observe (this._map.getContainer(), "mouseup", this._repositionMap);
		
	}
});

//Helper functions
function tgBrowserIsCompatible () {
	return true;
};
 
function tgUnload () {
 	_TG_MAPS.each(function (m) {
		m.stopObserving();
	});
 	return;
}
 
 function isConformCoordinate (longitude, latitude, upp) {
		var miMmPerPixel = upp*100;
		var mX           = longitude - NMAP_WORLD_X;
		var mY           = latitude - NMAP_WORLD_Z;
		if(isConformZoomlevel(upp)){
            var RealCellSize = NMAP_CELL_SIZE*miMmPerPixel/100;
			if (mX%RealCellSize != 0) {
				console.warn ("Non-Conform Longitude: "+longitude);
			}
			if (mY%RealCellSize != 0) {
				console.warn ("Non-Conform Latitude: "+latitude);
			}
           	return mX%RealCellSize==0 && mY%RealCellSize==0;
      	}
		return false;
}

function isConformZoomlevel (upp) {
		var zl           = 125;
		var miMmPerPixel = upp*100;
		while(zl<=miMmPerPixel && zl<=4096000 ) {
			if( zl==miMmPerPixel ) {
                  return true;
            }
            
             if( zl==4096000 ) { //Maximum Zoomlevel
                  return false;
            }
            zl <<= 1;			
		}
}

function getApiDomain () {
	if (location.hostname.search('.local') != -1) {
		return ("api.klicktel.local");
	}
	
	if (location.hostname.search('.release.klickurl') != -1) {
		return ("api.release.klickurl.de");
	}
	
	if (location.hostname.search('.release.klickdev') != -1) {
		return ("api.release.klickdev.de");
	}
	
	if (location.hostname.search('.dev.klickdev') != -1) {
		return ("api.dev.klickdev.de");
	}
	
	return ("api.klicktel.de");
}
