(function($) {  
	
	$.fn.infiniteCarousel = function (options) {
	
		/* 
		 * merge literal objects into options argument 
		 */
		var opts = $.extend({}, $.fn.infiniteCarousel.defaults, $.fn.infiniteCarousel.structure, options);
		
		/* 
		 * set the globals 
		 */
		var stageActive = false; 						// global var to track stage status
		var viewport = opts.viewPortElement;			// option variable for viewport jQuery selector, default to window
		var slider = $(this); 							// jQuery object of slider
		var stage = $(this).children('ul'); 			// jQuery object of stage				
		var pageWrapper = $(opts.pageWrapper); 			// jQuery object pageWrapper
		var loadControls = $(opts.loadControls); 		// jQuery object loadControls
		var contentWrapper = $(opts.contentWrapper); 	// jQuery object stage div.content
		var stageWrapper = slider; 						// jQuery object of slider
		var itemTitle = $(opts.itemTitle); 				// jQuery object of itemTitle
		
		// global counter
		var loadCnt = 0;					
			
		// get total numer of images to load
		var imgCnt = $(stage).children('li').children('img').length;
		
		/* 
		 * find the dimensions of the stage and it's wrapping element 
		 */
		
		// cross browser method to get the viewport
		function viewPort() {
			if(opts.viewPortElement === 'window'){
				var h = window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName('body')[0].clientHeight;
				var w = window.innerWidth ||  document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth;
			}else{			
				var h = $(opts.viewPortElement).innerHeight();
				var w = $(opts.viewPortElement).innerWidth();
			}
			return { width : w , height : h }
		}
		
		// find the find of the browsers scroller bar
		function scrollbarWidth() {
			if(jQuery(window).height() <= jQuery("body").height()){
			
			    var div = $('<div style="width:50px;height:50px;overflow:hidden;position:absolute;top:-200px;left:-200px;"><div style="height:100px;"></div>');
			    // Append our div, do our calculation and then remove it
			    $('body').append(div);
			    var w1 = $('div', div).innerWidth();
			    div.css('overflow-y', 'scroll');
			    var w2 = $('div', div).innerWidth();
			    $(div).remove();
			    return (w1 - w2);
		    }
		}
		
		// find stage dimensions
		function getStageDimensions(){
			
			//  scale and reset the stage dimenions
			var screenWidth = viewPort().width;
			//log(screenWidth);
			// look to see if a scroll bar exists on the page e.g. vertical scrolling and then compensate					
			if(jQuery(window).height() <= jQuery("body").height()){
				scrollBarWidth = scrollbarWidth();
				screenWidth = (screenWidth - scrollBarWidth);
			}
			
			var stageWidth = 0;
			var stageItems = 0;
			stage.children('li').each(function(){
				stageWidth += $(this).width();
				stageItems ++;
			});
	
			// set stage width
			stage.width(stageWidth);
		
			var stageLeftPosition = contentWrapper.position().left; 				// get stage left position based on div.content 
			if(opts.viewPortElement === 'window'){
				var screenLeftMargin = pageWrapper.offset().left; 						// get stage left position based on element's position relative to window
			}else{
				var screenLeftMargin = 0;
			}
			var stageLeftPadding = parseInt(contentWrapper.css('padding-left'));	// get any padding and convert to integer
			
			// calculate offsets
			var stageWrapperOffSet = (screenLeftMargin + stageLeftPosition + stageLeftPadding);
			
		 	var stageWrapperWidth = (screenWidth-stageWrapperOffSet);
		 	stageWrapperWidth = ''+stageWrapperWidth +'px';							// css friendly version
		 	
		 	// set stage width
		 	stageWrapper.css({'width':stageWrapperWidth});
			
			// reactivate the stage
			function reactivate(){
				
				if(stageWrapper.hasClass('inactive')){
					stageWrapper.removeClass('inactive');
					stageWrapper.addClass('active');
					
					slider.infiniteCarousel();
					
					// hide controls
					loadControls.show();	
				}
			}
			
			// reactivate the stage
			function deactivate(){
				
					stageWrapper.removeClass('active');
					stageWrapper.addClass('inactive');
					
					// hide controls
					loadControls.hide();	
					
					log('Note:You ned to have at least 3 items for the scroller to work');
			}
			
			// find stage width
			function checkStageWidth(){
			
				var largestItem = 0;
				var stageRemainder = 0;
			
				stage.children('li').each(function(){
					
					if($(this).width() > largestItem){
						largestItem = $(this).width();
					}
					
						
				});
				
				// re-calculate spacer element width
				function resetSpacer(){
					
		 	
		 			// when resizing the page make sure to adjust spacer width and stage width
					if(stage.children('li#spacer').length > 0){
				
						// reset spacer width
						spacer = stage.children('li#spacer');
						spacer.css({'width':stageWrapperWidth}); 
						
						// update the stage width
						stageWidth = parseInt(stage.width()) + parseInt(stageWrapperWidth);
						//console.log(slider.width());
						stage.width(stageWidth);
					}
				}
				
				// calculate offset
				stageRemainder = parseInt(stageWidth) -  parseInt(largestItem);

				// decide what to do based on offset
				if(stageItems >= 3){
					if(!(stageRemainder <= parseInt(largestItem)) || !(stageRemainder <= parseInt(stageWrapperWidth))){
						reactivate();
						resetSpacer();
					}
					
					return true;
				}else{
					deactivate();
					
					return false;
				}
			}
			
			return checkStageWidth(); // return boolean
		 	
		 	
		}
		
		// fire onresize
		$(window).resize(function(){
			$.doTimeout( 'resize', 250, function(){
	    		// find the new stage dimensions
				stageActive = getStageDimensions();
			});
		});
		
		/* 
		 * handle the plugin selector 
		 */
		return this.each(function () {	
			
			/* input preloader */
			function showLoadingScreen(){
				if(opts.preloadGraphic.length > 0 && slider.siblings('.preloader').length == 0){
					slider.before($('<div class="preloader">').css({'opacity':0.8,'height':pageWrapper.innerHeight()}).append($('<img>').attr('src', opts.preloadGraphic)));
				}
			}
			
			/* find if all images have loaded */
			function checkImagesLoaded(){
				
				$.each(stage.children('li'), function(key, value) {
					
					// see if first image loaded
	                $(value).children('img:first').one("load",function(){     
	                	// iterate counter on each load including from cache
						loadCnt ++; 
						
						// activate when all images are loaded
						if(loadCnt == imgCnt){
							activateCallBack();
						}else{
							showLoadingScreen();
						}
					})
	                .each(function(){
	                    if(this.complete) $(this).trigger("load");	// catch cached images
	                });

				});

			}
			
			/* callback function from checkImagesLoaded() */
			function activateCallBack(){
				
				if(opts.preloadGraphic.length > 0 && slider.siblings('.preloader').length > 0){
					slider.siblings('.preloader').animate(
					{ opacity: 0 }, // what we are animating
					{
						duration: 1000, // how fast we are animating
						easing: opts.easingType, // the type of easing
						complete: function() { // the callback
							$(this).remove();
						}
					});
				}
				
				/* activate the stage */
				stageActive = getStageDimensions();				

				// if all images loaded then the stage is active
				if(stageActive){
				
					// initialise the scroller
					initialise();
					
					// if active and autoscroll is on
					if(opts.autoScroll){
					
						var theInt = null;
						var theInterval = null;
						var curclicked = 0;
						var stop = 0;
						
						theInterval = function(cur){
							clearInterval(theInt);
						
							theInt = setInterval(function(){
								   
								// add autoscroll behaviour 
						        stage.children("li.in-view").triggerHandler('click',['AutoScrollTriggered']);
								
						    }, opts.autoScrollPause);
						    
						    // stop autoscroll on stage hover or controls hover
				            $.each([stage, loadControls], function(k,v) { 
				           		// bind to each array value e.g jQuery object
				           		
				           		// first key is the stage
				            	if(k==0){
									// bind mouseover
								    stage.bind('mouseover',function(){
										if (stop==0){
											//log('stop');
									        clearInterval(theInt);
									     	stop=1;
								     	}
								    });
							    }
							    
							    // bind mouseup event for loadControls to stop autoScroll
							    if(k==1){						    	
							    	$.each([loadControls.find('li.prev a'), loadControls.find('li.next a')], function(k,v) {
									    v.unbind('mouseup').bind('mouseup',function(){
											if (stop==0){
												//log('stop');
										        clearInterval(theInt);
										     	stop=1;
									     	}
									    });
								    });
							    }
							    
							    // if the touchwipe plugin exists and supportGestures is true then stop carousel with swipes
								if(opts.supportGestures && jQuery.fn.touchwipe){
									$(this).touchwipe({
									     wipeLeft: function() {	
										    clearInterval(theInt);
										    stop=1;
									     },
									     wipeRight: function() { 	
										    clearInterval(theInt);
										    stop=1;
				     					 },
									     preventDefaultEvents: true
									});
								}
							
							});
						    
						     // restart autoscroll on stage mouseout removing any previous bound events
						    stage.unbind('mouseout').bind('mouseout',function(){   
								//alert('start');    
				            	stop=0;
				            	theInterval();            	
				            });
						};
						
						// initial call
						theInterval();
					
					}	
				}
			}
			
			// check to see if all of the images have been loaded
			checkImagesLoaded();
			
			
			function initialise(){	
							
				// change css overflow-x : scroll to none and set overflow to hidden
				slider.addClass('active');		
				
				// then set the initial scrollLeft to be 0 - the width of the cloned item
				startLeftPosition = stage.children('li:last').width();
				
				// add the in-view class to the inital first item
				stage.children('li:first').addClass('in-view');
				
				if(opts.showTitles){
				
					//get the first items titles title and display
					if(typeof(stage.children('li:first').children('.content').children('h3:first'))!='undefined' && stage.children('li:first').children('.content').children('h3:first').length > 0){
						title = stage.children('li:first').children('.content').children('h3:first').text();
					}else if(typeof(stage.children('li:first').children('img').attr('alt'))!='undefined' && stage.children('li:first').children('img').attr('alt').length > 0){
						title = stage.children('li:first').children('img').attr('alt');
					}
				
					// update the title tag h2		    	
					if(typeof(title)!=='undefined'){
						itemTitle.children('h2').text(title);
						if ( !$.browser.msie ) {
							itemTitle.children('h2').fadeIn('fast');
						}else{
							itemTitle.children('h2').show();					
						}
					}else{
						// hide the title element from view
						itemTitle.children('h2').hide();						
					}
				}else{
					// hide the title element from view
					itemTitle.children('h2').hide();
				}
				
				// start by cloning the last item and added it to the start of the stage
				stage.filter(':first').prepend(stage.children('li:last').clone().removeClass('in-view').addClass('cloned'));
				
				// set leftScroller position
				slider.scrollLeft(startLeftPosition);
				
				// the save the start position
		       	slider.data('srcollOffset', startLeftPosition);
		       	
		       	// no remove the cloned item from the end of the stage
				stage.children('li:last').remove();
				
				// on the fist load look for the spacer. remove existing
				if(stage.children('li#spacer').length > 0){
					stage.children('li#spacer').remove();
				}
				
				// in spacer li
				spacer = $('<li id="spacer" class="spacer"></li>');
				spacer.css({'width':slider.width()+'px'});
				
				// update the staged with
				stage_width = parseInt(stage.width()) + parseInt(slider.width());
				// update stage css
				stage.css({'width':stage_width+'px'})
				
				
				// then add on a large blank LI the same size as the entire stage wrapper
				stage.filter(':last').append(spacer);
				
				//add clickable behaviour to each child li
				applyClickable();
				
				// bind to the forward and back buttons
		        $('#loader-controls li.next a').click(function () {
		        
		        	//slide to next item
		            getImage('next');
		            
		            return false;          
		        });
		        
		        $('#loader-controls li.prev a').click(function () {
		        
		        	//slide to prev item
		        	getImage('prev');
		        	
		            return false;
		        });	   

			}
		
			function applyClickable(){
				stage.children('li').each(function(){
					
					//unbind previous click event
					$(this).unbind('click');
					
					// bind clickable behaviour to items that are not currently set to in-view
					$(this).bind('click', function(){ 
					
						if($(this).hasClass('in-view')==false){
							scrollToItem($(this));
						}else{
							scrollToItem($(this).next());
						}
						
						// triggered event from auto scroller has additional params if they are not present then kill the auto scroll
						if(typeof(arguments[1]) == 'undefined'){
							// kill timer by unbinding mouseout from stage so that it can't restart
							stage.unbind('mouseout');
						}
						return false;
					}).addClass('clickable');				     
		        
					// if the touchwipe plugin exists and supportGestures is true then add swipe left and right
					if(opts.supportGestures && jQuery.fn.touchwipe){
						$(this).touchwipe({
						     wipeLeft: function() {		
	     						//slide to prev item
		        				getImage('next');
						     },
						     wipeRight: function() { 
	     						//slide to prev item
		        				getImage('prev');
	     					 },
						     min_move_x: 20,
						     min_move_y: 20,
						     preventDefaultEvents: true
						});
					}
					
					// look for child a external link adn open in new window
					if($(this).find('a').length > 0){
					
						// pass link to var
						var link = $(this).find('a');
						
						// unbind previous click event to stop continual binding
						link.unbind('click');
						
						// add title telling user what will happen when clicked
						link.attr('title', "Open link to "+link.attr('href')+" in a new window.");
						
						// bind click event
						link.bind('click', function(){							
							window.open($(this).attr('href'),'_blank');
						});
					}
					
					
					
				});
			}
		
			function scrollToItem(e){
				
				var objects = null;
				var distance = 0;
				var finalDistance = 0;
				var currentImage = stage.children('li.in-view');
				var nextImage =  e;
				var numberOfNewItems = e.prevAll('li').not($(this)).length;
				var numberOfClonesToMove = numberOfNewItems - 1;
				
				// get all items preceeding this
				e.prevAll('li').each(function(){
					
					//increment each
					distance += $(this).width();
					
					// when you get to the current inview item pass out to returning var
					if($(this).hasClass('in-view')){
						finalDistance = distance;
					}
					
					return finalDistance;
				});
				
				// create new object to hold curent and next li objects
				objects = {
					current:currentImage,
					newitem:nextImage
				};
				
				// move the slider
				moveSlider(finalDistance, objects, 'next', numberOfClonesToMove);
			}
		
			function getImage(direction){
			
				// unbind and existing stage events
				$('ul#stage').unbind();
				
				var objects = null;
				var currentImage = stage.children('li.in-view');
				
				switch(direction){
					case 'next':					
						var nextImage =  currentImage.next('li:first');
						var sliderDistance = currentImage.width();
					break;
					case 'prev':
						var nextImage =  currentImage.prev('li:first');
						
						//always get the width of the last item in the stage as this will be cloned
						var sliderDistance = nextImage.width(); 
					break;
				}
				
					
				//create new object to hold curent and next li objects
				objects = {
					current:currentImage,
					newitem:nextImage
				};
				
				
				moveSlider(sliderDistance, objects, direction);
				
			}
			
			function moveSlider(distance, objects, direction, numberOfClonesToMove){
				
				var animationComplete = false;
				
				if(typeof(numberOfClonesToMove)=='undefined'){
					numberOfClonesToMove = null;
				}
				
				// look for saved left position or set to 0
				if(slider.data('srcollOffset')){
					var leftPosition = slider.data('srcollOffset');
				}else{
					var leftPosition = 0;
				}
				
				// calculate new left position
				if(direction == 'next'){
					leftPosition += parseInt(distance);
				}else if(direction == 'prev'){
					leftPosition -= parseInt(distance);
				}
				
				//save new left position
				slider.data('srcollOffset', leftPosition);
				
				if(opts.showTitles){
					//fade out title
					if(typeof(title)!=='undefined'){
						itemTitle.children('h2').text(title);
						if ( !$.browser.msie ) {
							itemTitle.children('h2').fadeOut('fast');
						}else{
							itemTitle.children('h2').hide();					
						}
					}else{
						// hide the title element from view
						itemTitle.children('h2').hide();						
					}
				}
				
				slider.stop().animate(
					{ scrollLeft: leftPosition }, // what we are animating
					{
						duration: opts.transitionSpeed, // how fast we are animating
						easing: opts.easingType, // the type of easing
						complete: function() { // the callback
							// Animation complete.
				    		animationComplete = true;
				    		
				    		
						    if(direction=='next'){
						    	
						    	
						    	
						    	// clone first stage item and append to end
			       				stage.children('li#spacer').before(stage.children('li:first').clone().removeClass('in-view').addClass('cloned'));
			       				
			       				cloned = stage.children('li#spacer').prev();
			       				cloned.css({'width':cloned.width()+'px'});
			       				
			       				// remove first item
			       				stage.children('li:first').remove();
			       				
			       				// assign left position width of current item width
			       				finalLeftPosition = objects.current.width();
				       			
			       				if(numberOfClonesToMove > 1){
			       					numberOfClonesToMove --;
			       					while(numberOfClonesToMove > 0){
			       					
			       						// clone first stage item and append to end
					       				stage.children('li#spacer').before(stage.children('li:first').clone().removeClass('in-view').addClass('cloned'));
					       				
					       				// remove first item
					       				stage.children('li:first').remove();
					       				
			       						numberOfClonesToMove --;
			       					}
			       					
			       					// assign left position width of current item width
					       			finalLeftPosition = objects.newitem.prev('li').width();
			       				}
			       				
								
			       			}else if(direction=='prev'){
			       				     
			       				last_item = stage.children('li#spacer').prev();
			       				 
			       				// clone ;ast stage item and prepend to start  				
			       				stage.filter(':first').prepend(last_item.clone().removeClass('in-view').addClass('cloned'));
			       				
			       				// remove first item
			       				last_item.remove();
			       				
			       				// assign left position width of current first item width
			       				finalLeftPosition = stage.children('li:first').width();
			       			}
			       			
							// reset srcollLeft	       				
		       				slider.scrollLeft(finalLeftPosition);
		       				
		       				//save left position
		       				slider.data('srcollOffset', finalLeftPosition);
						    
						    //set next image to have class 'in-view'
					    	objects.current.removeClass('in-view');
					    	objects.newitem.addClass('in-view').removeClass('clickable');
					    
					    	//add clickable behaviour to each child li
							applyClickable();
					    	
					    	if(opts.showTitles){
						    	//get the curent titles title and display
						    	if(typeof(objects.newitem.children('.content').children('h3:first'))!='undefined' && objects.newitem.children('.content').children('h3:first').length > 0){
						    		title = objects.newitem.children('.content').children('h3:first').text();
						    	}else if(typeof(objects.newitem.children('img').attr('alt'))!='undefined' && objects.newitem.children('img').attr('alt').length > 0){
						    		title = objects.newitem.children('img').attr('alt');
						    	}else{
						    		title = null;
									// hide the title element from view
									itemTitle.children('h2').text('');						
								}
								
								// console.log(title);
							    	
						    	if(typeof(title)!=='undefined' & title != ''){
						    		itemTitle.children('h2').text(title);
						    		if ( !$.browser.msie ) {
										itemTitle.children('h2').fadeIn('fast');
									}else{
										itemTitle.children('h2').show();					
									}
	
						    	}
					    	}
					 	}
				});
							
			}
			
		});
	}
	/* literal object notation for default args */
	$.fn.infiniteCarousel.defaults = {
		viewPortElement: 'window',
		easingType:'swing', // requires the easing plugin
		showTitles:true,
		transitionSpeed:250,
		autoScroll: false,
		autoScrollPause: 5000,
		preloadGraphic:'/css/img/banners/ajax-loader.gif',
		supportGestures:true
	}
	/* literal object notation for structural elements */
	$.fn.infiniteCarousel.structure = {
		pageWrapper: '#slider-wrapper',
		loadControls: 'ul#loader-controls',
		contentWrapper: 'div.content',
		itemTitle:'#item-title'
	}
})(jQuery);
