/**
 * @projectDescription
 * Анимированный ховер для навигации.
 *
 * @author Vlad Yakovlev (scorpix@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @version 0.1 (10.11.2008)
 * @requires jQuery 1.2
 * @requires jTweener 0.2
 */
var hoverNavigation = (function() {
	/**
	 * Блоки навигационных ссылок.
	 * @type {jQuery}
	 */
	var linkBlocks;

	/**
	 * Объекты-аниматоры.
	 * @type {Array[Animator]}
	 */
	var backs = {};

	// В IE для VML надо добавить схему и стили.
	if ($.browser.msie && !document.namespaces['v']) {
		document.namespaces.add('v', 'urn:schemas-microsoft-com:vml');
		var ss = document.createStyleSheet();
		ss.cssText = 'v\\:* {behavior:url(#default#VML);display:block;}';
	}

	$(function() {
		linkBlocks = $('#main_navigation .main_navigation li, #main_navigation .extra_navigation li, #main_navigation .third_navigation li').not('.selected');
		linkBlocks.each(function(index) {
			var href = $(this).find('a').attr('href');

			backs[href] = new Animator({
				color: $(this).css('color'), // Цвет для ховера берется из CSS свойства цвета текста.
				parent: $(this),
				time: 300, // Время анимации.
				radius: 10, // Радиус закругления ховера.
				opacity: 0.5 // Коэффициент прозрачности ховера.
			});
			$(this).hover(function() {
				backs[href].show();
			}, function() {
				backs[href].hide();
			});
		});
	});

	/**
	 * Объект-аниматор ховера.
	 * @param {Object} options Настройки:
	 * <ul>
	 *  <li><code>color</code> - цвет заполнения,</li>
	 *  <li><code>parent</code> - родитель создаваемого элемента (объект jQuery),</li>
	 *  <li><code>time</code> - время анимации ховера в милисекундах,</li>
	 *  <li><code>radius</code> - радиус закругления ховера в пикселях,</li>
	 *  <li><code>opacity</code> - коэффициент прозрачности ховера.</li>
	 * </ul>
	 * @return {Animator}
	 */
	function Animator(options) {

		/**
		 * Флаг, проинициализирован ли элемент ховера (инициализация происходит при первом ховере).
		 */
		var isInited = false;

		/**
		 * Объект адаптера. Если бразуер поддерживает Canvas, то CanvasAdapter, иначе VmlAdapter.
		 * @type {CanvasAdapter|VmlAdapter}
		 */
		var adapter;

		/**
		 * Инициализирует элемент ховера.
		 */
		function init() {
			adapter = $.browser.msie ? new VmlAdapter(options) : new CanvasAdapter(options);
			isInited = true;
		}

		/**
		 * Показывает ховер с задержкой.
		 */
		this.show = function() {
			isInited || init();
			adapter.show(options.parent.outerWidth(), options.parent.outerHeight());
		}

		/**
		 * Скрывает ховер.
		 */
		this.hide = function() {
			isInited || init();
			adapter.hide();
		}
	}

	/**
	 * Адаптер для VML.
	 * @param {Object} options Те же настройки, которые передаются в <code>Animator</code>.
	 * @return {VmlAdapter}
	 */
	function VmlAdapter(options) {

		/**
		 * Флаг, можно ли показать ховер после задержки (юзер мог слинять со ссылки).
		 */
		var isMayShow = false;

		/**
		 * Ширина (<code>width</code>) и высота (<code>height</code>) блока, в котором будет ховер.
		 */
		var curSize = {
			height: options.parent.outerHeight(),
			width: options.parent.outerWidth()
		};

		/**
		 * Элемент ховера.
		 * @type {DomElement}
		 */
		var canvas = document.createElement('v:roundrect');

		canvas.style.position = 'absolute';
		canvas.style.top = 0;
		canvas.style.left = 0;
		canvas.style.width = '1px';
		canvas.style.height = '10px';
		canvas.stroked = 'False';
		canvas.arcsize = curSize.width < curSize.height ? options.radius / curSize.width : options.radius / curSize.height;
		$(canvas).prependTo(options.parent);
		var fill = document.createElement('v:fill');
		fill.color = options.color;
		fill.opacity = 0;
		canvas.appendChild(fill);

		/**
		 * Анимирует появление ховера.
		 * @param {Number} width Ширина блока ховера по окончании анимации в пикселях.
		 * @param {Number} height Высота блока ховера по окончании анимации в пикселях.
		 */
		function showAnimate(width, height) {
			if (!isMayShow) {
				return;
			}

			jTweener.removeTween(canvas);

			canvas.style.width = width + 'px';

			jTweener.addTween(canvas, {
				time: options.time / 1000,
				transition: 'easeInOutCubic',
				moveX: function(value) {
					canvas.style.height = Math.round(height * value) + 'px';
					canvas.style.top = Math.round(height * (1 - value) / 2) + 'px';
					$(canvas).children().eq(0).attr('opacity', options.opacity * value);
				},
				onComplete: function() {
					$('#main_navigation').css('visibility', 'hidden').css('visibility', '');
				}
			});
		}

		/**
		 * Анимирует скрытие ховера.
		 */
		function hideAnimate() {
			isMayShow = false;

			jTweener.removeTween(canvas);

			/** @type {Number} */
			var height = options.parent.outerHeight();
			/** @type {Number} */
			var width = options.parent.outerWidth();

			jTweener.addTween(canvas, {
				time: options.time / 1000,
				transition: 'easeOutCubic',
				moveX: function(value) {
					value = 1 - value;

					canvas.style.height = Math.round(height * value) + 'px';
					canvas.style.top = Math.round(height * (1 - value) / 2) + 'px';
					$(canvas).children().eq(0).attr('opacity', options.opacity * value);
				},
				onComplete: function() {
					canvas.style.width = 0;
					canvas.style.height = 0;
					$('#main_navigation').css('visibility', 'hidden').css('visibility', '');
				}
			});
		}

		/**
		 * Анимирует появление ховера с задержкой.
		 * @param {Number} width Ширина блока ховера по окончании анимации в пикселях.
		 * @param {Number} height Высота блока ховера по окончании анимации в пикселях.
		 */
		this.show = function(width, height) {
			isMayShow = true;
			setTimeout(function() {
				showAnimate(width, height);
			}, 200);
		};

		/**
		 * Анимирует скрытие ховера.
		 */
		this.hide = function() {
			hideAnimate();
		};
	}

	/**
	 * Адаптер для Canvas.
	 * @param {Object} options Те же настройки, которые передаются в <code>Animator</code>.
	 * @return {CanvasAdapter}
	 */
	function CanvasAdapter(options) {
		/**
		 * Флаг, можно ли показать ховер после задержки (юзер мог слинять со ссылки).
		 */
		var isMayShow = false;

		/**
		 * Элемент ховера.
		 * @type {jQuery}
		 */
		var canvas = $('<canvas></canvas>').prependTo(options.parent);

		canvas.css({
			position: 'absolute',
			top: 0,
			left: 0
		});

		/**
		 * Элемент контекста канваса.
		 */
		var ctx = canvas.get(0).getContext('2d');

		var lastHeight = 0;

		/**
		 * Анимирует появление ховера.
		 * @param {Number} width Ширина блока ховера по окончании анимации в пикселях.
		 * @param {Number} height Высота блока ховера по окончании анимации в пикселях.
		 */
		function showAnimate(width, height) {
			if (!isMayShow) {
				return;
			}

			jTweener.removeTween(canvas);
			canvas.attr('width', width);
			canvas.attr('height', height);

			jTweener.addTween(canvas, {
				time: options.time / 1000,
				transition: 'easeInOutCubic',
				moveX: function(value) {
					var curHeight = Math.round(value * height);
					var curRadius = options.radius;

					if (curRadius > Math.floor(curHeight / 2)) {
						curRadius = Math.floor(curHeight / 2);
					}
					if (curRadius > Math.floor(width / 2)) {
						curRadius = Math.floor(width / 2);
					}
					var curStartY = Math.round((height - curHeight) / 2);

					ctx.clearRect(0, 0, width, height);

					ctx.fillStyle = options.color;
					ctx.globalAlpha = options.opacity * value;
					ctx.beginPath();
					ctx.moveTo(0, curStartY + curRadius);
					ctx.quadraticCurveTo(0, curStartY, curRadius, curStartY);
					ctx.lineTo(width - curRadius, curStartY);
					ctx.quadraticCurveTo(width, curStartY, width, curStartY + curRadius);
					ctx.lineTo(width, curStartY + curHeight - curRadius);
					ctx.quadraticCurveTo(width, curStartY + curHeight, width - curRadius, curStartY + curHeight);
					ctx.lineTo(curRadius, curStartY + curHeight);
					ctx.quadraticCurveTo(0, curStartY + curHeight, 0, curStartY + curHeight - curRadius);
					ctx.lineTo(0, curStartY + curRadius);
					ctx.fill();
					ctx.closePath();

					lastHeight = curHeight;
				}
			});
		}

		/**
		 * Анимирует скрытие ховера.
		 */
		function hideAnimate() {
			isMayShow = false;

			jTweener.removeTween(canvas);

			var height = lastHeight;
			var width = options.parent.outerWidth();

			jTweener.addTween(canvas, {
				time: options.time / 1000,
				transition: 'easeOutCubic',
				moveX: function(value) {
					value = 1 - value;

					/** @type {Number} */
					var curHeight = Math.round(value * height);
					/** @type {Number} */
					var curStartY = Math.round((height - curHeight) / 2);
					/** @type {Number} */
					var curRadius = options.radius;

					if (curRadius > Math.floor(curHeight / 2)) {
						curRadius = Math.floor(curHeight / 2);
					}
					if (curRadius > Math.floor(width / 2)) {
						curRadius = Math.floor(width / 2);
					}

					ctx.clearRect(0, 0, width, height);
					ctx.fillStyle = options.color;
					ctx.globalAlpha = options.opacity * value;
					ctx.beginPath();
					ctx.moveTo(0, curStartY + curRadius);
					ctx.quadraticCurveTo(0, curStartY, curRadius, curStartY);
					ctx.lineTo(width - curRadius, curStartY);
					ctx.quadraticCurveTo(width, curStartY, width, curStartY + curRadius);
					ctx.lineTo(width, curStartY + curHeight - curRadius);
					ctx.quadraticCurveTo(width, curStartY + curHeight, width - curRadius, curStartY + curHeight);
					ctx.lineTo(curRadius, curStartY + curHeight);
					ctx.quadraticCurveTo(0, curStartY + curHeight, 0, curStartY + curHeight - curRadius);
					ctx.lineTo(0, curStartY + curRadius);
					ctx.fill();
					ctx.closePath();
				},
				onComplete: function() {
					canvas.attr('width', 1);
					canvas.attr('height', 1);
				}
			});
		}

		/**
		 * Анимирует появление ховера с задержкой.
		 * @param {Number} width Ширина блока ховера по окончании анимации в пикселях.
		 * @param {Number} height Высота блока ховера по окончании анимации в пикселях.
		 */
		this.show = function(width, height) {
			isMayShow = true;
			setTimeout(showAnimate, 200, width, height);
		};

		/**
		 * Анимирует скрытие ховера.
		 */
		this.hide = function() {
			hideAnimate();
		};
	}

	return {
		show: function(href) {
			backs[href].show();
		},
		hide: function(href) {
			backs[href].hide();
		}
	};
})();