var mapObj = null;
var geocoder = null;
var markers = [], logwnd = null, clearmarkers = null, printmap = null, overviewmap = null, field = '', cat_div_arr = [], cat_id_arr = [];
var qsopts;
var leftPanelWidth = 200;
var viewmode = getSrcParam('vm');


function e(el) { return document.getElementById(el) };


// extract out the parameters
function getSrcParam(param){
	var scriptname = 'googlemap.js';
	//var qs = $('script[src*=' + scriptname + ']').attr('src'); // if jQuery aviable
  var scripts = document.getElementsByTagName ('script');
  for (var s, i = scripts.length; i && (s = scripts[--i]);) {
    if ((s = s.getAttribute ('src')) && (s = s.match (new RegExp("^(.*)" + scriptname + "(\\?\\s*.+)?\\s*")))) {
      var qs = s[2];
      break;
    }
  }
	param = param.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");
	var p = (new RegExp("[\\?&]" + param + "=([^&#]*)")).exec(qs);
	return (p===null) ? "" : p[1];
}




/* Показываети пример запроса к геоданным */
function showAddressExample(elem) {
	document.getElementById('mapsearch').address.value = elem.innerHTML;
	showAddress(elem.innerHTML);
	return false;
}



/* Запрос к геоданным */
function showAddress(address) {
	if (geocoder) {
		geocoder.getLatLng(
			address,
			function(point) {
				if (!point) {
					alert("Адрес \"" + address + "\" не найден.\nПопробуйте упростить условие поиска и повторить попытку.");
				} else {
					mapObj.setCenter(point, 13);
					if (qsopts.act == "setobject") {
						markers[0].setLatLng(point);
						GEvent.trigger(markers[0], "click"); // нужно чтобы перерисовать инфо окно
					} else {
						var marker = new GMarker(point);
						mapObj.addOverlay(marker);
						marker.openInfoWindowHtml("<b>" + address + "</b><br><br>" +
						"Широта: " + point.lat() + "<br>" +
						"Долгота: " + point.lng());
					}
				}
			}
		);
	}
}


/* Декодирование типа карты */
function decodeMaptype(value) {
	var maptype = null;
	if (value == "m") maptype = G_NORMAL_MAP;
	if (value == "k") maptype = G_SATELLITE_MAP;
	if (value == "h") maptype = G_HYBRID_MAP;
	if (value == "p") maptype = G_PHYSICAL_MAP;
	return maptype;
}


function url2geo(url) {
	var val = url.split(","), geo = {lat: 0, lng: 0, zoom: null, maptype: null};
	if (parseFloat(val[0]) > 0) geo.lat = parseFloat(val[0]);
	if (parseFloat(val[1]) > 0) geo.lng = parseFloat(val[1]);
	if (val[2] && parseInt(val[2]) > 0) geo.zoom = parseInt(val[2]);
	if (val[3] && val[3].length > 0) geo.maptype = decodeMaptype(val[3]);
	return geo;
}


function geo2url(geo) {
	return geo.lat + ',' + geo.lng + ',' + geo.zoom + ',' + geo.maptype;
}


function setMapCenter(geo) {
	if (mapObj)	{
		if (typeof(geo.lat) != 'undefined' && typeof(geo.lng) != 'undefined')	{
			var point = new GLatLng(geo.lat, geo.lng);
			mapObj.setCenter(point);
			if (qsopts.act == "setobject") {
				if (markers && markers[0]) {
					markers[0].setLatLng(point);
					GEvent.trigger(markers[0], "click"); // нужно чтобы перерисовать инфо окно
				}
			}
		}
		if (typeof(geo.zoom) != 'undefined') mapObj.setZoom(geo.zoom);
		if (typeof(geo.maptype) != 'undefined') mapObj.setMapType(geo.maptype);
	}
}


function setMapCenterUrl(url) { setMapCenter(url2geo(url)); }


/* Анализ строки запроса */
function parseQueryString(query) {
	var pairs = query.split("&"), opts = {lat: 0, lng: 0, zoom: 0, maptype: G_NORMAL_MAP, url: "", id: [], div_id: [], lastid: 0, field: "", act: "viewmap", address: ""};

	for (var i = 0; i < pairs.length; i++) {
		var pos = pairs[i].indexOf("=");
		var argname = pairs[i].substring(0, pos).toLowerCase();
		var value = pairs[i].substring(pos + 1).toLowerCase();
		if (argname == "field") opts.field = value;
		if (argname == "lat") opts.lat = parseFloat(value);
		if (argname == "lng") opts.lng = parseFloat(value);
		if (argname == "zoom") opts.zoom = parseInt(value);
		if (argname == "act") opts.act = value;
		if (argname == "address") opts.address = unescape(value);;
		if (argname == "type") opts.maptype = decodeMaptype(opts.maptype);

		if (argname == "geo") opts.url = value;

		if (argname == "div_id")
		{
			for (var pairs2 = value.split(","), j = 0; j < pairs2.length; j++)
				if (parseInt(pairs2[j]) > 0 && !inArray(pairs2[j], cat_div_arr))
					opts.div_id.push(pairs2[j]);
		}
		if (argname == "id")
		{
			for (var pairs2 = value.split(","), j = 0; j < pairs2.length; j++)
			{
				opts.lastid = pairs2[j];
				if (parseInt(pairs2[j]) > 0 && !inArray(pairs2[j], cat_id_arr))
					opts.id.push(pairs2[j]);
			}
		}
	}
	return opts;
}








function load(startPoint)	{
	resizeApp();

	if (GBrowserIsCompatible())	{

		mapObj = new GMap2(e("map"));
		if (mapObj != "undefined" && mapObj != null) {

			//var url='55.94,37.87,12,m'; // Королев
			//var url='55.6138,36.9868,13,m'; // Голицыно

			var url = startPoint ? String(startPoint) : '50,40,3,m'; // Евразия
			geo = url2geo(url);

			qsopts = parseQueryString(location.search.substring(1));
			if (qsopts.url.length > 0) geo = url2geo(qsopts.url); // Если координаты заданы в строке запроса через ,

			field = qsopts.field;
			if (qsopts.lat > 0 && qsopts.lng > 0)	{
				geo.lat = qsopts.lat;
				geo.lng = qsopts.lng;
			}
			if (qsopts.zoom > 0) geo.zoom = qsopts.zoom;
			if (typeof(qsopts.maptype) != 'undefined') geo.maptype = qsopts.maptype;

			mapObj.addMapType(G_PHYSICAL_MAP);

			// Начальное положение карты
			setMapCenter(geo);


			if (viewmode != "s")
			{
				//mapObj.addControl(new GLargeMapControl());
				mapObj.addControl(new GLargeMapControl3D()); // a large pan/zoom control as now used on Google Maps. Appears in the top left corner of the map by default.

				// Add ContextMenuControl to the map
				if (typeof(ContextMenuControl) != 'undefined') mapObj.addControl(new ContextMenuControl());


				if (typeof(ExtMapTypeControl) != 'undefined')
				{

					mapObj.addControl(new GMenuMapTypeControl()); // Add GMenuTypeControl, выпадающий список типов карты
				  // Add ExtMapTypeControl with posRight option

					if (qsopts.act == "viewmap")
						mapObj.addControl(new ExtMapTypeControl({posRight: 97, showTraffic: true, showMore: true }))
					else
						mapObj.addControl(new ExtMapTypeControl({posRight: 97, showTraffic: false, showMore: false }));

					//mapObj.addControl(new ExtMapTypeControl({useMapTypes: true, showTraffic: true, showTrafficKey: true, showMore: true, showSave: false }));

				} else {
					mapObj.addControl(new GMapTypeControl()); // buttons that let the user toggle between map types (such as Map and Satellite)
				}

				mapObj.addControl(new GScaleControl());

				// Миникарта справа в нижнем углу для навигации
				overviewmap = new GOverviewMapControl();
				mapObj.addControl(overviewmap);

			  //mapObj.setUIToDefault(); // Set default UI

				mapObj.enableScrollWheelZoom(); // Zoom by wheel mouse
			}
			else
			{
				//mapObj.addControl(new GSmallMapControl());
				mapObj.addControl(new GSmallZoomControl3D());


	      // Create an EWindow
		    ew = new EWindow(mapObj, E_STYLE_7);
			  mapObj.addOverlay(ew);

	      // ========== Close the EWindow if theres a map click ==========
	      GEvent.addListener(mapObj, "click", function(overlay,point) {
		      if (!overlay) {
			      ew.hide();
				  }
				});


			}




			GEvent.addListener(mapObj, 'moveend', updateLinkMap);
			updateLinkMap();


			// Add control info window
			function LogWindowControl() {}
			LogWindowControl.prototype = new GControl();
			LogWindowControl.prototype.initialize = function(map) {
				var wnd = e('logwnd');
				map.getContainer().appendChild(wnd);
				return wnd;
			}
			LogWindowControl.prototype.getDefaultPosition = function() {
				return this.setCenterPosition();
			}
			LogWindowControl.prototype.setCenterPosition = function() {
				var wnd = e('logwnd'), map = mapObj.getContainer(),
					l = parseInt(map.offsetWidth)/2 - 150,
					t = parseInt(map.offsetHeight)/2 - 25;
				return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(l, t)).apply(wnd);
			};
			LogWindowControl.prototype.show = function(msg) {
				this.setCenterPosition();
				e('logwnd').style.display = "";
				if (typeof(msg) != 'undefined')
					e('logwndmsg').innerHTML = msg;
			}
			LogWindowControl.prototype.hide = function() {
				e('logwnd').style.display = "none";
			}
			logwnd = new LogWindowControl();
			mapObj.addControl(logwnd);



			//
			geocoder = new GClientGeocoder();


			if (qsopts.act == "setobject") {

				var m_html = '<div class="setinfownd">Установите мышкой маркер в нужную точку на карте и "сохраните положение". Если на карте не нанесено нужное здание включите режим "Гибрид" и установите маркер по фотографии со спутника.<br><a href="#" onclick="saveMarkerPosition(); return false;" style="float: right; margin-top: 8px;"><b>Сохранить положение</b></a></div>',
						m_title = 'Установите маркер в нужную точку на карте';

				var marker = createMarker(new GLatLng(geo.lat, geo.lng), {draggable: true, title: m_title}, {tooltip: m_html});

				marker.cat_id = 0;
				marker.cat_div = 0;
				GEvent.trigger(marker, "click");


			}

			// Если задан адрес для поиска и не заданы координаты то сразу запускаем поиск
			if (!(qsopts.lat > 0 && qsopts.lng > 0)  && qsopts.address.length > 0) showAddress(qsopts.address);

			if (qsopts.act == "viewmap") {

				if (viewmode != "s")
				{
					// Add control button CLEAR ALL MARKERS
					function ClearAllMarkersControl() {}
					ClearAllMarkersControl.prototype = new GControl();
					ClearAllMarkersControl.prototype.show = function() {
						e('clearallmarkers').style.display = '';
					};
					ClearAllMarkersControl.prototype.hide = function() {
						e('clearallmarkers').style.display = 'none';
					};

					ClearAllMarkersControl.prototype.initialize = function(map) {
						var btn = e('clearallmarkers');
						btn.style.display = "";
						GEvent.addDomListener(btn, "click", function() {
							clearAllMarkers();
						});
						map.getContainer().appendChild(btn);
						return btn;
					};
					ClearAllMarkersControl.prototype.getDefaultPosition = function() {
						return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(140, 7));

					};
					clearmarkers = new ClearAllMarkersControl();
					mapObj.addControl(clearmarkers);



					// Create control button PRINT
					function PrintMapControl() {};
					PrintMapControl.prototype = new GControl();
					PrintMapControl.prototype.show = function() {
						e('printmap').style.display = '';
					};
					PrintMapControl.prototype.hide = function() {
						e('printmap').style.display = 'none';
					};
					PrintMapControl.prototype.initialize = function(map) {
						var btn = e('printmap');
						GEvent.addDomListener(btn, "click", function() {
							if (isPanelShow())
							{
								hidePanel();
								window.print();
								showPanel();
							}	else {window.print();}
						});
						map.getContainer().appendChild(btn);
						return btn;
					};
					PrintMapControl.prototype.getDefaultPosition = function() {
						return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(70, 7));
					};
					printmap = new PrintMapControl();
					mapObj.addControl(printmap);


				}


				// read markers from query string
				getXMLMarkersObj({div_id: qsopts.div_id, id: qsopts.id});
				// read markers from dinamic query string
				if (window.opener && typeof(window.opener.cat_querystring) != 'undefined' && window.opener.cat_querystring.length > 0)
				{
					getXMLMarkers(window.opener.cat_querystring);
					window.opener.cat_querystring = "";
				}


			}
		}
	}
}



function inArray(elem, array)	{
	for (var i = 0, length = array.length; i < length; i++)
		if (array[ i ] == elem) return true;
	return false;
}




function createMarker(mpoint, opts, extra)
{
	if (!mapObj) return null;

	markers.push(new GMarker(mpoint, opts));

	var index = markers.length-1;
	markers[index].zIndex = 100; // начальное згначение z-index маркера, будет увеличиваться при каждом клике на него

	GEvent.addListener(markers[index], "dragstart", function() {
		var marker = this;
		marker.closeInfoWindow();
	});

	GEvent.addListener(markers[index], "dragend", function() {
		var marker = this;
		marker.openInfoWindowHtml(marker.tooltip); // show
	});


	GEvent.addListener(markers[index], "click", function() {
		var marker = this;
		if (marker.tooltip) // already loaded?
		{
			showMarker(marker); // show
		}	else {
			if (typeof(extra.tooltip) == 'undefined')
			{
				GDownloadUrl("mapxml.asp?mod=item&vm=" + viewmode + "&id=" + marker.cat_id, function (html) { // load

					if (viewmode == 's')
						marker.tooltip = html;
					else
						marker.tooltip = '<div class="infownd">' + html +'<br><a href="#" onclick="clearAllMarkers(getMarkerById(' + marker.cat_id + '));return false;">скрыть другие объекты</a></div>';

					showMarker(marker); // show
				});
			}	else {
				marker.tooltip = String(extra.tooltip);
				showMarker(marker); // show
			}

		}

	});

	mapObj.addOverlay(markers[index]);

	return markers[index];
}




function getXMLMarkersObj(opts)
{
	if (opts.div_id.length > 0 || opts.id.length > 0)
		getXMLMarkers("div_id=" + opts.div_id.join(",") + "&id=" + opts.id.join(","));
}



function getXMLMarkers(qs)
{
	if (mapObj && typeof(qs) != 'undefined' && qs.length > 0)
	{
		var qsopts = parseQueryString(qs);

		if (qsopts.div_id.length > 0 || qsopts.id.length > 0)
		{

			if (qsopts.div_id.length > 0) logwnd.show("Идет загрузка объектов. Ждите.."); // Если загружаем целый раздел, то показываем окошко

			if (qsopts.div_id.length > 0) cat_div_arr = cat_div_arr.concat(qsopts.div_id);
			if (qsopts.id.length > 0) cat_id_arr = cat_id_arr.concat(qsopts.id);



			GDownloadUrl("mapxml.asp?div_id=" + qsopts.div_id.join(",") + "&id=" + qsopts.id.join(",") + "&r=" + Math.random(), function(data) {
				var xml = GXml.parse(data);
				var point = null;
				var map = null;

				if (xml.documentElement) {
					var els = xml.documentElement.getElementsByTagName("obj");
					for (var i = 0; i < els.length; i++)
					{
						map = els[i].getElementsByTagName("map")[0];
						point = new GLatLng(parseFloat(map.getAttribute("latitude")), parseFloat(map.getAttribute("longitude")));

						if (point.x == 0 && point.y == 0) continue;

						var icon = new GIcon();

						if (els[i].getAttribute("in")) {
							icon.iconSize = new GSize(els[i].getAttribute("iw"), els[i].getAttribute("ih")); //размеры иконки
							icon.image = els[i].getAttribute("path") + "/" + els[i].getAttribute("in"); // путь к иконке
							icon.iconAnchor = new GPoint(parseInt(els[i].getAttribute("iw") / 2), parseInt(els[i].getAttribute("ih"))); // якорь иконки
							icon.infoWindowAnchor = new GPoint(parseInt(els[i].getAttribute("iw") / 2), parseInt(els[i].getAttribute("ih") / 2)); // якорь для всплывающего окна
						}
						else
						{
							if (els[i].getAttribute("div_in")) {
								icon.iconSize = new GSize(els[i].getAttribute("div_iw"), els[i].getAttribute("div_ih")); //размеры иконки
								icon.image = els[i].getAttribute("path") + "/" + els[i].getAttribute("div_in"); // путь к иконке
								icon.iconAnchor = new GPoint(parseInt(els[i].getAttribute("div_iw") / 2), parseInt(els[i].getAttribute("div_ih"))); // якорь иконки
								icon.infoWindowAnchor = new GPoint(parseInt(els[i].getAttribute("div_iw") / 2), parseInt(els[i].getAttribute("div_ih") / 2)); // якорь для всплывающего окна
							}
							else
							{
								icon.iconSize = new GSize(20, 20); // размеры иконки
								icon.iconAnchor = new GPoint(10, 10); // якорь иконки
								icon.infoWindowAnchor = new GPoint(10, 10); // якорь для всплывающего окна
								if (els[i].getAttribute("new") == "Y")
									icon.image = "img/flag_red.png"; // путь к иконке
								else
									icon.image = "img/flag_green.png"; // путь к иконке
							}
						}
						var marker = createMarker(point, {draggable: false, icon: icon, zIndexProcess: function (marker, unused) {return marker.zIndex;}}, {});
						marker.cat_id = els[i].getAttribute("id");
						marker.cat_div = els[i].getAttribute("div");

						// дополнительные параметры
						marker.zoom = parseInt(map.getAttribute("zoom"));
						marker.maptype = map.getAttribute("type");
					}
				}

				updateMarkersCount();
				updateLinkMap();
				logwnd.hide();

				if (qsopts.lastid > 0) clickMarkerById(qsopts.lastid); // show info window for first marker

			});

		} else {
			if (qsopts.lastid > 0) clickMarkerById(qsopts.lastid); // show hidden info window
		}
	}
}


function showMarker(marker) // Используется в обработчике события click
{
	//if (typeof(marker.zoom) != 'undefined') mapObj.setZoom(marker.zoom); // Устанавливает масштаб карты по данным из маркера
	//mapObj.panTo(marker.getPoint());
	//if (typeof(marker.maptype) != 'undefined') mapObj.setMapType(decodeMaptype(marker.maptype)); // Устанавливает тип карты по данным из маркера

	//marker.zIndex += 1;
	setToFront(marker);

	if (viewmode == 's') { // Если маленькое окно карты
		ew.openOnMarker(marker, marker.tooltip); // Кастомный тултип
		mapObj.panTo(marker.getPoint());				 // Центрирование карты на маркере
	}
	else
	{
		marker.openInfoWindowHtml(marker.tooltip); // Стандартный тултип
	}

}



// Помещает маркер наверх
function setToFront(marker) {

	if (!mapObj || markers.length == 0) return false;

	for (var i = 0; i < markers.length; i++) {
		if (markers[i].zIndex > 100)
		{
			markers[i].zIndex = 100;
			//markers[i].redraw(true); // не помогает, нужно оверлеить
			mapObj.removeOverlay(markers[i]);
			mapObj.addOverlay(markers[i]);
			//console.log(markers[i].cat_id + " - " + markers[i].zIndex);
		}
	}

	if (typeof(marker) != 'undefined')
	{
		marker.zIndex = 1000000; // над всеми
		//marker.redraw(true); // перерисуется сам при выводе инфоокна
		//console.log(marker.cat_id + " - " + marker.zIndex);
	}
}



function clickMarkerById(id)
{
	var marker = getMarkerById(id);
	if (marker)
		GEvent.trigger(marker, "click"); // Вызвать обработчик события
	else
		mapObj.closeInfoWindow(); // Скрывает все открытые на данный момент информационные окна маркеров
}


function getMarkerById(id) {
	for (var i = 0; i < markers.length; i++)
		if (markers[i].cat_id == id) return markers[i];
	//alert("Маркер не найден " + id)
	return null;
}


function updateMarkersCount()	{
	e("markerscount").innerHTML = markers.length;
}


function clearAllMarkers(except) {
	if (!mapObj || markers.length == 0) return false;
	if (typeof(except) == 'undefined') except = null;
	for (var i = 0; i < markers.length; i++) {
		if (markers[i] == except) continue;
		mapObj.removeOverlay(markers[i]);
	}

	cat_div_arr = [];
	cat_id_arr = [];

	if (except)
	{
		cat_id_arr = [except.cat_id];
		markers = [except];
	}
	else
		markers = [];

	updateMarkersCount();
	updateLinkMap();
}



function saveMarkerPosition() {
	if (markers[0]) {
		if (window.opener && !window.opener.closed) {
			if (typeof opener.saveMarkerPoint != 'undefined') {
				opener.saveMarkerPoint(field, markers[0].getPoint().lat(), markers[0].getPoint().lng(), mapObj.getZoom(), mapObj.getCurrentMapType().getUrlArg());
			}
		}
	}
}




function showPanel()
{
	if (clearmarkers) clearmarkers.show();
	e("panelhidearrow").style.display = "";
	e("panelshowarrow").style.display = "none";
	e("panel").style.left = "0px";
	e("paneltoggle").style.left = (leftPanelWidth - 8) + "px";
	e("map").style.marginLeft = "0px";

	if (overviewmap)
	{
		mapObj.removeControl(overviewmap);
		overviewmap = new GOverviewMapControl();
		mapObj.addControl(overviewmap);
	}
}

function hidePanel()
{
	e("panelhidearrow").style.display = "none";
	e("panelshowarrow").style.display = "";
	e("panel").style.left = "-" + leftPanelWidth + "px";
	e("paneltoggle").style.left = "0px";
	e("map").style.marginLeft = "-" + (leftPanelWidth-8) + "px";

	if (overviewmap)
	{
		mapObj.removeControl(overviewmap);
		overviewmap = new GOverviewMapControl();
		mapObj.addControl(overviewmap);
	}
}

function isPanelShow()
{
	return e("panelhidearrow") && e("panelhidearrow").style.display != 'none';
}

function togglePanel()
{
	if (isPanelShow()) hidePanel(); else showPanel();
}


function getWindowHeight()
{
	if (window.self && self.innerHeight)
	{
		return self.innerHeight;
	}
	if (document.documentElement && document.documentElement.clientHeight)
	{
		return document.documentElement.clientHeight;
	}
	return 0;
}

function resizeApp()
{
	var offsetTop = 0;
	var mapElem = e("map");
	if (mapElem)
	{
		for (var elem = mapElem; elem; elem = elem.offsetParent)
		{
			offsetTop += elem.offsetTop;
		}
		var height = getWindowHeight() - offsetTop;
		if (height >= 0)
		{
			mapElem.style.height = height + "px";
		}
	}

	offsetTop = 0;
	var panelElem = e("paneltoggle");
	if (panelElem)
	{
		for (var elem = panelElem; elem; elem = elem.offsetParent)
		{
			offsetTop += elem.offsetTop;
		}
		var height = getWindowHeight() - offsetTop;
		if (height >= 0)
		{
			panelElem.style.visibility = "visible";
			panelElem.style.height = height + "px";
			panelElem.style.paddingTop = (height/2-4) + "px";
		}
	}

	if (logwnd) logwnd.setCenterPosition();
}

function distanceRemovePoint(lng, lat)
{
	var canvas = e("distancecanvas");
	var i = -1;

	if (mapObj && canvas && canvas.style.display != 'none')
	{
		if (typeof lat == 'undefined' && typeof lng == 'undefined')
		{
			i = canvas.markers.length - 1;
		}
		else
		{
			for (var j = 0; j < canvas.markers.length; j++)
			{
				if (canvas.markers[j].getPoint().x == lat && canvas.markers[j].getPoint().y == lng)
				{
					i = j;
					break;
				}
			}
		}

		if (i != -1)
		{
			mapObj.removeOverlay(canvas.markers[i]);
			canvas.markers.splice(i, 1);
			canvas.redraw();

			if (canvas.markers.length > 0) {
				canvas.markers[0].setImage('http://maps.google.com/mapfiles/dd-start.png'); // Новый старт
				if (canvas.markers.length > 1) canvas.markers[canvas.markers.length-1].setImage('http://maps.google.com/mapfiles/dd-end.png');  // Новый финиш
				GEvent.trigger(canvas.markers[canvas.markers.length-1], "click");
			}
		}
	}
}

function toggleDistance()
{
	var canvas = e("distancecanvas");

	if (canvas && canvas.style.display == 'none')
	{ //show
		canvas.style.display = '';

		canvas.clearDistance = function() {
			if (typeof(canvas.markers) != "undefined")
			{
				for (var i = 0; i < canvas.markers.length; i++)
				{
					mapObj.removeOverlay(canvas.markers[i]);
				}
			}
			canvas.markers = [];
			e("distancelength").innerHTML = '0 км';
			if (canvas.polyline) mapObj.removeOverlay(canvas.polyline);
		};

		canvas.clearDistance();

		GEvent.addDomListener(e("distanceclear"), "click", canvas.clearDistance);

		GEvent.addDomListener(e("distancelast"), "click", function() {
			if (canvas.markers.length > 1)
			{
				distanceRemovePoint();
			}
		});

		canvas.redraw = function()
		{
			if (canvas.polyline) mapObj.removeOverlay(canvas.polyline);
			var points = [];
			for (var i = 0; i < canvas.markers.length; i++)
			{
				canvas.markers[i].distance = canvas.markers[i].getPoint().distanceFrom(canvas.markers[i].getPoint());
				if (i > 0) canvas.markers[i].distance = canvas.markers[i-1].distance + canvas.markers[i].getPoint().distanceFrom(canvas.markers[i-1].getPoint());
				points.push(canvas.markers[i].getPoint());
			}
			canvas.polyline = new GPolyline(points, "#ff0000", 8, 0.45, {"geodesic": true});
			mapObj.addOverlay(canvas.polyline);
			e("distancelength").innerHTML = (canvas.polyline.getLength() /1000).toFixed(2) + ' км';

			if (canvas.markers.length > 2)
			{
				for (var i = 1; i < canvas.markers.length-1; i++)
				{
					canvas.markers[i].setImage('http://maps.google.com/mapfiles/dd-pause.png');
				}
			}
		}

		canvas.clickMapListener = GEvent.addListener(mapObj, "click", function(marker, point) {
			if (!marker)
			{
				if (canvas.markers.length == 0)
					var icon = new GIcon(G_DEFAULT_ICON, 'http://maps.google.com/mapfiles/dd-start.png');
				else
					var icon = new GIcon(G_DEFAULT_ICON, 'http://maps.google.com/mapfiles/dd-end.png');

				canvas.markers.push(new GMarker(point, {draggable: true, icon: icon}));

				canvas.redraw();

				if (canvas.markers.length == 1)
					var title = "<div class=\"distinfownd\"><b>Координаты начальной точки (ш, д)</b><br>{pointf_y}, {pointf_x}" +
						"<br><a href=\"#\" onclick=\"distanceRemovePoint({point_y},{point_x}); return false;\">Удалить эту точку</a>";
				else
					var title = '<div class="distinfownd"><b>Координаты точки (ш, д)</b><br>{pointf_y}, {pointf_x}<br>' +
						"<b>Расстояние от начальной точки</b><br>{distance}" +
						"<br><a href=\"#\" onclick=\"distanceRemovePoint({point_y},{point_x}); return false;\">Удалить эту точку</a>";



				GEvent.addListener(canvas.markers[canvas.markers.length-1], "dragstart", function() {
					mapObj.closeInfoWindow();
				});

				GEvent.addListener(canvas.markers[canvas.markers.length-1], "dragend", function() {
					GEvent.trigger(this, "click");
				});

				GEvent.addListener(canvas.markers[canvas.markers.length-1], "changed", function(marker, prevpos) {
					canvas.redraw();
					GEvent.trigger(this, "click");
				});

				GEvent.addListener(canvas.markers[canvas.markers.length-1], "click", function(point) {
					var str = String(title);
					str = String(str).replace(/\{pointf_x\}/gi, (this.getPoint().x).toFixed(5));
					str = String(str).replace(/\{pointf_y\}/gi, (this.getPoint().y).toFixed(5));

					str = String(str).replace(/\{point_x\}/gi, this.getPoint().x);
					str = String(str).replace(/\{point_y\}/gi, this.getPoint().y);

					str = String(str).replace("{distance}", (this.distance / 1000).toFixed(3) + ' км');
					this.openInfoWindowHtml(str);
				});

				mapObj.addOverlay(canvas.markers[canvas.markers.length-1]);
				GEvent.trigger(canvas.markers[canvas.markers.length-1], "click");

			}
		});
	}
	else
	{ //hide
		canvas.style.display = 'none';
		GEvent.removeListener(canvas.clickMapListener);
		canvas.clearDistance();
	}
}

function printMap() {
	togglePanel();
	if (overviewmap) overviewmap.hide(true);
	if (clearmarkers) clearmarkers.hide();
	if (printmap) printmap.show();
	setTimeout(function(){window.print()}, 2000);
}



function updateLinkMap() {
	if (mapObj && e("linkmap"))
	{
		e("linkmap").href = "http://odintsovo.biz/map/?lat=" + mapObj.getCenter().lat() + "&lng=" + mapObj.getCenter().lng() + "&zoom=" + mapObj.getZoom() + "&type=" + mapObj.getCurrentMapType().getUrlArg();
		if (cat_div_arr.length > 0) e("linkmap").href += "&div_id=" + cat_div_arr.join(",");
		if (cat_id_arr.length > 0) e("linkmap").href += "&id=" + cat_id_arr.join(",");
	}
}


































/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @name ExtMapTypeControl
 * @version 1.5
 * @copyright 2007 Google
 * @author Pamela Fox, others
 * Added More button and buttons to save and restore position functionality.
 * This version of ExtMapTypeControl can also be used together with
 * GMapTypeControl, GHierarchicalMapTypeControl, GMenuMapTypeControl,
 * setUIToDefault and all self created map type buttons.
 * @copyright 2009 Wolfgang Pichler
 * @author Wolfgang Pichler (Pil), www.wolfpil.de
 * @fileoverview
 * <p>This class lets you add a control to the map which mimics GMapTypeControl
 * and allows for the addition of a traffic button/traffic key
 * plus a more button to display additional layers
 * and buttons to save the map position and return to it.
 * </p>
 */

/**
 * @name ExtMapTypeControlOptions
 * @class This class represents optional arguments to {@link ExtMapTypeControl}.
 * @desc Either option 'useMapTypes' or 'posRight' should be used.
 * @property {Boolean} [useMapTypes] Controls whether map type buttons are shown.
 * @property {Integer} [posRight] Defines the spacing in pixels of the button from the right map border.
 * @property {Boolean} [showTraffic] Controls whether traffic button is shown.
 * @property {Boolean} [showTrafficKey] Controls whether traffic key is shown.
 * @property {Boolean} [showMore] Controls whether more button is shown.
 * @property {Boolean} [showSave] Controls whether save/return buttons are shown.
 */

/**
 * @desc Creates a control with options specified in {@link ExtMapTypeControlOptions}.
 * @param {ExtMapTypeControlOptions} [opt_opts] Named optional arguments.
 * @constructor
 */
function ExtMapTypeControl(opt_opts) {
  this.options = opt_opts || {};
}

/**
 * Extends GOverlay class from the Google Maps API
 */
ExtMapTypeControl.prototype = new GControl();

/**
 * @desc Initialize the control on the map.
 * Creates the buttons for the map types and appends them to the map.
 * @param {GMap2} map The map that has had this ExtMapTypeControl added to it.
 * @return {DOM Object} Div that holds the map types buttons
 * @private
 */
ExtMapTypeControl.prototype.initialize = function(map) {
 var me = this;
 var extDiv = document.createElement("div");
 var posX;

 if (me.options.useMapTypes) {
  var mapTypes = map.getMapTypes();
  var mapTypeDivs = me.addMapTypeButtons_(map);

  GEvent.addListener(map, "addmaptype", function() {
    var newMapTypes = map.getMapTypes();
    var newMapType = newMapTypes.pop();
    var newMapTypeDiv = me.createButton_(newMapType.getName());
    newMapTypeDiv.setAttribute('title', newMapType.getAlt());
    mapTypes.push(newMapType);
    mapTypeDivs.push(newMapTypeDiv);
    me.resetButtonEvents_(map, mapTypeDivs);
    extDiv.appendChild(newMapTypeDiv);
  });
  GEvent.addListener(map, "removemaptype", function() {
    for (var i = 0; i < mapTypeDivs.length; i++) {
      GEvent.clearListeners(mapTypeDivs[i], "click");
      extDiv.removeChild(mapTypeDivs[i]);
    }
    mapTypeDivs = me.addMapTypeButtons_(map);
    me.resetButtonEvents_(map, mapTypeDivs);
    for (var i = 0; i < mapTypeDivs.length; i++) {
      extDiv.appendChild(mapTypeDivs[i]);
    }
  });

   for (var i = 0; i < mapTypeDivs.length; i++) {
    me.toggleButton_(mapTypeDivs[i].firstChild, false);
    extDiv.appendChild(mapTypeDivs[i]);
    if(map.getCurrentMapType().getName() == mapTypeDivs[i].name) {
     me.toggleButton_(mapTypeDivs[i].firstChild, true);
    }
   }
   map.getContainer().appendChild(extDiv);
   // Sets the proper spaces between the buttons
   posX = 98;
   switch (mapTypes.length) {
    case 2: posX += 72; break;
    case 3: posX += 142; break;
    case 4: posX += 213; break;
   }
 }
  else {
   // If no options were defined, 'posRight: 220' is assumed.
   posX = me.options.posRight || 220;
  }

  /*
  *  Loads SavePosControl when specified as option
  */
  if (me.options.showSave) {
   map.addControl(new SavePosControl(),
    new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 31)));
  }

  /*
  *  Loads MoreControl when specified as option
  */
  if (me.options.showMore) {
   var layers = [
    { name: "Фото", obj: new GLayer("com.panoramio.all") },
    { name: "Видео", obj: new GLayer("com.youtube.all") },
    { name: "Wikipedia", obj: new GLayer("org.wikipedia.en") }
	 //,{ name: "Webcams", obj: new GLayer("com.google.webcams") }
   ];

   map.addControl(new MoreControl(layers),
    new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(posX, 7)));
  }

  /*
  *  Loads TrafficControl when specified as option
  */
  if (me.options.showTraffic) {
    // Moves traffic button to the left if more button should also be displayed
    if(me.options.showMore)
     posX += 94;
    map.addControl(new TrafficControl(me.options),
     new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(posX, 7)));
  }
  return extDiv;
};

/**
 * Creates buttons for map types.
 * @param {GMap2} Map object for which to create buttons.
 * @return {Array} Divs containing the buttons.
 * @private
 */
ExtMapTypeControl.prototype.addMapTypeButtons_ = function(map) {
  var me = this;
  var mapTypes = map.getMapTypes();
  var mapTypeDivs = [];
  for (var i = 0; i < mapTypes.length; i++) {
    mapTypeDivs[i] = me.createButton_(mapTypes[i].getName());
    mapTypeDivs[i].name = mapTypes[i].getName();
    mapTypeDivs[i].setAttribute('title', mapTypes[i].getAlt());
  }
  me.resetButtonEvents_(map, mapTypeDivs);
  return mapTypeDivs;
};

/**
 * Ensures that map type button events are assigned correctly.
 * @param {GMap2} Map object for which to reset events.
 * @param {Array} mapTypeDivs Divs containing map type buttons.
 * @private
 */
ExtMapTypeControl.prototype.resetButtonEvents_ = function(map, mapTypeDivs) {
  var me = this;
  var mapTypes = map.getMapTypes();
  for (var i = 0; i < mapTypeDivs.length; i++) {
    var otherDivs = [];
    for (var j = 0; j < mapTypes.length; j++) {
      if (j != i) {
        otherDivs.push(mapTypeDivs[j]);
      }
    }
    me.assignButtonEvent_(mapTypeDivs[i], map, mapTypes[i], otherDivs);
  }
  GEvent.addListener(map, "maptypechanged", function() {
    var divIndex = 0;
    var mapType = map.getCurrentMapType();
    for (var i = 0; i < mapTypes.length; i++) {
      if (mapTypes[i] == mapType) {
        divIndex = i;
      }
    }
    GEvent.trigger(mapTypeDivs[divIndex], "click");
  });
};

/**
 * Creates buttons with text nodes.
 * @param {String} text Text to display in button
 * @return {DOM Object} The div for the button.
 * @private
 */
ExtMapTypeControl.prototype.createButton_ = function(text) {
  var buttonDiv = document.createElement("div");
  this.setButtonStyle_(buttonDiv);
  buttonDiv.style.cssFloat = "left";
  buttonDiv.style.styleFloat = "left";
  var textDiv = document.createElement("div");
  textDiv.appendChild(document.createTextNode(text));
  textDiv.style.width = "6em";
  buttonDiv.appendChild(textDiv);
  return buttonDiv;
};

/**
 * Assigns events to MapType buttons to change maptype
 * and toggle button styles correctly for all buttons
 * when button is clicked.
 * @param {DOM Object} div Button's div to assign click to
 * @param {GMap2} Map object to change maptype of.
 * @param {Object} mapType GMapType to change map to when clicked
 * @param {Array} otherDivs Array of other button divs to toggle off
 * @private
 */
ExtMapTypeControl.prototype.assignButtonEvent_ = function(div, map, mapType, otherDivs) {
  var me = this;
  GEvent.addDomListener(div, "click", function() {
    for (var i = 0; i < otherDivs.length; i++) {
      me.toggleButton_(otherDivs[i].firstChild, false);
    }
    me.toggleButton_(div.firstChild, true);
    map.setMapType(mapType);
  });
};

/**
 * Changes style of button to appear on/off depending on boolean passed in.
 * @param {DOM Object} div inner button div to change style of
 * @param {Boolean} boolCheck Used to decide to use on style or off style
 * @private
 */
ExtMapTypeControl.prototype.toggleButton_ = function(div, boolCheck) {
  div.style.fontWeight = boolCheck ? "bold" : "normal";
  div.style.border = boolCheck ? "1px solid #483d8b" : "1px solid #fff";
  var shadow = boolCheck ? "#6495ed" : "#c0c0c0";
  var edges = ["RightColor", "BottomColor"];
   for (var j = 0; j < edges.length; j++) {
    div.style["border" + edges[j]] = shadow;
   }
};

/**
 * Required by GMaps API for controls.
 * @return {GControlPosition} Default location for map types buttons
 * @private
 */
ExtMapTypeControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 7));
};

/**
 * Sets the proper CSS for the given button element.
 * @param {DOM Object} button Button div to set style for
 * @private
 */
ExtMapTypeControl.prototype.setButtonStyle_ = function(button) {
  button.style.color = "#000000";
  button.style.backgroundColor = "white";
  button.style.font = "small Arial";
  button.style.border = "1px solid black";
  button.style.padding = "0px";
  button.style.margin= "0px";
  button.style.textAlign = "center";
  button.style.fontSize = "12px";
  button.style.cursor = "pointer";
};

/**
 * @desc Constructor for TrafficControl.
 * Option hash to decide whether traffic key is shown.
 */
function TrafficControl(opt_opts) {
  this.options = opt_opts;
}

/*
 * It's more efficient to inherit ExtMapTypeControl's prototypes only
 */
function Inherit() {};
Inherit.prototype = ExtMapTypeControl.prototype;
TrafficControl.prototype = new Inherit();

/**
 * Creates the div that holds the traffic button
 * and - if specified - appends the div that holds the traffic key.
 * @param {GMap2} map The map that has had this Control added to it.
 * @return {DOM Object} Div that holds the button.
 * @private
 */
TrafficControl.prototype.initialize = function(map) {
  var me = this;
  var trafficDiv = me.createButton_("Пробки");
  trafficDiv.setAttribute('title', 'Показать пробки');
  trafficDiv.style.visibility = "hidden";
  trafficDiv.style.width = "6em";
  trafficDiv.firstChild.style.cssFloat = "left";
  trafficDiv.firstChild.style.styleFloat = "left";
  me.toggleButton_(trafficDiv.firstChild, false);

  // Sending true makes traffic overlay hidden by default
  var trafficInfo = new GTrafficOverlay({hide: true});
  trafficInfo.hidden = true;

  // Checks whether traffic data is available in viewport,
  // shows and hides the traffic button accordingly.
  GEvent.addListener(trafficInfo, "changed", function(hasTrafficInView) {
   if (hasTrafficInView) {
      trafficDiv.style.visibility = "visible";
   } else {
      trafficDiv.style.visibility = "hidden";
     }
  });
  map.addOverlay(trafficInfo);

  GEvent.addDomListener(trafficDiv.firstChild, "click", function() {
    if (trafficInfo.hidden) {
     trafficInfo.hidden = false;
     trafficInfo.show();
    } else {
     trafficInfo.hidden = true;
     trafficInfo.hide();
    }
    me.toggleButton_(trafficDiv.firstChild, !trafficInfo.hidden);
  });

  /*
  *  Appends traffic key when defined as option
  */
  if(me.options.showTrafficKey) {
   trafficDiv.style.width = "7.8em";
   var keyDiv = document.createElement("div");
   keyDiv.style.width = "1.5em";
   keyDiv.style.position = "absolute";
   keyDiv.style.top = "0px";
   keyDiv.style.right = "0px";
   keyDiv.innerHTML = "?";

   var keyExpandedDiv = document.createElement("div");
   keyExpandedDiv.style.clear = "both";
   keyExpandedDiv.style.padding = "2px";

	var keyInfo = [{"color": "#30ac3e", "text": "&gt; 80 км/ч"},
                 {"color": "#ffcf00", "text": "40-80 км/ч"},
                 {"color": "#ff0000", "text": "&lt; 40 км/ч"}];


    for (var i = 0; i < keyInfo.length; i++) {
      keyExpandedDiv.innerHTML += "<div style='text-align: left'><span style='background-color: " + keyInfo[i].color + "'>&nbsp;&nbsp;</span>"
    +  "<span style='color: " + keyInfo[i].color + "'> " + keyInfo[i].text + " </span>" + "</div>";
    }
    keyExpandedDiv.style.display = "none";

    GEvent.addDomListener(keyDiv, "click", function() {
      if (me.keyExpanded) {
        me.keyExpanded = false;
        keyExpandedDiv.style.display = "none";
      }
       else {
        me.keyExpanded = true;
        keyExpandedDiv.style.display = "block";
       }
       me.toggleButton_(keyDiv, me.keyExpanded);
    });
    me.toggleButton_(keyDiv, me.keyExpanded);
    trafficDiv.appendChild(keyDiv);
    trafficDiv.appendChild(keyExpandedDiv);
  }
  map.getContainer().appendChild(trafficDiv);
  return trafficDiv;
};


/**
 * @desc Constructor for MoreControl.
 * Immutable shared property moved to prototype.
 */
function MoreControl(layers) {
  MoreControl.prototype.layers = layers;
  this.chosen_ = [];
  this.boxes_ = [];
}

/**
* Inherits ExtMapTypeControl's prototypes only
*/
MoreControl.prototype = new Inherit();

/*
 * Primarily creates an outer div that holds
 * all necessary elements needed for the more button.
 * @param {GMap2} map The map that has had this Control added to it.
 * @return {DOM Object} Div that holds all button elements.
 * @private
 */
MoreControl.prototype.initialize = function(map) {
  var me = this;
  me.map_ = map;
  var outer = document.createElement("div");
  me.moreDiv = me.createButton_("Больше...");
  me.moreDiv.setAttribute("title", "Показать/скрыть слой");
  me.moreDiv.firstChild.style.width = "7em";
  me.toggleButton_(me.moreDiv.firstChild, false);
  outer.appendChild(me.moreDiv);
  outer.appendChild(me.createLayerBox_());

  GEvent.addDomListener(outer, "mouseover", function() {
  if(window.timer) clearTimeout(timer);
   me.layerboxDiv.style.display = "block";
   me.moreDiv.firstChild.style.height = "23px";
  });
  GEvent.addDomListener(outer, "mouseout", function() {
   timer = window.setTimeout(function() {
    me.layerboxDiv.style.display = "none";
    me.moreDiv.firstChild.style.height = "";
   }, 300);
  });
  GEvent.addDomListener(me.moreDiv, "click", function() {
   if(me.chosen_.length > 0 ) {
    /* Makes an independent copy of chosen array since it will be
    *  reset by switchLayer, which might not be useful here
    */
    var copy = me.chosen_.slice();
    for(var i = 0; i < copy.length; i++) {
     var index = parseInt(copy[i]);
     me.switchLayer_(true, me.layers[index].obj);
     me.boxes_[index].checked = true;
    }
   }
   else {
    me.hideAll_();
   }
  });
 map.getContainer().appendChild(outer);
 return outer;
};

/**
 * Primarily creates the outer div that holds the checkboxes.
 * @return {DOM Object} Div that holds all elements underneath the More...Button.
 * @private
 */
MoreControl.prototype.createLayerBox_ = function() {
  var me = this;
  me.layerboxDiv = document.createElement("div");
  // For nested elements position:absolute means relative to its parent
  me.layerboxDiv.style.position = "absolute";
  me.layerboxDiv.style.top = "20px";
  me.layerboxDiv.style.left = "0px";
  me.layerboxDiv.style.marginTop = "-1px";
  me.layerboxDiv.style.font = "small Arial";
  me.layerboxDiv.style.fontSize = "12px";
  me.layerboxDiv.style.padding = "4px";
  me.layerboxDiv.style.width = "120px";
  me.layerboxDiv.style.color = "#000";
  me.layerboxDiv.style.backgroundColor = "#fff";
  me.layerboxDiv.style.border = "1px solid gray";
  me.layerboxDiv.style.borderTopColor = "#e2e2e2";
  me.layerboxDiv.style.cursor = "default";

  var input = [];
  for (var i = 0; i < me.layers.length; i++) {
   input[i] = me.createCheckbox_(i, me.layers[i].name);
   me.layerboxDiv.appendChild(input[i] );
  }

  var ruler = document.createElement("hr");
  ruler.style.width = "92%";
  ruler.style.height = "1px";
  ruler.style.textAlign = "center";
  ruler.style.border = "1px";
  ruler.style.color = "#e2e2e2";
  ruler.style.backgroundColor = "#e2e2e2";
  var boxlink = document.createElement("a");
  boxlink.setAttribute("href", "javascript:void(0)");
  boxlink.style.color = "#a5a5a5";
  boxlink.style.textDecoration = "none";
  boxlink.style.cursor = "default";
  boxlink.style.marginLeft = "33px";
  boxlink.appendChild(document.createTextNode("Скрыть все"));

  me.layerboxDiv.appendChild(ruler);
  me.layerboxDiv.appendChild(boxlink);

  GEvent.addDomListener(boxlink, "click", function() {
   me.hideAll_();
  });
  me.layerboxDiv.style.display = "none";
  return me.layerboxDiv;
};

/**
 * Creates checkboxes with a click event inside of a div element.
 * @param {Number} nr The array index of the layers array
 * @param {String} name The name of the layer the checkbox belongs to
 * @return {DOM Object} Div that holds the checkbox and its related text node
 * @private
 */
MoreControl.prototype.createCheckbox_ = function(nr, name) {
  var me = this;
  var innerDiv = document.createElement("div");
	innerDiv.style.textAlign = "left"; //added
  var checkbox = document.createElement("input");
  checkbox.setAttribute("type", "checkbox");
	checkbox.style.marginRight = "4px";
	var label = document.createElement("label");
	label.appendChild(checkbox);
	label.appendChild(document.createTextNode(name));
	label.style.clear = "both";
	innerDiv.appendChild(label);

  me.boxes_.push(checkbox);

  GEvent.addDomListener(checkbox, "click", function() {
   me.switchLayer_(this.checked, me.layers[nr].obj);
  });
  return innerDiv;
};

/**
 * Changes style of layerbox to appear on/off depending on passed boolean.
 * @param {DOM Object} elem element to change style of
 * @param {Boolean} boolCheck Used to decide between on or off style
 * @private
 */
MoreControl.prototype.toggleBox_ = function(elem, boolCheck) {
  elem.style.borderWidth = boolCheck ? "2px": "1px";
  elem.style.width = boolCheck ? "119px" : "120px";
};

/**
 * Adds and removes the chosen layers to/from the map.
 * Styles the link inside the layer box and the more button accordingly.
 * @param {Boolean} checked Value of checked or unchecked checkbox
 * @param {Object} layer The GLayer object to add or to remove
 * @private
 */
MoreControl.prototype.switchLayer_ = function(checked, layer) {
  var me = this;
  var link = me.layerboxDiv.lastChild;
  var button = me.moreDiv.firstChild;
  if(checked) {
   me.map_.addOverlay(layer);
   // Resets chosen array
   me.chosen_.length = 0;
   /*
   *  Toggles the elements
   */
   link.style.color = "#0000cd";
   link.style.textDecoration = "underline";
   link.style.cursor = "pointer";
   me.toggleButton_(button, true);
   me.toggleBox_(me.layerboxDiv, true);
  }
  else {
   me.map_.removeOverlay(layer);
   /*  Resets the elements
    * if all checkboxes were unchecked
   */
   if(!me.checkChecked()) {
    link.style.color = "#a5a5a5";
    link.style.textDecoration = "none";
    link.style.cursor = "default";
    me.toggleButton_(button, false);
    me.toggleBox_(me.layerboxDiv, false);
   }
  }
};

/**
 * Calls switchLayer to remove all displayed layers.
 * Stores index of removed layers in chosen array.
 * @private
 */
MoreControl.prototype.hideAll_ = function() {
  var me = this;
  for(var i = 0; i < me.boxes_.length; i++) {
   if(me.boxes_[i].checked) {
    me.boxes_[i].checked = false;
    me.switchLayer_(false, me.layers[i].obj);
    me.chosen_.push(i);
   }
  }
};

/**
 * Returns true if a checkbox is still checked, otherwise false.
 * @return {Boolean}
 */
MoreControl.prototype.checkChecked = function() {
  var me = this;
  for(var i = 0; i < me.boxes_.length; i++) {
   if(me.boxes_[i].checked) return true;
  }
  return false;
};


/**
 * @desc Constructor for SavePosControl.
 */
function SavePosControl() {};

/**
* Inherits ExtMapTypeControl's prototypes only
*/
SavePosControl.prototype = new Inherit();

/**
 * Creates the buttons for saving position and the back button.
 * @param {GMap2} map The map that has had this Control added to it.
 * @return {DOM Object} Div that holds both buttons.
 * @private
 */
SavePosControl.prototype.initialize = function(map) {
  var me = this;
  var saved = [];
  var saveDiv = document.createElement("div");
  var saveButtonDiv = document.createElement("div");

  saveButtonDiv.setAttribute("title", "Save actual position and zoomlevel");
  me.setButtonStyle_(saveButtonDiv);
  // Overwrites a few 'normal' styles of these buttons
  saveButtonDiv.style.width = "7em";
  saveButtonDiv.style.padding = "1px";
  saveButtonDiv.style.marginBottom = "4px";
  saveButtonDiv.style.whiteSpace = "nowrap";
  saveButtonDiv.appendChild(document.createTextNode("Save Position"));
  saveDiv.appendChild(saveButtonDiv);
  var backButtonDiv = document.createElement("div");
  backButtonDiv.setAttribute("title", "Back to saved position");
  me.setButtonStyle_(backButtonDiv);
  backButtonDiv.style.width = "7em";
  backButtonDiv.style.padding = "1px";
  backButtonDiv.appendChild(document.createTextNode("To Saved"));
  saveDiv.appendChild(backButtonDiv);

  GEvent.addDomListener(saveButtonDiv, "click", function() {
   var center = map.getCenter();
   var zoom = map.getZoom();
   saved.splice(0, 2, center, zoom);
   alert("Saved Position: "+ center.toUrlValue()+ "\nZoomlevel: "+ zoom);
  });
  GEvent.addDomListener(backButtonDiv, "click", function() {
   if (saved.length > 0) {
    map.setZoom(saved[1]);
    map.panTo(saved[0]);
   }
  });
 map.getContainer().appendChild(saveDiv);
 return saveDiv;
};





























































/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @name ContextMenuControl
 * @version 1.0
 * @copyright 2009 Wolfgang Pichler
 * @author Wolfgang Pichler (Pil), www.wolfpil.de
 * @fileoverview
 * <p>This class lets you add a control to the map which mimics
 * the context menu of Google Maps. The control supports all of the six
 * items supported by Google Maps: finding directions to/from a location,
 * zooming in/out, centering the map, and finding the address at the clicked
 * location. Any of these items may be suppressed by passing options into
 * the constructor. This control extends the
 * <a href="http://www.google.com/apis/maps/documentation/reference.html#GControl">
 * GControl</a> interface.</p>
 * <p>Note: ContextMenuControl doesn't work in Opera because Opera doesn't
 * support the oncontextmenu event and doesn't give access to right mouse clicks.
 * </p>
 */

/**
 * @name ContextMenuControlOptions
 * @class This class represents optional arguments to {@link ContextMenuControl}.
 * @property {Boolean} [dirsFrom = true] Shows "Directions from here" item.
 * @property {Boolean} [dirsTo = true] Shows "Directions to here" item.
 * @property {Boolean} [zoomIn = true] Shows "Zoom in" item.
 * @property {Boolean} [zoomOut = true] Shows "Zoom out" item.
 * @property {Boolean} [centerMap = true] "Shows "Center map here" item.
 * @property {Boolean} [whatsHere = true] "Shows "What's here?" item.
 */

/**
 * @desc Creates a control with options specified in {@link ContextMenuControlOptions}.
 * @param {ContextMenuControlOptions} [opt_opts] Named optional arguments.
 * @constructor
 */
function ContextMenuControl(opt_opts) {
  if (opt_opts) {
    this.options = {
      dirsFrom: opt_opts.dirsFrom,
      dirsTo: opt_opts.dirsTo,
      zoomIn: opt_opts.zoomIn,
      zoomOut: opt_opts.zoomOut,
      centerMap: opt_opts.centerMap,
      whatsHere: opt_opts.whatsHere
    };
  } else {
    this.options = {};
  }
  this.dirmarks_ = [];
  this.heremarks_ = [];
  this.letterindex_ = -1;
}

/**
 * Extends GOverlay class from the Google Maps API.
 *  Second param (selectable) should be set to true
 * @private
 */
ContextMenuControl.prototype = new GControl(false, true);

/**
 * @desc Initialize the control on the map.
 * @param {GMap2} map The map that has had this control added to
 * @return {Element} mapdiv Div that holds the map
 * @private
 */
ContextMenuControl.prototype.initialize = function (map) {
  var me = this;
  me.map_ = map;
  var mapdiv = map.getContainer();
	var marker;

  // Prevents the browser's own context menu to appear.
  if (mapdiv.addEventListener) {
    mapdiv.addEventListener("contextmenu", function (e) {
      e.stopPropagation();
      e.preventDefault();
      }, false);
  } else if (mapdiv.attachEvent) {
    mapdiv.attachEvent("oncontextmenu", function () {
      var e = window.event;
      e.cancelBubble = true;
      e.returnValue = false;
    });
  }
  me.createContextMenu_();

  // Displays our context menu on single right mouse click
  GEvent.addListener(map, "singlerightclick", function (pixelPoint, src, ov) {
   var d = me.dirmarks_; // массив маркеров направлений
   me.rej_ = null;
   if (d.length > 0) {
     // Right click on a marker
     if (ov instanceof GMarker) {
      for (var i = 0; i < d.length; i++) {
        // If it's a dir marker it should be removable
        if (ov.getLatLng().equals(d[i].getLatLng())) {
          me.rej_ = ov.getLatLng();
          break;
        }
      }
      if (me.rej_) {
        me.rebuildMenu_("remove");
      } else {
       me.rebuildMenu_("add");
      }
     } else  {
      me.rebuildMenu_("add");
     }
   } else {
     me.rebuildMenu_();
   }

    me.clickedPoint_ = map.fromContainerPixelToLatLng(pixelPoint);

    // Correction of IE bug
    var posX = document.all ? (pixelPoint.x - 40) : pixelPoint.x;
    var posY = document.all ? (pixelPoint.y + 10) : pixelPoint.y;

    var mapwidth = map.getSize().width;
    var mapheight = map.getSize().height;
    var menuwidth = me.menuList.offsetWidth;
    var menuheight = me.menuList.offsetHeight;

    // Adjusts the position of the context menu
    if (mapwidth - menuwidth < posX) {
      posX = posX - menuwidth;
    }
    if (mapheight - menuheight < posY) {
      posY = posY - menuheight - 20;
    }
    me.menuList.style.visibility = "visible";
    me.menuList.visible = true;
    var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(posX, posY));
    pos.apply(me.menuList);
  });

  // Closes context menu when the cursor is being moved out of the map.
  // This DomListener is a workaround for Internet Explorer because
  //  the 'normal' GEvent Listener doesn't work correctly in IE.
  GEvent.addDomListener(mapdiv, "mouseout", function (e) {
    if (me.menuList.visible) {
      if (!e) {
        var e = window.event;
      }
      if (me.checkMouseLeave_(mapdiv, e)) {
        me.hideMenu_();
      }
    }
  });

  // Closes context menu in case of a left click on the map
  GEvent.addListener(map, "click", function () {
    me.hideMenu_();
  });

  // Closes context menu after dragging the map
  GEvent.addListener(map, "dragend", function () {
    me.hideMenu_();
  });
  // Return a dummy element to keep the API happy
  return document.createElement("b");
};

/**
 * Creates a draggable marker for searching directions.
 * @param {String} letter Used to decide which icon to display.
 * @private
 */
ContextMenuControl.prototype.searchDirs_ = function (letter) {
  var me = this;
  me.setChosen_(letter);
  var point = me.clickedPoint_;
  var html;

  if (me.actual) {
    var waypoints = [point, me.actual.getLatLng()];
    me.getDirs_(waypoints);
  } else {
    var image = "marker" + letter + ".png";
    var icon = me.createIcon_(image);
    html = me.makeForm_(letter);
    var marker = new GMarker(point, {icon: icon, draggable: true, title: 'Drag', bouncy: false, dragCrossMove: true});
    me.actual = marker;
    me.map_.addOverlay(marker);
    marker.openInfoWindowHtml(html);

    GEvent.addListener(marker, "click", function () {
      html = me.makeForm_(letter);
      marker.openInfoWindowHtml(html);
    });

    GEvent.addListener(marker, "dragstart", function () {
      me.map_.closeInfoWindow();
    });
  }
};

/**
 * Creates an initially hidden unordered menu list.
 * @param {String} change Used to decide which item to replace.
 * @return {Element} ul that holds the list entries of the context menu.
 * @private
 */
ContextMenuControl.prototype.createContextMenu_ = function (change) {
  var me = this;
  me.menuList = document.createElement("ul");
  me.menuList.style.font = "small Arial";
  me.menuList.style.whiteSpace = "nowrap";
  me.menuList.style.color = "#000000";
  me.menuList.style.backgroundColor = "#fff";
  me.menuList.style.listStyle = "none";
  me.menuList.style.padding = "0px";
  me.menuList.style.width = "30ex";
  me.menuList.style.border = "1px solid #666";
  me.menuList.style.position = "absolute";

	me.menuList.style.textAlign = "left";


  if (me.options.dirsFrom !== false && !change) {
    me.menuList.appendChild(me.createListItem_("Как проехать отсюда", "from"));
  }
  if (me.options.dirsTo !== false && !change) {
    me.menuList.appendChild(me.createListItem_("Как проехать сюда", "to"));
  }
  if (change == "add") {
    me.menuList.appendChild(me.createListItem_("Добавить пункт назначения", "add"));
  }
  if (change == "remove") {
    me.menuList.appendChild(me.createListItem_("Удалить этот пункт назначения", "rem"));
  }


  if (me.options.Iam !== false) {
    me.menuList.appendChild(me.createListItem_("Я здесь", "iam"));
  }

/*
  me.menuList.appendChild(me.createRuler_());
  if (me.options.zoomIn !== false) {
    me.menuList.appendChild(me.createListItem_("Приблизить", "in"));
  }
  if (me.options.zoomOut !== false) {
    me.menuList.appendChild(me.createListItem_("Отдалить", "out"));
  }
*/
	if (me.options.addNews !== false) {
    me.menuList.appendChild(me.createListItem_("Добавить новость", "addnews"));
  }
	if (me.options.addPhoto !== false) {
    me.menuList.appendChild(me.createListItem_("Добавить фото", "addphoto"));
  }
	if (me.options.addOrg !== false) {
    me.menuList.appendChild(me.createListItem_("Добавить организацию", "addorg"));
  }
	/*
	if (me.options.centerMap !== false) {
    me.menuList.appendChild(me.createListItem_("Расположить по центру карты", "center"));
  }
  if (me.options.whatsHere !== false) {
    me.menuList.appendChild(me.createListItem_("What\'s here?", "here"));
  }	*/
  me.hideMenu_();
  // Adds context menu to the map container
  me.map_.getContainer().appendChild(me.menuList);
  return me.menuList;
};

/**
 * Avoids firing a mouseout event when the mouse moves over a child element.
 * This will be caused by event bubbling.
 * Borrowed from: http://www.faqts.com/knowledge_base/view.phtml/aid/1606/fid/145
 * @param {Element} element Parent div
 * @param {Event} evt The passed mouse event
 * @return {Boolean}
 * @private
 */
ContextMenuControl.prototype.checkMouseLeave_ = function (element, evt) {
  if (element.contains && evt.toElement) {
    return !element.contains(evt.toElement);
  } else if (evt.relatedTarget) {
    return !this.containsDOM_(element, evt.relatedTarget);
  }
};

/**
 * Checks if the mouse leaves the parent element.
 * @param {Element} container Parent div
 * @param {Event} containee Event of node that the mouse entered when leaving the target
 * @return {Boolean}
 * @private
 */
ContextMenuControl.prototype.containsDOM_ = function (container, containee) {
  var isParent = false;
  do {
    if ((isParent = container == containee)) {
      break;
    }
    containee = containee.parentNode;
  } while(containee != null);
  return isParent;
};








/**
 * Creates clickable context menu list items.
 * @param {String} text Text to display in list item.
 * @param {String} arg Used to identify the clicked entry.
 * @return {Element} List item that holds the entry.
 * @private
 */
ContextMenuControl.prototype.createListItem_ = function (text, arg) {
  var me = this;
  var entry = document.createElement("li");
  entry.style.padding = "2px 6px";
  entry.style.lineHeight = "1.6em";
  entry.appendChild(document.createTextNode(text));

  GEvent.addDomListener(entry, "mouseover", function () {
    entry.style.cursor = "pointer";
    entry.style.backgroundColor = "#d6e9f8";
  });

  GEvent.addDomListener(entry, "mouseout", function () {
    entry.style.cursor = "default";
    entry.style.backgroundColor = "#fff";
  });

  GEvent.addDomListener(entry, "click", function () {
    if (arg == "from") {
      me.searchDirs_("A");
    } else if (arg == "to") {
      me.searchDirs_("B");
    } else if (arg == "add") {
      me.addDest_();
    } else if (arg == "rem") {
      me.removeDest_();
    } else if (arg == "in") {
      me.map_.zoomIn();
    } else if (arg == "out") {
      me.map_.zoomOut();

		} else if (arg == "addnews") {
      var point = me.clickedPoint_;
			var strUrl = '/news/add.asp'
			var strQuery = '?map=' + point.lat() +',' + point.lng() +',' + me.map_.getZoom() +',' + me.map_.getCurrentMapType().getUrlArg();
			top.location.href = strUrl + strQuery;

    } else if (arg == "addphoto") {
      var point = me.clickedPoint_;
			var strUrl = '/photo/edt.asp'
			var strQuery = '?map=' + point.lat() +',' + point.lng() +',' + me.map_.getZoom() +',' + me.map_.getCurrentMapType().getUrlArg();
			top.location.href = strUrl + strQuery;


    } else if (arg == "addorg") {
      var point = me.clickedPoint_;
			var strUrl = 'http://odintsovo.biz/anketa.asp'
			var strQuery = '?map=' + point.lat() +',' + point.lng() +',' + me.map_.getZoom() +',' + me.map_.getCurrentMapType().getUrlArg();
			top.location.href = strUrl + strQuery;
    } else if (arg == "center") {
      var point = me.clickedPoint_;
      me.map_.panTo(point);
    } else if (arg == "here") {
      me.getReverseGeocode_();
    }
    // Hides the menu after it's been used
    me.hideMenu_();
  });
  return entry;
};

/**
 * Removes direction markers on contextual click.
 * Creates a new route when there are still more than
 * two markers on the map after removing a marker.
 * @private
 */
ContextMenuControl.prototype.removeDest_ = function () {
  var me = this;
  var d = me.dirmarks_;
  var waypoints = [];

  for (var i = 0; i < d.length; i++) {
    if (me.rej_.equals(d[i].getLatLng())) {
      me.map_.removeOverlay(d[i]);
      d.splice(i, 1); break;
    }
  }
  if (d.length == 0) {
    me.removeOld_();
  } else if (d.length == 1) {
    me.map_.closeInfoWindow();
    me.map_.removeOverlay(me.poly_);
  } else if (d.length > 1) {
    for (var j = 0; j < d.length; j++) {
      waypoints[j] = d[j].getLatLng();
    }
    me.getDirs_(waypoints);
  }
};

/**
 * Adds previously removed and further destinations.
 * @private
 */
ContextMenuControl.prototype.addDest_ = function () {
  var me = this;
  var d = me.dirmarks_;
  var waypoints = [];

  // Re-adds removed A or B when only A and B are shown
  if (d.length == 1) {
    if (d[0].letter == "A") {
      me.setChosen_("B");
    } else {
      me.setChosen_("A");
    }
    waypoints[0] = me.clickedPoint_;
    waypoints[1] = d[0].getLatLng();
  } else if (d.length > 1) {
    // Adds further destinations
    me.setChosen_();
    for (var i = 0; i < d.length; i++) {
      waypoints[i] = d[i].getLatLng();
    }
    waypoints.push(me.clickedPoint_);
  }
  me.getDirs_(waypoints);
};

/**
 * Creates a styled horizontal ruler between the list entries.
 * @return {Element} hr as separator.
 * @private
 */
ContextMenuControl.prototype.createRuler_ = function () {
  var hr = document.createElement("hr");
  hr.style.height = "1px";
  hr.style.border = "1px";
  hr.style.color = "#e2e2e2";
  hr.style.backgroundColor = "#e2e2e2";
  // Further IE bug
  if (document.all) {
    hr.style.display = "block";
    hr.style.margin = "-6px";
  } else {
    hr.style.margin = "0px";
  }
  return hr;
};

/**
 * Hides the context menu and sets its property visible to false.
 * @private
 */
ContextMenuControl.prototype.hideMenu_ = function () {
  this.menuList.style.visibility = "hidden";
  this.menuList.visible = false;
};

/**
 * Removes the context menu from the map container and adds a changed one.
 * @param {String} arg Used to decide which item to replace.
 * @private
 */
ContextMenuControl.prototype.rebuildMenu_ = function (arg) {
  this.map_.getContainer().removeChild(this.menuList);
  this.createContextMenu_(arg);
};

/**
 * Checks the finally touched marker to request the appropriate route.
 * @param {String} opt_letter
 * @private
 */
ContextMenuControl.prototype.setChosen_ = function (letter) {
  if (letter == "A") {
    this.chosen = {A: true, B: false};
  } else if (letter == "B") {
    this.chosen = {A: false, B: true};
  } else {
    this.chosen = {A: false, B: false};
  }
};

/**
 * Creates alphabetically arranged capital letters for direction markers.
 * @return {String} letter
 * @private
 */
ContextMenuControl.prototype.makeLetter_ = function () {
  this.letterindex_++;
  return String.fromCharCode(this.letterindex_ + 65);
};

/**
 * Creates required properties for icons.
 * @param {GIcon} icon
 * @private
 */
ContextMenuControl.prototype.createIcon_ = function (image) {
  var icon = new GIcon();
  var url = "http://maps.google.com/mapfiles/";
  if (image == "arrow") {
    icon.image = url + "arrow.png";
    icon.shadow = url + "arrowshadow.png";
    icon.iconSize = new GSize(39, 34);
    icon.shadowSize = new GSize(39, 34);
    icon.iconAnchor = new GPoint(20, 34);
    icon.infoWindowAnchor = new GPoint(20, 0);
  } else {
    icon.image = url + image;
    icon.shadow = url + "shadow50.png";
    icon.iconSize = new GSize(20, 34);
    icon.shadowSize = new GSize(37, 34);
    icon.iconAnchor = new GPoint(9, 34);
    icon.infoWindowAnchor = new GPoint(19, 2);
  }
  return icon;
};


/**
 * Creates and adds direction markers to the map.
 * @param {GLatLng) point The GLatLng for the marker.
 * @param {String) letter The markers letter.
 * @private
 */
ContextMenuControl.prototype.makeDirMarker_ = function (point, letter) {
  var me = this;
  var d = me.dirmarks_;
  var r = me.routes_;
  var iw;
  var waypoints = [];
  var image = "marker_green" + letter + ".png";
  var icon = me.createIcon_(image);
  var marker = new GMarker(point, {icon: icon, draggable: true, title: 'Drag to change route', bouncy: false, dragCrossMove: true});

  marker.letter = letter;
  me.map_.addOverlay(marker);
  d.push(marker);

  if (letter == "A") {
    iw = r.iws[0];
  } else {
    iw = r.iws[(r.iws.length - 1)];
  }

  GEvent.addListener(marker, "click", function () {
    if (d.length > 1) {
      me.map_.openInfoWindowHtml(point, iw);
    }
  });

  // Finds out dragged marker and closes infowindow
  GEvent.addListener(marker, "dragstart", function () {
    me.map_.closeInfoWindow();
    me.actual = marker;
    if (d.length <= 2) {
      me.setChosen_(letter);
    }
  });

  // Creates a new route when two or more markers are shown
  GEvent.addListener(marker, "dragend", function () {
    if (d.length == 2) {
      var sticky = me.chosen.A ? d[d.length - 1].getLatLng() : d[0].getLatLng();
      waypoints.splice(0, 0, me.actual.getLatLng(), sticky);
    } else if (d.length > 2) {
      d.splice((letter.charCodeAt() - 65), 1, me.actual);
      for (var i = 0; i < d.length; i++) {
        waypoints[i] = d[i].getLatLng();
      }
    }
    if (d.length >= 2) {
      me.getDirs_(waypoints);
    }
  });
};

/**
 * Error alerts for failed direction queries.
 * @private
 */
ContextMenuControl.prototype.handleErrors_ = function () {
  var status = this.gdir.getStatus().code;
  var reason = [];
  // 400
  reason[G_GEO_BAD_REQUEST] = "A directions request could not be successfully parsed.";
  // 500
  reason[G_GEO_SERVER_ERROR] = "A geocoding, directions or maximum zoom level request could not be successfully processed.";
  // 601
  reason[G_GEO_MISSING_QUERY] = "No query was specified in the input.";
  // 602
  reason[G_GEO_UNKNOWN_ADDRESS] = "No corresponding geographic location could be found for one of the specified addresses.";
  // 603
  reason[G_GEO_UNAVAILABLE_ADDRESS] = "The geocode for the given address or the route for the given directions query cannot be returned due to legal or contractual reasons.";
  // 604
  reason[G_GEO_UNKNOWN_DIRECTIONS] = "The GDirections object could not compute directions between the points mentioned in the query.";
  // 610
  reason[G_GEO_BAD_KEY] = "The given key is either invalid or does not match the domain for which it was given.";
  // 620
  reason[G_GEO_TOO_MANY_QUERIES] = "The given key has gone over the daily requests limit or too many requests were submitted too fast.";

  if (reason[status]) {
    alert(reason[status] + "\nError code: " + status);
  } else {
    alert("An unknown error occurred.");
  }
};

/**
 * Callback function for direction queries.
 * @private
 */
ContextMenuControl.prototype.dirsLoad_ = function () {
  var me = this;
  me.routes_ = {sections: [], iws: [], num: 0};
  var r = me.routes_;

  // Removes possible 'What's here' marker
  if (me.heremarks_.length > 0) {
    me.removeOld_("here");
  }
  // Removes existing search marker
  if (me.actual) {
    me.map_.removeOverlay(me.actual);
    me.actual = null;
  }
  // Removes previous
  if (me.dirmarks_.length > 0) {
    me.removeOld_();
  }
  // Draws polyline for all routes
  me.poly_ = me.gdir.getPolyline();
  me.map_.addOverlay(me.poly_);
  var numRoutes = me.gdir.getNumRoutes();
  r.num = numRoutes;

  // Stores items of every route
  for (var m = 0; m < numRoutes; m++) {
    var route = me.gdir.getRoute(m);
    var numSteps = route.getNumSteps();

    if (m == 0) {
      var spoint = route.getStep(0).getLatLng();
      var saddr = route.getStartGeocode().address;
      r.sections.push(spoint);
      r.iws.push(me.createRouteInfo(saddr, 0));
      me.makeDirMarker_(spoint, me.makeLetter_());
    }

    for (var n = 0; n < numSteps; n++) {
      var count = r.sections.length;
      var step = route.getStep(n);
      r.sections.push(step.getLatLng());
      r.iws.push(me.createRouteInfo(step.getDescriptionHtml(), count));
    }
    var epoint = route.getEndLatLng();
    var eaddr = route.getEndGeocode().address;
    r.sections.push(epoint);
    r.iws.push(me.createRouteInfo(eaddr, (count + 1), (m + 1)));
    me.makeDirMarker_(epoint, me.makeLetter_());
  }
};

/**
 * Creates info windows for direction steps.
 * @param {String} info The returned description for each step.
 * @param {Number} nr counter to identify the appropriate info.
 * @param {Number} i counter to identify the last route.
 * @return {Element} The styled info window.
 * @private
 */
ContextMenuControl.prototype.createRouteInfo = function (info, nr, i) {
  var disabled = function (io) {
    io.style.color = "#a5a5a5";
    io.style.textDecoration = "none";
    io.removeAttribute("href");
    io.style.cursor = "default";
  };
  var me = this;
  var r = me.routes_;
  var iw = document.createElement("div");
  iw.style.width = "240px";
  iw.innerHTML = info;
  var p = document.createElement("p");
  p.style.fontSize = "small";
  p.style.textAlign = "center";
  p.style.marginTop = "20px";

  var zlink = document.createElement("a");
  zlink.setAttribute("href", "javascript:void(0)");
  zlink.innerHTML = "Zoom In";
  zlink.style.marginRight = "20px";

  var blink = document.createElement("a");
  blink.setAttribute("href", "javascript:void(0)");
  blink.innerHTML = "&laquo; Previous";
  blink.style.marginRight = "20px";
  if (nr == 0) {
    disabled(blink);
  }

  var flink = document.createElement("a");
  flink.setAttribute("href", "javascript:void(0)");
  flink.innerHTML = "Next &raquo;";
  if (i == r.num) {
    disabled(flink);
  }

  p.appendChild(blink);
  p.appendChild(zlink);
  p.appendChild(flink);
  iw.appendChild(p);

  // Due to an API bug we do not use addDomListeners here
  // Zoom in link
  zlink.onclick = function () {
    me.map_.zoomIn(r.sections[nr], {doCenter: true});
  };
  // Back link
  blink.onclick = function () {
    if (nr > 0) {
      me.map_.openInfoWindowHtml(r.sections[(nr - 1)], r.iws[(nr - 1)]);
    }
  };
  // Forward link
  flink.onclick = function () {
    if (nr < (r.sections.length - 1)) {
      me.map_.openInfoWindowHtml(r.sections[(nr + 1)], r.iws[(nr + 1)]);
    }
  };
  return iw;
};

/**
 * Handles direction queries.
 * Either first or second param must be passed in.
 * @param {GLatLng[]} [points] Array of GLatLngs to load direction from waypoints.
 * @param {String} [addr] The form input value.
 * @private
 */
ContextMenuControl.prototype.getDirs_ = function (points, addr) {
  var me = this;
  me.gdir = new GDirections();
  GEvent.bind(me.gdir, "error", me, me.handleErrors_);
  GEvent.bind(me.gdir, "load", me, me.dirsLoad_);
  var opts = {getPolyline: true, getSteps: true };
  if (me.opts_) {
    opts.avoidHighways = me.opts_.avoidHighways;
  }
  if (points) {
    if (me.chosen.B) {
      points = points.reverse();
    }
    me.gdir.loadFromWaypoints(points, opts);
  } else {
    var point = me.actual.getLatLng();
    var latlng = point.lat() + "," + point.lng();

    if (me.chosen.A) {
      me.gdir.load("from: " + latlng + " to: " + addr, opts);
    } else {
      me.gdir.load("from: " + addr + " to: " + latlng, opts);
    }
  }
};

/**
 * Creates the form for searching directions displayed in infowindow.
 * @param {String} letter Used to decide which form to load.
 * @return {Element} Nested elements in outer form.
 * @private
 */
ContextMenuControl.prototype.makeForm_ = function (letter) {
  var me = this;
  var text = (letter == "A") ?["Как проехать отсюда", "End address:"]: ["Как проехать сюда", "Start address:"];
  var html = document.createElement("div");

  // Header
  html.appendChild(document.createTextNode("Direction: "));
  var bold = document.createElement("b");
  bold.appendChild(document.createTextNode(text[0]));
  html.appendChild(bold);
  html.appendChild(document.createElement("br"));
  var small = document.createElement("small");
  small.appendChild(document.createTextNode(text[1]));
  html.appendChild(small);
  html.appendChild(document.createElement("br"));

  // Input field
  var input = document.createElement("input");
  input.type = "text";
  input.value = "";
  input.style.width = "32ex";
  html.appendChild(input);

  // Dropdown box and submit button
  var p1 = document.createElement("p");
  var select = document.createElement("select");
  select.size = "1";
  select.style.width = "15ex";
  var opt1 = document.createElement("option");
  opt1.appendChild(document.createTextNode("By car"));
  opt1.setAttribute("value", "");
  select.appendChild(opt1);
  var opt2 = document.createElement("option");
  opt2.appendChild(document.createTextNode("Avoid highways"));
  opt2.setAttribute("value", "nohighways");
  select.appendChild(opt2);
  var button = document.createElement("input");
  button.type = "submit";
  button.value = "Get Direction";
  button.style.width = "15ex";
  button.style.marginLeft = "16px";
  p1.appendChild(select);
  p1.appendChild(button);
  html.appendChild(p1);

  // Link for removing the marker
  var p2 = document.createElement("p");
  var small2 = document.createElement("small");
  small2.appendChild(document.createTextNode("Drag or "));
  var rlink = document.createElement("a");
  rlink.setAttribute("href", "javascript:void(0)");
  rlink.appendChild(document.createTextNode("remove this point"));
  small2.appendChild(rlink);
  p2.appendChild(small2);
  html.appendChild(p2);

  var form = document.createElement("form");
  form.appendChild(html);

  setTimeout(function () {
    var inputfield = html.childNodes[5];
    if (inputfield.nodeName == "INPUT") {
      inputfield.focus();
    }
  }, 1000);

  GEvent.addDomListener(rlink, "click", function () {
    if (me.actual) {
      me.map_.removeOverlay(me.actual);
      me.actual = null;
    }
  });

  me.opts_ = {avoidHighways: false};
  GEvent.addDomListener(select, "change", function () {
    // This option should be 'session consistent'.
    // So it won't be passed to the function.
    if (select.options[select.selectedIndex].value == "nohighways") {
      me.opts_.avoidHighways = true;
    }
  });

  GEvent.addDomListener(form, "submit", function (e) {
    me.getDirs_(null, input.value);
    if (window.event) {
      event.returnValue = false;
    } else if (e) {
      e.stopPropagation();
      e.preventDefault();
    }
  });
  return form;
};

/**
 * Removes markers and polylines from the map and resets globals.
 * @param {String} what Used to decide what to remove
 * @private
 */
ContextMenuControl.prototype.removeOld_ = function (what) {
  if (what == "here") {
    for (var i = 0; i < this.heremarks_.length; i++) {
      this.map_.removeOverlay(this.heremarks_[i]);
    }
    this.heremarks_.length = 0;
  } else {
    this.map_.closeInfoWindow();
    this.map_.removeOverlay(this.poly_);
    for (var j = 0; j < this.dirmarks_.length; j++) {
      this.map_.removeOverlay(this.dirmarks_[j]);
    }
    this.dirmarks_.length = 0;
    this.letterindex_ = -1;
    if (this.actual) {
      this.map_.removeOverlay(this.actual);
      this.actual = null;
    }
  }
};

/**
 * Tries to reverse geocode the clicked point and
 * creates two markers to show 'What's here'.
 * @private
 */
ContextMenuControl.prototype.getReverseGeocode_ = function () {
  var dec2deg = function (dec) {
    var sign = (dec > 0) ? "+" : "";
    var deg = parseInt(dec, 10);
    dec = Math.abs(dec - deg);
    var min = parseInt((dec * 60), 10);
    dec = (dec * 60) % 1;
    var sec = Math.round(Math.abs(dec * 60) * 100) / 100;
    return sign + deg + "&deg; " + min + "&prime; " + sec + "&Prime;";
  };
  var me = this;
  if (me.heremarks_.length > 0) {
    me.removeOld_("here");
  }
  var point = me.clickedPoint_;
  var p_string = point.toUrlValue();
  var coords = "<div style='width:240px;'>" +
   "<big>" + p_string + "<\/big>" +
   "<p style='margin-top:7px;font-size:small;'>" +
   dec2deg(point.lat()) + ", " + dec2deg(point.lng()) + "<\/p><\/div>";
  var icon = me.createIcon_("arrow");
  var arrow_mark = new GMarker(point, {icon: icon, title: p_string });
  me.heremarks_.push(arrow_mark);
  me.map_.addOverlay(arrow_mark);

  GEvent.addListener(arrow_mark, "click", function () {
    arrow_mark.openInfoWindowHtml(coords);
  });

  var geo = new GClientGeocoder();
  geo.getLocations(point, function (response) {
    if (response.Status.code == 200) {
      var place = response.Placemark[0];
      var latlng = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
      var red_mark = new GMarker(latlng);
      me.heremarks_.push(red_mark);
      me.map_.addOverlay(red_mark);
      var addr = "<div style='width:240px;'><big>Address:<\/big><p style='margin-top:7px;font-size:small;'>" + place.address + "<\/p><\/div>";

      GEvent.addListener(red_mark, "click", function () {
        red_mark.openInfoWindowHtml(addr);
      });
    } else {
      // No more error handling needed since the coords are already shown.
      return;
    }
  });
};
