// Global convienence variable containing the latest ETMap instance
var etmap;

/**
 * ElavToit map class
 *
 * Uses the Google maps api. If no latitude or longitude is given, center of
 * Estonia is used and zoom level default to 8.
 *
 * @param string canvasName Id of the canvas element (usually div)
 * @param bool debugMode Is debug mode enabled
 * @param float centerLatitude Map center default latitude
 * @param float centerLongitude Map center default longitude
 * @param int defaultZoom Default zoom level
 * @param array[mixed] translations Associative translations array
 */
var ETMap = function(canvasName, debugMode, centerLatitude, centerLongitude, defaultZoom, translations)
{
	this.canvasName     = canvasName;                // Canvas container name
	this.debugMode      = debugMode ? true : false;  // Debug mode is off by default
	this.canvas         = null;                      // Canvas container object
	this.map            = null;                      // Google maps object
	this.geocoder       = null;                      // Google maps geocoder object
	this.directions     = null;                      // Driving directions resolver
	this.customControls = null;                      // Custom controls object
	this.cluster        = null;                      // Markers cluster
	
	this.defaultLatitude  = centerLatitude != null  ? centerLatitude  : 58.7;
	this.defaultLongitude = centerLongitude != null ? centerLongitude : 25.5;
	this.defaultZoom      = defaultZoom != null     ? defaultZoom     : 7;
	
	this.clientLatitude  = -1;
	this.clientLongitude = -1;
	
	this.wideviewMode        = true;
	this.fullscreenMode      = false;
	this.locationSpecified   = false;
	this.cookieLocation      = false;
	this.radiusInfoDisplayed = false;
	
	this.routeFrom           = null;
	this.routeTo             = null;
	this.routeStartMarker    = null;
	this.routeStartIcon      = null;
	this.merchantIcon        = null;
	this.locationMarker      = null;
	this.locationIcon        = null;
	this.radiusOverlay       = null;
	
	this.previousRadius      = -1;
	
	this.markers             = new Array();
	this.merchantMarkers     = new Array();
	
	if(translations != null)
	{
		this.translations = translations;	
	}
	else
	{
		this.translations = this._getDefaultTranslations();	
	}
	
	etmap = this;
}

/**
 * Attemps to initialize the map
 *
 * Call this when you wish to start rendering the map
 *
 * @return bool Whether initializing the map succeeded
 * @see ETMap::error()
 */
ETMap.prototype.initialize = function()
{
	if(typeof google != 'undefined')
	{
		google.load("maps", "2", {callback: function(){ etmap._setup() }, 'locale' : 'et_EE'});
	}
	else
	{
		this.error('Loading google API failed');	
	}
}

/**
 * Called when the map has been loaded
 *
 * Overload this to add some data on the map
 */
ETMap.prototype.onLoad = function() {}

/**
 * This is called whenever a debug message is added
 *
 * Levels:
 * - 1: info
 * - 2: success
 * - 3: failure
 *
 * @param string message Debug message
 * @param int level Message level
 */
ETMap.prototype.onDebug = function(message, level) {}

/**
 * Default error handler, just alerts the message
 *
 * Override this for custom error handling
 *
 * @param string message Error message
 */
ETMap.prototype.onError = function(message)
{
	this.debug(' - '+message);
	
	alert(message);	
}

/**
 * Shows default view by zooming and panning to default values
 */
ETMap.prototype.showDefaultView = function()
{
	this.debug('! Showing default full view');
	
	etmap.map.setZoom(etmap.defaultZoom);
	
	window.setTimeout(function() {
		etmap.map.panTo(new google.maps.LatLng(etmap.defaultLatitude, etmap.defaultLongitude));
	}, 500);
}

/**
 * Updates the clustering
 *
 * Call this after adding all the markers of current view. The clusterer isn't initiated before first used
 */
ETMap.prototype.updateClustering = function()
{
	this.debug('! Clustering '+etmap.merchantMarkers.length+' markers');
	
	if(etmap.cluster != null)
	{
		etmap.cluster.removeMarkers();
		etmap.cluster.addMarkers(etmap.merchantMarkers);
	}
	else
	{
		etmap.cluster = new ClusterMarker(etmap.map, {'markers' : etmap.merchantMarkers});
		etmap.cluster.clusterMarkerTitle = etmap.translate('Click here to view a group of %count merchants');	
	}
	
	etmap.cluster.refresh(true);
}

/**
 * Toggles fullscreen mode
 */
ETMap.prototype.toggleFullscreenMode = function()
{
	if(this.fullscreenMode == true)
	{
		this.disableFullscreenMode();
	}
	else
	{
		this.enableFullscreenMode();	
	}
}

/**
 * Enables fullscreen mode
 */
ETMap.prototype.enableFullscreenMode = function()
{
	if(!this.fullscreenMode)
	{
		var previousCenter = this.map.getCenter();
		
		document.getElementById(etmap.canvasName).className = 'fullscreen';
		
		window.setTimeout(function() {
			etmap.map.checkResize();
			etmap.map.panTo(previousCenter);
		}, 500);
		
		this.fullscreenMode = true;
		
		if(this.customControls != null)
		{
			this.customControls.enteredFullscreenMode();	
		}
		
		this.debug('+ Enabled fullscreen mode');
	}
}

/**
 * Disables fullscreen mode
 */
ETMap.prototype.disableFullscreenMode = function()
{
	if(this.fullscreenMode)
	{
		var previousCenter = this.map.getCenter();
		
		document.getElementById(etmap.canvasName).className = '';
		
		window.setTimeout(function() {
			etmap.map.checkResize();
			etmap.map.panTo(previousCenter);
		}, 500);
		
		this.fullscreenMode = false;
		
		if(this.customControls != null)
		{
			this.customControls.exitedFullscreenMode();	
		}
		
		this.debug('+ Disabled fullscreen mode');
	}
}

/**
 * Returns whether fullscreen mode is active
 */
ETMap.prototype.isFullscreenMode = function()
{
	return this.fullscreenMode;	
}

/**
 * Toggles videview mode
 */
ETMap.prototype.toggleWideviewMode = function()
{
	if(this.wideviewMode == true)
	{
		this.disableWideviewMode();
	}
	else
	{
		this.enableWideviewMode();	
	}
}

/**
 * Enables wideview mode
 */
ETMap.prototype.enableWideviewMode = function()
{
	if(!this.wideviewMode)
	{
		var previousCenter = this.map.getCenter();
		
		document.getElementById(etmap.canvasName).className = 'wideview';
		
		if(document.getElementById('map_top') != null)
		{
			document.getElementById('map_top').style.width = '100%';
		}
		
		window.setTimeout(function() {
			etmap.map.checkResize();
			etmap.map.panTo(previousCenter);
		}, 500);
		
		this.wideviewMode = true;
		
		if(this.customControls != null)
		{
			this.customControls.enteredWideviewMode();	
		}
		
		this.debug('+ Enabled viewview mode');
	}
}

/**
 * Disables wideview mode
 */
ETMap.prototype.disableWideviewMode = function()
{
	if(this.wideviewMode)
	{
		var previousCenter = this.map.getCenter();
		
		document.getElementById(etmap.canvasName).className = '';
		
		if(document.getElementById('map_top') != null)
		{
			document.getElementById('map_top').style.width = '75%';
		}
		
		window.setTimeout(function() {
			etmap.map.checkResize();
			etmap.map.panTo(previousCenter);
		}, 500);
		
		this.wideviewMode = false;
		
		if(this.customControls != null)
		{
			this.customControls.exitedWideviewMode();	
		}
		
		this.debug('+ Disabled wideview mode');
	}
}

/**
 * Returns whether wideview mode is active
 */
ETMap.prototype.isWideviewMode = function()
{
	return this.wideviewMode;	
}

/**
 * Creates a marker at given coordinates with given settings
 *
 * Use this method instead of the default new google.maps.Marker(...), because the markers are stored
 * in an array so they can later be modified
 *
 * @param float latitude Marker latitude
 * @param float longitude Marker longitude
 * @param object Settings object
 */
ETMap.prototype.createMarker = function(latitude, longitude, settings)
{
	var marker = new google.maps.Marker(new google.maps.LatLng(latitude, longitude), settings);
	
	this.markers.push(marker);
	
	return marker;
}

/**
 * Adds a clickable marker that displays given html when clicked
 *
 * @param google.maps.LatLng location Marker location
 * @param string html Html to display when clicked
 */
ETMap.prototype.addMarker = function(latitude, longitude, html)
{
	var marker = this.createMarker(latitude, longitude);
	
	google.maps.Event.addListener(marker, "click", function() {
		marker.openInfoWindowHtml(html);
	});
	
	this.map.addOverlay(marker);
	
	this.debug('+ Created marker at '+latitude+' x '+longitude);
	
	return marker;
}

/**
 * Returns custom merchant icon
 *
 * @return google.maps.Icon Merchant icon
 */
ETMap.prototype.getMerchantIcon = function()
{
	if(this.merchantIcon == null)
	{
		this.merchantIcon = new google.maps.Icon(G_DEFAULT_ICON);
		this.merchantIcon.image = "http://www.elavtoit.com/images/map/marker.png";
		this.merchantIcon.shadow = "http://www.elavtoit.com/images/map/marker_shadow.png";
		this.merchantIcon.iconSize = new google.maps.Size(20, 34);
		this.merchantIcon.shadowSize = new google.maps.Size(40, 36);
		this.merchantIcon.iconAnchor = new google.maps.Point(10, 34);
		this.merchantIcon.infoWindowAnchor = new google.maps.Point(13, 15);	
	}
	
	return this.merchantIcon;
}

/**
 * Draggable marker that displays its coordinates when clicked, for testing purposes
 *
 * @param float latitude Latitude
 * @param float longitude Longitude
 * @return google.maps.Marker Created marker
 */
ETMap.prototype.createPositionMarker = function(latitude, longitude)
{
	var marker = this.createMarker(latitude, longitude, {'draggable' : true, 'title' : this.translate('Draggable position marker')});
	
	google.maps.Event.addListener(marker, "click", function() {
		marker.openInfoWindowHtml("Latitude: "+marker.getLatLng().lat()+"<br />"+
								  "Longitude: "+marker.getLatLng().lng()+"<br />");
	});
	
	this.map.addOverlay(marker);
	
	this.debug('+ Created draggable position marker at '+latitude+' x '+longitude);
	
	return marker;
}

/**
 * Adds a merchant marker on the map
 *
 * Everything exept coordinates are optional
 *
 * @param int id Merchant ID
 * @param float latitude Location latitude
 * @param float longitude Location longitude
 * @param string name Merchant name
 * @param string email Merchant email
 * @param string address Merchant address
 * @param string phone Phone number
 * @param array[string] offers Offers array
 * @return google.maps.Marker Marker object or null if no coordinates given
 */
ETMap.prototype.addMerchant = function(id, latitude, longitude, name, email, address, phone, offers, directionsEnabled)
{
	directionsEnabled = directionsEnabled ? true : false;
	
	if(latitude != null && longitude != null)
	{
		latitude = parseFloat(latitude);
		longitude = parseFloat(longitude);
		
		var marker = new google.maps.Marker(new google.maps.LatLng(latitude, longitude), {'title' : offers != null && offers.length > 0 ? this.translate('Sells')+': '+etmap.concatArray(offers, 100) : '', 'icon' : this.getMerchantIcon()});
		
		this.merchantMarkers.push(marker);
		
		google.maps.Event.addListener(marker, 'click', function() {
			marker.openInfoWindowHtml('<div class="bubble-contents">'+
									  (name != null && name.length > 0 ? '<b>'+etmap.translate('Name')+":</b> "+name+"<br />" : '')+
									  (email != null && email.length > 0 ? '<b>'+etmap.translate('Email')+":</b> "+email+"<br />" : '')+
									  (address != null && address.length > 0 ? '<b>'+etmap.translate('Address')+":</b> "+address+"<br />" : '')+
									  (phone != null && phone.length > 0 ? '<b>'+etmap.translate('Phone')+":</b> "+phone+"<br />" : '')+
									  (offers != null && offers.length > 0 ? '<br />'+'<b>'+etmap.translate('Sells')+":</b> "+etmap.concatArray(offers, 200)+"<br />" : '')+
									  '<br /><div style="width:230px; height:19px;"><a href="javascript:void(0);" onclick="etmap.displayMerchantInfo(\''+id+'\');"><img src="http://www.elavtoit.com/images/map/more_info_btn.png" style="border:0px;" alt="'+etmap.translate('Click here for more information')+'" title="'+etmap.translate('Click here for more information')+'" /></a><a id="routeicon-'+id+'" href="javascript:void(0);"><img src="http://www.elavtoit.com/images/map/directions_btn.png" style="border:0px; margin-left:10px;" alt="'+etmap.translate('Get directions to this location')+'" title="'+etmap.translate('Get directions to this location')+'" /></a></div><br />'+
									  //'<br /><a href="javascript:void(0);" onclick="etmap.displayMerchantInfo(\''+id+'\');">'+etmap.translate('Click here for more information')+'</a><br />'+
									  //(directionsEnabled ? '<img id="routeicon-'+id+'" src="images/map/route.png" class="map-route-icon" title="'+etmap.translate('Get directions to this location')+'" />' : '')+
									  '</div>');
			
			window.setTimeout(function() {
				google.maps.Event.addDomListener(document.getElementById('routeicon-'+id), 'click', function() {
					marker.closeInfoWindow();
					etmap.showDirectionsTo(latitude, longitude);
				});
			}, 500);
		});
		
		//this.debug('+ Added merchant at '+latitude+' x '+longitude);
		
		return marker;
	}
	else
	{
		this.error('Invalid merchant coordinates: '+latitude+'x'+longitude);	
	}
	
	this.debug('- At least coordinates need to be given to add a new merchant on the map');
	
	return null;
}

/**
 * Displays merchant info.. TODO
 * 
 * The info is requested using AJAX
 *
 * @param int id Merchant id
 */
ETMap.prototype.displayMerchantInfo = function(id)
{
	// TODO: Get info using ajax..
	//document.getElementById('infowindow').innerHTML = 'Kaupmehe info (ID: '+id+')<br /><a href="javascript:void(0);" onclick="etmap.hideInfopage();">Peida..</a>';
	
	var topContainer = document.getElementById('map_top');
	
	if(topContainer != null)
	{
		document.getElementById('map_top').style.display = 'block';
		document.getElementById('show_merchant').style.display = 'block';
	}
	else
	{
		this.error('Unable to display merchant info, container element "map_top" not found in DOM');	
	}
}

/**
 * Clears merchant markers
 */
ETMap.prototype.clearMerchants = function()
{
	if(this.cluster != null)
	{
		this.cluster.removeMarkers();
		
		for(key in this.merchantMarkers)
		{
			this.map.removeOverlay(this.merchantMarkers[key]);
		}
		
		this.merchantMarkers = [];
	}
}

/**
 * Hides the infopage
 */
ETMap.prototype.hideInfopage = function(id)
{
	document.getElementById('infopage').style.display = 'none';
}

/**
 * Returns user location
 *
 * If client location could be determined, that location is returned. Else
 * the default location is returned
 *
 * @return google.maps.LatLng User location
 */
ETMap.prototype.getUserLocation = function()
{
	if(this.clientLatitude != -1 && this.clientLongitude != -1)
	{
		return new google.maps.LatLng(this.clientLatitude, this.clientLongitude);	
	}
	else
	{
		return new google.maps.LatLng(this.defaultLatitude, this.defaultLongitude);	
	}
}

/**
 * Stores user location in a cookie
 *
 * @param float latitude Client latitude
 * @param float longitude Client longitude
 */
ETMap.prototype.storeUserLocation = function(latitude, longitude)
{
	setCookie('ETMap.clientLatitude', latitude, 365);
	setCookie('ETMap.clientLongitude', longitude, 365);
	
	this.clientLatitude = parseFloat(latitude);
	this.clientLongitude = parseFloat(longitude);
	
	this.cookieLocation = true;
	
	this.debug('! Stored client location to '+latitude+'x'+longitude);
}

/**
 * Shows directions between client location and given coordinates
 *
 * @param float latitude Target location latitude
 * @param float longitude Target longitude
 * @param function errorHandler Optional handler of errors, gets error message as parameter
 */
ETMap.prototype.showDirectionsTo = function(latitude, longitude, errorHandler)
{
	this.routeFrom = this.getUserLocation();
	this.routeTo = new google.maps.LatLng(latitude, longitude);
	
	this.debug('! Routing from '+this.routeFrom.lat()+'x'+this.routeFrom.lng()+' to '+this.routeTo.lat()+'x'+this.routeTo.lng());
	
	if(errorHandler == null)
	{
		errorHandler = function(error) {
			alert(etmap.translate(error));
		};
	}
	
	if(this.directions == null)
	{
		this.directions = new google.maps.Directions(this.map);
	}
	else
	{
		this.directions.clear();	
	}
	
	google.maps.Event.addListener(this.directions, 'load', function() {
		var pointCount = etmap.directions.getNumGeocodes();
		
		if(pointCount > 0)
		{
			var startMarker = etmap.directions.getMarker(0);
			var endMarker = etmap.directions.getMarker(pointCount - 1);
			
			if(startMarker != null)
			{
				// The marker is not instantly available for some reason
				window.setTimeout(function() {
					startMarker.hide();
					endMarker.hide();
					etmap.createRouteStartMarker(etmap.routeFrom.lat(), etmap.routeFrom.lng());
				}, 100);
			}
		}
	});
	
	google.maps.Event.addListener(this.directions, 'error', function() {
		if(etmap.directions.getStatus().code == G_GEO_UNKNOWN_ADDRESS)
		{
			errorHandler('No corresponding geographic location could be found for one of the specified addresses');
		}
		else if(etmap.directions.getStatus().code == G_GEO_SERVER_ERROR)
		{
			errorHandler('Directions request could not be successfully processed, yet the exact reason for the failure is not known');
		}
		else if(etmap.directions.getStatus().code == G_GEO_MISSING_QUERY)
		{
			errorHandler('Missing directions query');
		}
		else if(etmap.directions.getStatus().code == G_GEO_BAD_KEY)
		{
			errorHandler('The given key is either invalid or does not match the domain for which it was given');
		}
		else if(etmap.directions.getStatus().code == G_GEO_BAD_REQUEST)
		{
			errorHandler('A directions request could not be successfully parsed');
		}
		else if(etmap.directions.getStatus().code == G_GEO_UNKNOWN_DIRECTIONS)
		{
			errorHandler('Unable to find a route between given locations');
		}
		else
		{
			errorHandler('Unable to find a route between given locations');
		}
	});
	
	this.directions.load('from: '+this.routeFrom.lat()+','+this.routeFrom.lng()+' to: '+this.routeTo.lat()+','+this.routeTo.lng(), {'preserveViewport' : true});
}

ETMap.prototype.showDraggablePosition = function(latitude, longitude)
{
	etmap.showDraggableLocation(address, function(latitude, longitude, marker) {
		
		if(latitude != null && longitude != null)
		{
			draggableMarker = marker;
			
			if(defaultMarkerMoved)
			{
				defaultMarker.hide();
				
				marker.openInfoWindowHtml('<div class="bubble-contents">'+etmap.translate('We think your\'e here')+'<br /><br /><a href="javascript:void(0);" onclick="restoreDefaultView();">'+etmap.translate('This is not near my real location')+'</a></div>');
			
				var delay = 1000;
				
				if(previousZoom < 12)
				{
					delay += 3000;
				}
				else if(previousZoom < 15)
				{
					delay += 1500;
				}
				
				window.setTimeout(function() {
					if(window.confirm(etmap.translate('You have already chose a location on map, would you like to use the one that was determined by the address instead?')))
					{
						document.getElementById('latitude').value = latitude;
						document.getElementById('longitude').value = longitude;
					}
					else
					{
						marker.closeInfoWindow();
						marker.hide();
						defaultMarker.show();
						
						defaultMarker.openInfoWindowHtml('<div class="bubble-contents">'+etmap.translate('Please move this marker to<br />your real location')+'</div>');
						
						etmap.map.setZoom(previousZoom);
						etmap.map.panTo(previousCenter);
					}
				}, delay);
			}
			else
			{
				defaultMarker.hide();
					
				marker.openInfoWindowHtml('<div class="bubble-contents">'+etmap.translate('We think your\'e here')+'<br /><br />'+etmap.translate('You can specify the location by dragging the marker')+'<br /><br /><a href="javascript:void(0);" onclick="restoreDefaultView();">'+etmap.translate('This is not near my real location')+'</a></div>');
				
				document.getElementById('latitude').value = latitude;
				document.getElementById('longitude').value = longitude;
				
				locationGuessed = true;
			}
		}
		else
		{
			if(!defaultMarker.isHidden() && !defaultMarkerMoved)
			{
				defaultMarker.openInfoWindowHtml('<div class="bubble-contents">'+etmap.translate('We were unable to determine your location<br />based on the address, please move this<br />marker to your real location by hand')+'</div>');
			}
			else
			{
				if(draggableMarker != null)
				{
					draggableMarker.show();
				}
			}
			// If user enters invalid address
			restoreDefaultView();
			defaultMarker.openInfoWindowHtml('<div class="bubble-contents">'+etmap.translate('We were unable to determine your location<br />based on the address, please move this<br />marker to your real location by hand')+'</div>');
		}
	}, 10);
}

/**
 * Creates draggable custom route start marker at given coordinates
 *
 * When movin the marker, the route is automaticly updated
 *
 * @param float latitude Marker latitude
 * @param float longitude Marker longitude
 * @return google.maps.Marker Route start marker
 */
ETMap.prototype.createRouteStartMarker = function(latitude, longitude)
{
	if(this.directions == null)
	{
		return null;
	}
	
	if(this.routeStartMarker == null)
	{
		this.routeStartMarker = this.createMarker(latitude, longitude, {'draggable' : true, 'icon' : this.getRouteStartIcon(), 'title' : this.translate('Your location')});
		this.map.addOverlay(this.routeStartMarker);
		
		if(this.cookieLocation)
		{
			this.routeStartMarker.openInfoWindowHtml('<div class="bubble-contents">'+this.translate('Your location<br /><br />You can specify it by dragging the<br />house icon to your actual location<br /><br />The route will be updated automatically.')+'<br /><br />'+this.translate('Distance')+': '+this.directions.getDistance()['html']+'<div style="height:18px; line-height:18px; margin-top:-18px; text-align:right;"><a href="javascript:void(0);" onclick="etmap.clearRoute();">'+this.translate('Clear route')+'</a></div></div>');
		}
		else
		{
			this.routeStartMarker.openInfoWindowHtml('<div class="bubble-contents">'+(this.clientLatitude != -1 && this.clientLongitude != -1 ? this.translate('We believe you are located in this area.')+'<br /><br />' : '')+this.translate('Please drag this marker to your real location.<br /><br />The route will be updated automatically.')+'<br /><br />'+this.translate('Distance')+': '+this.directions.getDistance()['html']+'<div style="height:18px; line-height:18px; margin-top:-18px; text-align:right;"><a href="javascript:void(0);" onclick="etmap.clearRoute();">'+this.translate('Clear route')+'</a></div></div>');
		}
		
		google.maps.Event.addListener(this.routeStartMarker, 'dragstart', function(location) {
			etmap.routeStartMarker.closeInfoWindow();
		});
		
		google.maps.Event.addListener(this.routeStartMarker, 'dragend', function(location) {
			etmap.locationSpecified = true;
			etmap.routeFrom = location;
			etmap.clientLatitude = location.lat();
			etmap.clientLongitude = location.lng();
			etmap.storeUserLocation(location.lat(), location.lng());
			etmap.directions.load('from: '+etmap.routeFrom.lat()+','+etmap.routeFrom.lng()+' to: '+etmap.routeTo.lat()+','+etmap.routeTo.lng(), {'preserveViewport' : true});
		});
	}
	else
	{
		if(this.routeStartMarker.isHidden())
		{
			this.routeStartMarker.show();	
		}
		
		if(this.locationSpecified)
		{
			this.routeStartMarker.openInfoWindowHtml('<div class="bubble-contents">'+this.translate('Optimal route from your<br />location to selected merchant.')+'<br /><br />'+this.translate('Distance')+': '+this.directions.getDistance()['html']+'<div style="height:18px; line-height:18px; margin-top:-18px; text-align:right;"><a href="javascript:void(0);" onclick="etmap.clearRoute();">'+this.translate('Clear route')+'</a></div></div>');
		}
		else
		{
			this.routeStartMarker.openInfoWindowHtml('<div class="bubble-contents">'+(this.clientLatitude != -1 && this.clientLongitude != -1 ? this.translate('We believe you are located in this area.')+'<br /><br />' : '')+this.translate('Please drag this marker to your real location.<br /><br />The route will be updated automatically.')+'<br /><br />'+this.translate('Distance')+': '+this.directions.getDistance()['html']+'<div style="height:18px; line-height:18px; margin-top:-18px; text-align:right;"><a href="javascript:void(0);" onclick="etmap.clearRoute();">'+this.translate('Clear route')+'</a></div></div>');
		}
		
		this.routeStartMarker.setLatLng(new google.maps.LatLng(latitude, longitude));
	}
	
	google.maps.Event.addListener(this.routeStartMarker, 'click', function() {
		etmap.routeStartMarker.openInfoWindowHtml('<div class="bubble-contents">'+etmap.translate('Optimal route from your<br />location to selected merchant.')+'<br /><br />'+etmap.translate('Distance')+': '+etmap.directions.getDistance()['html']+'<div style="height:18px; line-height:18px; margin-top:-18px; text-align:right;"><a href="javascript:void(0);" onclick="etmap.clearRoute();">'+etmap.translate('Clear route')+'</a></div></div>');
	});
	
	return this.routeStartMarker;
}

/**
 * Returns custom icon for route start marker
 *
 * @return google.maps.Icon Custom icon representing route start
 */
ETMap.prototype.getRouteStartIcon = function()
{
	if(this.routeStartIcon == null)
	{
		this.routeStartIcon = new google.maps.Icon(G_DEFAULT_ICON);
		this.routeStartIcon.image = "http://www.elavtoit.com/images/map/route_start_icon.png";
		this.routeStartIcon.shadow = "http://www.elavtoit.com/images/map/route_start_icon_shadow.png";
		this.routeStartIcon.imageMap = [0,10, 20,10, 20,34, 0,34];
		this.routeStartIcon.infoWindowAnchor = new google.maps.Point(9, 15);
		this.routeStartIcon.iconAnchor = new google.maps.Point(9, 28);
	}
	
	return this.routeStartIcon;
}


/**
 * Returns custom icon for user location marker
 *
 * @return google.maps.Icon Custom icon representing user location
 */
ETMap.prototype.getUserLocationIcon = function()
{
	if(this.locationIcon == null)
	{
		this.locationIcon = new google.maps.Icon(G_DEFAULT_ICON);
		this.locationIcon.image = "http://www.elavtoit.com/images/map/user_location_icon.png";
		this.locationIcon.shadow = "http://www.elavtoit.com/images/map/user_location_icon_shadow.png";
		this.locationIcon.imageMap = [0,10, 20,10, 20,34, 0,34];
		this.locationIcon.infoWindowAnchor = new google.maps.Point(9, 15);
		this.locationIcon.iconAnchor = new google.maps.Point(9, 28);
	}
	
	return this.locationIcon;
}

/**
 * Clears created route
 *
 * Also hides the infowindow and start marker
 */
ETMap.prototype.clearRoute = function()
{
	if(this.directions != null)
	{
		this.directions.clear();
		this.routeStartMarker.closeInfoWindow();
		this.routeStartMarker.hide();
	}
}

/**
 * Attempts to find coordinates of given address
 *
 * When the request is complete, the given callback is called with the latitude and longitude.
 * If the request fails, the callback is called with nulls as arguments.
 *
 * @param string address Address to locate
 * @param function Callback callback function with attributes latitude and longitude
 */
ETMap.prototype.locateAddress = function(address, callback)
{
	this.debug('! Locating address "'+address+'"');
	
	if(callback == null)
	{
		this.debug('- no location callback for address location given, terminating request');
		
		return false;
	}
	
	if(this.geocoder != null)
	{
		this.geocoder.getLatLng(address, function(location) {
			if(location != null)
			{
				etmap.debug('+ Found the address at '+location.lat()+' x '+location.lng());
				callback(location.lat(), location.lng());
			}
			else
			{
				etmap.debug('- Unable to find address "'+address+'"');
				callback(null, null);
			}
		});
		
		return true;
	}
	else
	{
		this.debug('- No geocoder available for locating address');
	}
	
	callback(null, null);
	
	return false;
}

/**
 * Creates a draggable marker at given coordinates with position change callback
 *
 * The callback gets parameters latitude, longitude and the marker and is called
 * when the marker is initially placed and every time it is dragged to a new location.
 *
 * @param float latitude Latitude coordinate
 * @param float longitude Longitude coordinate
 * @param function positionChangeCallback Called when marker position changes with latitude, longitude and marker object
 * @param object preferences Marker options
 */
ETMap.prototype.createDraggableMarker = function(latitude, longitude, positionChangeCallback, preferences)
{
	if(preferences == null)
	{
		preferences = {'draggable' : true};
	}
	else
	{
		preferences['draggable'] = true;	
	}
	
	var marker = this.createMarker(latitude, longitude, preferences);
	this.map.addOverlay(marker);
	
	google.maps.Event.addListener(marker, 'dragend', function(location) {
		if(positionChangeCallback != null)
		{
			positionChangeCallback(location.lat(), location.lng(), marker);
		}
	});
	
	return marker;
}

/**
 * Shows a draggable location marker initially positioned at given address
 *
 * The callback gets parameters latitude, longitude and the marker and is called
 * when the marker is initially placed and every time it is dragged to a new location.
 * If the address is not found, the callback is still called but with latitude and
 * longitude set to null
 *
 * @param string address Address to show
 * @param function positionChangeCallback Called when marker position changes with latitude, longitude and marker object
 * @param integer zoomLevel Zoom level to show
 */
ETMap.prototype.showDraggableLocation = function(address, positionChangeCallback, zoomLevel)
{
	zoomLevel = zoomLevel != null ? zoomLevel : 15;
	
	this.debug('! Trying to show location of address "'+address+'"');
	
	this.locateAddress(address, function(latitude, longitude) {
		if(latitude == null || longitude == null)
		{
			etmap.debug('- Address "' + address + '" could not be found');
			
			if(positionChangeCallback != null)
			{
				positionChangeCallback(null, null, marker);
			}
        }
		else
		{
			etmap.debug('+ Found address, showing marker');
			
        	etmap.map.panTo(new google.maps.LatLng(latitude, longitude));
			
			window.setTimeout(function() {
					etmap.map.setZoom(zoomLevel);
			}, 1000);
			
			var marker = etmap.createMarker(latitude, longitude, {'draggable' : true});
        	etmap.map.addOverlay(marker);
			
			google.maps.Event.addListener(marker, 'dragend', function(location) {
				if(positionChangeCallback != null)
				{
					positionChangeCallback(location.lat(), location.lng(), marker);
				}
			});
			
			if(positionChangeCallback != null)
			{
				positionChangeCallback(latitude, longitude, marker);
			}
        }
	});
}

/**
 * Displays user location with the ability yo move it around
 *
 * Help text is displayed once per session and differs when the user has laready once set
 * his position (saved in cookies) and if the location was guessed.
 */
ETMap.prototype.checkUserLocation = function()
{
	this.debug('! Checking user location');
	
	if(this.locationMarker != null)
	{
		this.map.removeOverlay(this.locationMarker);
		this.locationMarker = null;
	}
	
	this.locationMarker = this.createDraggableMarker(this.clientLatitude, this.clientLongitude, function(latitude, longitude, marker) {
		etmap.storeUserLocation(latitude, longitude);
		marker.closeInfoWindow();
		if(etmap.previousRadius > 0)
		{
			etmap.updateRadiusOverlay(etmap.clientLatitude, etmap.clientLongitude, etmap.previousRadius);	
		}
	}, {'icon' : this.getUserLocationIcon(), 'title' : this.translate('Your location, click here to focus')});
	
	google.maps.Event.addListener(this.locationMarker, 'click', function(location) {
		
		if(etmap.radiusOverlay != null)
		{
			etmap.zoomPanRadiusView(etmap.clientLatitude, etmap.clientLongitude, etmap.previousRadius);
		}
	});
	
	/* Use this instead of the next drag listener if drawing the circle again so often is too slow
	google.maps.Event.addListener(this.locationMarker, 'dragstart', function(location) {
		etmap.removeRadiusOverlay();
	});
	*/
	
	google.maps.Event.addListener(this.locationMarker, 'drag', function(location) {
		
		if(etmap.radiusOverlay != null)
		{
			etmap.radiusOverlay.setCenter(location);
		}
	});
	
	if(!this.radiusInfoDisplayed)
	{
		if(this.cookieLocation)
		{
			this.locationMarker.openInfoWindowHtml('<div class="bubble-contents">'+this.translate('You can move this marker<br />to specify your location<br /><br />This is used for the radius search.')+'</div>');
			this.radiusInfoDisplayed = true;
			this.map.panTo(new google.maps.LatLng(this.clientLatitude, this.clientLongitude));
		}
		else
		{
			this.locationMarker.openInfoWindowHtml('<div class="bubble-contents">'+this.translate('Please move this marker<br />to your current location<br /><br />This is used for the radius search.')+'</div>');	
			this.radiusInfoDisplayed = true;
		}
	}
}

/**
 * Updates the radius of the radius circle overlay
 *
 * Also pans the map to user coordinates and zoom to appopriate level. If the
 * optional input object is given, the value of is is reset if invalid radius is given.
 *
 * @param int radius New radius
 * @param object Optional input object
 */
ETMap.prototype.updateRadius = function(radius, inputObj)
{
	var radius = parseInt(radius);
	
	this.debug('! Radius changed: '+radius);
	
	if(!isNaN(radius))
	{
		if(radius > 0)
		{
			if(radius != this.previousRadius)
			{
				this.updateRadiusOverlay(this.clientLatitude, this.clientLongitude, radius);
				
				this.zoomPanRadiusView(this.clientLatitude, this.clientLongitude, radius);
			}
		}
		else
		{
			this.removeRadiusOverlay();	
			this.previousRadius = -1;
		}
	}
	else
	{
		this.removeRadiusOverlay();
		this.previousRadius = -1;
		
		if(inputObj != null && inputObj.value != null)
		{
			inputObj.value = '';
		}
	}
}

/**
 * Redraws the radius overlay at given coordinates at given coordinates and radius
 *
 * Creates the overlay if does not already exist
 *
 * @param float latitude New latitude
 * @param float longitude New longitude
 * @param int radius New radius
 * @param string strokeColor Line stroke color in html-style hex
 * @param int strokeWidth Stroke width
 * @param float strokeOpacity Stroke opacity in range 0..1, one beign entirely opaque
 * @param string fillColor Fill color as html-style hex
 * @param float fillOpacity Fill opacity in range 0..1
 * @param int numVertices How many vertices to use to make up the circle
 */
ETMap.prototype.updateRadiusOverlay = function(latitude, longitude, radius, strokeColor, strokeWidth, strokeOpacity, fillColor, fillOpacity, numVertices)
{
	radius = parseInt(radius);
    strokeColor   = strokeColor   || '#990000';
    strokeWidth   = strokeWidth   || 2;
    strokeOpacity = strokeOpacity || 0.75;
    fillColor     = fillColor     || '#990000';
    fillOpacity   = fillOpacity   || 0.1;
	numVertices   = numVertices   || 64;
	
	if(radius > 0)
	{
		if(this.radiusOverlay == null)
		{
			this.radiusOverlay = new CircleOverlay(new google.maps.LatLng(parseFloat(latitude), parseFloat(longitude)), radius, strokeColor, strokeWidth, strokeOpacity, fillColor, fillOpacity, numVertices);
			this.map.addOverlay(this.radiusOverlay);
			
			this.debug('! Create new radius overlay with radius: '+radius);
		}
		else
		{
			this.radiusOverlay.setCenter(new google.maps.LatLng(latitude, longitude), false);
			this.radiusOverlay.setRadius(radius);
			
			this.debug('! Updated radius overlay location and radius');
		}
		
		this.previousRadius = radius;
	}
	else
	{
		this.removeRadiusOverlay();
		this.previousRadius = -1;
	}
}

/**
 * Zooms and pans to best display circle area
 *
 * @param float latitude Location latitude
 * @param float longitude Location longitude
 * @param int radius Circle radius
 */
ETMap.prototype.zoomPanRadiusView = function(latitude, longitude, radius)
{
	var zoomLevel = 6;
				
	if(radius < 4)
	{
		zoomLevel = 15 - radius;	
	}
	else if(radius < 6)
	{
		zoomLevel = 12;	
	}
	else if(radius < 11)
	{
		zoomLevel = 11;	
	}
	else if(radius < 21)
	{
		zoomLevel = 10;	
	}
	else if(radius < 41)
	{
		zoomLevel = 9;	
	}
	else if(radius < 81)
	{
		zoomLevel = 8;	
	}
	else if(radius < 161)
	{
		zoomLevel = 7	
	}
	else
	{
		zoomLevel = 6;	
	}
	
	this.map.panTo(new google.maps.LatLng(latitude, longitude));
	this.map.setZoom(zoomLevel);
	this.map.panTo(new google.maps.LatLng(latitude, longitude));
}

/**
 * Removes the radius overlay
 *
 * Its safe to call this even if there is no overlay created at the time.
 */
ETMap.prototype.removeRadiusOverlay = function()
{
	if(this.radiusOverlay != null)
	{
		this.debug('! Remove existing radius overlay');
		
		this.map.removeOverlay(this.radiusOverlay);
		this.radiusOverlay = null;
	}	
}

/**
 * Concatenates an array limiting the resulting length
 *
 * As elements are added, if the length exceeds maxLength, no more elements
 * are added and three dots are postfixed
 *
 * @param array[string] Array to concatenate
 * @param maxLength Maximum resulting string length
 */
ETMap.prototype.concatArray = function(array, maxLength)
{
	string = '';
	
	for(var i = 0; i < array.length; i++)
	{
		if(maxLength == null || string.length + array[i].length + 5 <= maxLength)
		{
			if(string != '')
			{
				string += ', ';
			}
			
			string += array[i];
		}
		else
		{
			string += '...';
			
			break;
		}
	}
	
	return string;
}

/**
 * Enabled ETMapControl controls
 */
ETMap.prototype.enableCustomControls = function(widepageEnabled, printEnabled)
{
	this.customControls = new ETMapControl(this, 'etcontrol-', widepageEnabled, printEnabled);
	
	this.map.addControl(this.customControls);
}

/**
 * Adds small map control for zoom and pan
 */
ETMap.prototype.enableSmallMapControl = function()
{
	this.map.addControl(new google.maps.SmallMapControl());
}

/**
 * Adds large map control for zoom and pan
 */
ETMap.prototype.enableBigMapControl = function()
{
	var pos = new google.maps.ControlPosition(G_ANCHOR_TOP_LEFT, new google.maps.Size(10, 30));
	
	this.map.addControl(new google.maps.LargeMapControl3D(), pos);
}

/**
 * Adds map type control
 */
ETMap.prototype.enableMapTypeControl = function()
{
	this.map.addControl(new google.maps.MapTypeControl());
}

/**
 * Adds any control supported by Google maps
 */
ETMap.prototype.addControl = function(control)
{
	this.map.addControl(control);
}

/**
 * Map error handler
 *
 * Just alerts the error by default. Enables sprintf syntax, eg:
 *   map.error('Address "%s" not found', 'Foo, Bar 24');
 * Easy to add translating capability
 *
 * @param str Error message
 * @param ... Printf-style parameters
 * @return boolean Always returns false
 */
ETMap.prototype.error = function(str)
{
	if(!arguments || arguments.length < 1 || !RegExp)
	{
		return;
	}
	
	var re = /([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X)(.*)/;
	var a = b = [], numSubstitutions = 0, numMatches = 0;
	
	while(a = re.exec(str))
	{
		var leftpart = a[1], pPad = a[2], pJustify = a[3], pMinLength = a[4];
		var pPrecision = a[5], pType = a[6], rightPart = a[7];
		numMatches++;
		if (pType == '%')
		{
			subst = '%';
		}
		else
		{
			numSubstitutions++;
			
			if (numSubstitutions >= arguments.length)
			{
				alert('Error! Not enough function arguments (' + (arguments.length - 1) + ', excluding the string)\nfor the number of substitution parameters in string (' + numSubstitutions + ' so far).');
			}
			
			var param = arguments[numSubstitutions];
			var pad = '';
			if (pPad && pPad.substr(0,1) == "'") pad = leftpart.substr(1,1);
			else if (pPad) pad = pPad;
			var justifyRight = true;
			if (pJustify && pJustify === "-") justifyRight = false;
			var minLength = -1;
			if (pMinLength) minLength = parseInt(pMinLength);
			var precision = -1;
			if (pPrecision && pType == 'f') precision = parseInt(pPrecision.substring(1));
			var subst = param;
			if (pType == 'b') subst = parseInt(param).toString(2);
			else if (pType == 'c') subst = String.fromCharCode(parseInt(param));
			else if (pType == 'd') subst = parseInt(param) ? parseInt(param) : 0;
			else if (pType == 'u') subst = Math.abs(param);
			else if (pType == 'f') subst = (precision > -1) ? Math.round(parseFloat(param) * Math.pow(10, precision)) / Math.pow(10, precision): parseFloat(param);
			else if (pType == 'o') subst = parseInt(param).toString(8);
			else if (pType == 's') subst = param;
			else if (pType == 'x') subst = ('' + parseInt(param).toString(16)).toLowerCase();
			else if (pType == 'X') subst = ('' + parseInt(param).toString(16)).toUpperCase();
		}
		
		str = leftpart + subst + rightPart;
	}
	
	this.onError(str);
	
	return str;
}

/**
 * Adds a new translation
 *
 * @param string phrase Phrase to translate
 * @param string translation Phrase translation
 */
ETMap.prototype.addTranslation = function(phrase, translation)
{
	this.translations[phrase] = translation;	
}

/**
 * Returns whether translation for given phrase exists
 *
 * @param string phrase Phrase to check
 * @param boolean Does the phrase exist
 */
ETMap.prototype.translationExists = function(phrase)
{
	return this.translations[phrase] != null;
}

/**
 * Translates a phrase if possible, else returns the phrase unchanged
 *
 * @param String phrase Phrase to translate
 */
ETMap.prototype.translate = function(phrase)
{
	if(this.translations[phrase] != null)
	{
		return this.translations[phrase];
	}
	
	this.debug('- Translation for "'+phrase+'" could not be found');
	
	return phrase;
}

/**
 * Adds a debug message to the debug interface if debug mode is enabled
 *
 * Prefix with "-" for error, "+" for successful action and "!" for info, these are colored differently
 *
 * @param string txt Debug text
 */
ETMap.prototype.debug = function(txt)
{
	if(txt.length > 0)
	{
		if(this.debugMode)
		{
			debugElement = document.getElementById('debug');
			
			if(txt.substr(0,1) == '+')
			{
				txt = '<span style="color:#009900;">'+txt+'</span>';
			}
			else if(txt.substr(0,1) == '-')
			{
				txt = '<span style="color:#990000;">'+txt+'</span>';
			}
			
			if(debugElement != null)
			{   
				debugElement.innerHTML += txt+'<br />';
				
				currentScroll = debugElement.scrollTop;
				debugElement.scrollTop = currentScroll+500;
			}
			else
			{
				alert(txt);
			}
		}
		
		if(txt.substr(0,1) == '+')
		{
			this.onDebug(txt, 2);
		}
		else if(txt.substr(0,1) == '-')
		{
			this.onDebug(txt, 3);
		}
		else
		{
			this.onDebug(txt, 1);	
		}
	}
	
	return true;	
}

/**
 * Actually sets up the map
 *
 * Called when the map has been loaded using ajax
 */
ETMap.prototype._setup = function()
{
	this.canvas = document.getElementById(this.canvasName);
	
	if(this.canvas == null)
	{
		this.error('Map canvas element called "%s" not found', this.canvasName);
		
		return false;
	}
	
	if(!GBrowserIsCompatible())
	{
		this.error('Browser is not Google maps compatible');
		
		return false;
	}
	
	this.map = new google.maps.Map2(this.canvas);
	this.map.setCenter(new google.maps.LatLng(this.defaultLatitude, this.defaultLongitude), this.defaultZoom);
	//this.map.enableContinuousZoom();
	this.map.enableScrollWheelZoom();
	
	if(this.debugMode)
	{
		this._initDebugger();	
	}
	
	this.geocoder = new google.maps.ClientGeocoder();
	
	var cookieLatitude = parseFloat(getCookie('ETMap.clientLatitude', -1));
	var cookieLongitude = parseFloat(getCookie('ETMap.clientLongitude', -1));
	
	if(cookieLatitude >= 0 && cookieLongitude >= 0)
	{
		this.clientLatitude = parseFloat(cookieLatitude);
		this.clientLongitude = parseFloat(cookieLongitude);
		
		this.cookieLocation = true;
		
		this.debug('+ Restored client location from cookie');
	}
	else
	{
		if(google.loader.ClientLocation)
		{
			this.clientLatitude = google.loader.ClientLocation.latitude;
			this.clientLongitude = google.loader.ClientLocation.longitude;
			
			this.debug('+ Found client approximate location');
		}
		else
		{
			this.debug('- Could not determine client location');
		}
	}
	
	this.debug('+ Initialised ETMap');
	
	this.onLoad();
	
	this.map.checkResize();
	
	return true;
}

/**
 * Initiates debugger interface, registers event listeners
 */
ETMap.prototype._initDebugger = function()
{
	var debugDiv = document.getElementById('debug')
	
	if(debugDiv != null)
	{
		debugDiv.style.display = 'block';	
		/*
		google.maps.Event.addListener(this.map, 'click', function(overlay, location, overlayLocation) {
			if(location != null)
			{
				etmap.debug('! Map was clicked at '+location.lat()+" x "+location.lng());
			}
			
			if(overlay != null)
			{
				etmap.debug(' > also hit an overlay at '+overlayLocation.lat()+" x "+overlayLocation.lng());
			}
		});
		
		google.maps.Event.addListener(this.map, 'dblclick', function(overlay, location) {
			etmap.debug('! Double-click at '+location.lat()+" x "+location.lng());
			
			if(overlay != null)
			{
				etmap.debug(' > also hit an overlay');
			}
		});
		
		google.maps.Event.addListener(this.map, 'singlerightclick', function(point, element, overlay) {
			etmap.debug('! Right-click at '+point.x+" x "+point.y);
			
			if(overlay != null)
			{
				etmap.debug(' > also hit an overlay');
			}
		});
		
		google.maps.Event.addListener(this.map, 'movestart', function() {
			etmap.debug('! Started move at '+etmap.map.getCenter().lat()+" x  "+etmap.map.getCenter().lng());
		});
		
		google.maps.Event.addListener(this.map, 'moveend', function() {
			etmap.debug('! Ended move at '+etmap.map.getCenter().lat()+" x  "+etmap.map.getCenter().lng());
		});
		
		google.maps.Event.addListener(this.map, 'dragstart', function() {
			etmap.debug('! Started drag at '+etmap.map.getCenter().lat()+" x  "+etmap.map.getCenter().lng());
		});
		
		google.maps.Event.addListener(this.map, 'dragend', function() {
			etmap.debug('! Ended drag at '+etmap.map.getCenter().lat()+" x  "+etmap.map.getCenter().lng());
		});
		
		google.maps.Event.addListener(this.map, 'zoomend', function(oldLevel, newLevel) {
			etmap.debug('! Zoomed from '+oldLevel+" to "+newLevel);
		});

		google.maps.Event.addListener(this.map, 'zoomend', function(oldLevel, newLevel) {
			etmap.debug('! Zoomed from '+oldLevel+" to "+newLevel);
		});
		*/
		
		this.debug('+ Debugging interface initiated');
	}
}

/**
 * Returns map component default translations
 *
 * You can provide your own tranlations in the contructor
 */
ETMap.prototype._getDefaultTranslations = function()
{
	return {'Name' : 'Nimi',
			'Email' : 'Email',
			'Phone' : 'Telefon',
			'Address' : 'Aadress',
			'Sells' : 'Müüb',
			'Click here for more information' : 'Kliki siia, et saada rohkem infot',
			'Please move this marker to your<br />location or enter your address first so we can<br />try to guess your location and you can<br />further correct it later' : 'Sisestage oma aadress, et<br />saaksime üritada Teie asukohta arvata.<br /><br />Seda markerit nihutades saate<br />oma asukohta täpsustada.',
			'We think your\'e here' : 'Arvame, et asute siin',
			'Location of' : 'Adressi asukoht kaardil',
			'You can specify the location by dragging the marker' : 'Oma asukoha täpsustamiseks saate<br />seda markerit nihutada',
			'You have already chose a location on map, would you like to use the one that was determined by the address instead?' : 'Olete juba valinud asukoha kaardil. Kas soovite kasutada meie väljapakutud asukohta? Valides OK jääb asukohaks hetkel kaardil kuvatav punkt, mida saate veel täpsustada. Valides Cancel, kasutatakse viimati valitud asukohta.',
			'We were unable to determine your location<br />based on the address, please move this<br />marker to your real location by hand' : 'Meil ei õnnestunud teie asukohta<br />aadressi järgi kindlaks teha,<br />palun määärake see käsitsi.',
			'This is not near my real location' : 'See pole minu tegeliku asukoha läheduses',
			'Please move this marker to<br />your real location' : 'Palun liigutage see marker<br />oma tõelise asukoha juurde',
			'Standard' : 'Tavaline kaart',
			'OpenStreetMap' : 'OpenStreetMap',
			'Maaamet kaart' : 'Maaamet kaart',			
			'Maaamet aerofotod' : 'Maaamet aerofotod',			
			'Maaamet fotokaart' : 'Maaamet fotokaart',			
			'Satellite' : 'Satelliidikaart',
			'Orto' : 'Ortokaart',
			'Hybrid' : 'Hübriidkaart',
			'Print the map' : 'Prindi kaart',
			'Restore normal view' : 'Taasta tavaline vaade',
			'View in full width' : 'Kuva täislaiuses',
			'Get directions to this location' : 'Sõidujuhised antud asukohta',
			'No corresponding geographic location could be found for one of the specified addresses' : 'Valitud aadressidele vastavaid geograafilisi asukohti ei leitud',
			'Directions request could not be successfully processed, yet the exact reason for the failure is not known' : 'Marsuudi loomine ebaõnnestus serveripoolse vea tõttu',
			'Missing directions query' : 'Marsuudi loomine ebaõnnestus tühja päringu tõttu',
			'The given key is either invalid or does not match the domain for which it was given' : 'Marsuudi koostamine ebaõnnestus tehnilistel põhjustel',
			'Unable to find a route between given locations' : 'Valitud punktide vahel marsuudi koostamine ebaõnnestus.\nTäpsustage enda asukohta ja proovige uuesti',
			'Please drag this marker to your real location.<br /><br />The route will be updated automatically.' : 'Lohistage see maja oma täpsele asukohale,<br />uus marsuut koostatakse automaatselt.',
			'Your location' : 'Teie asukoht',
			'We believe you are located in this area.' : 'Arvame, et asute selles piirkonnas.',
			'Distance' : 'Distants',
			'Clear route' : 'Tühista marsuut',
			'Optimal route from your<br />location to selected merchant.' : 'Optimaalne teekond Teie valitud<br />asukohast kaupmehe juurde.',
			'Your location<br /><br />You can specify it by dragging the<br />house icon to your actual location<br /><br />The route will be updated automatically.' : '<b>Teie asukoht</b><br /><br />Saate oma asukohta täpsustada<br />nihutades seda maja ikooni, uus<br />marsuut koostatakse automaatselt.',
			'Click here to view a group of %count merchants' : 'Kliki siia, et näha %count kaupmeest',
			'markercount' : ['ühte', 'kahte', 'kolme', 'nelja', 'viit', 'kuut', 'seitset', 'kaheksat', 'üheksat', 'kümmet'],
			'You can move this marker<br />to specify your location<br /><br />This is used for the radius search.' : 'Oma asukoha täpsustamiseks<br />liigutage seda markerit.<br /><br />Asukohta kasutatakse<br/>raadiusepõhiseks otsinguks.',
			'Please move this marker<br />to your current location<br /><br />This is used for the radius search.' : 'Liigutage see marker oma<br />tõelisele asukohale kaardil.<br /><br />Asukohta kasutatakse<br/>raadiusepõhiseks otsinguks.',
			'Your location, click here to focus' : 'Teie asukoht. Fokuseerimiseks kliki siia.',
			'Move this marker to update your position.' : 'Oma asukoha täpsustamiseks<br />nihuta seda markerit.'
	};	
}

/**
 * ElavToit map controls class contructor
 *
 * Adds translatable and styleable controls for choosing map type,
 * triggering widemode and printing
 *
 * @param ETMap etmap ElavToit map object
 * @param string cssPrefix Prefix added to used css elements, default to "etcontrol-"
 * @param boolean wideviewEnabled Is switching to wideview enabled
 * @param boolean printEnabled Is the print button visible
 */
function ETMapControl(etmap, cssPrefix, wideviewEnabled, printEnabled)
{
	this.etmap = etmap;
	this.cssPrefix = cssPrefix != null ? cssPrefix : 'etcontrol-';
	this.activeButton = null;
	this.printMode = false;
	this.wideviewEnabled = wideviewEnabled != null ? wideviewEnabled : true;
	this.printEnabled =    printEnabled    != null ? printEnabled    : true;
	
	this.wideviewDiv = null;
	this.printDiv = null;
	
	ETMapControl.prototype = new google.maps.Control();
}

/**
 * Initializes the controls
 *
 * @param google.maps.Map2 map Google map object
 */
ETMapControl.prototype.initialize = function(map)
{
	var self = this;
	
	var outerContainer = document.createElement('div');
	outerContainer.className = this.cssPrefix+'outer-container';
	
	var innerContainer = document.createElement('span');
	innerContainer.className = this.cssPrefix+'inner-container';
	
	outerContainer.appendChild(innerContainer);

	if(this.wideviewEnabled)
	{
		this.wideviewDiv = document.createElement('div');
		
		this.wideviewDiv.className = this.cssPrefix+'wideview-enabled';
		this.wideviewDiv.title = this.etmap.translate('Restore normal view');				
	
		this.wideviewDiv.innerHTML = '&nbsp;';
		innerContainer.appendChild(this.wideviewDiv);
		
		google.maps.Event.addDomListener(this.wideviewDiv, 'click', function() {
			if(self.etmap.isWideviewMode())
			{
				self.etmap.disableWideviewMode();
			}
			else
			{
				self.etmap.enableWideviewMode();	
			}
		});
	}

	if(this.printEnabled)
	{
		this.printDiv = document.createElement('div');
		this.printDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-print';
		this.printDiv.innerHTML = '<span>'+this.etmap.translate('Print the map')+'</span>';
		innerContainer.appendChild(this.printDiv);
		
		google.maps.Event.addDomListener(this.printDiv, 'click', function() {
			if(self.printMode)
			{
				self.etmap.disableFullscreenMode();
				
				self.printDiv.innerHTML = '<span>'+self.etmap.translate('Print the map')+'</span>';
				self.printMode = false;
			}
			else
			{
				self.etmap.enableFullscreenMode();
				
				self.printDiv.innerHTML = '<span>'+self.etmap.translate('Restore normal view')+'</span>';
				self.printMode = true;
				
				window.setTimeout(function() {
					window.print();		   
				}, 2000);
			}
		});
	}



	var WMS_NASA = 'http://wms.jpl.nasa.gov/wms.cgi?';
	
	var NASA_MAP = createWMSSpecNASA(WMS_NASA, "daily_planet", "wms", "daily_planet", "default", "image/jpeg", "1.1.1");		
	var nasaTypeDiv = document.createElement('div');
	nasaTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-standard';
	nasaTypeDiv.innerHTML = '<span>'+this.etmap.translate('NASA')+'</span>';
	innerContainer.appendChild(nasaTypeDiv);
	
	google.maps.Event.addDomListener(nasaTypeDiv, 'click', function() {
		map.setMapType(NASA_MAP);
		self.setActiveButton(nasaTypeDiv);
	});


	var WMS_MAAAMET_VERSTA = 'http://kaart.maaamet.ee/wms/ajalooline?';

	var MAAAMET_VERSTA = createWMSSpecMAAAMET(WMS_MAAAMET_VERSTA, "nltopo_uuem_25T", "WMS", "nltopo_uuem_25T", "default", "image/png", "1.1.1");

	var maaametverstakaartTypeDiv = document.createElement('div');
	maaametverstakaartTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-standard';
	maaametverstakaartTypeDiv.innerHTML = '<span>'+this.etmap.translate('Maa-amet topo')+'</span>';
	innerContainer.appendChild(maaametverstakaartTypeDiv);

	google.maps.Event.addDomListener(maaametverstakaartTypeDiv, 'click', function() {
		map.setMapType(MAAAMET_VERSTA );
		self.setActiveButton(maaametverstakaartTypeDiv);
	});

	var WMS_MAAAMET_AJALUGU = 'http://kaart.maaamet.ee/wms/ajalooline?';

	var MAAAMET_AJALUGU = createWMSSpecMAAAMET(WMS_MAAAMET_AJALUGU, "MA-AJAL", "WMS", "MA-AJAL", "default", "image/png", "1.1.1");
	var maaametAjaluguTypeDiv = document.createElement('div');
	maaametAjaluguTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-standard';
	maaametAjaluguTypeDiv.innerHTML = '<span>'+this.etmap.translate('Maa-amet ajalooline')+'</span>';
	innerContainer.appendChild(maaametAjaluguTypeDiv);

	google.maps.Event.addDomListener(maaametAjaluguTypeDiv, 'click', function() {
		map.setMapType(MAAAMET_AJALUGU );
		self.setActiveButton(maaametAjaluguTypeDiv);
	});


	var WMS_MAAAMET_FOTO = 'http://kaart.maaamet.ee/wms/fotokaart?';

	var MAAAMET_FOTO = createWMSSpecMAAAMET(WMS_MAAAMET_FOTO, "reljeef", "WMS", "reljeef", "default", "image/png", "1.1.1");
	var maaametfotokaartTypeDiv = document.createElement('div');
	maaametfotokaartTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-standard';
	maaametfotokaartTypeDiv.innerHTML = '<span>'+this.etmap.translate('Maa-amet reljeef')+'</span>';
	innerContainer.appendChild(maaametfotokaartTypeDiv);

	google.maps.Event.addDomListener(maaametfotokaartTypeDiv, 'click', function() {
		map.setMapType(MAAAMET_FOTO );
		self.setActiveButton(maaametfotokaartTypeDiv);
	});


	var WMS_MAAAMET_AERO = 'http://kaart.maaamet.ee/wms/alus-geo?';

	var MAAAMET_AEROFOTO = createWMSSpec(WMS_MAAAMET_AERO, "of10000", "WMS", "of10000", "default", "image/png", "1.1.1");
	var maaametaerofotoTypeDiv = document.createElement('div');
	maaametaerofotoTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-standard';
	maaametaerofotoTypeDiv.innerHTML = '<span>'+this.etmap.translate('Maa-amet aerofoto')+'</span>';
	innerContainer.appendChild(maaametaerofotoTypeDiv);

	google.maps.Event.addDomListener(maaametaerofotoTypeDiv, 'click', function() {
		map.setMapType(MAAAMET_AEROFOTO );
		self.setActiveButton(maaametaerofotoTypeDiv);
	});



	var MAAAMET_KAART = createWMSSpec(WMS_MAAAMET_AERO, "MAG-ALUSKAART", "WMS", "MAG-ALUSKAART", "default", "image/png", "1.1.1");		
	var maaametkaartTypeDiv = document.createElement('div');
	maaametkaartTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-standard';
	maaametkaartTypeDiv.innerHTML = '<span>'+this.etmap.translate('Maa-amet kaart')+'</span>';
	innerContainer.appendChild(maaametkaartTypeDiv);
	
	google.maps.Event.addDomListener(maaametkaartTypeDiv, 'click', function() {
		map.setMapType(MAAAMET_KAART);
		self.setActiveButton(maaametkaartTypeDiv);
	});
		

	
    var copyOSM = new GCopyrightCollection("<a href=\"http://www.openstreetmap.org/\">OpenStreetMap</a>");
    copyOSM.addCopyright(new GCopyright(1, new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)), 0, " "));

    var tilesMapnik     = new GTileLayer(copyOSM, 1, 17, {tileUrlTemplate: 'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png'});
    var mapMapnik     = new GMapType([tilesMapnik],     G_NORMAL_MAP.getProjection(), "Mapnik");

	var openStreetMapTypeDiv = document.createElement('div');
	openStreetMapTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-standard';
	openStreetMapTypeDiv.innerHTML = '<span>'+this.etmap.translate('OpenStreetMap')+'</span>';
	innerContainer.appendChild(openStreetMapTypeDiv);
	
	google.maps.Event.addDomListener(openStreetMapTypeDiv, 'click', function() {
		map.setMapType(mapMapnik);
		self.setActiveButton(openStreetMapTypeDiv);
	});


	var terrainTypeDiv = document.createElement('div');
	terrainTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-standard';
	terrainTypeDiv.innerHTML = '<span>'+this.etmap.translate('Orto')+'</span>';
	innerContainer.appendChild(terrainTypeDiv);
	
	google.maps.Event.addDomListener(terrainTypeDiv, 'click', function() {
		map.setMapType(G_PHYSICAL_MAP );
		self.setActiveButton(terrainTypeDiv);
	});


	var hybridTypeDiv = document.createElement('div');
	hybridTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-hybrid';
	hybridTypeDiv.innerHTML = '<span>'+this.etmap.translate('Hybrid')+'</span>';
	innerContainer.appendChild(hybridTypeDiv);
	
	google.maps.Event.addDomListener(hybridTypeDiv, 'click', function() {
		map.setMapType(G_HYBRID_MAP);
		self.setActiveButton(hybridTypeDiv);
	});

	var satelliteTypeDiv = document.createElement('div');
	satelliteTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-satellite';
	satelliteTypeDiv.innerHTML = '<span>'+this.etmap.translate('Satellite')+'</span>';
	innerContainer.appendChild(satelliteTypeDiv);
	
	google.maps.Event.addDomListener(satelliteTypeDiv, 'click', function() {
		map.setMapType(G_SATELLITE_MAP);
		self.setActiveButton(satelliteTypeDiv);
	});

	var standardTypeDiv = document.createElement('div');
	standardTypeDiv.className = this.cssPrefix+'button '+this.cssPrefix+'button-standard';
	standardTypeDiv.innerHTML = '<span>'+this.etmap.translate('Standard')+'</span>';
	innerContainer.appendChild(standardTypeDiv);
	
	google.maps.Event.addDomListener(standardTypeDiv, 'click', function() {
		map.setMapType(G_NORMAL_MAP);
		self.setActiveButton(standardTypeDiv);
	});





	

	switch(map.getCurrentMapType())
	{
		case G_NORMAL_MAP:
			this.activeButton = standardTypeDiv;
		break;
		
		case G_SATELLITE_MAP:
			this.activeButton = satelliteTypeDiv;
		break;
		
		case G_HYBRID_MAP:
			this.activeButton = hybridTypeDiv;
		break;

		case G_PHYSICAL_MAP :
			this.activeButton = terrainTypeDiv;
		break;
	}
	
	this.activeButton.className = this.activeButton.className.replace(this.cssPrefix+'button', this.cssPrefix+'button-active');
	
	map.getContainer().appendChild(outerContainer);
	
	this.etmap.debug('+ Initialised ETMapControl');
	
	
	
	return outerContainer;
}

/**
 * Sets active button by div object
 *
 * Changes the button div classname button-active, previous active one
 * back to just button
 *
 * @param object btnDiv New active button div object
 */
ETMapControl.prototype.setActiveButton = function(btnDiv)
{
	this.activeButton.className = this.activeButton.className.replace(this.cssPrefix+'button-active', this.cssPrefix+'button');
	btnDiv.className = btnDiv.className.replace(this.cssPrefix+'button', this.cssPrefix+'button-active');
	
	this.activeButton = btnDiv;
}

/**
 * Called when map enters fullscreen mode
 */
ETMapControl.prototype.enteredFullscreenMode = function()
{
	this.wideviewDiv.style.display = 'none';
}

/**
 * Called when map exits fullscreen mode
 */
ETMapControl.prototype.exitedFullscreenMode = function()
{
	this.wideviewDiv.style.display = '';
}

/**
 * Called when map enters wideview mode
 */
ETMapControl.prototype.enteredWideviewMode = function()
{
	this.wideviewDiv.className = this.cssPrefix+'wideview-enabled';
	this.wideviewDiv.title = this.etmap.translate('Restore normal view');
}

/**
 * Called when map exits wideview mode
 */
ETMapControl.prototype.exitedWideviewMode = function()
{
	this.wideviewDiv.className = this.cssPrefix+'wideview-disabled';
	this.wideviewDiv.title = this.etmap.translate('View in full width');
}

/**
 * Returns component default position
 *
 * @return google.maps.ControlPosition Position
 */
ETMapControl.prototype.getDefaultPosition = function()
{
	return new google.maps.ControlPosition(G_ANCHOR_TOP_RIGHT, new google.maps.Size(0, 0));
}

/**
 * Returns whether this component is printable
 *
 * @return boolean Is this component printable
 */
ETMapControl.prototype.printable = function()
{
	return false;
}

/**
 * Returns whether this component is selectable
 *
 * @return boolean Is this component selectable
 */
ETMapControl.prototype.selectable = function()
{
	return false;
}

/**
 * Returns whether changing visibility is allowed
 *
 * @return boolean Is changing visibility allowed
 */
ETMapControl.prototype.allowSetVisibility = function()
{
	return true;
}

/**
 * Custom circle overlay
 *
 * @param google.maps.LatLng latLng circle coordinates
 * @param float radius Circle radius in kilometres
 * @param string strokeColor Line stroke color in html-style hex
 * @param int strokeWidth Stroke width
 * @param float strokeOpacity Stroke opacity in range 0..1, one beign entirely opaque
 * @param string fillColor Fill color as html-style hex
 * @param float fillOpacity Fill opacity in range 0..1
 * @param int numVertices How many vertices to use to make up the circle
 */
function CircleOverlay(latLng, radius, strokeColor, strokeWidth, strokeOpacity, fillColor, fillOpacity, numVertices)
{
	this.latLng        = latLng;
    this.radius        = radius        || 10.0;
    this.strokeColor   = strokeColor   || '#990000';
    this.strokeWidth   = strokeWidth   || 2;
    this.strokeOpacity = strokeOpacity || 0.75;
    this.fillColor     = fillColor     || '#990000';
    this.fillOpacity   = fillOpacity   || 0.1;
	this.numVertices   = numVertices   || 64;
	
	this.coordinates = new Array();
	
	this.map     = null;
	this.polygon = null;
}

/**
 * Initializes the circle
 *
 * @param google.maps.Map2 map Google map
 */
CircleOverlay.prototype.initialize = function(map)
{
    this.map = map;
	
	if(this.latLng == null)
	{
		this.latLng = this.map.getCenter();
	}
}

/**
 * Removes the circle overlay from the map
 */
CircleOverlay.prototype.clear = function()
{
    if(this.polygon != null && this.map != null) {
        this.map.removeOverlay(this.polygon);
    }
}

/**
 * Redraw the circle overlay
 *
 * The argument force will be true if the zoom level or the pixel offset of the
 * map view has changed, so that the pixel coordinates need to be recomputed.
 *
 * @param boolean force Is the redraw forced
 */
CircleOverlay.prototype.redraw = function(force)
{
	this.coordinates = [];
	
    var d2r = Math.PI / 180.0;
	var r2d = 180.0 / Math.PI;
    var circleCoordinates = new Array();
	
	var circleLat = (this.radius / 6378.1370) * r2d;
    var circleLng = circleLat / Math.cos(this.latLng.lat() * d2r);
	
    for (var i = 0; i < this.numVertices + 1; i++) {
        var theta = Math.PI * (i / (this.numVertices / 2));
        var vertexLat = this.latLng.lat() + (circleLat * Math.sin(theta));
        var vertexLng = this.latLng.lng() + (circleLng * Math.cos(theta));
        var vertextCoordinate = new google.maps.LatLng(vertexLat, vertexLng);
        this.coordinates.push(vertextCoordinate);
    }
   	
    this.clear();
    this.polygon = new google.maps.Polygon(this.coordinates, this.strokeColor, this.strokeWidth, this.strokeOpacity, this.fillColor, this.fillOpacity);
    this.map.addOverlay(this.polygon);
}

/**
 * Removes the overlay
 */
CircleOverlay.prototype.remove = function()
{
    this.clear();
}

/**
 * Checks whether the circle contains given coordinates
 *
 * @param google.maps.LatLng latLng Coordinates
 * @return boolean Does the circle contain the coordinates
 */
CircleOverlay.prototype.containsLatLng = function(latLng)
{
    if(this.polygon != null) {
        return this.polygon.containsLatLng(latLng);
    }
	
	return false;
}

/**
 * Sets new circle radius
 *
 * @param float radius Circle radius
 * @param boolean redraw Should the overlay be rerawn after this, defaults to true
 */
CircleOverlay.prototype.setRadius = function(radius, redraw)
{
    this.radius = radius;
	
	if(redraw != null && redraw == true)
	{
		this.redraw();
	}
}

/**
 * Sets the center of the circle
 *
 * @param google.maps.LatLng latLng New center of the circle
 * @param boolean redraw Should the overlay be rerawn after this, defaults to true
 */
CircleOverlay.prototype.setCenter = function(latLng, redraw)
{
    this.latLng = latLng;
	this.redraw();
	
	if(redraw != null && redraw == true)
	{
		this.redraw();
	}
}

/**
 * Creates a new cookie with given name and value that lasts given days
 *
 * @param string name Cookie name
 * @param string value Cookie value
 * @param int days Number of days the cookie should be valid
 */
function setCookie(name, value, days)
{
	var expires = "";
	
	if(days)
	{
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		expires = "; expires="+date.toGMTString();
	}

	document.cookie = name+"="+value+expires+"; path=/";
}

/**
 * Reads cookie by name
 *
 * @param string name Name of the cookie
 * @param mixed defaultValue This is returned if the cookie has not been set
 * @return string Returns cookie value or given default value if not set
 */
function getCookie(name, defaultValue)
{
	var cookiesArray = document.cookie.split( ';' );
	var tempCookie = '';
	var cookieName = '';
	var cookieValue = '';
	var cookieFound = false;

	for(i = 0; i < cookiesArray.length; i++)
	{
		tempCookie = cookiesArray[i].split('=');

		cookieName = tempCookie[0].replace(/^\s+|\s+$/g, '');

		if (cookieName == name)
		{
			cookieFound = true;
			
			if(tempCookie.length > 1)
			{
				cookieValue = unescape(tempCookie[1].replace(/^\s+|\s+$/g, ''));
			}
			
			return cookieValue;
		}
		
		tempCookie = null;
		cookieName = '';
	}
	
	if(!cookieFound)
	{
		return defaultValue;
	}
}

/**
 * Erases cookie by name
 *
 * @param string name Cookie name
 */
function eraseCookie(name)
{
	createCookie(name, '', -1);
}




var MAGIC_NUMBER = 6356752.3142;
var DEG2RAD = 0.0174532922519943;
var PI = 3.14159267;
function dd2MercMetersLng(p_lng) {
    return MAGIC_NUMBER * (p_lng * DEG2RAD);
}

function dd2MercMetersLat(p_lat) {
    if (p_lat >= 85) p_lat = 85;
    if (p_lat <= -85) p_lat = -85;
    return MAGIC_NUMBER * Math.log(Math.tan(((p_lat * DEG2RAD) + (PI / 2)) / 2));
}

var CustomGetTileUrl = function(a, b, c) {
    if (typeof(window['this.myMercZoomLevel']) == "undefined") this.myMercZoomLevel = 0;
    if (typeof(window['this.myStyles']) == "undefined") this.myStyles = "";
    var lULP = new GPoint(a.x * 256, (a.y + 1) * 256);
    var lLRP = new GPoint((a.x + 1) * 256, a.y * 256);
    var lUL = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lULP, b, c);
    var lLR = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lLRP, b, c);
    // switch between Mercator and DD if merczoomlevel is set
    if (this.myMercZoomLevel != 0 && map.getZoom() < this.myMercZoomLevel) {
        var lBbox = dd2MercMetersLng(lUL.lngDegrees) + "," + dd2MercMetersLat(lUL.latDegrees) + "," + dd2MercMetersLng(lLR.lngDegrees) + "," + dd2MercMetersLat(lLR.latDegrees);
        var lSRS = "EPSG:4326";
    } else {
        var lBbox = lUL.x + "," + lUL.y + "," + lLR.x + "," + lLR.y;
        var lSRS = "EPSG:4326";
    }
    var lURL = this.myBaseURL;
    lURL += "&REQUEST=GetMap";
    lURL += "&SERVICE=WMS";
    lURL += "&VERSION=" + this.myVersion;
    lURL += "&LAYERS=" + this.myLayers;
    lURL += "&STYLES=" + this.myStyles;
    lURL += "&FORMAT=" + this.myFormat;
    lURL += "&BGCOLOR=" + this.myBgColor;
    lURL += "&TRANSPARENT=TRUE";
    lURL += "&SRS=" + lSRS;
    lURL += "&BBOX=" + lBbox;
    lURL += "&WIDTH=256";
    lURL += "&HEIGHT=256";
    lURL += "&reaspect=false";
    return lURL;
}


/** Create WMS type spec as a GMap Spec. */
function createWMSSpec(wmsURL, gName, gShortName, wmsLayers, wmsStyles, wmsFormat, wmsVersion, wmsBgColor, wmsSrs) {
    var tile = new GTileLayer(new GCopyrightCollection(""), 1, 17);
    tile.myLayers = wmsLayers;
    tile.myStyles = (wmsStyles ? wmsStyles : "");
    ;
    tile.myFormat = (wmsFormat ? wmsFormat : "image/gif");
    ;
    tile.myVersion = (wmsVersion ? wmsVersion : "1.1.1");
    tile.myBgColor = (wmsBgColor ? wmsBgColor : "0xFFFFFF");
    tile.myBaseURL = wmsURL;
    tile.getTileUrl = CustomGetTileUrl;
    //tileCounty.getOpacity = function() {return 0.5;}


    var layer = [tile];

    var mapType = new GMapType(layer, G_SATELLITE_MAP.getProjection(), gName, G_SATELLITE_MAP);

    return mapType;
}




var CustomGetTileUrlMAAAMET = function(a, b, c) {
    if (typeof(window['this.myMercZoomLevel']) == "undefined") this.myMercZoomLevel = 0;
    if (typeof(window['this.myStyles']) == "undefined") this.myStyles = "";
    var lULP = new GPoint(a.x * 256, (a.y + 1) * 256);
    var lLRP = new GPoint((a.x + 1) * 256, a.y * 256);
    var lUL = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lULP, b, c);
    var lLR = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lLRP, b, c);
    // switch between Mercator and DD if merczoomlevel is set

    if (this.myMercZoomLevel != 0 && map.getZoom() < this.myMercZoomLevel) {
		var punkt1 = L_EST(dd2MercMetersLng(lUL.lngDegrees),dd2MercMetersLat(lUL.latDegrees));
		var punkt2 = L_EST(dd2MercMetersLng(lLR.lngDegrees),dd2MercMetersLat(lLR.latDegrees));
        var lBbox = punkt2[1] + "," + punkt2[0] + "," + punkt1[1] + "," + punkt1[0];
        var lSRS = "EPSG:3301";

    } else {
		var punkt1 = L_EST(lUL.y, lUL.x);
		var punkt2 = L_EST(lLR.y, lLR.x);

		//var lBbox = lLR.x+","+lLR.y+","+punkt2[1] + "," + punkt2[0] + "," +lLR.x+","+lLR.y+","+ punkt1[1] + "," + punkt1[0];
		var lBbox = punkt1[1] + "," + punkt1[0] + "," + punkt2[1] + "," + punkt2[0];
        var lSRS = "EPSG:3301";
    }
    var lURL = this.myBaseURL;
    lURL += "&REQUEST=GetMap";
    lURL += "&SERVICE=WMS";
    lURL += "&VERSION=" + this.myVersion;
    lURL += "&LAYERS=" + this.myLayers;
    lURL += "&STYLES=" + this.myStyles;
    lURL += "&FORMAT=" + this.myFormat;
    lURL += "&BGCOLOR=" + this.myBgColor;
    lURL += "&TRANSPARENT=TRUE";
    lURL += "&SRS=" + lSRS;
    lURL += "&BBOX=" + lBbox;
    lURL += "&WIDTH=256";
    lURL += "&HEIGHT=256";
    lURL += "&reaspect=false";
    return lURL;
}


/** Create WMS type spec as a GMap Spec. */
function createWMSSpecMAAAMET(wmsURL, gName, gShortName, wmsLayers, wmsStyles, wmsFormat, wmsVersion, wmsBgColor, wmsSrs) {
    var tile = new GTileLayer(new GCopyrightCollection(""), 1, 17);
    tile.myLayers = wmsLayers;
    tile.myStyles = (wmsStyles ? wmsStyles : "");
    ;
    tile.myFormat = (wmsFormat ? wmsFormat : "image/gif");
    ;
    tile.myVersion = (wmsVersion ? wmsVersion : "1.1.1");
    tile.myBgColor = (wmsBgColor ? wmsBgColor : "0xFFFFFF");
    tile.myBaseURL = wmsURL;
    tile.getTileUrl = CustomGetTileUrlMAAAMET;
    //tileCounty.getOpacity = function() {return 0.5;}


    var layer = [tile];

    var mapType = new GMapType(layer, G_SATELLITE_MAP.getProjection(), gName, G_SATELLITE_MAP);

    return mapType;
}


var CustomGetTileUrlNASA = function(a, b, c) {
    if (typeof(window['this.myMercZoomLevel']) == "undefined") this.myMercZoomLevel = 0;
    if (typeof(window['this.myStyles']) == "undefined") this.myStyles = "";
    var lULP = new GPoint(a.x * 256, (a.y + 1) * 256);
    var lLRP = new GPoint((a.x + 1) * 256, a.y * 256);
    var lUL = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lULP, b, c);
    var lLR = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lLRP, b, c);
    // switch between Mercator and DD if merczoomlevel is set
    if (this.myMercZoomLevel != 0 && map.getZoom() < this.myMercZoomLevel) {
        var lBbox = dd2MercMetersLng(lUL.lngDegrees) + "," + dd2MercMetersLat(lUL.latDegrees) + "," + dd2MercMetersLng(lLR.lngDegrees) + "," + dd2MercMetersLat(lLR.latDegrees);
        var lSRS = "EPSG:4326";
    } else {
        var lBbox = lUL.x + "," + lUL.y + "," + lLR.x + "," + lLR.y;
        var lSRS = "EPSG:4326";
    }
    var lURL = this.myBaseURL;
    lURL += "request=GetMap";
    lURL += "&width=512";
    lURL += "&height=512";

//    lURL += "&service=WMS";
//    lURL += "&version=" + this.myVersion;
	lURL += "&layers=" + this.myLayers;
    lURL += "&styles=" + this.myStyles;
    lURL += "&srs=" + lSRS;
    lURL += "&format=" + this.myFormat;
//    lURL += "&bgcolor=" + this.myBgColor;
//    lURL += "&transparent=true";

    lURL += "&bbox=" + lBbox;

//    lURL += "&reaspect=false";
    return lURL;
}


/** Create WMS type spec as a GMap Spec. */
function createWMSSpecNASA(wmsURL, gName, gShortName, wmsLayers, wmsStyles, wmsFormat, wmsVersion, wmsBgColor, wmsSrs) {
    var tile = new GTileLayer(new GCopyrightCollection(""), 1, 17);
    tile.myLayers = wmsLayers;
    tile.myStyles = (wmsStyles ? wmsStyles : "");
    ;
    tile.myFormat = (wmsFormat ? wmsFormat : "image/gif");
    ;
    tile.myVersion = (wmsVersion ? wmsVersion : "1.1.1");
    tile.myBgColor = (wmsBgColor ? wmsBgColor : "0xFFFFFF");
    tile.myBaseURL = wmsURL;
    tile.getTileUrl = CustomGetTileUrlNASA;
    //tileCounty.getOpacity = function() {return 0.5;}


    var layer = [tile];

    var mapType = new GMapType(layer, G_SATELLITE_MAP.getProjection(), gName, G_SATELLITE_MAP);

    return mapType;
}



function L_EST(x,y) {


var B=x;
var L=y;


var B0=57+31/60.0+3.19415/3600;
var L0=24;

var X0=6375000.000;
var Y0=500000.000;

var B1=58;
var B2=59+20/60;

var a=6378137;
var e=0.081819191;

var M_PI=Math.PI;

var m1 = cos(deg2rad(58))/sqrt(1-pow(e,2)*pow(sin(deg2rad(58)),2));
var m2 = cos(deg2rad(59+1/3))/sqrt(1-pow(e,2)*pow(sin(deg2rad(59+1/3)),2));
var m = cos(deg2rad(58+5/60))/sqrt(1-pow(e,2)*pow(sin(deg2rad(58+5/60)),2));
var m = cos(deg2rad(58+5/60))/sqrt(1-pow(e,2)*pow(sin(deg2rad(58+5/60)),2));
var t1=tan(M_PI/4-deg2rad(B1/2))/pow(((1-e*sin(deg2rad(B1)))/(1+e*sin(deg2rad(B1)))),e/2);
var t2=tan(M_PI/4-deg2rad(B2/2))/pow(((1-e*sin(deg2rad(B2)))/(1+e*sin(deg2rad(B2)))),e/2);
var t0=tan(M_PI/4-deg2rad(B0/2))/pow(((1-e*sin(deg2rad(B0)))/(1+e*sin(deg2rad(B0)))),e/2);
var t=tan(M_PI/4-deg2rad(B/2))/pow(((1-e*sin(deg2rad(B)))/(1+e*sin(deg2rad(B)))),e/2);
var n=(log(m1)-log(m2))/(log(t1)-log(t2));
var F=m1/(n*pow(t1,n));
var p0=a*F*pow(t0,n);	
var p=a*F*pow(t,n);
var Q=n*deg2rad(L-L0);
var X=Math.floor((p0-p*cos(Q)+X0)*100)/100;
var Y=Math.floor((p*sin(Q)+Y0)*100)/100;

var result = new Array(X,Y);

return result; 

}

function cos (arg) {
    return Math.cos(arg);
}

function deg2rad (angle) {
    return (angle/180)*Math.PI;
}

function sqrt (arg) {  
    return Math.sqrt(arg);
}

function pow (base, exp) {
    return Math.pow(base, exp);
}

function sin (arg) {
    return Math.sin(arg);
}

function tan (arg) {
    return Math.tan(arg);
}

function log (arg, base) {
    if (base === undefined) {
        return Math.log(arg);
    } else {
        return Math.log(arg)/Math.log(base);
    }
}





var ajaxRequest = false;
function ajaxRequestData(url) {
	ajaxRequest = false;
	if (window.XMLHttpRequest) { // Mozilla, Safari,...
		ajaxRequest = new XMLHttpRequest();
		if (ajaxRequest.overrideMimeType) {
			ajaxRequest.overrideMimeType('text/plain');
		}
	} else if (window.ActiveXObject) { // IE
		try {
			ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {}
		}
	}

	if (!ajaxRequest) {
		// Cannot create XMLHTTP instance
		return false;
	}

	ajaxRequest.open('GET', url, false);
	ajaxRequest.send(null);
	return ajaxRequest.responseText;
}

function getL_EST(x,y) {
	var val = [x, y];
	val = ajaxRequestData('http://www.elavtoit.com/l_est.php?x=' + x + '&y=' + y);
	return val;
}
