(function($)
{
	$.fn.animate_sprite = function(options)
	{
		
		// Set the options.
		var o = $.extend({}, $.fn.animate_sprite.defaults, options);
		
		// Go through each sprite.
		return this.each(function()
		{
			// Stop current animations and set/reset the current frame index.
			$(this).stop().css({frame: 0});

			// Create a frame object.
			var frame = {};
			
			// Get the frame width dimensions.
			o.frame = o.frame.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '').split(' ');
			frame.width = o.frame[0];
			frame.height = o.frame[1];
			
			// Create a sprite object.
			var sprite = {current: {}, start: {}, end: {}, delta: {}};
			
			// Get the current background position.
			var backgroundPosition = $(this).css('backgroundPosition').split(' ');
			
			// Get the current background positions.
			sprite.current.x = 1 * backgroundPosition[0].split('px')[0];
			sprite.current.y = 1 * backgroundPosition[1].split('px')[0];
			
			// Check if the start parameter was given.
			if (o.start)
			{
				o.start = o.start.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '').split(' ');
				  
				// Check if the x coordinate is auto.
				sprite.start.x = (o.start[0] == 'auto') ? sprite.current.x : -1 * o.start[0] * frame.width;
				sprite.start.y = (o.start[1] == 'auto') ? sprite.current.y : -1 * o.start[1] * frame.height;

				if (sprite.start.x != sprite.current.x)
				{
					sprite.start.x_extra = true;
				}
				if (sprite.start.y != sprite.current.y)
				{
					sprite.start.y_extra = true;
				}
			}
			else
			{
				// Use the given coordinates.
				sprite.start.x = sprite.current.x;
				sprite.start.y = sprite.current.y;
			}
			// Get the end coordinates.
			o.end = o.end.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '').split(' ');
			sprite.end.x = (o.end[0] == 'auto') ? sprite.current.x : -1 * o.end[0] * frame.width;
			sprite.end.y = (o.end[1] == 'auto') ? sprite.current.y : -1 * o.end[1] * frame.height;
			
			// Calculate the delta coordinates.
			sprite.delta.x = sprite.end.x - sprite.start.x;
			sprite.delta.y = sprite.end.y - sprite.start.y;
			
			sprite.delta.x_sign = (sprite.delta.x == 0) ? 0 : ((sprite.delta.x > 0) ? 1 : -1);
			sprite.delta.y_sign = (sprite.delta.y == 0) ? 0 : ((sprite.delta.y > 0) ? 1 : -1);
			
			sprite.delta.x_abs = Math.abs(sprite.delta.x);
			sprite.delta.y_abs = Math.abs(sprite.delta.y);
			
			// Create frames.
			
			// The animation will start on the next frame, except when start is specified and the start frame is not the current frame. 
			var less = 1;
			
			if (sprite.delta.x_abs / frame.width >= sprite.delta.y_abs / frame.height)
			{
				animation_length = Math.abs(sprite.delta.x / frame.width);
				
				if (o.start && o.start.length && sprite.start.x_extra)
				{
					less = 0;
					animation_length++;
				}
				
				// Populate the frame array with background positions.
				for (var i = 0; i < animation_length; i++)
				{
					frame[i] =
					{
						x: sprite.start.x + (sprite.delta.x_sign * (i + less) * frame.width) + 'px',
						y: sprite.start.y + (sprite.delta.y_sign * (i + less) * frame.width / sprite.delta.x_abs * sprite.delta.y_abs) + 'px'
					};
				}
			}
			else
			{
				// Get the length of the animation.
				animation_length = Math.abs(sprite.delta.y / frame.height);
				
				if (o.start && o.start.length && sprite.start.y_extra)
				{
					less = 0;
					animation_length++;
				}
				// Populate the frame array with background positions.
				for (var i = 0; i < animation_length; i++)
				{
					frame[i] =
					{
						y: sprite.start.y + (sprite.delta.y_sign * (i + less) * frame.width) + 'px',
						x: sprite.start.x + (sprite.delta.x_sign * (i + less) * frame.width / sprite.delta.y_abs * sprite.delta.x_abs) + 'px'
					};
				}
			}
			
			// Calculate the duration of the frame.
			var duration = 1000 * animation_length / o.fps;
			
			$(this)
			.animate
			(
				{frame: animation_length},
				{
					duration: duration,
					easing: 'linear',
					step: function()
					{
						// Get the current index and remove px if found.
						i = Math.floor($(this).css('frame').split('px')[0]);
						
						if (i < animation_length + 1)
						{
							$(this).css('backgroundPosition', frame[i].x + ' ' + frame[i].y);
						}
					},
					complete: function()
					{
						if (o.complete)
						{
							o.complete.call(this);
						}
					}
				}
			);
		});
	};
	// Public defaults
	$.fn.animate_sprite.defaults = o = {};
	o.fps = 10;
	o.active = true;

})(jQuery);
