/*!
 * @author Vlad Yakovlev (red.scorpix@gmail.com)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @link www.scorpix.ru
 * @version 0.1.9
 * @date 2010-06-28
 * @requires jQuery
 * @requires jCommon
 * @requires photoSizer
 */

function mapControl(mapPath, placesAjaxPath, coords) {

	var
		/** Количество градусов по Гринвичу. */
		worldDegrees = 180,
		/** Масштаб координат карты. */
		coordScale = 10,
		/** Количество градусов в видимой части карты при минимальном масштабе. */
		minDegrees = $c.browser.msie ? 20 : 5;

	/**
	 * Управлятор карты.
	 * @param {String|Element|jQuery} rootEl Элемент карты.
	 * @param {Object} coords Координаты для карты.
	 * @param {Object} previews
	 * @param {Number} initX
	 * @param {Number} initY
	 * @param {Function} onUpdate
	 * @return {mapMover}
	 */
	function mapMover(rootEl, coords, previews, initX, initY, initWidth, onUpdate) {

		rootEl = $(rootEl);

		if (null === initWidth) {
			initWidth = worldDegrees * 2;
		}

		var
			/** Порог в градусах, при котором видны контуры стран. */
			countriesDegrees = 110,
			/** Толщина границ континентов и островов. */
			continentsStrokeWidth = rootEl.hasClass('in_content') ? 0.1 : 1,
			/** Толщина границ между странами. */
			countriesStrokeWidth = rootEl.hasClass('in_content') ? 0.05 : 0.5,
			/** Цвет границ континентов и островов. */
			continentsColor = '#7a7a7a',
			/** Цвет границ между странами. */
			countriesColor = '#7a7a7a';

		var
			viewEl = rootEl.find('.view'),
			closerEl = rootEl.find('.previews');

		var
			/**
			 * Элемент, у которого меняется масштаб.
			 * @type {Element}
			 */
			svgEl,
			/**
			 * Элемент, у которого меняется толщина границ континентов и островов.
			 * @type {Element}
			 */
			continentsEl,
			/**
			 * Элемент, у которого меняется толщина границ между странами.
			 * @type {Element}
			 */
			countriesGroup,
			/** Позиция по горизонтали у вектора в начале драг-н-дропа. */
			startVectorDndX,
			/** Позиция по вертикали у вектора в начале драг-н-дропа. */
			startVectorDndY,
			/** Позиция курсора мыши по горизонтали в начале драг-н-дропа. */
			startDndX,
			/** Позиция курсора мыши по вертикали в начале драг-н-дропа. */
			startDndY,
			/** Масштаб карты в процессе драг-н-дропа. */
			mapResolution,
			/**
			 * Горизонтальная и вертикальная позиции, ширина и высота видимой части карты.
			 */
			viewBox = [initX, initY, initWidth, initWidth / 2],

			oldWidth = -1,

			timeoutId,

			isDnd,

			disabledEls;

		/**
		 * Функции работы с вектором.
		 */
		var
			createVector,
			updateVector;

		if ($c.support.svg) {
			/**
			 * Работа с SVG.
			 */
			createVector = function() {
				svgEl = $c.svg('svg').svgAttr({
					'class': 'shape',
					preserveAspectRatio: 'xMinYMin meet'
				}).appendTo(viewEl);

				var defsEl = $c.svg('defs').appendTo(svgEl);

				/**
				 * Добавляем контуры континтентов.
				 */
				continentsEl = $c.svg('path').svgAttr({
					stroke: continentsColor,
					fill: 'none',
					id: 'continents_vector',
					d: coords.continents
				}).appendTo(defsEl);

				var continentsUseEl = $c.svg('use').svgAttr({
					x: 0,
					y: 0,
					width: worldDegrees * coordScale * 2,
					height: worldDegrees * coordScale,
					href: '#continents_vector'
				}).appendTo(svgEl);

				/**
				 * Добавляем контуры стран.
				 */
				countriesGroup = $c.svg('path').svgAttr({
					fill: 'none',
					id: 'countries_vector',
					d: coords.countries
				}).appendTo(defsEl);

				var countriesUseEl = $c.svg('use').svgAttr({
					x: 0,
					y: 0,
					width: worldDegrees * coordScale * 2,
					height: worldDegrees * coordScale,
					href: '#countries_vector'
				}).appendTo(svgEl);

				updateVector();
			};

			updateVector = function() {
				// Меняем толщину границ.
				if (oldWidth != viewBox[2]) {
					continentsEl.svgAttr('stroke-width', continentsStrokeWidth * viewBox[2] / 2 / worldDegrees * coordScale);

					var attrs;

					if (viewBox[2] / 2 < countriesDegrees) {
						attrs = {
							strokeWidth: countriesStrokeWidth * viewBox[2] / 2 / worldDegrees * coordScale,
							stroke: countriesColor
						};
					} else {
						attrs = { stroke: 'none' };
					}

					countriesGroup.svgAttr(attrs);
				}

				svgEl.svgAttr('viewBox', [viewBox[0] * coordScale, viewBox[1] * coordScale, viewBox[2] * coordScale, viewBox[3] * coordScale].join(' '));
			};
		} else {
			/**
			 * Работа с VML.
			 */
			createVector = function() {
				svgEl = $('<div></div>').css({
					height: '100%',
					left: 0,
					overflow: 'hidden',
					position: 'absolute',
					top: 0,
					width: '100%'
				});
				svgEl.appendTo(viewEl);

				setTimeout(function() {
					var el = $(document.createElement('v:shape'))
						.attr({
							strokecolor: continentsColor,
							strokeweight: continentsStrokeWidth * 10,
							filled: 'False',
							path: coords.continents
						})
						.css({
							left: 0,
							width: viewEl.width(),
							height: viewEl.height(),
							top: 0
						})
						.addClass('shape')
						.appendTo(svgEl);

					$(window).resize(function() {
						el.css({
							width: viewEl.width(),
							height: viewEl.height()
						});
					});

					updateVector();
				}, 1);
			};

			updateVector = function() {
				// Меняем толщину границ.
				if (oldWidth != viewBox[2]) {
					svgEl.find('.shape').attr('strokeweight', continentsStrokeWidth * viewBox[2] / 2 / worldDegrees * coordScale);

					/*var attrs;

					if (viewBox[2] / 2 < countriesDegrees) {
						attrs = {
							'stroke-width': countriesStrokeWidth * viewBox[2] / 2 / worldDegrees * coordScale,
							stroke: countriesColor
						};
					} else {
						attrs = { stroke: 'none' };
					}

					setAttributes(countriesGroup, attrs);*/
				}

				svgEl.find('.shape')
					.css('display', 'none')
					.attr({
						coordorigin: [Math.round(viewBox[0] * coordScale), Math.round(viewBox[1] * coordScale)].join(' '),
						coordsize: [Math.round(viewBox[2] * coordScale), Math.round(viewBox[3] * coordScale)].join(' ')
					})
					.css('display', 'block');
			};
		}

		// Рисуем карту.
		createVector();
		// Вешаем событие на драг-н-дроп.
		closerEl.mousedown(startMapDnd);
		$(window).resize(function() {
			if (rootEl.hasClass('in_content')) {
				$('#content').height($('#layout').height() - $('#header').height() - $('#footer').outerHeight());
			}

			setViewBox(viewBox[0], viewBox[1], viewBox[2], viewEl.height() / viewEl.width() * viewBox[2]);
		}).resize();
		previews.scale(viewBox);

		function cancel(evt) {
			return false;
		}

		function enableChilds() {
			if (disabledEls) {
				disabledEls.unbind('click', cancel);
				disabledEls = null;
			}
		}

		function disableChilds(evt) {
			disabledEls = $(evt.target).parentsUntil($(this).parent()[0]);
			disabledEls.click(cancel);
		}

		/**
		 * Начала драг-н-дропа карты.
		 * @param {Event} evt
		 */
		function startMapDnd(evt) {
			startVectorDndX = viewBox[0];
			startVectorDndY = viewBox[1];
			startDndX = parseInt(evt.pageX);
			startDndY = parseInt(evt.pageY);
			mapResolution = viewBox[2] / viewEl.width();

			isDnd = false;

			$(document)
				.mouseup(stopMapDnd)
				.mousemove(mapDnd);

			return false;
		}

		/**
		 * Драг-н-дроп карты.
		 * @param {Event} evt
		 */
		function mapDnd(evt) {

			rootEl.addClass('map_drop');

			var
				pageX = parseInt(evt.pageX),
				pageY = parseInt(evt.pageY);

			if (0 > pageX || pageX > $(window).width() || 0 > pageY || pageY > $(window).height()) {
				stopMapDnd(evt);
				return;
			}

			if (!isDnd) {
				disableChilds(evt);
				isDnd = true;
			}

			var
				x = startVectorDndX - mapResolution * (pageX - startDndX),
				y = startVectorDndY - mapResolution * (pageY - startDndY);

			setViewBox(x, y, undefined, undefined, true);

			return false;
		}

		/**
		 * Завершение драг-н-дропа карты.
		 */
		function stopMapDnd(evt) {

			var
				pageX = parseInt(evt.pageX),
				pageY = parseInt(evt.pageY),
				x = startVectorDndX - mapResolution * (pageX - startDndX),
				y = startVectorDndY - mapResolution * (pageY - startDndY);

			setViewBox(x, y);

			setTimeout(function() {
				enableChilds();
			}, 1);
			$(document)
				.unbind('mousemove', mapDnd)
				.unbind('mouseup', stopMapDnd);

			rootEl.removeClass('map_drop');

			return false;
		}

		/**
		 * Обновляет видимую часть карты.
		 * @param {Number} x
		 * @param {Number} y
		 * @param {Number} [width]
		 * @param {Number} [height]
		 */
		function setViewBox(x, y, width, height, notCorrect) {
			x = parseFloat(x);
			y = parseFloat(y);

			width = undefined === width ? viewBox[2] : parseFloat(width);
			height = undefined === height ? viewBox[3] : parseFloat(height);

			while (0 < y + height - worldDegrees / 2) {
				y = worldDegrees / 2 - height;
			}

			while (-worldDegrees / 2 > y) {
				y = -worldDegrees / 2;
			}

			if (-worldDegrees > x) {
				x = -worldDegrees;
			}
			if (worldDegrees < x + width) {
				x = worldDegrees - width;
			}

			previews.scale([x, y, width, height], notCorrect ? false : true);
			viewBox = [x, y, width, height];

			updateVector();
			onUpdate && onUpdate(viewBox);
		}

		/**
		 * Масштабирует карту.
		 * @params {Number} value
		 */
		function scale(value) {
			var
				degrees = worldDegrees - (worldDegrees - minDegrees) * value,
				movedDegrees = viewBox[2] / 2 - degrees,
				width = 2 * degrees,
				height = viewBox[3] / viewBox[2] * width;

			setViewBox(viewBox[0] + movedDegrees, viewBox[1] + movedDegrees / 2, width, height, true);
		}

		return { scale: scale };
	}

	/**
	 * Контролятор бегунка масштаба карты.
	 * @param {String|Element|jQuery} rootEl
	 * @param {mapMover} map
	 * @param {Number} initWidth
	 */
	function scaleControl(rootEl, map, initWidth) {

		rootEl = $(rootEl);

		var
			/** Минимальная позиция бегунка. */
			minPos = 12,
			/** Максимальная позиция бегунка. */
			maxPos = 124;

		var handleEl = rootEl.find('.handle');

		var
			timeoutId,
			startDndPos,
			startDndMouse,
			/** Текущая позиция бегунка. */
			curPos = -1,
			controlState = 'isActive',

			plusEl = rootEl.find('.plus').data(controlState, false),
			minusEl = rootEl.find('.minus').data(controlState, false);

		init();

		rootEl.find('.plus, .minus').mousedown(function() {
			$(this).bind('mouseup mouseout', stopStepScale);
			stepScale(this, $(this).hasClass('plus') ? 10 : -10);

			return false;
		});
		handleEl.mousedown(startHandlerDnd);

		function init() {
			if (null === initWidth) {
				setPos(0);
			} else {
				if (minDegrees > initWidth) {
					initWidth = minDegrees;
				}
				if (worldDegrees * 2 < initWidth) {
					initWidth = worldDegrees * 2;
				}

				var value = 1 - (initWidth - minDegrees) / worldDegrees * 2;
				map.scale(value);

				curPos = value * (maxPos - minPos);
				handleEl.css('top', maxPos - curPos);
			}

			checkControls();
		}

		/**
		 * Перемещает бегунок масштаба при клике на плюс/минус и меняет масштаб карты.
		 * Срабатывает при нажатии. Пока не отожмешь, будет масштабировать.
		 */
		function stepScale(el, step) {
			var pos = curPos + step;

			setPos(pos);

			if (0 > pos || maxPos - minPos < pos) {
				$(el).unbind('mouseup mouseout', stopStepScale);
			} else {
				timeoutId = setTimeout(function() {
					stepScale(el, step);
				}, 200);
			}

			checkControls();
		}

		/**
		 * Завершает масштабирование карты при клике на плюс/минус.
		 * Срабатывает при отжатии.
		 */
		function stopStepScale() {
			clearTimeout(timeoutId);
			$(this).unbind('mouseup mouseout', stopStepScale);
		}

		/**
		 * Начало драг-н-дропа на бегунке масштаба.
		 * @param {Event} evt
		 */
		function startHandlerDnd(evt) {
			startDndPos = curPos;
			startDndMouse = parseInt(evt.pageY);

			$(document).one('mouseup', stopHandlerDnd).mousemove(handlerDnd);

			return false;
		}

		/**
		 * Драг-н-дроп бегунка масштаба.
		 * @param {Event} evt
		 */
		function handlerDnd(evt) {
			setPos(startDndPos - parseInt(evt.pageY) + startDndMouse);
			checkControls();

			return false;
		}

		/**
		 * Завершение драг-н-дропа бегунка масштаба.
		 */
		function stopHandlerDnd(evt) {
			$(document).unbind('mousemove', handlerDnd);
		}

		function setPos(pos) {
			if (pos == curPos) return;

			if (pos > maxPos - minPos) {
				pos = maxPos - minPos;
			} else if (0 > pos) {
				pos = 0;
			}

			map.scale(pos / (maxPos - minPos));

			curPos = pos;

			handleEl.css('top', maxPos - curPos);
		}

		function checkControls() {
			var scale = curPos / (maxPos - minPos);

			if (1 <= scale && plusEl.data(controlState)) {
				plusEl
					.data(controlState, false)
					.removeClass('active');
			} else if (1 > scale && !plusEl.data(controlState)) {
				plusEl
					.data(controlState, true)
					.addClass('active');
			}

			if (0 >= scale && minusEl.data(controlState)) {
				minusEl
					.data(controlState, false)
					.removeClass('active');
			} else if (0 < scale && !minusEl.data(controlState)) {
				minusEl
					.data(controlState, true)
					.addClass('active');
			}
		}
	}

	/**
	 * Управляет масштабированием превьюшек.
	 */
	function previewsScale(rootEl, placesAjaxPath) {
		rootEl = $(rootEl);

		var
			maxCountriesShowDegrees = 200,
			maxPlacesShowDegrees = 200,
			countryVisibleFactor = 8,
			kmInGrad = 111,
			//minDegrees = 5,

			countries = [],
			places = {},
			placesCache = {},
			visiblePlaceIds = null;

		var
			previewsEl = rootEl.find('.previews'),
			containerEl = previewsEl.find('.container'),
			countriesEl = containerEl.find('.countries'),
			placesEl = containerEl.find('.places');

		var
			viewBox,
			countryCloner = '<div class="country id_{id}"><a href="{href}"><ins class="b"></ins><em>{name}</em></a></div>',
			updateTimeoutId;

		init();

		function init() {
			photoSizer.getStep();
			fillCountries($('.countries_info'));
		}

		/**
		 * Добавляет элементы стран в DOM.
		 * @param {Object} Информация о странах.
		 */
		function fillCountries(infoEl) {
			infoEl.find('.item').each(function(index) {
				var
					country = getCoords($(this).find('ins').text()),
					el = $($c.supplant(countryCloner, {
						id: index,
						href: $(this).find('a').attr('href'),
						name: $(this).find('a').text()
					}));

				country.inDom = false;
				country.el = el;
				countries.push(country);
			}).remove();
		}

		function scale(newViewBox, fast) {
			newViewBox[2] < maxCountriesShowDegrees
				? rootEl.addClass('show_countries')
				: rootEl.removeClass('show_countries');

			if (!viewBox) {
				viewBox = newViewBox;
				honestUpdate();

				return;
			}

			clearTimeout(updateTimeoutId);

			var
				width = previewsEl.width(),
				height = previewsEl.height(),
				newWidth = Math.round(viewBox[2] / newViewBox[2] * width),
				resolution = newWidth / viewBox[2],
				marginLeft = Math.round((viewBox[0] - newViewBox[0]) * resolution);

			containerEl.css({
				width: newWidth,
				height: Math.round(newWidth * height / width),
				marginLeft: marginLeft,
				marginTop: Math.round((viewBox[1] - newViewBox[1]) * resolution)
			});

			if (fast) {
				viewBox = newViewBox;
				honestUpdate();
			} else {
				/**
				 * Выдерживаем время, чтобы добавление и удаление блоков происходило
				 * после анимации масштаба или перетаскивания карты.
				 */
				updateTimeoutId = setTimeout(function() {
					viewBox = newViewBox;
					honestUpdate();
				}, 500);
			}
		}

		/**
		 * Апдейт элементов стран и мест.
		 */
		function honestUpdate() {

			$.each(countries, function(id) {
				if (this.longitude >= viewBox[0] && this.longitude <= viewBox[0] + viewBox[2] &&
					this.latitude >= viewBox[1] && this.latitude <= viewBox[1] + viewBox[3]) {

					if (viewBox[2] * kmInGrad / countryVisibleFactor <= Math.sqrt(this.area) || minDegrees >= viewBox[2]) {
						var
							left = 100 * ((this.longitude - viewBox[0] + 720) % 360) / viewBox[2],
							top = 100 * (this.latitude - viewBox[1]) / viewBox[3],
							el;

						if (this.inDom) {
							el = countriesEl.find('.id_' + id);
						} else {
							el = this.el.appendTo(countriesEl);
							this.inDom = true;
						}

						el.css({
							left: left + '%',
							top: top + '%'
						});

						return;
					}
				}

				if (this.inDom) {
					this.el = countriesEl.find('.id_' + id).detach();
					this.inDom = false;
				}
			});

			honestUpdatePlaces(viewBox);

			containerEl.css({
				marginLeft: '',
				marginTop: '',
				width: '',
				height: ''
			});
		}

		function honestUpdatePlaces(viewBox) {
			var viewBoxId = getViewBoxId(viewBox);

			if (undefined === placesCache[viewBoxId]) {
				loadPlaces(viewBox);

				visiblePlaceIds && $.each(visiblePlaceIds, function() {
					var
						place = places[this],
						left = 100 * ((place.longitude - viewBox[0] + 720) % 360) / viewBox[2],
						top = 100 * (place.latitude - viewBox[1]) / viewBox[3];

					placesEl.find('.id_' + this).css({
						left: left + '%',
						top: top + '%'
					});
				});

				return;
			}

			/**
			 * Удаляем ненужные элементы из DOM'а.
			 */
			var delEls = visiblePlaceIds ? visiblePlaceIds.filter(function(el) {
				if (-1 == $.inArray(el, placesCache[viewBoxId])) {
					return el;
				}
			}) : [];

			$.each(delEls, function() {
				places[this].inDom = false;
				places[this].el = placesEl.find('.id_' + this).detach();
			});

			if (undefined === placesCache[viewBoxId]) {
				return;
			}

			visiblePlaceIds = placesCache[viewBoxId];

			/**
			 * Вставляем места в DOM.
			 */
			$.each(placesCache[viewBoxId], function() {
				var
					place = places[this],
					left = 100 * ((place.longitude - viewBox[0] + 720) % 360) / viewBox[2],
					top = 100 * (place.latitude - viewBox[1]) / viewBox[3],
					el;

				if (place.inDom) {
					// Элемент места уже в DOM'е.
					el = placesEl.find('.id_' + this);
				} else {
					// Элемент места надо засунуть в DOM.
					el = place.el;
					el.appendTo(placesEl);

					if (!place.isInit) {
						place.isInit = true;

						el.find('img, .hover_name')
							.mouseover(function() { showHoverName(el); })
							.mouseout(function() { hideHoverName(el); });
					}

					var
						imgEl = el.find('img'),
						imgHeight = parseInt(imgEl.attr('height')),
						imgWidth = parseInt(imgEl.attr('width')),
						byWidth = imgWidth > imgHeight,
						css = { visibility: '' };

					imgEl.css('visibility', 'hidden');
					css[byWidth ? 'width' : 'height'] = 0;

					$c.loadImage(imgEl.attr('src'), function() {
						var
							toWidth,
							toHeight;

						if (byWidth) {
							toWidth = imgEl.width();
							toHeight = Math.round(toWidth / imgWidth * imgHeight);
						} else {
							toHeight = imgEl.height();
							toWidth = Math.round(toHeight / imgHeight * imgWidth);
						}

						css.marginBottom = Math.round(toHeight / 2);
						css.marginRight = Math.round(toWidth / 2);
						imgEl.css(css);

						var params = {
							marginBottom: 0,
							marginRight: 0
						};
						params[byWidth ? 'width' : 'height'] = byWidth ? toWidth : toHeight;

						el.find('img').animate(params, {
							easing: 'swing',
							duration: 200,
							complete: function() {
								imgEl.css({
									marginBottom: '',
									marginRight: ''
								});
								imgEl.css(byWidth ? 'width' : 'height', '');
							}
						});
					});

					place.inDom = true;
				}

				el.css({
					left: left + '%',
					top: top + '%'
				});
			});
		}

		function loadPlaces(loadViewBox) {
			var viewBoxId = getViewBoxId(loadViewBox);
			if (undefined !== placesCache[viewBoxId]) return;

			placesCache[viewBoxId] = [];

			var clearNumber = function(number) {
				return parseFloat(number.toString().substr(0, 10));
			};
			var rnd = function(number) {
				return Math.round(number * 1000) / 1000;
			};

			/**
			 * Запрашиваем у сервера места по прямоугольнику: нижний левый угол и ширина с высотой.
			 */
			var
				x = clearNumber(loadViewBox[0]),
				y = clearNumber(loadViewBox[1]),
				width = clearNumber(loadViewBox[2]),
				height = clearNumber(loadViewBox[3]);

			if (y + height > 90) {
				height = 90 - y;
			}
			while (-180 > x) x += 360;
			while (180 < x) x -= 360;

			y += height;

			$.ajax({
				url: placesAjaxPath + '?lt=' + rnd(-y + height) + ',' + rnd(x) + '&wh=' + rnd(height) + ',' + rnd(width),
				dataType: 'html',
				success: function(data) {
					fillPlaces(viewBoxId, data);
					honestUpdate();
				}
			});
		}

		/**
		 * Создает элементы мест для последующего показа в DOM'е.
		 */
		function fillPlaces(viewBoxId, xmlData) {
			$(xmlData).each(function() {
				var
					placeEl = $(this),
					placeId = parseInt($c.attrSuffix(placeEl, 'id_'));

				placesCache[viewBoxId].push(placeId);

				if (undefined !== places[placeId]) return;

				var
					previewEl = placeEl.find('>p'),
					width = parseInt(previewEl.attr('width'), 10),
					height = parseInt(previewEl.attr('height'), 10);

				places[placeId] = getCoords(placeEl.find('.coords').text());
				placeEl.find('.coords').remove();

				places[placeId].inDom = false;
				places[placeId].isInit = false;
				places[placeId].id = placeId;
				places[placeId].el = placeEl;
			});
		}

		/**
		 * Показывает название места.
		 * @param {jQuery} el
		 */
		function showHoverName(el) {
			el.data('hover', true);

			var hoverEl = el.find('.hover_name');

			hoverEl
				.stop()
				.css('visibility', 'visible')
				.css('left', -Math.round((hoverEl.find('em').width() + el.find('img').width()) / 2));

			if ($.browser.msie) {
				hoverEl.css('width', hoverEl.find('em').width());
			} else {
				hoverEl.animate({ opacity: 1 }, {
					duration: 200,
					easing: 'swing'
				});
			}
		}

		/**
		 * Скрывает название места.
		 * @param {jQuery} el
		 */
		function hideHoverName(el) {
			el.data('hover', false);

			var hoverEl = el.find('.hover_name');

			/**
			 * Скрыть плавно через какое-то время.
			 */
			setTimeout(function() {
				if (el.data('hover')) return;

				if ($.browser.msie) {
					hoverEl.css('visibility', 'hidden');
				} else {
					hoverEl.animate({ opacity: 0 }, {
						duration: 500,
						easing: 'swing',
						complete: function() {
							hoverEl.css('visibility', 'hidden');
						}
					});
				}
			}, 800);
		}

		function getCoords(coords) {
			coords = coords.split(',');

			var result = {
				latitude: -parseFloat(coords[0]),
				longitude: parseFloat(coords[1])
			};

			if (undefined !== coords[2]) {
				result.area = parseInt(coords[2], 10);
			}

			return result;
		}

		function getViewBoxId(viewBox) {
			var
				left = Math.round(viewBox[0]),
				right = Math.round(viewBox[1]),
				width = Math.round(viewBox[2]),
				height = Math.round(viewBox[3]),
				step = Math.round(height / 10),
				roundLeft = Math.round(left / step) * step,
				roundRight = Math.round(right / step) * step;

			return 360 == width && 180 == height ? [width, height].join(' ') : [roundLeft, roundRight, width, height].join(' ');
		}

		return {
			scale: scale
		};
	}

	/**
	 * Проверяем адресную строку на наличие координат.
	 */
	function getViewBoxFromAddressPath() {
		var result = [-worldDegrees, -worldDegrees / 2, null];

		var coords = location.href.split('#');
		if (2 > coords.length) return result;
		coords = coords[1];
		if ('lt' != coords.substr(0, 2)) return result;
		coords = coords.substr(2).split(',');
		if (3 > coords.length) return result;

		result[0] = parseFloat(coords[0]);
		result[1] = parseFloat(coords[1]);
		result[2] = parseFloat(coords[2]);

		return result;
	}

	var rootEl = $('.map');
	if (!rootEl.length) return;

	var fileName = $c.support.svg ? 'map_coords_svg.js' : 'map_coords_vml.js';

	if (coords) {
		coords[2] = Math.sqrt(coords[2]) / 400;
		coords[0] -= coords[2] / 2;
		coords[1] -= coords[2] / 2;
	} else {
		coords = getViewBoxFromAddressPath();
	}

	var fullScreenEl = $('.full_screen');
	var fullScreenFunc = fullScreenEl.length ? function(viewBox) {
		var f = function(v) {
			return Math.round(v * 1000) / 1000;
		}
		fullScreenEl.find('a').attr('href', '/#lt' + f(viewBox[0]) + ',' + f(viewBox[1]) + ',' + f(viewBox[2]));
	} : null;

	$.ajax({
		dataType: 'json',
		url: mapPath + fileName,
		success: function(data) {
			rootEl.each(function() {
				var
					previews = previewsScale($(this), placesAjaxPath),
					map = mapMover($(this), data, previews, coords[0], coords[1], coords[2], fullScreenFunc),
					scale = scaleControl($(this).find('.scale'), map, coords[2]);
			});
		}
	});
}
