	/*
	Depens on these functions:
	
	
	Called when user clicks on marker.
	@ param index - Marker index.
	function onMakerClicked(index) {
	}
	
	Called everytime map drag ends.
	@ param bounds - New map visible area.
	bounds type GLatLngBounds.
	http://code.google.com/apis/maps/documentation/reference.html#GLatLngBounds
	Exmaple:
	var sw = bounds.getSouthWest(); // GLatLng http://code.google.com/apis/maps/documentation/reference.html#GLatLng
	var ne = bounds.getNorthEast(); // GLatLng http://code.google.com/apis/maps/documentation/reference.html#GLatLng
		sw.lat() - returns latitude
		sw.lng() - returns longitude

		ne.lat() - returns latitude
		ne.lng() - returns longitude
	function onMapViewChanged(bounds) {
	}	
	*/
	var map;
	var msc_over;
	var msc_mgr;
	var dir;
	
	var mscSelectMarkerZoomLevel = 16;
	var mscMapViewChangeEnabled= true;

	var simpleTileClusterManager;
	
	function mscEnableMapViewChange() {
	 mscMapViewChangeEnabled = true;
	}
	function mscDisableMapViewChange() {
	 mscMapViewChangeEnabled = false;
	}
	function mscIsMapViewChange() {
		return mscMapViewChangeEnabled;
	}

	function initialize(mapid) {
	  if (GBrowserIsCompatible()) {  	
	    map = new GMap2(document.getElementById(mapid));

		var bounds = new GLatLngBounds(new GLatLng(56.45034902929675, 20.50048828125), new GLatLng(53.82659674299412, 27.3779296875));
		map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));

	    //map.setCenter(new GLatLng(55.30413773740139, 23.7744140625), 7);
	    //map.setCenter(new GLatLng(55.141209644495056, 23.8787841796875), 7);
	    
	
	     

		var topRight = new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10,10));
        var bottomRight = new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(10,10));
        var bottomLeft = new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(5,35));
        
	    map.addControl(new GSmallMapControl());
	    
	    msc_over = new GOverviewMapControl();
	    map.addControl(msc_over);
	    //over.hide();  //undocumented feature
	    
	    map.addControl(new GMenuMapTypeControl());
	    map.addControl(new GScaleControl(), bottomLeft);

		map.enableScrollWheelZoom();
		
		
		//map.enableGoogleBar();
		
		GEvent.addListener(map, "infowindowopen", function() {
			onInfoWindowOpen();
		});

		GEvent.addListener(map, "moveend", function() {
			if (mscMapViewChangeEnabled) {
        			mscMapViewChanged(map.getBounds());
        		}
        	});
        
       		 GEvent.addListener(map, "zoomend", function() {
			refresh();
        	});

		msc_mgr = new MarkerManager(map);

		simpleTileClusterManager = new MarkerManager(map);
		
		fixOverview();
		fixCopyright("map_canvas");

                GEvent.addListener(map, "load", onMapLoad_core);
	  }
	}

        function onMapLoad_core() {
                if (typeof(onMapLoad) == 'function') {
                        onMapLoad();
                }
        }

	function initDirections() {
		if (dir != null) {
			dir.clear();
			return;
		}
		
		dir = new GDirections(map);
		GEvent.addListener(dir, "load", onDirectionsLoad_core);
	}

	function onDirectionsLoad_core() {
		if (onDirectionsLoad) {
			onDirectionsLoad();
		}
	}
	
	function mscShowOver() {
		msc_over.show();
	}
	function mscHideOVer() {
		msc_over.hide();
	}
	function fixCopyright() {
		var element = document.getElementById("map_canvas");
		if (element.firstChild) {
			if (element.firstChild.nextSibling) {
				var cp = element.firstChild.nextSibling.nextSibling;
				cp.style.whiteSpace = "normal";
			}
		}
	}
	
	function fixOverview() {
		var element = document.getElementById("map_canvas_overview");
		if (element && element.firstChild && element.firstChild.firstChild) {
			var child = element.firstChild.firstChild;
			child.style.left = "";
			child.style.top = "";
			child.style.right = "0px";
			child.style.bottom = "0px";
		}
	}

	function uloadMap() {
		GUnload();
	}

	function mscMapViewChanged(bounds) {
		onMapViewChanged(bounds);
	}
	
	/**
	  * Returns curent map visible area.
	  * @return Returns GLatLngBounds object.
		http://code.google.com/apis/maps/documentation/reference.html#GLatLngBounds
		Exmaple:
		var sw = bounds.getSouthWest(); // GLatLng http://code.google.com/apis/maps/documentation/reference.html#GLatLng
		var ne = bounds.getNorthEast(); // GLatLng http://code.google.com/apis/maps/documentation/reference.html#GLatLng
			sw.lat() - returns latitude
			sw.lng() - returns longitude
	
			ne.lat() - returns latitude
			ne.lng() - returns longitude
	  */
	function mscGetCurrentMap() {
		return map.getBounds();
	}

	var markers = [];
	var markersIndex = [];

	/**
	 * Trigers marker selection.
	 * @param index Marker index
	 */	
	function mscSelectMarker(index) {
		if ((markersIndex[index] != undefined) && (markers[markersIndex[index]] != undefined)) {
			GEvent.trigger(markers[markersIndex[index]], "click");
			map.setZoom(mscSelectMarkerZoomLevel);
		}
	}
	
	function mscClickMarker(index) {
		if ((markersIndex[index] != undefined) && (markers[markersIndex[index]] != undefined)) {
			GEvent.trigger(markers[markersIndex[index]], "click");
			map.setZoom(16);
		}
	}
	
	/**
	 * Shows search results in map.
	 * Search results are shown as markers.
	 * Markers are clustered by type.   
	 * @param searchResults Array of search results.
	 * For exmaple:
	 * var rez = [];
	 * rez.push({lat: lat_, lng: lng_, d: desc_, index: index_, type: type_, title: title_});
	 * 	lat, lng - event location (latitude, longitude)
	 *	d - event description
	 *  index - event unique id. Used for marker selection. 
	 *  type - event type. Used for clustering.
	 *  title - event title. Shown as marker title. 
	 */
	function mscShowResults(sr2, opt_zoomToMarkers) {
		var d0 = new Date();
		var sr = sr2;
		/*
		// just and new markers
		if (markers.length != 0) {
			sr = [];
			for (var i = 0; i < sr2.length; i++) {
				if (markersIndex[sr2[i].index] != undefined) {
				sr.push(sr2[i]);
				}
			}
		}
		*/

		markers = [];
		markersIndex = [];


		for (var i = 0; i < sr.length; i++) {
			if (sr[i]) {
				var marker = createMSCCustomMarker(sr[i]);
				markers.push(marker);
				markersIndex[sr[i].index] = markers.length-1;
			}
				

			//map.addOverlay(marker);
		}
		
		if (opt_zoomToMarkers) {	
			
			mscChangeMap(markers);
		}
		
					
			

		mscMarkers = markers;
		var d1 = new Date();
		log("init markers " + " time: " + (d1-d0));
		
		refresh();
	}
	function refresh() {
		if (mscMarkers) {
			var d1 = new Date();
			showMarkers();
			var d2 = new Date();
			log("show markers " + " time: " + (d2-d1));
		}
	}
	
	function mscChangeMap(markers) {
		var bounds = getMarkerBounds(markers);
		mscChangeMapBounds(bounds);
	}
	
	var mscInitialZoomLevel = 11;  // -1 disable
	function mscChangeMapBounds(bounds, opt_disableMapViewChange) {
		mscDisableMapViewChange();
		var zl = map.getBoundsZoomLevel(bounds);
		if (mscInitialZoomLevel != -1) {
			zl = Math.min(mscInitialZoomLevel, zl);
		}
		map.setCenter(bounds.getCenter(), zl);
		mscEnableMapViewChange();
	}
	
	function mscSetMapBounds(sw, ne) {
		var bounds = new GLatLngBounds(sw, ne);
		map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
	}

	
	
	function getMarkerBounds(markers) {
		var bounds = new GLatLngBounds(markers[0].getPoint(), markers[0].getPoint());
 		for (var i = 0; i < markers.length; i++) {
 			bounds.extend(markers[i].getPoint());
		}
		return bounds;
	}

	var mscMarkers;

	var showMarkersClusterType = 5;
	function showMarkers() {
		if (showMarkersClusterType == 0) {
			showMarkersSimple();
			log("shown markers: #" + msc_mgr.shownMarkers_);
		}

		if (showMarkersClusterType == 3) {
			showMarkersClear();
		}

		if (showMarkersClusterType == 4) {
			showMarkersSimpleTile();
		}

		if (showMarkersClusterType == 5) {
			showMarkersSimpleGroupTile();
		}
	}


	function clearAllClusters() {
		msc_mgr.clearMarkers();
		simpleTileClusterManager.clearMarkers();
		
	}
	
	function showMarkersSimpleTile() {
	clearAllClusters();
		showMarkersClusterType = 4;

		var d1 = new Date();
		var clusterMarkers = clusterTile(mscMarkers);
		var d2 = new Date();

		log("make cluster time: " + (d2 - d1));
		log("shown markers: " + "all: #" + mscMarkers.length  + " clusters: #" + clusterMarkers.length);

		simpleTileClusterManager.addMarkers(clusterMarkers, 1);
		simpleTileClusterManager.refresh();
	}
	
	
	function enableIndexOf() {
		if(!Array.indexOf){
		    Array.prototype.indexOf = function(obj){
		        for(var i=0; i<this.length; i++){
		            if(this[i]==obj){
		                return i;
		            }
		        }
		        return -1;
		    }
		}
	}

	
	function showMarkersSimpleGroupTile() {
	
	log("showMarkersSimpleGroupTile");
	clearAllClusters();
		showMarkersClusterType = 5;

		var d1 = new Date();
		
		var allmarkers = new Array();
		var allmarkersTypes = new Array();
		for(i=mscMarkers.length-1; i>=0; i--) {
			var type = mscMarkers[i].mscType;
			if (allmarkersTypes.indexOf(type) != -1) {
			} else {
				allmarkersTypes[allmarkersTypes.length] = type;
			}
			var index = allmarkersTypes.indexOf(type); 
			if (!allmarkers[index]) {
				allmarkers[index] = new Array();
			}
			allmarkers[index][allmarkers[index].length] = mscMarkers[i]; 
		}

		
		var clusterMarkers = new Array();
		log(Array.concat);		
		for(i=allmarkers.length-1; i>=0; i--){
			var clusterMarkers1 = clusterTile(allmarkers[i], allmarkersTypes[i]);
			log("cm: " + "all: #" + allmarkers[i].length  + " clusters: #" + clusterMarkers1.length);
			
			clusterMarkers = clusterMarkers.concat(clusterMarkers1);
			//simpleTileClusterManager.addMarkers(clusterMarkers1, 1);
		}
			
		
		var d2 = new Date();

		log("make cluster time: " + (d2 - d1));
		log("shown markers: " + "all: #" + mscMarkers.length  + " clusters: #" + clusterMarkers.length);

		simpleTileClusterManager.addMarkers(clusterMarkers, 1);
		simpleTileClusterManager.refresh();
		
		
	}
	
	function showMarkersSimple() {
	clearAllClusters();
		showMarkersClusterType = 0;


		msc_mgr.addMarkers(mscMarkers, 1);
		msc_mgr.refresh();
	}

	function showMarkersClear() {
		clearAllClusters();
		showMarkersClusterType = 3;
	}




	function createMSCCustomMarker(sr) {
			var lat = sr.lat;
			var lng = sr.lng;
			var desc = sr.d;
			var events = sr.events;
			//desc += "</br>" + sr.lat + " " + sr.lng;
			var latlng = new GLatLng(lat, lng);
			var index = sr.index;
			var title = sr.title;
			
			
			var marker;
			if (sr.customIcon) {
				marker = createCustomMarker(latlng, desc, title, index, sr.customIcon, sr.markerInfo, events);
			} else {
				marker = createMarker(new GLatLng(lat, lng), desc, title, index, sr.markerInfo, events);
			}
			
        	marker.mscIndex = index;
        	marker.mscTitle = sr.d;
        	marker.mscShortTitle = sr.title;
        	marker.mscType = sr.type;

			return marker;
	}

	//Marker that is under our mouse currently (for right click handling)
	var overMarker = null;

        function onMarkerMouseOver() {
                overMarker = this;
        }

        function onMarkerMouseOut() {
                overMarker = null;
        }

	function newMarker(latlng,options) {
		var marker = new GMarker(latlng,options);
		
                GEvent.addListener(marker,'mouseover', onMarkerMouseOver);
                GEvent.addListener(marker,'mouseout',onMarkerMouseOut);

		return marker;
	}
	
	
	function createMarker(latlng, desc, title_, index, markerInfo, events) {	
		var marker = newMarker(latlng, {title:title_});
		marker.objectId=index;

		GEvent.addListener(marker,"click", function() {
			onMakerClicked(index, markerInfo, latlng, desc, events);
		});

		return marker;
	}
	
	function createCustomMarker(latlng, desc, title_, index, customIcon, markerInfo, events) {

		if (title_) 
			markerOptions = { icon:customIcon, title:title_ };
		else
			markerOptions = { icon:customIcon};

	    var marker = newMarker(latlng, markerOptions);
		marker.objectId=index;


		//  
		var url = customIcon.outimage;
		var hover_url = customIcon.overimage;
		if (hover_url != undefined) {
			GEvent.addListener(marker, "mouseover", function() {
				marker.setImage(hover_url);
			});

			GEvent.addListener(marker, "mouseout", function() {
				marker.setImage(url);
			});
		}


		//
	    GEvent.addListener(marker,"click", function() {
			//marker.openInfoWindowHtml(desc);
			//map.openInfoWindowHtml(latlng, desc);
			//var tabs = new Array();
			//tabs.push(new GInfoWindowTab("tab1",  dd));
			//tabs.push(new GInfoWindowTab("tab2",  dd));
			//map.openInfoWindowTabs(latlng, tabs);

			//map.panTo(latlng);
			//map.setCenter(latlng);
			onMakerClicked(index, markerInfo, latlng, desc, events);
	     });

      	return marker;
	}

function mscUpdateInfoWindow() {
	map.updateInfoWindow(map.getInfoWindow().getTabs());
}

function mscIsInfoWindowHidden() {
	return map.getInfoWindow().isHidden();
}
	
	
	function log(msg) {
		//document.getElementById("log").innerHTML += msg + "</br>";
		//GLog.write(msg);
	}
	
	
/* MAP MENU INITIALIZATION */
function ContextMenu(oMap,items){this.initialize(oMap,items);}

//Construct the DOM tree of the menu
//items = new array();
//items[0].name = element.name
//items[0].id = element.id
//items[0].caption = element.innerHTML
//items[0].onclick = onclick function
ContextMenu.prototype.initLink = function(oMap,items){
	this.ul_container.innerHTML="";

	var that=this;
	var a_link = null;
	
	for (var i=0;i<items.length;i++) {
		var item = items[i];
		
		if (item.isVisible) {
			if (typeof(item.isVisible) == 'function')
				if (item.isVisible() == false)
					continue;
			else
				if (item.isVisible == false)
					continue;
		}

		a_link = document.createElement("li");
		a_link.name=item.name;
		a_link.id=item.id;
		a_link.innerHTML="<span>"+item.caption+"</span>";
		a_link.menu=that;
		
		GEvent.addDomListener(a_link, 'click', item.onclick);
		GEvent.addDomListener(a_link, 'click', function () {that.contextmenu.style.display='none';});
		this.ul_container.appendChild(a_link);
	}
}

ContextMenu.prototype.bind = function(method) {
	var self = this;
	var opt_args = [].slice.call(arguments, 1);
	return function() {
		var args = opt_args.concat([].slice.call(arguments));
		return method.apply(self, args);
	}
}

//The object 'constructor'
ContextMenu.prototype.initialize = function(oMap,items){
	this.map = oMap;
	var that=this;
	this.contextmenu = document.createElement("div");
	this.contextmenu.style.display="none";
//CSS class name of the menu
	this.contextmenu.className="contextmenu";
	this.ul_container = document.createElement("ul");
	this.ul_container.id="context_menu_ul";
	this.contextmenu.appendChild(this.ul_container);
	this.items = items;	
	this.initLink(oMap,items);
	this.map.getContainer().appendChild(this.contextmenu);	

//Event listeners that will interact with our context menu
	GEvent.addListener(oMap,"singlerightclick",function(pixel,tile) {
		if (typeof(that.onShow) == 'function' && that.onShow() == false) {
			return false;
		}

		that.initLink(that.map,that.items);
		that.clickedPixel = pixel;
		that.point = that.map.fromContainerPixelToLatLng(pixel);
		var x=pixel.x;
		var y=pixel.y;

		if (x > that.map.getSize().width - 150) { x = that.map.getSize().width - 150 }
		if (y >that.map.getSize().height - 50) { y = that.map.getSize().height - 50 }

		var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(x,y));  
		pos.apply(that.contextmenu);
		that.contextmenu.style.display = "";
	});	
	GEvent.addListener(oMap, "move", function() {
		that.contextmenu.style.display="none";
	});
	GEvent.addListener(oMap, "click", function(overlay,point) {
		that.contextmenu.style.display="none";
	});	
}
