function BaseMap(element, width, height, exportPath, basetoolUpload, linkurl, clustered, lang, basetoolUser, showDirections, directionsElement)
{
	/* DECLARATION */
	var _Directions;
	var _DirectionsElement;
	var _ExportPath = exportPath;
	var _MarkersMax = 1000;
	var _LoadingControl = new LoadingControl();
	var _BasetoolUpload = basetoolUpload;
	var _LinkUrl = linkurl;
	var _Lang = lang;
	var _BasetoolUser = basetoolUser;
	var _Geocoder = new GClientGeocoder();
	var _IsExpanded = true;	
	var _PolyLine;
	var _Self = this;
	var _Markers = [];
	
	var _Icon = new GIcon();
	_Icon.image = "/googlemaps/images/markerblue.png";
	_Icon.shadow = "/googlemaps/images/markerblue_shadow.png";
	_Icon.iconSize = new GSize(12.0, 27.0);
	_Icon.shadowSize = new GSize(26.0, 27.0);
	_Icon.iconAnchor = new GPoint(6.0, 13.0);
	_Icon.infoWindowAnchor = new GPoint(6.0, 13.0);
	//_Icon.infoShadowAnchor = new GPoint(18, 25);
		
	this.Translate = new Object();
	this.Clustered = clustered;
	this.ErrorControl = new ErrorControl();
	//this.WeatherControl = new WeatherControl();
	
	this.SetCenter = SetCenter;
	this.Search = Search;
	this.Mark = Mark;
	this.Geocode = Geocode;
	this.ShowError = ShowError;
	this.ShowWeather = ShowWeather;
	this.LoadDirections = LoadDirections;
	this.ClearDirections = ClearDirections;
	this.QueryDirections = QueryDirections;		
	this.DrawEncodedPolyline = DrawEncodedPolyline;
	this.MarkProducts = MarkProducts;
	
	/*--------------- CONSTRUCT ---------------------*/			
	this.Map = new GMap2(element);
	
	if (height <= 401)
	{
		this.Map.addControl( new GSmallZoomControl3D());
	}
	else
	{
		this.Map.addControl(new GLargeMapControl3D());
		this.Map.addControl(new GMapTypeControl());	
		var ovcontrol = new GOverviewMapControl(new GSize(parseInt(width / 3.5), parseInt(height / 3.5)));
		this.Map.addControl(ovcontrol);
	}
	
	if (this.Clustered)
	{
		this.Cluster = new Clusterer(this.Map);
		this.Cluster.SetMaxVisibleMarkers(2);
		this.Cluster.SetMinMarkersPerCluster(2);
	}
	
	if (showDirections)
	{
		_DirectionsElement = document.getElementById(directionsElement);
		_Directions = new GDirections(this.Map, _DirectionsElement);
		GEvent.addListener(_Directions, "load", CreateMethodReference(this, Directions_Load));		
		GEvent.addListener(_Directions, "error", CreateMethodReference(this, Directions_Error));
	}
	
	//this.Map.addControl( this.WeatherControl );
	//GEvent.addListener(this.WeatherControl, "timeset", CreateMethodReference(this, WeatherControl_Callback));
	/* ------------------ /CONSTRUCT ----------------- */

	/*------------------- LOAD LANG ------------------ */
	this.Map.addControl(_LoadingControl);
	
	var xmlLang = _Lang;
	if (xmlLang == "se")
		xmlLang = "sv";
	GDownloadUrl("/googlemaps/lang/" + xmlLang + ".xml", CreateMethodReference(this, Lang_Callback));
	
	function Lang_Callback(data, responseCode)
	{
		var xml;		
		if (responseCode == 200)
		{
			xml = GXml.parse(data);
			this.Translate.QueryDirections = xml.documentElement.getElementsByTagName("querydirections")[0].childNodes[0].data;
			this.Translate.WeatherError = xml.documentElement.getElementsByTagName("weathererror")[0].childNodes[0].data;
			this.Translate.GeocodeError = xml.documentElement.getElementsByTagName("geocodeerror")[0].childNodes[0].data;
			this.Translate.SearchError = xml.documentElement.getElementsByTagName("searcherror")[0].childNodes[0].data;
			this.Translate.SearchNoHits = xml.documentElement.getElementsByTagName("searchnohits")[0].childNodes[0].data;
			this.Translate.SearchToManyHits = xml.documentElement.getElementsByTagName("searchtomanyhits")[0].childNodes[0].data;
			this.Translate.ReadMore = xml.documentElement.getElementsByTagName("readmore")[0].childNodes[0].data;
			this.Translate.DirectionsHere = xml.documentElement.getElementsByTagName("directionshere")[0].childNodes[0].data;
			this.Translate.DirectionsUnknownAddress = xml.documentElement.getElementsByTagName("directionsunknownaddress")[0].childNodes[0].data;
			this.Translate.DirectionsError = xml.documentElement.getElementsByTagName("directionserror")[0].childNodes[0].data;
		}		
		this.Map.removeControl(_LoadingControl);
	}
	/*------------------- /LOAD LANG ----------------- */				
	
	/* PUBLIC METHODS */
	function MarkProducts(prodIds)
	{
		this.Map.addControl(_LoadingControl);
		GDownloadUrl(_ExportPath + "/proxy.aspx?action=getProducts&p=" + prodIds + "&l=" + _Lang + "&u=" + _BasetoolUser, function(data, responseCode) { Search_Callback(data, responseCode, false); });
	}
		
	function SetCenter(lat, lng, zoom)
	{
		this.Map.setCenter(new GLatLng(lat, lng), zoom);
	}
	
	function Search(areaId, categoryId, siteId, nameNo, areaName, text, serviceId, stars, bookableOnly, ratH, ratM, ratS, ratR, ratA, from, to, allyear, vild, prodName, capacity, priceLevel, cat, facility, inBounds, count)
	{
	
		var lngN = '0', lngS = '0', latW = '0', latE = '0';
		
		if (inBounds)
		{
			bounds = this.Map.getBounds();
			lngN = bounds.getNorthEast().lng();
			lngS = bounds.getSouthWest().lng();
			latW = bounds.getSouthWest().lat();
			latE = bounds.getNorthEast().lat();
		}

		if (count == undefined || count == 0)
			count = _MarkersMax;
			
		this.Map.addControl(_LoadingControl);

		GDownloadUrl(_ExportPath + "/proxy.aspx?action=search&a=" + areaId + "&c=" + cat + "&s=" + siteId + "&n=" + encodeURIComponent(nameNo) + "&cap=" + capacity + "&prodName=" + encodeURIComponent(prodName) + "&an=" + encodeURIComponent(areaName) + "&q=" + encodeURIComponent(text) + "&se=" + serviceId + "&r=" + stars + "&b=" + ((bookableOnly) ? 1 : 0) + "&ratH=" + ratH + "&ratM=" + ratM + "&ratS=" + ratS + "&ratR=" + ratR + "&ratA=" + ratA + "&from=" + from + "&to=" + to + "&allyear=" + allyear + "&vild=" + vild + "&i=" + count + "&lngn=" + lngN + "&lngs=" + lngS + "&latw=" + latW + "&late=" + latE + "&l=" + _Lang + "&u=" + _BasetoolUser, CreateMethodReference(this, Search_Callback));	
		
		//GDownloadUrl(_ExportPath + "/proxy.aspx?action=search&a=" + areaId + "&l=" + _Lang + "&u=" + _BasetoolUser, CreateMethodReference(this, Search_Callback));	
	}
	
	function Mark(lat, lng, zoom, categoryId, clear, id)
	{		
		if (clear)
			this.Map.clearOverlays();
		
		this.SetCenter(lat, lng, 8);

		var marker = GetMarker(new GLatLng(lat, lng), id, null, false);
		this.Map.addOverlay(marker);
	}
	
	function Geocode(address)
	{
		_Geocoder.getLatLng(address, CreateMethodReference(this, Geocode_Callback));
	}
	
	function ClearDirections()
	{
		if (!_IsExpanded)
		{
			var width = parseInt(this.Map.getContainer().style.width);
			this.Map.getContainer().style.width = (width + 210) + "px";
			this.Map.checkResize();
			_IsExpanded = true;
		}
		_DirectionsElement.style.display = "none";
		_Directions.clear();
	}
	
	function QueryDirections(lat, lng)
	{
		var from = prompt(this.Translate.QueryDirections, "");
		if (from != null)
			this.LoadDirections('from: ' + from + ' to:' + lat + ' ' + lng);
	}
	
	function LoadDirections(query)
	{
		this.Map.addControl(_LoadingControl);
		_Directions.load(query, {"locale": _Lang});
	}
	
	function ShowWeather()
	{	
		this.Map.clearOverlays();		
		var d = 0;
		if (arguments.length > 0)
			d = parseInt(arguments[0]);
		this.Map.addControl(_LoadingControl);			
		GDownloadUrl("/smhi/weather.aspx?d=" + d, CreateMethodReference(this, ShowWeather_Callback));
	}
	
	function DrawEncodedPolyline(encodedPoints, encodedLevels, clear)
	{	
		if (clear && _PolyLine)
			this.Map.removeOverlay(_PolyLine);

		_PolyLine = new GPolyline.fromEncoded({
					color: "#0000FF",
					weight: 8,
					points: encodedPoints,
					levels: encodedLevels,
					zoomFactor: 32,
					numLevels: 4
				});
				
		this.Map.addOverlay(_PolyLine);

		var arrPoints = DecodePolyline(encodedPoints);
		this.SetCenter(arrPoints[0].lat(), arrPoints[0].lng(), 14);
	}
	
	/* PRIVATE METHODS */		
	function Directions_Load()
	{
		this.Map.removeControl(_LoadingControl);
		if (_IsExpanded)
		{
			var width = parseInt(this.Map.getContainer().style.width);
			this.Map.getContainer().style.width = (width - 210) + "px";
			this.Map.checkResize();
			_IsExpanded = false;
		}
		_DirectionsElement.style.display = "block";
	}
	
	function Directions_Error()
	{
		this.Map.removeControl(_LoadingControl);
		if (_Directions.getStatus().code == G_GEO_UNKNOWN_ADDRESS)
			this.ShowError(this.Translate.DirectionsUnknownAddress);
		else
			this.ShowError(this.Translate.DirectionsError);
	}
	
	function WeatherControl_Callback()	
	{
		this.ShowWeather(this.WeatherControl.ActiveTime);
	}
	
	function ShowWeather_Callback(data, responseCode)
	{
		var xml, areas;
		
		if (responseCode == 200)
		{
			xml = GXml.parse(data);
			areas = xml.documentElement.getElementsByTagName("area");
		}
		else
		{
			this.Map.removeControl(_LoadingControl);
			this.ShowError(this.Translate.WeatherError);
			return;
		}
		
		var period = (this.WeatherControl.ActiveTime % 4);
		try
		{
			var time = areas[0].childNodes[period].getAttribute("time");
		}
		catch (ex) {}
		
		for (var i = 0; i < areas.length; i++)
		{
			var lat = parseFloat(areas[i].getAttribute("lat"));
			var lng = parseFloat(areas[i].getAttribute("lng"));
			if (areas[i].childNodes.length <= period)
				period = areas[i].childNodes.length - 1;
			var icon = areas[i].childNodes[period].getElementsByTagName("icon")[0].getAttribute("url");
			var windspeed = areas[i].childNodes[period].getElementsByTagName("wind")[0].getAttribute("speed");
			var temp = areas[i].childNodes[period].getElementsByTagName("temp")[0].childNodes[0].data;
			this.Map.addOverlay(new WeatherOverlay(lat, lng, icon, temp, windspeed));			
		}
		
		this.Map.removeControl(_LoadingControl);		
	}
	
	function Geocode_Callback(point)
	{
		if (point)
		{
			this.Mark(point.lat(), point.lng(), 15, 0, false);
		}
		else
		{
			this.ShowError(this.Translate.GeocodeError)
		}
	}
		
	function Search_Callback(data, responseCode)
	{			
		var markers, xml;
		var latTot = 0, lngTot = 0;
		_Self.Map.clearOverlays();
		_Markers = [];
		var infoWin = true;		
		if (arguments.length > 2 && arguments[2] == false)
			infoWin = false;
		var bounds = new GLatLngBounds;
		
		if (responseCode == 200)
		{
			xml = GXml.parse(data);
			markers = xml.documentElement.getElementsByTagName("marker");
		}
		else
		{
			_Self.Map.removeControl(_LoadingControl);
			_Self.ShowError(this.Translate.SearchError);
			return;		
		}
		
		if (markers.length <= 0)
		{
			_Self.Map.removeControl(_LoadingControl);
			_Self.ShowError(this.Translate.SearchNoHits);
			return;
		}
		
		if (markers.length <= _MarkersMax)
		{
			for (var i = 0; i < markers.length; i++)
			{
				var point = new GLatLng(parseFloat(markers[i].getAttribute("lng")), parseFloat(markers[i].getAttribute("lat")));
				latTot += point.lat()
				lngTot += point.lng();
				var prodId = parseInt(markers[i].getAttribute("prodid"));
				var nodeInfo = markers[i].getElementsByTagName("info")[0];
				var marker = GetMarker(point, prodId, nodeInfo, infoWin);
				if (!_Self.Clustered)
					_Self.Map.addOverlay(marker);
				else
					_Self.Cluster.AddMarker(marker, "Test");
				bounds.extend(point);
			}
		}
		else
		{
			_Self.Map.removeControl(_LoadingControl);
			_Self.ShowError(this.Translate.SearchToManyHits);
			return;
		}
		
		_Self.Map.removeControl(_LoadingControl);
		
		latMed = latTot / parseFloat(markers.length);
		lngMed = lngTot / parseFloat(markers.length);
			
		_Self.Map.setCenter(bounds.getCenter());
		var zl = _Self.Map.getBoundsZoomLevel(bounds);
		if (zl == 19)
		{
			zl = 12;
		}
			
		_Self.Map.setZoom(zl);
	}
	
	function GetMarker(point, prodId, nodeInfo)
	{
		var marker = new GMarker(point, {icon:_Icon});
		var infoWin = true;
		if (arguments.length > 3 && arguments[3] == false)
			infoWin = false;
		
		marker.Id = prodId;			
		
		if (nodeInfo != null)
		{
			var name = nodeInfo.childNodes[0].childNodes[0].data;
			var text = nodeInfo.childNodes[1].childNodes[0].data;
			var img = nodeInfo.childNodes[2].childNodes[0].data;			
			if (!img || img == "")
				img = "/GoogleMaps/images/defaultItem.gif";
			else
				img = _BasetoolUpload + '/' + img;
			marker.title = name;

			if (infoWin)
			{
				marker.HTML = '<div class="InfoWindow"><div class="Name">' + name + '</div><div class="Row"><div class="Image"><img src="' + img + '" width="100" height="80"/></div><div class="Text">' + text + '</div></div>' + ((_LinkUrl != null) ? '<div class="Link"><a href="' + _LinkUrl + prodId + '">' + window.BaseMap.Translate.ReadMore + '</a></div></div>' : '</div>');
				GEvent.addListener(marker, "click", function() {
					marker.openInfoWindowHtml(marker.HTML);
				});
			}
		}

		if (!infoWin)
			GEvent.addListener(marker, "click", function() {window.location = _LinkUrl + marker.Id;});
			GEvent.addListener(marker, "mouseover", function() {document.getElementById('divName').innerHTML = name; document.getElementById('divSign').style.display = 'block'});
			GEvent.addListener(marker, "mouseout", function() {document.getElementById('divName').innerHTML = ''; document.getElementById('divSign').style.display = 'none'});
			
		
		return marker;
	}	
	
	function DecodePolyline (encoded)
	{
	  var len = encoded.length;
	  var index = 0;
	  var array = [];
	  var lat = 0;
	  var lng = 0;

	  while (index < len) {
		var b;
		var shift = 0;
		var result = 0;
		do {
		  b = encoded.charCodeAt(index++) - 63;
		  result |= (b & 0x1f) << shift;
		  shift += 5;
		} while (b >= 0x20);
		var dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
		lat += dlat;

		shift = 0;
		result = 0;
		do {
		  b = encoded.charCodeAt(index++) - 63;
		  result |= (b & 0x1f) << shift;
		  shift += 5;
		} while (b >= 0x20);
		var dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
		lng += dlng;

		array.push(new GLatLng(lat * 1e-5, lng * 1e-5));
	  }

	  return array;
	}
		
	function ShowError(message)
	{
		this.ErrorControl.Message = message;
		this.Map.addControl(this.ErrorControl);		
		setTimeout("window.BaseMap.Map.removeControl(window.BaseMap.ErrorControl)", 10000);
	}
}


/* HELPER METHODS */
function CreateMethodReference(object, method)
{
    if (!(method instanceof Function))
        method = object[method];

    return function () {
        method.apply(object, arguments);
    }
}