/**
 *  jQuery Carousel plugin
 *  by Zach Waugh
 *  v1.1 - 1/12/2009
 */

(function($){
	// Private vars
	var isAnimating = false;
	
	/**
	 * Expose carousel plugin
	 * @params options (Object)
	 */
	$.fn.carousel = function(options)
	{	
		this.each(function(){
			// Default settings, can be overridden when function is called
			var settings = $.extend({
				next: 'a.next',
				previous: 'a.previous',
				page: 'a.page',
				current_page: '.current_page',
				total_pages: '.total_pages',
				item: 'li',
				items: '.carousel_items',
				container: '.carousel_container',
				duration: 750,
				padding: 0,
				paginated: false,
				perpage: false,
				controls: false,
				easing: 'swing',
				horizontal: true
			}, options);
			
			$(this).addClass('has_carousel');
			
			if(settings.horizontal)
			{
				initHorizontal($(this), settings);
			}
			else
			{
				initVertical($(this), settings);
			}
		});
	};
	
	/**
	 * setup and configure horizontal carousel
	 * @params carousel (jQuery Object), settings (Object)
	 */
	function initHorizontal(carousel, settings)
	{	
		settings.width = calculateWidth(carousel, settings);
	
		// check if using in paginated mode
		if(settings.paginated)
		{			
			if(!settings.perpage)
			{
				itemsize = carousel.find(settings.items).children(settings.item).innerWidth();
				settings.perpage = Math.round(settings.width / itemsize);
			}
		
			total = carousel.find(settings.items).children(settings.item).size();
		
			// Figure out how many pages are needed
			// Based on total / x, where x = items per page
			settings.pages = Math.ceil(total / settings.perpage);
			carousel.find(settings.total_pages).html(settings.pages);
			
			// Add controls if more than one page and controls == true
			if (settings.controls && settings.pages > 1)
			{
				buildControls(carousel, settings);
				carousel.find(settings.page).click(gotoPage);
			}
		}
		
		// Store settings on carousel jQuery object
		carousel.data('settings', settings);
		
		// Bind events
		carousel.find(settings.previous).click(previousPage);
		carousel.find(settings.next).click(nextPage);
	}
	
	/**
	 * setup and configure vertical carousel
	 * @params carousel (jQuery Object), settings (Object)
	 */
	function initVertical(carousel, settings)
	{	
		// Calculate height of displayed area
		var height = carousel.find(settings.container).css('height');
	
		// Width not set in CSS in IE6/IE7
		if (height == 'auto')
		{
			height = carousel.find(settings.container).innerHeight() + settings.padding;
		}
		else
		{
			height = parseFloat(height) + settings.padding;
		}
		
		settings.height = height;
	
		// Get number of items per "page"
		if(settings.paginated)
		{
			var perpage;
			if(!settings.perpage)
			{
				itemsize = carousel.find(settings.items).children(settings.item).innerHeight();
				perpage = Math.round(height / itemsize);
			}
			else
			{
				perpage = settings.perpage;
			}

			total = carousel.find(settings.items).children(settings.item).size();

			// Figure out how many pages are needed
			// Based on total / x, where x = items per page
		  settings.pages = Math.ceil(total / perpage);
			carousel.find(settings.total_pages).html(settings.pages);
			
			// Add controls if more than one page and controls == true
			if (settings.controls && settings.pages > 1)
			{
				buildControls(carousel, settings);
			}
		}

		// Store settings on carousel jQuery object
		carousel.data('settings', settings);
		
		// Bind Events
		carousel.find(settings.previous).click(previousPage);
		carousel.find(settings.next).click(nextPage);
	}
	
	/**
	 * buildControls: dynamically build html for controls
	 * @params carousel (jQuery Object), settings (Object)
	 */
	function buildControls(carousel, settings)
	{
		var controls = '<div class="controls">\n<ul>\n<li><a href="" class="previous disabled"> Previous </a></li>';
		for(var i = 0; i < settings.pages; i++) {
			if (i === 0)
			{
				controls += '<li><a href="" class="page selected"> * </a></li>\n';
			}
			else
			{
				controls += '<li><a href="" class="page"> * </a></li>\n';
			}
		}
		controls += '<li><a href="" class="next"> Next </a></li>\n</ul>\n</div><br class="clear" />';
		
		// Add controls node to DOM
		carousel.prepend(controls);
	}
	
	/**
	 * previousPage: go to previous page
	 * @params event (jQuery Event Object)
	 */
	function previousPage(event)
	{
		event.preventDefault();
		
		// Prevent clicking while animating
		if(isAnimating)
		{
			return false;
		}
		
		// Figure out which carousel to operate on
		var carousel = $(this).parents('.has_carousel');
		var settings = $(carousel).data('settings');
		
		if (settings.horizontal && carousel.find(settings.items).css('left') != '0px')
		{
			slideLeft(carousel, settings);
		}
		
		if (!settings.horizontal && carousel.find(settings.items).css('top') != '0px')
		{
			slideUp(carousel, settings);
		}

		return false;
	}
	
	/**
	 * nextPage: go to next page
	 * @params event (jQuery Event Object)
	 */
	function nextPage(event)
	{
		event.preventDefault();
		
		// Prevent clicking while animating
		if(isAnimating || $(this).hasClass('disabled'))
		{
			return false;
		}
		
		// Figure out which carousel to operate on
		var carousel = $(this).parents('.has_carousel');
	  var settings = $(carousel).data('settings');
		
		if(settings.horizontal)
		{ 
			slideRight(carousel, settings);
		}
		else
		{
			slideDown(carousel, settings);
		}
		
		return false;
	}
	
	/**
	 * gotoPage: handles clicking a particular page
	 * @params event (jQuery Event Object)
	 */
	function gotoPage(event)
	{
		// Prevent clicking while animating
		if(isAnimating || $(this).hasClass('disabled'))
		{
			return false;
		}
		
		// Figure out which carousel to operate on
		var carousel = $(this).parents('.has_carousel');
	  var settings = carousel.data('settings');
	
		if(!$(this).hasClass('active'))
		{
			slideTo(carousel, settings, carousel.find(settings.page).index(this));
		}

		return false;
	}
	
	/**
	 * slideTo: slide to a particular page in the current carousel
	 * @params carousel (jQuery Object), settings (Object), page (int)
	 */
	function slideTo(carousel, settings, page)
	{
		var current_page = currentPage(carousel, settings);
		var width = calculateWidth(carousel, settings);
		
		if(page < current_page)
		{
			slideLeft(carousel, settings, (current_page - page) * width);
		}
		else
		{
			slideRight(carousel, settings, (page - current_page) * width);
		}
	}
	
	/**
	 * slideLeft: slide left horizontally a given distance
	 * @params carousel (jQuery Object), settings (Object), distance (float)
	 */
	function slideLeft(carousel, settings, distance)
	{
		isAnimating = true;
		var width = calculateWidth(carousel, settings);
		
		if(distance === undefined)
		{
			distance = width;
		}
		
		carousel.find(settings.items).animate({left: '+=' + distance + 'px'}, {duration: settings.duration, easing: settings.easing, complete: function(){
			isAnimating = false;
			
			carousel.find(settings.next).removeClass('disabled');
			
			if(settings.paginated)
			{
			  selectPage(carousel, settings);	
			}
			
			if (carousel.find(settings.items).css('left') == '0px')
			{
				carousel.find(settings.previous).addClass('disabled');
			}
		}});
	}

	/**
	 * slideRight: slide right horizontally a given distance
	 * @params carousel (jQuery Object), settings (Object), distance (float)
	 */
	function slideRight(carousel, settings, distance)
	{
		isAnimating = true;
		var width = calculateWidth(carousel, settings);
		
		if(distance === undefined)
		{
			distance = width;
		}
		
		carousel.find(settings.items).animate({left: '-=' + distance + 'px'}, {duration: settings.duration, easing: settings.easing, complete: function(){
			isAnimating = false;
			
			carousel.find(settings.previous).removeClass('disabled');
			
			if (settings.paginated)
			{
				selectPage(carousel, settings);
			}
			
			// Figure out if last item is outside the bounds of the current window
			// if not, make next disabled
			var position = carousel.find(settings.item + ':last').position();
			var left = parseFloat(carousel.find(settings.items).css('left')) * -1;
			
			if (position.left < (left + width))
			{
				carousel.find(settings.next).addClass('disabled');
			}
		}});
	}
	
	/**
	 * slideUp: slide up vertically a given distance
	 * @params carousel (jQuery Object), settings (Object), distance (float)
	 */
	function slideUp(carousel, settings)
	{
		isAnimating = true;
		carousel.find(settings.items).animate({top: '+=' + settings.height + 'px'}, {duration: settings.duration, easing: settings.easing, complete: function(){
			isAnimating = false;
			
			carousel.find(settings.next).removeClass('disabled');
			
			if(settings.paginated)
			{
				selectPage();
			}
			
			if (carousel.find(settings.items).css('top') == '0px')
			{
				carousel.find(settings.previous).addClass('disabled');
			}
		}});
	}

	/**
	 * slideUp: slide up vertically a given distance
	 * @params carousel (jQuery Object), settings (Object), distance (float)
	 */
	function slideDown(carousel, settings)
	{
		isAnimating = true;
		carousel.find(settings.items).animate({top: '-=' + settings.height + 'px'}, {duration: settings.duration, easing: settings.easing, complete: function(){
			isAnimating = false;
			
			carousel.find(settings.previous).removeClass('disabled');
			
			if(settings.paginated)
			{	
				selectPage();
			}
			
			
			var position = carousel.find(settings.item + ':last').position();
			var top = parseFloat(carousel.find(settings.items).css('top')) * -1;
			
			if (position.top < (top + settings.height))
			{
				carousel.find(settings.next).addClass('disabled');
			}
		}});
	}

	/**
	 * selectPage: handles highlighting which page is selected
	 * @params carousel (jQuery Object), settings (Object)
	 */
	function selectPage(carousel, settings)
	{
		var page = currentPage(carousel, settings);
		carousel.find(settings.page).removeClass('selected');
		carousel.find(settings.page).eq(page).addClass('selected');
		carousel.find(settings.current_page).html(page + 1);
	}
 
	/**
	 * currentPage: calculates which page we're on
	 * @params carousel (jQuery Object), settings (Object)
	 */
	function currentPage(carousel, settings)
	{
		var left = carousel.find(settings.items).css('left');
		var page = 0;
		var width = calculateWidth(carousel, settings);
		
		if (left != 'auto')
		{
			var position = Math.abs(left.substr(0, left.length - 2));
			page = position / width;
		}

		return page;
	}
	
	/**
	 * calculateWidth: calculate total width of carousel
	 * @params carousel (jQuery Object), settings (Object)
	 */
	function calculateWidth(carousel, settings)
	{
		var width = carousel.find(settings.container).css('width');

		// Width not set in CSS in IE6/IE7
		if (width == 'auto')
		{
			width = carousel.find(settings.container).innerWidth() + settings.padding;
		}
		else
		{
			width = parseFloat(width) + settings.padding;
		}
		
		return width;
	}
})(jQuery);