
	// Global namespace.
	var JohnsonsBabyUX = {};
	
	(function($){
	
		JohnsonsBabyUX.carousels = {

			//_________________________________________________________________
				
			instances: [],
			
			//_________________________________________________________________
			
			defaults: {
				next	: null,
				prev	: null,
				speed	: 125,
				step	: null,
				snap	: true,
				easing	: 'linear'
			},
			
			//_________________________________________________________________
			
			instance: function(id) {
				var instance = null;
				var instances = this.instances;
				for(var i = 0; i < instances.length; i++) {
					instance = instances[i];
					if(instance.id == id) {
						break;
					}
				}
				
				return instance;
			},
			
			//_________________________________________________________________
			
			factory: function(element, options) {
				
				// Persist this scope.
				var carousels = this;
				
				var carousel = function(element, options) {
					// Persist this scope.
					var carousel = this;
					
					// Create an options object by
					// merging the supplied options
					// with the default options.
					this.options = $.extend({},
						JohnsonsBabyUX.carousels.defaults,
						options);
					
					// Setup the carousel container.
					this.container = element.css({
						overflow: 'hidden'
					});
					
					// If the container has an ID attribute
					// value, then use it to identify this
					// carousel, otherwise warn the developer.
					this.id = element.attr('id');
					if(this.id == '' && console) {
						// Warn and inform the developer
						// if a console is available.
						console.warn('This carousel container is'
							+ ' missing an ID attribute value:');
						console.info(carousel.container);
					}
					
					// Setup the list container -
					// both ul and ol are supported.
					this.list = $("ul, ol", this.container).css({
						position: 'relative',
						left: '0px',
						padding: 0,
						margin: 0
					});
					
					// Handles to buttons.
					var buttons = {
						next: $(carousel.options.next),
						prev: $(carousel.options.prev)
					}
					
					// Bind the next button click event handler.
					buttons.next.click(function() {
						carousel.next();
					});
					
					// Bind the previous button click event handler.
					buttons.prev.click(function() {
						carousel.prev();
					});
					
					// Default scrolling flag.
					this.scrolling = false;
					
					// Default position value.
					this.position = 0;
					
					//_____________________________________________________________
					
					this.load = function() {
						var width = 0;
						var item = null;
						
						// Handle to the list items collection.
						carousel.items = $("li", carousel.list);
						
						// Loop through the list items to ensure
						// each item is floated left and to determine
						// the total width of the list container.
						for(var i = 0; i < carousel.items.length; i++) {
							width += $(carousel.items[i])
								.css('float', 'left')
								.width();
						}
						
						// Resize the list container.
						this.list.css('width', width + 'px');
					}
					
					//_____________________________________________________________
					
					this.snap = function(position) {
						var left = 0;
						var item = null;
						
						var mark_1 = 0;		// Snappable area left marker.
						var mark_2 = 0;		// Snappable area right marker.
						
						var width_1 = 0;	// Width of the current item.
						var width_2 = 0;	// Width of the previous item.
						
						for(var i = 0; i < carousel.items.length; i++) {
							item = $(carousel.items[i]);
							width_1 = item.width();
							
							// Calculate the snappable area
							// bounds of the current item.
							mark_1 = left - (width_2 / 2);
							mark_2 = left + (width_1 / 2);

							// If the specified position is within the
							// bounds of the current item's snappable area
							// then use the current item's position instead.
							if(position >= mark_1 && position <= mark_2) {
								position = left;
								break;
							}
							
							width_2 = width_1;
							left += width_1;
						}
						
						return position;
					}
					
					//_____________________________________________________________
					
					var index = function(trim) {
						var index = 0;
						var width = 0;
						var item = null;
						var left = -carousel.position;
						
						for(var i = 0; i < carousel.items.length; i++) {
							item = $(carousel.items[i]);
							width = item.width();
							if((left + width) > 0) {
								index = i;
								break;
							} else {
								left += width;
							}
						}
						
						if(trim) {
							index += trim;
						}
						
						if(index < 0) {
							index = 0;
						} else {
							if(index >= carousel.items.length) {
								index = carousel.items.length - 1;
							}
						}
						
						return index;
					}
					
					//_____________________________________________________________
					
					this.next = function() {
						var item = $(carousel.items[index()]);
						var position = carousel.position + item.width();
						to(carousel.snap(position));
					}
					
					//_____________________________________________________________
					
					this.prev = function() {
						var item = $(carousel.items[index(-1)]);
						var position = carousel.position - item.width();
						to(carousel.snap(position));
					}
					
					//_____________________________________________________________
					
					this.first = function() {
						to(0);
					}
					
					//_____________________________________________________________
					
					this.last = function() {
						var maximum = carousel.list.width()
						- carousel.container.width();
						to(maximum);
					}
					
					//_____________________________________________________________
					
					this.scroll = function(amount) {
						//console.log(carousel.position + amount);
						carousel.to(carousel.position + amount);
					}
					
					//_____________________________________________________________
					
					this.to = function(position, speed, easing) {
						// Snap the position to the nearest
						// item if we've been asked to do so.
						if(carousel.options.snap) {
							position = carousel.snap(position);
						}
						
						// Scroll.
						to(position, speed, easing);
					}
					
					//_____________________________________________________________
					
					var to = function(position, speed, easing) {
						
						speed = speed ? speed : carousel.options.speed;
						easing = easing ? easing : carousel.options.easing;
						
						// Ensure the list wont be scrolled
						// past the bounds of the container.
						if(position < 0) {
							position = 0;
						} else {
							var maximum = carousel.list.width()
								- carousel.container.width();
							if(maximum < 0) {
								position = 0;
							} else if(position > maximum) {
								position = maximum;
							}
						}
						
						// If we aren't dealing with another request.
						if(carousel.scrolling == false) {
							// We can't accept any other requests from now.
							carousel.scrolling = true;
							// Move the carousel list
							// using $.animate() to
							// the requested position.
							carousel.list.animate(
								{left: -position + 'px'},
								speed,
								easing,
								function() {
									// Update the carousel position.
									var left = carousel.list.css('left');
									carousel.position = Math.abs(parseInt(left));
									// We're ready for more requests.
									carousel.scrolling = false;
									// Call the onStop handler if it exists.
									if($.isFunction(carousel.options.onStop)) {
										carousel.options.onStop.apply(carousel, [carousel]);
									}
								}
							);
						}
					}
					
					//_____________________________________________________________
					
					// Initialize the carousel.
					this.load();
					
					// Return the instance.
					return this;
				}
				
				// Create a new carousel instance.
				var instance = new carousel(element, options);
				
				// Add the instance to the instances array.
				this.instances.push(instance);
				
				// Return the new instance.
				return instance;
			}
		}
		
		//_____________________________________________________________________
		
		$.fn.JohnsonsBabyUXCarousel = function(options) {
			return this.each(function() {
				// Create a new carousel instance.
				JohnsonsBabyUX.carousels.factory(
					$(this), options);
			});
		}
		
	})(jQuery);
