/****************************************************
carousel.js is designed to work with carousel.css 
and an unordered list set up to specifications as
outlined in the Gov 2.0 documentation. carousel.js
is designed to take in external parameters, however
a default set of parameters is defaulted to if no
parameter(s) is passed.

*Defenition: For the purposes of this script, a
"carousel element" is defined as one slide which
makes up the carousel. This side must be a child
of an unodered list and be made of the following
components: <div><a><img> in that order.

Last edited May 2011 RL
***************************************************/

(function($){

	$.fn.Carousel = function(options){

		var defaults = {
			/******************************************************************
				Values for general cofiguration of look and feel of carousel
			******************************************************************/
			display_num: 7,       					// number of elements to be visible (should be a uneven number)
			speed: 400,          					// duration of slide animation
			margin_right:-34,     					// right margin to be applied to each <li> (negative values induce overlap)
			off_center_multiplier:0.9,				// adjust scale (percent wise) of successive off center list item
			height_diff:18,							// defines height of each successive off center list item
			max_width: 195,							// width of icons used for carousel
			max_height: 117,						// height of icons used for carousel
			carousel_width: 941,					// width of carousel container
			carousel_height: 175,					// height of carousel container
			ul_margin_left:-8,						// left margin on the <ul> element of the carousel (used for fine tuning)
			
			/************************************************************
				Values for carousel in put fields
			************************************************************/
			
			/*Button Controls*/
			controls: false,						// enable/disable button controls
			next_text: '',    						// text to be used for the 'next' control (animation from right to left)
			prev_text: '',							// text to be used for the 'prev' control (animation from left to right)
			next_image: '',     					// image for 'next' control (animation from right to left)
			prev_image: '',							// image for 'next' control (animation from left to right)
			
			/*Hover Zone Controls*/
			hover_zones: true,						// enable/disable on hover controls				
			hover_over_height: 215,					// defines the height of the mouse over area for the hover zone
			hover_over_width: 400,					// defines the width of the mouse over area for the hover zone
			
			
			/************************************************************
				Values for implementation of hover over text box
			************************************************************/
			carousel_top_padding: 40,				// padding at the top of the carousel to create space for hover over box
			hoverBox_height:120,					// height of hover over box
			hoverBox_width:119,						// width of hover over box
			hoverBox_marginTop:-40,					// vertical margin offset for hover over box
			hoverBox_marginLeft:50,					// horizontal margin offset for hover over box
			right_item_hover_margin_left: 0			// *special horizontal offset for right most hover over box
		};

		var options = $.extend(defaults, options);
		
		/********************************************************************
			MAIN METHOD
			This method guides the general functionality of this carousel
		********************************************************************/
		return this.each(function() {
			
			//Global Variables
			var $this = $(this);       						// denotes <ul> value of the Carousel         
			var li = $(this).children('li.carousel');		// denotes <li> value of the Carousel
			var first = 0;
			var fe = 0;
			var last = options.display_num-1;
			var le = options.display_num-1;
			var is_working = false;
			var is_hovering = false;
			var j = '';
			var clicked = false;		
			var shift_width;								// denotes horizontal distance to shift carousel
			var ULwidth;									// denotes 
			var dynamic_wrapper_name = "carousel_container";
			var displayed_entries;
			var main_src;
			var hover_src;
			var agent=navigator.userAgent.toLowerCase();
			var moz_offset = options.carousel_width+3;
			var scale_percent = options.img_scale_percent;

			
			/****************************************
				Initial set up for the carousel
			****************************************/
			
			//apply styles to <ul> (carousel)
			ULwidth = (options.max_width + options.margin_right) * (options.display_num+2);
			$(this).css({
				'margin-left':options.ul_margin_left,
				'width': ULwidth,
				'display':'block'
			});
			//apply styles to <li> (carousel elements)
			li.css({
				'marginRight':options.margin_right
			});
			
			//set up container div to define the dimentions of the carousel
			$(this).wrap('<div class="'+dynamic_wrapper_name+'"></div>');
			//apply dimentions
			if (agent.indexOf("firefox") != -1){
				$(this).parent().css({
					'position':'relative',
					'backgroundColor':'#FFFFFF',
					'height':options.carousel_height,
					'width':moz_offset,
					'overflow':'hidden',
					'padding':0,
					'z-index':1,
					'padding-top':options.carousel_top_padding
				});
			}
			else {
				$(this).parent().css({
					'position':'relative',
					'backgroundColor':'#FFFFFF',
					'height':options.carousel_height,
					'width':options.carousel_width,
					'overflow':'hidden',
					'padding':0,
					'z-index':1,
					'padding-top':options.carousel_top_padding
				});
			}
			//Set up Hover Box
			$('.carousel').find('.callout').css({
				'width':options.hoverBox_width, 
				'height':options.hoverBox_height,
				'margin-left':options.hoverBox_marginLeft, 
				'margin-top':options.hoverBox_marginTop
			});
			
			//display controls as specified by external variables
			if(options.controls)
				print_controls();
			if(options.hover_zones)
				print_hoverzones();
			
			/***********************************************************
				Secondary setup for the carousel. Here we dynamically
				set the look and feel of the carousel and initialize
				the functionality (animation) of the carousel
			***********************************************************/

			//resize all carousel elements to their smallest size
			size_elements();
			//determine appropriate width of shift this animation
			shift_width = li.outerWidth(true);
			/*
				slice up list to elements
				!EXPLANATION
				slice up list from the first element to one past what is to be displayed
				*Note, we will append the last element in front of the first element
				 later
			*/
			displayed_entries = li.slice(0, options.display_num).clone();	
			//replaced with modified version of the list
			$(this).empty().append(displayed_entries);	
			//resize carousel elements and set special offset for right most hover box
			resize();
			//initialize hover effects (image resizing and image swapping abilities)
			enable_hover();
			prepend_ul();								//complete list (before)
			append_ul();								//completed list (after)
			//reposition for variace created by resizing of carousel elements
			$(this).css({'position':'relative', 'left':-(shift_width)});	

			/***********************************************************
				Animation sequence. This portion of the main method
				governs the animation sequence. There are two days to
				activation the animation sequence: on click and on 
				mouse enter.
				
				The animation sequence is comprised of the following steps:
				-action (click or hover)
				-resize the carousel elements
				-increment/decrement position tracker
				-append new element to start/end of carousel
				-enable on hover effect for new element
				
			***********************************************************/

			// click actions
			$('.next').click(function(){
				slide_next();
				clearInterval(j);
				clicked = true;
				return false;	
			});		
			$('.prev').click(function(){
				slide_prev();
				clearInterval(j);
				clicked = true;
				return false;
			});
			
			// hover over actions
			$('.right_over').bind('mouseenter', function(){	
				this.iid = setInterval(function() {
					slide_next();
				}, options.speed);
			}).bind('mouseleave', function(){
				this.iid && clearInterval(this.iid);
				this.clearQueue();
			});
			
			$('.left_over').bind('mouseenter', function(){
				//continue sliding
				this.iid = setInterval(function() {
					slide_prev();	
				}, options.speed);
			}).bind('mouseleave', function(){
				this.iid && clearInterval(this.iid);
				this.clearQueue();
			});

			//END MAIN
			
	/*********************************************************
	
	print_hoverzones
	
	INPUTS: 	None
	PUROPOSE:	The purpose of this function is to display and
				action the hover over navigation of the carousel
	RETURNS: 	0
	
	*********************************************************/
			
			function print_hoverzones(){
				var left_over = '<div class="left_over"></div>';
				var right_over = '<div class="right_over"></div>';
				$this.after(left_over);
				$this.after(right_over);
				$(".left_over").css({'position':'absolute', 'top':0, 'left':0, 'height':options.hover_over_height, 'width':options.hover_over_width, 'float':'left', 'z-index':0});
				$(".right_over").css({'position':'absolute', 'top':0, 'right':0,'height':options.hover_over_height, 'width':options.hover_over_width, 'float':'right', 'z-index':0});
				return 0;
			}
			
	/*********************************************************
	
	print_controls
	
	INPUTS: 	None
	PUROPOSE:	The purpose of this function is to display and
				action the clickable navigation of the carousel
	RETURNS: 	0
	
	*********************************************************/		
		
			function print_controls(){
				if(options.next_image != '' || options.prev_image != ''){
					var controls = '<a href="" class="prev"><img src="'+options.prev_image+'"/></a><a href="" class="next"><img src="'+options.next_image+'"/></a>';
				}
				else{
					var controls = '<a href="" class="prev">'+options.prev_text+'</a><a href="" class="next">'+options.next_text+'</a>';
				}	
				$this.parent().after(controls);
				return 0;
			}

	/*********************************************************
	
	slide_next
	
	INPUTS: 	None
	PUROPOSE:	This slides the carousel to the left and
				removes unneeded carousel elements
	RETURNS: 	0
	
	*********************************************************/	

			function slide_next(){
				if(!is_working){
					is_working = true;
					animate_next();
					set_pos('next');
					$this.animate({left:'-='+shift_width}, options.speed, function(){
						$this.find('li.carousel').slice(0, 1).remove();
						$this.css('left',-(shift_width));
						append_ul();
						is_working = false;			
					});
				}
			}
			
	/*********************************************************
	
	slide_prev
	
	INPUTS: 	None
	PUROPOSE:	This slides the carousel to the right and
				removes unneeded carousel elements
	RETURNS: 	0
	
	*********************************************************/				
			
			function slide_prev(){
				if(!is_working){
					is_working = true;
					animate_prev();
					set_pos('prev');
					$this.animate({left:'+='+shift_width}, options.speed, function(){
						$this.find('li.carousel').slice(-1).remove();
						$this.css('left',-(shift_width));
						prepend_ul();
						is_working = false;                 
					});
				}
			}
			
	/*********************************************************
	
	append_ul
	
	INPUTS: 	None
	PUROPOSE:	Copies and appends 'first' element to list
	RETURNS: 	0
	
	*********************************************************/		

			function append_ul(){
				var str = "";
				var lix = li.clone();
				le = last;	
				le++
				
				if(lix[le] != undefined){
					str = $(lix[le]);    
				}else{
					le = 0;
					str = $(lix[le]);
				}
				add_hover(str);		//add hover effect to new element
				$this.append(str);
			}

	/*********************************************************
	
	prepent_ul
	
	INPUTS: 	None
	PUROPOSE:	Copies and prepends 'last' element to list
	RETURNS: 	0
	
	*********************************************************/

			function prepend_ul(){
				var str = "";
				var lix = li.clone();
				fe = first;
				fe--
				
				if(lix[fe] != undefined){
					str = $(lix[fe]);    
				}else{
					fe = li.length-1;
					str = $(lix[fe]);
				}   
				add_hover(str);		//add hover effect to new element
				$this.prepend(str);
			}

	/*********************************************************
	
	set_pos
	
	INPUTS: 	dir
	PUROPOSE:	the method is used to increment/decrement
				the last/first counters to keep track of the
				last/first position of the carousel
	RETURNS: 	0
	
	*********************************************************/

			function set_pos(dir){
				if (dir == 'next') {
					first += 1;
					if (first >= li.length) {
						first = first % li.length;
					}
					last += 1;
					if (last >= li.length){
						last = last % li.length;
					}
				}else if (dir == 'prev'){
					first -= 1;
					if (first < 0){
						first = li.length + first;
					}
					last -= 1;
					if (last < 0){
						last = li.length + last;
					}
				}
			}

	/*********************************************************
	size_elements()
	
	INPUTS: 	None
	PUROPOSE:	This method iniatializes all carousel elements
				to their 'base state', setting width, height
				and z-index to their smallest size. This is
				done to establish a base size which we can
				'size up' from. This is done by explicitly
				defining the size of the <div> component of the
				carousel element
				
	RETURNS: 	0
	
	*********************************************************/	
	
			function size_elements(){
				var se_num = Math.floor(options.display_num/2);
				var init_width=(options.max_width*(Math.pow(options.off_center_multiplier,se_num)));
				var init_height=(options.max_height*(Math.pow(options.off_center_multiplier,se_num)));
				$('li.carousel div').css({
					'width':init_width,'height':init_height,'top':0,'z-index':0
				});
				
				return 0;
			}
			
	/*********************************************************
	
	resize()
	
	INPUTS: 	None
	PUROPOSE:	This method resizes all carousel elements
				acroding to conditions set by external 
				variables. Height, width, and size of carousel 
				element are all set relative to the middle element.
				Verticle offset is set relative to the 'top' of
				the parent unordered list. This is done by
				applying the modifications to the <div> component
				of the carousel element
				!- resize() is also responsible to get the
				special offest for the right most hover box
	RETURNS: 	0
	
	*********************************************************/				

			function resize(){
				var d_count = 0;
				var d_width = 0;
				var d_height = 0;
				var d_diff = 0;
				var d_z_index =0;
				var d_middle = Math.floor(options.display_num/2);
				
				//sort out first half
				for (d_count=0; d_count<d_middle; d_count++){
					d_width = (options.max_width*(Math.pow(options.off_center_multiplier,(d_middle - d_count))));
					d_height = (options.max_height*(Math.pow(options.off_center_multiplier,(d_middle - d_count))));
					d_diff = options.height_diff * d_count;
					$('li.carousel:eq('+d_count+') div').css({
						'width':d_width,
						'height':d_height,
						'top':d_diff,
						'position':'relative',
						'z-index':d_count
					});
				}
				
				//sort out middle element
				d_diff = options.height_diff * d_count;
				d_count++;
				$('li.carousel:eq('+d_middle+') div').css({
					'width':options.max_width,
					'height':options.max_height,
					'top':d_diff,
					'position':'relative',
					'z-index':d_count
				});
				
				//sort out last half
				for(d_count; d_count<options.display_num; d_count++){
					d_width = (options.max_width*(Math.pow(options.off_center_multiplier,(d_count - d_middle))));
					d_height = (options.max_height*(Math.pow(options.off_center_multiplier,( d_count - d_middle))));
					d_diff = options.height_diff * (options.display_num - d_count - 1);
					d_z_index = options.display_num - d_count;
					$('li.carousel:eq('+d_count+') div').css({
						'width':d_width,
						'height':d_height,
						'top':d_diff,
						'position':'relative',
						'z-index':d_z_index
					});
				}
				
				//shift hover box
				d_count--;
				$('li.carousel:eq('+d_count+') div').parent().find(".callout").css({'margin-left':options.right_item_hover_margin_left});
				
				return 0;
			}
	
	/*********************************************************
	
	animate_prev
	
	INPUTS: 	None
	PUROPOSE:	This method is responsible for the resizing of the
				carousel elements as the carousel rotates from 
				left to right
	RETURNS: 	0
	
	*********************************************************/

			function animate_prev(){
			
				var ap_count = 0;
				var ap_height = 0;
				var ap_diff = 0;
				var ap_z_index = 0;
				var ap_width = 0;
				var ap_middle = Math.floor(options.display_num/2);
				// sort out first half
				for (ap_count = 0; ap_count < ap_middle; ap_count++){	
					ap_width = (options.max_width*(Math.pow(options.off_center_multiplier,(ap_middle - ap_count))));
					ap_height = (options.max_height*(Math.pow(options.off_center_multiplier,(ap_middle - ap_count))));
					ap_diff = options.height_diff * ap_count;	
					$('li.carousel:eq('+ap_count+') div').animate({
						width: ap_width,
						height: ap_height,
						top: ap_diff
						}, options.speed
					);//animate complete
					$('li.carousel:eq('+ap_count+') div').css({
						'position':'relative',
						'z-index':ap_count
					});
				}
				
				//sort out middle element
				ap_diff = options.height_diff * ap_count;
				ap_count++;
				$('li.carousel:eq('+ap_middle+') div').animate({
					width: options.max_width,
					height:options.max_height,
					top:ap_diff
					}, options.speed
				);//animate complete
				$('li.carousel:eq('+ap_middle+') div').css({
					'position':'relative',
					'z-index':ap_count
				});
				//sort out last half
				for (ap_count; ap_count < options.display_num; ap_count++){
					ap_width = (options.max_width*(Math.pow(options.off_center_multiplier,(ap_count - ap_middle))));
					ap_height = (options.max_height*(Math.pow(options.off_center_multiplier,(ap_count - ap_middle))));
					ap_diff = options.height_diff * (options.display_num - ap_count - 1);
					ap_z_index = options.display_num - ap_count;
					$('li.carousel:eq('+ap_count+') div').animate({
						width: ap_width,
						height: ap_height,
						top: ap_diff
						}, options.speed
					);//animate complete
					$('li.carousel:eq('+ap_count+') div').css({
						'position':'relative',
						'z-index':ap_z_index
					});
				}
				
				//shift hover box of last (right most) element
				ap_count--;
				$('li.carousel:eq('+ap_count+') img').parent().find(".callout").css({'margin-left':options.right_item_hover_margin_left});
				
				ap_count++;
				//shifted out elements does not overlap displayed elements
				$('li.carousel:eq('+ap_count+') div').css({'z-index':0});
				return 0;
			}

	/*********************************************************
	
	animate_next
	
	INPUTS: 	None
	PUROPOSE:	animate dynamic icons
	RETURNS: 	0
	
	*********************************************************/

			function animate_next(){
				
				var an_count = 2;
				var an_width = 0;
				var an_height = 0;
				var an_diff = 0;
				var an_z_index = 0;
				var an_middle = Math.floor(options.display_num/2)+2;
				
				for(an_count=2; an_count < an_middle; an_count++){
					an_width = options.max_width*(Math.pow(options.off_center_multiplier,(an_middle - an_count)));
					an_height = options.max_height*(Math.pow(options.off_center_multiplier, (an_middle - an_count)));
					an_diff = options.height_diff * (an_count - 2);	
					
					$('li.carousel:eq('+an_count+') div').animate({
						width: an_width,height: an_height,top: an_diff
						}, options.speed
					);//animate complete
					$('li.carousel:eq('+an_count+') div').css({'position':'relative','z-index':an_count-2});
				}
				//sort out middle element
				an_diff = options.height_diff * (an_count-2);
				an_count++;
				$('li.carousel:eq('+an_middle+') div').animate({
					width: options.max_width,height:options.max_height,top:an_diff
					}, options.speed
				);//animate complete
				$('li.carousel:eq('+an_middle+') div').css({'position':'relative','z-index':(an_count-2)});
				//sort out last half
				for(an_count; an_count < (options.display_num+2); an_count++){
					an_width = (options.max_width*(Math.pow(options.off_center_multiplier,(an_count - an_middle))));
					an_height = (options.max_height*(Math.pow(options.off_center_multiplier,(an_count - an_middle))));
					an_diff = options.height_diff * (options.display_num - an_count + 1);	
					an_z_index = options.display_num - an_count + 2;
					$('li.carousel:eq('+an_count+') div').animate({
						width: an_width,height: an_height,top: an_diff
						},options.speed
					);//animate complete
					$('li.carousel:eq('+an_count+') div').css({'position':'relative','z-index':an_z_index});

					//move hover box back to where it should be
					$('li.carousel:eq('+an_count+') img').parent().find(".callout").css({'margin-left':options.hoverBox_marginLeft});
	
				}
				//shift hover box
				an_count--;
				$('li.carousel:eq('+an_count+') div').parent().find(".callout").css({'margin-left':options.right_item_hover_margin_left});
				


				return 0;

			}

	/*********************************************************
	
	enable_hover()
	
	INPUTS: 	None
	PUROPOSE:	This method is responsible for the on hover
				carousel element effect. On hover over a 
				carousel element, the image is swapped out for
				the one contained in the 'id' attribute of the
				<img> tag. The z-index of the containing <div>
				is modified to display this item 'on top' of
				it's siblings. 
				On mouseout, the original values are restored
				and the 'callout' box (if used) is hidden.
				**This method is used once during the 
				inialization.
				
	RETURNS: 	0
	
	*********************************************************/

			function enable_hover(){
				var my_z_index;
				var callout_margin;
				
				$('.carousel').hover(
					
					function() {
						//expand icon
						my_z_index = $(this).find('.carousel_image_div').css("z-index");
						main_src = $(this).find('img').attr("src");
						hover_src = $(this).find('img').attr("id");
						
						$(this).find('.carousel_image_div').css({
							'z-index':200
						});
						//change icon
						$(this).find('img').attr("src", hover_src); 
						//show callout
						$(this).find('.callout').css({'display':'block'});
						
					},
					function() {
						//restore icon
						$(this).find('.carousel_image_div').css({
							'z-index':my_z_index
						});
						$(this).find('img').attr("src", main_src); 
						$(this).find('.callout').css({'display':'none'});
					}  	
				);
			}//end enable_hover
			
	/*********************************************************
	
		add_hover
	
		INPUTS: 	element to have action allied to (usually a 
		PUROPOSE:	
		RETURNS: 	0
	
	*********************************************************/
			
			function add_hover(me){
				var my_z_index;
				var callout_margin;
			
				me.hover(		
					function() {
						my_z_index = $(this).find('.carousel_image_div').css("z-index");
						main_src = $(this).find('img').attr("src");
						hover_src = $(this).find('img').attr("id");
						
						$(this).find('.carousel_image_div').css({
							'z-index':my_z_index+20
						});
						//change icon
						$(this).find('img').attr("src", hover_src); 
						$(this).find('.callout').css({'display':'block'});
					},
					function() {	
						$(this).find('.carousel_image_div').css({
							'z-index':my_z_index
						});
						$(this).find('img').attr("src", main_src); 
						$(this).find('.callout').css('display', 'none');
					}
				);
			}
			
			
		
		//END FUNCTIONS

		});			
	} 
	
})(jQuery);

