/*
	jQuery Plugin: cp_carousel
	Author: Caleb Pierce
	Date: June 2011
	
	Creates a circular carousel for horizontal scrolling
	
	Usage:
		This plugin assumes that your carousel will have three key elements:
			1. A 'move left' link
			2. The list of items for the carousel (ul or ol)
			3. A 'move right' link
			
		Call the plugin on the wrapper element of all the carousel items. For example, if this is your DOM structure:
		
		div#carousel
			a.carousel-left
			ul#my-carousel
				li*
			a.carousel-right
		
		Use this to call the plugin:
		
		$('#my-carousel').cp_carousel({
			//parameters here...
		});
		
		
	Parameters:
		transitionSpeed: time in milliseconds for the slide transition
		slideDistance: how far the carousel should slide each time it's moved
		moveLeftElement: the element that moves the carousel to the left when clicked
		moveRightElement: the element that moves the carousel to the right when clicked
		onSlideStart: function that's called before the carousel begins to slide
		onSlideComplete: function that's called when the carousel finishes sliding
		
		
	IMPORTANT:
		- The z-index for the .carousel element must be lower than the z-index of its parent
		- Set overflow: hidden; for the carousel's wrapping element
		- each list item should have a width defined. This plugin will attempt to set the width if none is already set through CSS,
			but it's much better to set it in the stylesheets.
		
*/
(function($){
	$.fn.cp_carousel = function(options) {

		//default parameters
		var defaults = {
			transitionSpeed: 1000,
			slideDistance: 500,
			moveLeftElement: $('.carousel-left', $(this)),
			moveRightElement: $('.carousel-right', $(this)),
			onSlideStart: function(){},
			onSlideComplete: function(){}
		};

		var options = $.extend(defaults, options);

		return this.each(function() {
			
			//save a reference to the carousel element, its children, and the original number of children
			var $wrapper = $(this);
			var $carousel = $('.carousel:first', this);
			var $original_items = $('li', $carousel);

			/*
				first, the necessary width for the carousel is calculated and set
			*/
			var original_width = 0;
			$original_items.each(function(){
				
				//if the width of this li needs to be set, *attempt* to set it properly
				if ( ! $(this).width()){
					var li_width = 0;
					$('> *', this).each(function(){
						li_width += $(this).width() + parseInt($(this).css('margin-left').replace('px', '')) + parseInt($(this).css('margin-right').replace('px', ''));
					});
					
					//set the calculated width
					$(this).css({'width':li_width});
				} //if
				
				original_width += $(this).width() + parseInt($(this).css('margin-left').replace('px', '')) + parseInt($(this).css('margin-right').replace('px', ''));
			});
			
			/*
				next, apply all other necessary CSS attributes to the carousel and its elements
			*/			
			$carousel.css({
				'width' : original_width + 'px'
			}).find('li').css({
				'float':'left'
			});

			/*
				set up left arrow click listener
			*/
			options.moveLeftElement.click(function(e){
				
				//prevent the event from bubbling up
				e.preventDefault();
				
				//find the left-hand margin of the list to determine the current left side offset
				var left = (parseInt($carousel.css('left').replace('px', '')) ? parseInt($carousel.css('left').replace('px', '')) : 0);
				
				//using the left margin and size of the list, find the size of the hidden portion of the list
				var left_hidden = $wrapper.width() - (left + $carousel.width());

				//if the hidden portion isn't big enough, add original list items to the beginning (to make it circular)
				if (Math.abs(left) <= options.slideDistance){

					//prepare new list items to be prepended
					var $new_items = $original_items.clone();

					//increase the carousel width and adjust the margin to account for the new items that have been added
					$carousel.width($carousel.width() + original_width).prepend($new_items).css({'left':left - original_width});
				}
				
				//call the 'before carousel move' function
				options.onSlideStart();
				
				//move the carousel
				$carousel.stop().animate({ //.stop() prevents the carousel from moving too much on multiple clicks
					left: '+=' + options.slideDistance
					},
					options.transitionSpeed,
					options.onSlideComplete
				);
			});

			/*
				set up right arrow click listener
			*/
			options.moveRightElement.click(function(e){
				
				//prevent the event from bubbling up
				e.preventDefault();
				
				//find the left-hand margin of the list
				var left = (parseInt($carousel.css('left').replace('px', '')) ? parseInt($carousel.css('left').replace('px', '')) : 0);

				//find the size of the hidden portion of the list
				var right_hidden = (left + $carousel.width()) - $wrapper.width();

				//if the hidden portion isn't big enough, add original list items to the beginning (to make it circular)
				if (right_hidden <= options.slideDistance){

					//prepare new list items to be appended
					var $new_items = $original_items.clone();

					//increase the carousel width
					$carousel.width($carousel.width() + original_width).append($new_items);
				}
				
				//call the 'before carousel move' function
				options.onSlideStart();
				
				//move the carousel
				$carousel.stop().animate({ //.stop() prevents the carousel from moving too much on multiple clicks
					left: '-=' + options.slideDistance
					},
					options.transitionSpeed,
					options.onSlideComplete
				);

			});
			
		}); //each()
	}; //fn
})(jQuery);

