(function($)
{
	$.fn.slideWindow = function(options)
	{
		options = $.extend({ }, $.fn.slideWindow.defaults, options);
		return this.each(function()
		{
			var $this = $(this);
			
			var list = $this.addClass('slider');
			var items = list.children('li');
			var currentItem = items.eq(0);
			
			$.data(this, 'list', list);
			$.data(this, 'items', items);
			$.data(this, 'options', options);
			$.data(this, 'currentItem', currentItem);
			
			list.css('position', 'relative').wrap('<div class="slide-window"></div>');
			var container = list.parent();
			$.data(this, 'container', container);
			
			// Container must be positioned relatively for this to work in IE7 for some reason.
			container.css({ overflow: 'hidden', position: 'relative' });
			if (!options.vertical)
			{
				items.css('float', 'left');
			}
			
			// Manually set the width/height of the list to the sum of the widths/heights of its items.
			var calculateExtent;
			if (options.vertical)
			{
				calculateExtent = function()
				{
					var height = 0;
					items.each(function()
					{
						height += getTotalHeight(this);
					});
					list.height(height);
				}
			}
			else
			{
				calculateExtent = function()
				{
					var width = 0;
					items.each(function()
					{
						width += getTotalWidth(this);
					});
					list.width(width);
				}
			}
			
			// First calculate and set the natural width/height of the list.
			calculateExtent();
			
			// Jump to the start item.
			var startItem = dynamicFilter(options.startItem, items);
			if (startItem.length > 0)
			{
				$this.slideToItem(startItem, true);
			}
			
			// If the list contains images, we'll need to re-calculate the width/height once
			// they've all loaded, in case the items are being pushed out by the images.
			var images = items.find('img')
			if (images.length > 0)
			{
				var loadCount = 0;
				images.load(function()
				{
					loadCount++;
					if (loadCount == images.length)
					{
						// Finished loading images; update width/height.
						calculateExtent();
						
						// Jump back to the start item.
						if (startItem.length > 0)
						{
							$this.slideToItem(startItem, true);
						}
						
						if ($.isFunction(options.callback))
						{
							options.callback();
						}
					}
				});
			}
		});
	};
	
	$.fn.slideWindow.defaults =
	{
		callback: null,
		vertical: false,
		slideSpeed: 'slow',
		startItem: null
	};
	
	$.fn.slideToItem = function(item, jump, callback)
	{
		return this.each(function()
		{
			// Find a unique item to scroll to.
			var items = $.data(this, 'items');
			item = dynamicFilter(item, items);
			
			var list = $.data(this, 'list');
			var options = $.data(this, 'options');
			var container = $.data(this, 'container');
			
			if (item.length > 0)
			{
				item = item.slice(0, 1);
				var position = item.position();
				list.stop();
				
				// Check that we're not trying to slide past the end of the list.
				if (options.vertical)
				{
					if (list.css('margin-top') != (container.height() - list.height()) + 'px' || position.top < list.height() - container.height())
					{
						var top = Math.min(position.top, list.height() - container.height());
						var target = { marginTop: -top };
						if (jump)
						{
							list.css(target);
						}
						else
						{
							list.animate(target, options.slideSpeed, null, callback);
						}
						$.data(this, 'currentItem', item);
					}
				}
				else
				{
					if (list.css('margin-left') != (container.width() - list.width()) + 'px' || position.left < list.width() - container.width())
					{
						var left = Math.min(position.left, list.width() - container.width());
						var target = { marginLeft: -left };
						if (jump)
						{
							list.css(target);
						}
						else
						{
							list.animate(target, options.slideSpeed, null, callback);
						}
						$.data(this, 'currentItem', item);
					}
				}
			}
		});
	};
	
	$.fn.currentItem = function()
	{
		return $.data(this[0], 'currentItem');
	};
	
	$.fn.slideNext = function(callback)
	{
		return this.each(function()
		{
			var items = $.data(this, 'items');
			var currentItem = $.data(this, 'currentItem');
			if (typeof currentItem != 'undefined' && typeof items != 'undefined')
			{
				$(this).slideToItem(currentItem.next(items), false, callback);
			}
		});
	};
	
	$.fn.slidePrevious = function(callback)
	{
		return this.each(function()
		{
			var items = $.data(this, 'items');
			var currentItem = $.data(this, 'currentItem');
			if (typeof currentItem != 'undefined' && typeof items != 'undefined')
			{
				$(this).slideToItem(currentItem.prev(items), false, callback);
			}
		});
	};
	
	function getTotalWidth(element)
	{
		element = $(element);
		var width = element.width();
		width += getPixelCount(element.css('padding-left')) + getPixelCount(element.css('padding-right'));
		width += getPixelCount(element.css('margin-left')) + getPixelCount(element.css('margin-right'));
		width += getPixelCount(element.css('border-left-width')) + getPixelCount(element.css('border-right-width'));
		return width;
	}
	
	function getTotalHeight(element)
	{
		element = $(element);
		var height = element.height();
		height += getPixelCount(element.css('padding-top')) + getPixelCount(element.css('padding-bottom'));
		height += getPixelCount(element.css('margin-top')) + getPixelCount(element.css('margin-bottom'));
		height += getPixelCount(element.css('border-top-width')) + getPixelCount(element.css('border-bottom-width'));
		return height;
	}
	
	function getPixelCount(string)
	{
		return parseInt(string.match(/(\d+)px/i)[1]);
	}
	
	function dynamicFilter(item, items)
	{
		switch (typeof item)
		{
			case 'number':
				// Get the item at the specified index.
				item = items.eq(item);
				break;
			case 'string':
				// Get the items matching the given expression.
				item = items.filter(item);
				break;
			default:
				// Ensure that the item is a jQuery object.
				item = $(item);
				
				// Check that the item is in the given context.
				if (items.index(item) < 0)
				{
					item = $([]);
				}
				
				break;
		}
		
		// Make sure there's only one being returned.
		return item.eq(0);
	}
})(jQuery);
