/**
 * @projectDescription
 * Управление слайд-шоу.
 *
 * @author Vlad Yakovlev (scorpix@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @version 0.1 (25.11.2008)
 * @requires jQuery 1.2
 * @requires jTweener
 */
var slideshow = (function() {

	/**
	 * Объекты блоков.
	 * @type {Object}
	 */
	var blocks;

	/**
	 * @type {Array[Object]}
	 */
	var images = [];

	/**
	 * Ширина (<code>width</code>) и высота (<code>height</code>) окна в пикселях.
	 * @type {Object}
	 */
	var winSize;

	/**
	 * Реальные ширина (<code>width</code>) и высота (<code>height</code>) изображения в пикселях.
	 * @type {Object}
	 */
	var imageRealSize;

	/**
	 * Ширина (<code>width</code>) и высота (<code>height</code>) схлопывающегося блока в пикселях.
	 * @type {Object}
	 */
	var collapseBlockSize;

	/**
	 * Ширина превьюшки в пикселях.
	 */
	var previewWidth = 50;

	/**
	 * Ширина блока превьюшек в пикселях.
	 * @type {Number}
	 */
	var previewsBlockWidth;

	/**
	 * Индекс из массива <code>images</code> текущего изображения.
	 */
	var curHtmlIndex = 0;

	var curImageIndex = 0;

	/**
	 * Индекс из массива <code>images</code> первой видимой превьюшки.
	 * type {Number}
	 */
	var firstHtmlIndex;

	/**
	 * Вертикальные отступы изображения от окна в пикселях.
	 */
	var marginBottom = 148;//78;

	/**
	 * Типы разделов изображений.
	 */
	var types = {};

	/**
	 * Количество превьюшек по бокам.
	 */
	var previewBorderCount = 2;

	/**
	 * Время между сменами изображений в слайдшоу в милисекундах.
	 */
	var slideshowTime = 5000;

	/**
	 * Граничная линия от верха окна в пикселях, при которой схлопывающийся блок анимируется.
	 */
	var hoverTop = 200;

	/**
	 * Флаг анимации.
	 */
	var isAnimate = false;

	/**
	 * Флаг слайдшоу.
	 */
	var isSlideshow = false;

	var logoSize;

	var navigationPaddings;

	var hoverImages = {
		prev: false,
		next: false
	};

	var canvas;

	var ctx;

	var className = 'shape';

	var slideshowInterval;


	$(function() {
		blocks = {
			imageBlock: $('#image'),
			image: $('#image img'),
			playNavigation: $('#play_navigation'),
			previewContent: $('#play_navigation .content'),
			previewsBlock: $('#play_navigation .content .previews'),
			scroller: $('#scroller'),
			layout: $('#layout'),
			sections: $('#play_navigation .content .sections li'),
			fader: $('#fader'),
			arrowPrev: $('#play_navigation .content .line .gallery_arrow.l span'),
			arrowNext: $('#play_navigation .content .line .gallery_arrow.r span'),
			previewsList: null,
			previews: null,
			collapse: $('#play_navigation .content .collapse'),
			collapseContainer: $('#play_navigation .content .collapse_container'),
			logo: $('#play_navigation .content .logo'),
			notice: $('#description'),
			controls: $('#controls'),
			controlsContainer: $('#controls .container'),
			controlsPlayPause: $('#controls .play_pause'),
			controlsPrev: $('#controls .prev'),
			controlsNext: $('#controls .next')
		};

		$(window).load(onLoad).resize(onResize);
		blocks.previewsBlock.click(onChangeImage);
		blocks.sections.click(onChangeSection);
		blocks.arrowPrev.click(onPrevImage);
		blocks.controlsPrev.click(onPrevImage);
		blocks.arrowNext.click(onNextImage);
		blocks.controlsNext.click(onNextImage);
		blocks.layout.click(onImageClick);
		blocks.layout.mouseover(onImageMove).mousemove(onImageMove).mouseout(onImageOut);
		blocks.controlsPlayPause.click(onPlayPause);

		init();

		if (!$.browser.msie) {
			canvas = $('<canvas class="' + className + '"></canvas>').appendTo(blocks.imageBlock);
			ctx = canvas.get(0).getContext('2d');
			blocks.image.remove();
		}
	});

	/**
	 * Событие при загрузке окна браузера.
	 */
	function onLoad() {
		winSize = {
			height: blocks.scroller.height(),
			width: blocks.scroller.width()
		};
		previewsBlockWidth = blocks.previewsBlock.width();
		collapseBlockSize = {
			height: blocks.collapse.height(),
			width: winSize.width * 0.88 - 110
		};
		logoSize = {
			height: blocks.logo.height(),
			width: blocks.logo.width()
		};
		navigationPaddings = {
			height: blocks.playNavigation.height() - blocks.collapseContainer.height(),
			width: blocks.playNavigation.width() - blocks.collapseContainer.width()
		};

		loadImage(0, function() {
			fadeOut();
			startSlideshow();
		});

		if ($.browser.msie) {
			blocks.playNavigation.find('.opac').css('opacity', 0.7);
		}
	}

	/**
	 * Событие при ресайзе окна браузера.
	 */
	function onResize() {
		winSize = {
			height: blocks.scroller.height(),
			width: blocks.scroller.width()
		};

		collapseBlockSize.width = winSize.width * 0.88 - 110;
		previewsBlockWidth = blocks.previewsBlock.width();
		updateImageBlockSize();

		if (!$.browser.msie) {
			updateCanvas();
		}
	}

	function onImageMove(event) {
		/** @type {Number} */
		var mouseOffsetX = event.pageX;
		/** @type {Number} */
		var layoutWidth = blocks.layout.outerWidth();

		if (mouseOffsetX > Math.round(layoutWidth / 2)) {
			if (hoverImages.prev) {
				blocks.controlsPrev.find('span').removeClass('hover');
				hoverImages.prev = false;
			}
			if (!hoverImages.next) {
				blocks.controlsNext.find('span').addClass('hover');
				hoverImages.next = true;
			}
		} else {
			if (hoverImages.next) {
				blocks.controlsNext.find('span').removeClass('hover');
				hoverImages.next = false;
			}
			if (!hoverImages.prev) {
				blocks.controlsPrev.find('span').addClass('hover');
				hoverImages.prev = true;
			}
		}
	}

	function onImageOut() {
		if (hoverImages.prev) {
			blocks.controlsPrev.find('span').removeClass('hover');
			hoverImages.prev = false;
		}

		if (hoverImages.next) {
			blocks.controlsNext.find('span').removeClass('hover');
			hoverImages.next = false;
		}
	}

	function onImageClick(event) {
		if (isAnimate) {
			return;
		}

		/** @type {Number} */
		var mouseOffsetX = event.pageX;
		/** @type {Number} */
		var layoutWidth = blocks.layout.outerWidth();

		if (mouseOffsetX > Math.round(layoutWidth / 2)) {
			onNextImage();
		} else {
			onPrevImage();
		}
	}

	/**
	 * Событие при выборе предыдущего изображения из списка.
	 */
	function onPrevImage() {
		if (isAnimate) {
			return;
		}

		/** @type {Number} */
		var index = getCorrectIndex(curHtmlIndex - 1);

		if (isSlideshow) {
			clearInterval(slideshowInterval);
			slideshowInterval = setInterval(slideshowStep, slideshowTime);
		}

		//stopSlideshow();
		animatePreviews(index, function() {
			changeImage(index);
		});
	}

	/**
	 * Событие при выборе следующего изображения из списка.
	 */
	function onNextImage() {
		if (isAnimate) {
			return;
		}

		/** @type {Number} */
		var index = getCorrectIndex(curHtmlIndex + 1);

		if (isSlideshow) {
			clearInterval(slideshowInterval);
			slideshowInterval = setInterval(slideshowStep, slideshowTime);
		}

		//stopSlideshow();
		animatePreviews(index, function() {
			changeImage(index);
		});
	}

	function onPlayPause(event) {
		if (isAnimate) {
			return;
		}

		if (blocks.previewsBlock.hasClass('slideshow')) {
			stopSlideshow();
		} else {
			startSlideshow();
		}

		event.stopPropagation();
	}

	/**
	 * Событие при щелчке на превьюшке.
	 * @param {Event} event Событие мыши.
	 */
	function onChangeImage(event) {
		if (isAnimate || 'SPAN' != event.target.nodeName) {
			return;
		}

		/** @type {jQuery} */
		var el = $(event.target.parentNode);

		if (el.hasClass('selected')) {
			if (blocks.previewsBlock.hasClass('slideshow')) {
				stopSlideshow();
			} else {
				startSlideshow();
			}

			return;
		}

		/** @type {Number} */
		var index = getCorrectIndex(el.prevAll().size() + firstHtmlIndex);

		if (isSlideshow) {
			clearInterval(slideshowInterval);
			slideshowInterval = setInterval(slideshowStep, slideshowTime);
		}

		//stopSlideshow();
		animatePreviews(index, function() {
			changeImage(index);
		});
	}

	/**
	 * Событие при выборе секции изображения.
	 * @param {Event} event Событие мыши.
	 */
	function onChangeSection(event) {
		if (isAnimate) {
			return;
		}

		/** @type {jQuery} */
		var el = $(this);

		if (el.hasClass('selected') || isAnimate) {
			return;
		}

		if (isSlideshow) {
			clearInterval(slideshowInterval);
			slideshowInterval = setInterval(slideshowStep, slideshowTime);
		}

		//stopSlideshow();

		isAnimate = true;

		// Выясняем, на какой раздел переходим

		/** @type {Array} */
		var classes = el.attr('class').split(' ');
		/** @type {String} */
		var type;

		for (var i = 0; i < classes.length; i++) {
			if ('type_' == classes[i].substr(0, 5)) {
				type = classes[i].substr(5);

				break;
			}
		}

		/**
		 * Индекс новой самой левой превьюшки из массива <code>images</code>.
		 * @type {Number}
		 */
		var firstIndex = getCorrectIndex(types[type].start - previewBorderCount);

		// Узнаем, в каком направлении анимировать ближе.
		var f1 = firstIndex - curHtmlIndex;
		var f2 = images.length - f1 + 1;

		var prevCount = curHtmlIndex > firstIndex ? f1 : f2;
		var nextCount = curHtmlIndex <= firstIndex ? f1 : f2;

		/**
		 * Количество добавляемых блоков.
		 * @type {Number}
		 */
		var count;

		/** type {Number} */
		var newBlocksWidth;

		/** @type {jQuery} */
		var newEls;

		if (prevCount < nextCount) {
			// Анимация слева направо

			count = getCorrectIndex(firstHtmlIndex - firstIndex);

			if (count) {
				newBlocksWidth = count * (previewWidth + 12);
				blocks.previewsList.width(getPreviewsBlockWidth(newBlocksWidth));

				// Добавляем превьюшки для последующей анимации.
				var html = '';

				for (var i = 0; i < count; i++) {
					html += '<li class="hidden"><span></span></li>';
				}

				$(html).prependTo(blocks.previewsList);
				newEls = blocks.previewsList.find('li.hidden');

				// Для каждой превьюшки устанавливаем изображения.
				newEls.each(function(index) {
					/** @type {Number} */
					var correctIndex = getCorrectIndex(firstIndex + index);
					$(this).css('background', getPreviewBackground(correctIndex));
				});

				newEls.removeClass('hidden');
				blocks.previewsList.css('left', parseInt(blocks.previewsList.css('left')) - newBlocksWidth);
			}

			jTweener.addTween(blocks.previewsList, {
				left: 0,
				time: 1,
				transition: 'easeInOutCubic',
				onComplete: function() {
					if (count) {
						// Удаляем ненужные превьюшки (столько же, сколько и добавили).
						blocks.previewsList.find('li').eq(blocks.previews.size() - 1).nextAll().remove();
						// Обновляем список превьюшек.
						blocks.previews = blocks.previewsList.find('li');
					}

					firstHtmlIndex = firstIndex;

					isAnimate = false;

					changeImage(types[type].start);
				}
			});
		} else {
			// Анимация справа налево

			count = getCorrectIndex(firstIndex - firstHtmlIndex);

			if (count) {
				newBlocksWidth = count * (previewWidth + 12);
				blocks.previewsList.width(getPreviewsBlockWidth(newBlocksWidth));

				// Добавляем превьюшки для последующей анимации.
				var html = '';

				for (var i = 0; i < count; i++) {
					html += '<li class="hidden"><span></span></li>';
				}

				$(html).appendTo(blocks.previewsList);
				newEls = blocks.previewsList.find('li.hidden');

				/** @type {jQuery} */
				var reservedCount = blocks.previews.size();

				// Для каждой превьюшки устанавливаем изображения.
				newEls.each(function(index) {
					/** @type {Number} */
					var correctIndex = getCorrectIndex(firstHtmlIndex + reservedCount + index);
					$(this).css('background', getPreviewBackground(correctIndex));
				});

				newEls.removeClass('hidden');
			}

			jTweener.addTween(blocks.previewsList, {
				left: parseInt(blocks.previewsList.css('left')) - newBlocksWidth,
				time: 1,
				transition: 'easeInOutCubic',
				onComplete: function() {
					if (count) {
						// Удаляем ненужные превьюшки (столько же, сколько и добавили).
						blocks.previewsList.find('li').eq(count).prevAll().remove();
						blocks.previewsList.css('left', parseInt(blocks.previewsList.css('left')) + newBlocksWidth);
						// Обновляем список превьюшек.
						blocks.previews = blocks.previewsList.find('li');
					}

					firstHtmlIndex = firstIndex;

					isAnimate = false;

					changeImage(types[type].start);
				}
			});
		}
	}

	/**
	 * Инициализирует слайдшоу при загрузке страницы.
	 */
	function init() {
		firstHtmlIndex = images.length - previewBorderCount;

		/**
		 * Количество превьшек. Формируется исходя из разрешения экрана монитора юзера.
		 * @type {Number}
		 */
		var count = Math.ceil(screen.width / (previewWidth + 12));

		// Добавляем превьюшки на страницу.
		var html = '';

		for (var i = 0; i < count; i++) {
			html += '<li><span></span></li>';
		}

		html = '<ul class="hidden">' + html + '</ul>';

		blocks.previewsBlock.html(html);
		blocks.previewsList = blocks.previewsBlock.find('ul');
		blocks.previews = blocks.previewsList.find('li');

		// Для каждой превьюшки надо установить изображение.
		blocks.previews.each(function(index) {
			/** @type {Number} */
			var correctIndex = previewBorderCount <= index ? index - 2 : firstHtmlIndex + index;
			$(this).css('background', getPreviewBackground(correctIndex));
		});

		blocks.previews.eq(previewBorderCount).addClass('selected');
		blocks.previewsBlock.addClass('slideshow');
		blocks.controlsContainer.addClass('slideshow');
		blocks.previewsBlock.find('ul').css('width', getPreviewsBlockWidth()).removeClass('hidden');

		blocks.sections.eq(0).addClass('selected');
	}

	/**
	 * Обновляет размер блока изображения.
	 */
	function updateImageBlockSize() {
		/**
		 * Максимальные размеры изображения.
		 */
		var maxSize = {
			height: winSize.height - marginBottom,
			width: winSize.width - 18
		};
		/**
		 * Высота изображения в зависимости от 100%-ой ширины.
		 * @type {Number}
		 */
		var heightByWidth = Math.round(imageRealSize.height * maxSize.width / imageRealSize.width);
		/**
		 * Ширина изображения в зависимости от 100%-ой высоты.
		 * @type {Number}
		 */
		var widthByHeight = Math.round(imageRealSize.width * maxSize.height / imageRealSize.height);

		// Решаем, ширина зависит от высоты или высота от ширины.
		if (heightByWidth > maxSize.height) {
			blocks.imageBlock.css({
				height: Math.round(imageRealSize.height * widthByHeight / imageRealSize.width),
				width: widthByHeight
			});
		} else {
			blocks.imageBlock.css({
				height: heightByWidth,
				width: Math.round(imageRealSize.width * heightByWidth / imageRealSize.height)
			});
		}

		//blocks.imageBlock.css('width', heightByWidth > maxSize.height ? widthByHeight : '');
	}

	/**
	 * Меняет изображение.
	 * @param {Number} index Индекс из массива <code>images</code>.
	 */
	function changeImage(index) {
		curHtmlIndex = index;
		changePreview(index);

		fadeIn(function() {
			loadImage(index, fadeOut);
		});
	}

	/**
	 * Загружает изображение.
	 * @param {Number} index Индекс из массива <code>images</code>.
	 * @param {Function} postFunc Функция, которая выполнится после загрузки.
	 */
	function loadImage(index, postFunc) {
		// Изображение загружается.
		/*if (images[index].loading) {
			return;
		}*/

		// Изображение уже загрузилось.
		if (images[index].loaded) {

			curHtmlIndex == index && showImage(index);
			postFunc && postFunc();

			return;
		}

		images[index].loading = true;
		images[index].object = new Image();
		images[index].object.onload = function() {

			images[index].loading = false;
			images[index].loaded = true;

			if (curHtmlIndex == index) {
				showImage(index);
				postFunc && postFunc();
			}
		};
		images[index].object.src = images[index].path;
	}

	/**
	 * Меняет превьюшку.
	 * @param {Number} index Индекс превьюшки из массива <code>images</code>, которая будет текущей.
	 */
	function changePreview(index) {
		/** @type {Number} */
		var realIndex = getCorrectIndex(index - firstHtmlIndex);
		/** @type {Number} */
		var curType = getTypeByIndex(index);

		blocks.previews.filter('.selected').removeClass('selected');
		blocks.previews.eq(realIndex).addClass('selected');

		if (!blocks.sections.filter('.type_' + curType).hasClass('selected')) {
			blocks.sections.filter('.selected').removeClass('selected');
			blocks.sections.filter('.type_' + curType).addClass('selected');
		}
	}

	/**
	 * Меняет изображение.
	 * @param {Number} index Индекс из массива <code>images</code>.
	 */
	function showImage(index) {
		imageRealSize = {
			height: images[index].object.height,
			width: images[index].object.width
		};

		curImageIndex = index;

		if ($.browser.msie) {
			blocks.image.attr('src', images[index].path);
			updateImageBlockSize();
		} else {
			updateImageBlockSize();
			updateCanvas();
		}

		blocks.notice.html(images[index].notice);

		// Загрузим сразу предыдущее и последующее изображение, чтоб потом быстро можно было их показать.
		loadImage(getCorrectIndex(index - 1));
		loadImage(getCorrectIndex(index + 1));
	}

	function updateCanvas() {
		var height = blocks.imageBlock.height();
		var width = blocks.imageBlock.width();

		canvas.attr({
			height: height,
			width: width
		});
		ctx.drawImage(images[curImageIndex].object, 0, 0, images[curImageIndex].object.width, images[curImageIndex].object.height, 0, 0, width, height);
	}

	/**
	 * Запускает слайдшоу.
	 */
	function startSlideshow() {
		if (isSlideshow) {
			return;
		}

		blocks.controls.removeClass('hidden');
		hidePlayNavigation();

		isSlideshow = true;
		blocks.previewsBlock.addClass('slideshow');
		blocks.controlsContainer.addClass('slideshow');

		slideshowInterval = setInterval(slideshowStep, slideshowTime);
	}

	/**
	 * Меняет изображение при слайдшоу.
	 */
	function slideshowStep() {
		clearInterval(slideshowInterval);

		if (!isSlideshow) {
			return;
		}

		/** @type {Number} */
		var index = getCorrectIndex(curHtmlIndex + 1);

		animatePreviews(index, function() {
			curHtmlIndex = index;

			fadeIn(function() {
				changePreview(index);
				loadImage(index, function() {
					fadeOut();
					slideshowInterval = setInterval(slideshowStep, slideshowTime);
				});
			});
		});
	}

	/**
	 * Останавливаем слайдшоу.
	 */
	function stopSlideshow() {
		if (!isSlideshow) {
			return;
		}

		blocks.controls.addClass('hidden');
		showPlayNavigation();

		isSlideshow = false;
		blocks.previewsBlock.removeClass('slideshow');
		blocks.controlsContainer.removeClass('slideshow');
	}

	/**
	 * Анимирует перемещение текущей превьюшки на середину, если она слишком далеко от центра.
	 * @param {Number} index Индекс превьюшки из массива <code>images</code>, которую надо проверить.
	 * @param {Function} postFunc Функция, которая выполнится после проверки и анимации, если она будет.
	 */
	function animatePreviews(index, postFunc) {
		/**
		 * Количество видимых на экране превьюшек.
		 * @type {Number}
		 */
		var visibleCount = Math.ceil(previewsBlockWidth / (previewWidth + 12));
		/**
		 * Индекс превьюшки от левой границы, индексация с нуля.
		 * @type {Number}
		 */
		var curVisibleIndex = getCorrectIndex(index - firstHtmlIndex);
		/**
		 * Индекс центральной превьюшки.
		 * @type {Number}
		 */
		var centerVisibleIndex;
		/**
		 * Количество дополнительных блоков, которое потребуется при анимации.
		 * @type {Number}
		 */
		var count;

		/**
		 * Индекс новой самой левой превьюшки из массива <code>images</code>.
		 * @type {Number}
		 */
		var firstIndex;

		/** @type {Number} */
		var newBlocksWidth;

		/** @type {jQuery} */
		var newEls;

		if (curVisibleIndex < previewBorderCount) {
			// Изображение слишком уперлось в левый край - анимация слева направо
			isAnimate = true;

			centerVisibleIndex = Math.ceil(visibleCount / 2);
			count = centerVisibleIndex - curVisibleIndex;
			curHtmlIndex = getCorrectIndex(curHtmlIndex - count);
			firstIndex = getCorrectIndex(firstHtmlIndex - count);
			newBlocksWidth = count * (previewWidth + 12);
			blocks.previewsList.width(getPreviewsBlockWidth(newBlocksWidth));

			// Добавляем превьюшки для последующей анимации.
			var html = '';

			for (var i = 0; i < count; i++) {
				html += '<li class="hidden"><span></span></li>';
			}

			$(html).prependTo(blocks.previewsList);
			newEls = blocks.previewsList.find('li.hidden');

			// Для каждой превьюшки устанавливаем изображения.
			newEls.each(function(index) {
				var correctIndex = getCorrectIndex(firstIndex + index);
				$(this).css('background', getPreviewBackground(correctIndex));
			});
			newEls.removeClass('hidden');
			blocks.previewsList.css('left', parseInt(blocks.previewsList.css('left')) - newBlocksWidth);

			jTweener.addTween(blocks.previewsList, {
				left: 0,
				time: 1,
				transition: 'easeInOutCubic',
				onComplete: function() {
					// Удаляем ненужные превьюшки (столько же, сколько и добавили).
					blocks.previewsList.find('li').eq(blocks.previews.size() - 1).nextAll().remove();
					// Обновляем список превьюшек.
					blocks.previews = blocks.previewsList.find('li');

					firstHtmlIndex = firstIndex;

					isAnimate = false;

					postFunc && postFunc();
				}
			});
		} else if (curVisibleIndex >= visibleCount - previewBorderCount) {
			// Изображение слишком уперлось в правый край - анимация справа налево

			isAnimate = true;

			centerVisibleIndex = Math.floor(visibleCount / 2);
			count = curVisibleIndex - centerVisibleIndex;
			curHtmlIndex = getCorrectIndex(curHtmlIndex + count);
			firstIndex = getCorrectIndex(firstHtmlIndex + count);
			newBlocksWidth = count * (previewWidth + 12);
			blocks.previewsList.width(getPreviewsBlockWidth(newBlocksWidth));

			// Добавляем превьюшки для последующей анимации.
			var html = '';

			for (var i = 0; i < count; i++) {
				html += '<li class="hidden"><span></span></li>';
			}

			$(html).appendTo(blocks.previewsList);
			newEls = blocks.previewsList.find('li.hidden');

			/** @type {Number} */
			var reservedCount = blocks.previews.size();

			// Для каждой превьюшки устанавливаем изображения.
			newEls.each(function(index) {
				var correctIndex = getCorrectIndex(firstHtmlIndex + reservedCount + index);
				$(this).css('background', getPreviewBackground(correctIndex));
			});
			newEls.removeClass('hidden');

			jTweener.addTween(blocks.previewsList, {
				left: parseInt(blocks.previewsList.css('left')) - newBlocksWidth,
				time: 1,
				transition: 'easeInOutCubic',
				onComplete: function() {
					// Удаляем ненужные превьюшки (столько же, сколько и добавили).
					blocks.previewsList.find('li').eq(count).prevAll().remove();
					blocks.previewsList.css('left', parseInt(blocks.previewsList.css('left')) + newBlocksWidth);
					// Обновляем список превьюшек.
					blocks.previews = blocks.previewsList.find('li');

					firstHtmlIndex = firstIndex;

					isAnimate = false;

					postFunc && postFunc();
				}
			});
		} else {
			postFunc && postFunc();
		}
	}

	/**
	 * Скрывает изображение за чернотой.
	 * @param {Function} postFunc Функция, которая выполнится после анимации.
	 */
	function fadeIn(postFunc) {
		jTweener.removeTween(blocks.fader);
		blocks.fader.removeClass('hidden');
		jTweener.addTween(blocks.fader, {
			opacity: 1,
			time: 0.3,
			onComplete: function() {
				postFunc && postFunc();
			}
		});
	}

	/**
	 * Показывает изображение из черноты.
	 * @param {Function} postFunc Функция, которая выполнится после анимации.
	 */
	function fadeOut(postFunc) {
		jTweener.removeTween(blocks.fader);
		blocks.fader.removeClass('hidden');
		jTweener.addTween(blocks.fader, {
			opacity: 0,
			time: 0.6,
			onComplete: function() {
				blocks.fader.addClass('hidden');
				postFunc && postFunc();
			}
		});
	}

	/**
	 * Показывает панель с превьюшками.
	 */
	function showPlayNavigation() {
		jTweener.removeTween(blocks.collapse);

		blocks.collapseContainer.css({
			height: collapseBlockSize.height,
			width: winSize.width * 0.88 - navigationPaddings.width
		});

		var startWidth = blocks.playNavigation.width();
		var finishWidth = winSize.width * 0.88;

		jTweener.addTween(blocks.collapse, {
			height: collapseBlockSize.height,
			width: winSize.width * 0.88 - navigationPaddings.width,
			time: 1,
			moveX: function(k) {
				blocks.playNavigation.css('width', startWidth + k * (finishWidth - startWidth));
			},
			onComplete: function() {
				blocks.playNavigation.css({
					height: '',
					width: ''
				});
				blocks.collapse.css({
					height: '',
					width: ''
				});
				blocks.collapseContainer.css({
					height: '',
					width: ''
				});
			}
		});
	}

	function hidePlayNavigation() {
		jTweener.removeTween(blocks.collapse);

		blocks.collapseContainer.css({
			height: blocks.collapseContainer.height(),
			width: blocks.collapseContainer.width()
		});
		blocks.collapse.css({
			height: blocks.collapseContainer.height(),
			width: blocks.collapseContainer.width()
		});

		var startWidth = blocks.playNavigation.width();
		var finishWidth = blocks.playNavigation.width() - blocks.collapseContainer.width();

		jTweener.addTween(blocks.collapse, {
			height: logoSize.height,
			width: 0,
			time: 1,
			moveX: function(k) {
				blocks.playNavigation.css('width', startWidth + k * (finishWidth - startWidth));
			}
		});
	}

	/**
	 * Возвращает ширину блока превьюшек в пикселях.
	 * @param {Number} plus Количество пикселей, которое нужно приплюсовать.
	 * @return {Number} Количество пикселей.
	 */
	function getPreviewsBlockWidth(plus) {
		/** @type {Number} */
		var result = screen.width + previewWidth + 12;

		return plus ? result + plus : result;
	}

	/**
	 * Возвращает корректный индекс, исходя из размера массива <code>images</code>.
	 * @param {Number} index Индекс, корректировку которого нужно сделать.
	 * @return {Number} Корректный индекс.
	 */
	function getCorrectIndex(index) {
		while (0 > index) {
			index += images.length;
		}

		return index % images.length;
	}

	/**
	 * Возвращает тип секции по индексу из массива <code>images</code>.
	 * @param {Number} index Индекс из массива <code>images</code>.
	 * @return {String} Тип секции.
	 */
	function getTypeByIndex(index) {
		for (var typeIndex in types) {
			if (types[typeIndex].start <= index && index < types[typeIndex].start + types[typeIndex].length) {
				return typeIndex;
			}
		}

		return '';
	}

	/**
	 * Возвращает значение CSS свойства <code>background</code> для превьюшки.
	 * @param {Number} index Индекс превьюшки из массива <code>images</code>.
	 * @return {String} Значение CSS свойства.
	 */
	function getPreviewBackground(index) {
		/** @type {String} */
		var type = getTypeByIndex(index);

		return 'url(' + types[type].previews.src + ') -' + (previewWidth * (index - types[type].start)) + 'px 0';
	}

	return {
		/**
		 * Получает входные данные и инициализирует переменные.
		 * @param {Array[Object]} Массив объектов с параметрами:
		 * <ul>
		 *  <li><code>type</code> - тип секции с изображениями,</li>
		 *  <li><code>previews</code> - путь к файлу с превьюшками,</li>
		 *  <li><code>images</code> - массив объектов с параметрами:
		 *   <ul>
		 *    <li><code>path</code> - путь к изображению,</li>
		 *    <li><code>notice</code> - описание изображения (необязательный параметр).</li>
		 *   </ul>
		 *  </li>
		 * </ul>
		 */
		setImages: function(inImages) {
			for (var i = 0; i < inImages.length; i++) {
				types[inImages[i].type] = {
					start: images.length,
					length: inImages[i].images.length
				};

				types[inImages[i].type].previews = new Image();
				types[inImages[i].type].previews.onload = function() {};
				types[inImages[i].type].previews.src = inImages[i].previews;

				for (var ii = 0; ii < inImages[i].images.length; ii++) {
					images.push({
						notice: inImages[i].images[ii].notice ? inImages[i].images[ii].notice : '',
						path: inImages[i].images[ii].path,
						loading: false,
						loaded: false,
						object: null
					});
				}
			}
		}
	};
})();
