(function($) {
	// Add some additional easing calculations.
	$.extend($.easing,{
		easein: function(p, n, firstNum, delta, duration) {
			return delta*(n/=duration)*n*n + firstNum;
		},
		
		easeout: function(p, n, firstNum, delta, duration) {
			return -delta * ((n=n/duration-1)*n*n*n - 1) + firstNum;
		},
		
		easeboth: function(p, n, firstNum, delta, duration) {
			if ((n/=duration/2) < 1)
				return delta/2*n*n*n*n + firstNum;
				return -delta/2 * ((n-=2)*n*n*n - 2) + firstNum;
		},
		
		bounceout: function(p, n, firstNum, delta, duration) {
			if ((n/=duration) < (1/2.75)) {
				return delta*(7.5625*n*n) + firstNum;
			} else if (n < (2/2.75)) {
				return delta*(7.5625*(n-=(1.5/2.75))*n + .75) + firstNum;
			} else if (n < (2.5/2.75)) {
				return delta*(7.5625*(n-=(2.25/2.75))*n + .9375) + firstNum;
			} else {
				return delta*(7.5625*(n-=(2.625/2.75))*n + .984375) + firstNum;
			}
		},
		
		bouncein: function(p, n, firstNum, delta, duration) {
			if (jQuery.easing.bounceout)
				return delta - jQuery.easing.bounceout (p, duration - n, 0, delta, duration) + firstNum;
			return firstNum + delta;
		},
		
		bounceboth: function(p, n, firstNum, delta, duration) {
			if (jQuery.easing.bouncein && jQuery.easing.bounceout)
				if (n < duration/2)
					return jQuery.easing.bouncein(p, n*2, 0, delta, duration) * .5 + firstNum;
				return jQuery.easing.bounceout(p, n*2-duration, 0, delta, duration) * .5 + delta*.5 + firstNum; 
			return firstNum + delta;
		},
		
		elasticin: function(p, n, firstNum, delta, duration) {
			var a, s;
			if (n == 0)
				return firstNum;
			if ((n/=duration)==1)
				return firstNum+delta;
			a = delta * 0.3;
			p=duration*.3;
			if (a < Math.abs(delta)) {
				a=delta;
				s=p/4;
			} else { 
				s = p/(2*Math.PI) * Math.asin (delta/a);
			}
			return -(a*Math.pow(2,10*(n-=1)) * Math.sin( (n*duration-s)*(2*Math.PI)/p )) + firstNum; 
		},
		
		elasticout:function(p, n, firstNum, delta, duration) {
			var a, s;
			if (n==0)
				return firstNum;
			if ((n/=duration/2)==2)
				return firstNum + delta;
			a = delta * 0.3;
			p=duration*.3;
			if (a < Math.abs(delta)){
				a = delta;
				s=p/4;
			} else { 
				s = p/(2*Math.PI) * Math.asin (delta/a);
			}
			return a*Math.pow(2,-10*n) * Math.sin( (n*duration-s)*(2*Math.PI)/p ) + delta + firstNum;
		},
		
		elasticboth: function(p, n, firstNum, delta, duration) {
			var a, s;
			if (n==0)
				return firstNum;
			if ((n/=duration/2)==2)
				return firstNum + delta;
			a = delta * 0.3;
			p=duration*.3;
			if (a < Math.abs(delta)){
				a = delta;
				s=p/4;
			} else { 
				s = p/(2*Math.PI) * Math.asin (delta/a);
			}
			if (n < 1) {
				return -.5*(a*Math.pow(2,10*(n-=1)) * Math.sin( (n*duration-s)*(2*Math.PI)/p )) + firstNum;
			}
			return a*Math.pow(2,-10*(n-=1)) * Math.sin( (n*duration-s)*(2*Math.PI)/p )*.5 + delta + firstNum; 
		}
	});

	// A simple scrolling ticker.
	$.fn.scroller = function(){

		return this.each(function(i){

			if (this._scroller) return;
			this._scroller = true;

			// Make sure we have at least 2 child items to render.
			var div = $(this);
			var items = div.children();
			
			// Get the scrolling dimensions.
			div.css({display:'block',position:'relative',visibility:'visible',overflow:'hidden',overflowX:'hidden',overflowY:'hidden'});
			div._width = div.innerWidth();
			div._height = div.innerHeight();
			div._orientation = div.attr('_orientation')=='Horizontal'?'left':'top';
			div._forward = div.attr('_direction')!='Forward'?-1:1;
			div._speed = $.toInt(div.attr('_speed'))||1000;
			div._animation = div.attr('_animation')||'swing';
			div._css = {};
			div._css[div._orientation] = (div._forward>0?'+=':'-=')+(div._orientation=='left'?div._width:div._height)+'px';
			div._pos = 0;
			div._last = {
				top:div._orientation=='left'?0:(items.length-1)*div._height*div._forward*-1,
				left:div._orientation=='left'?(items.length-1)*div._width*div._forward*-1:0
			};
			this._delay = $.toInt(div.attr('_delay'))||4000;

			// The delay has to be longer than the speed or we will run into animation conflicts.
			if (this._delay<div._speed+250)
				this._delay = div._speed+250;

			// Set up the child elements.
			items.each(function(i){
				// Get the child margins.
				var child = $(this);
				var m = {
					top:$.toInt(child.css('marginTop'))+$.toInt(child.css('borderTopWidth')),
					right:$.toInt(child.css('marginRight'))+$.toInt(child.css('borderRightWidth')),
					bottom:$.toInt(child.css('marginBottom'))+$.toInt(child.css('borderBottomWidth')),
					left:$.toInt(child.css('marginLeft'))+$.toInt(child.css('borderLeftWidth'))
				};
				// Set its CSS.
				child.css({
					display:'block',
					position:'absolute',
					overflow:'hidden',
					top:div._orientation=='left'?0:i*div._height*div._forward*-1,
					left:div._orientation=='left'?i*div._width*div._forward*-1:0,
					width:div._width-m.left-m.right,
					height:div._height-m.top-m.bottom
				});
			});

			// Don't bother to animate if there's only one item.
			if (items.length<=1) return;

			// Set up the animation script.
			this._fn = function(div,items){
				return function(){
					// If this element has been removed from document and is now in a fragment, remove the timer event.
					if (!div[0] || !div[0].parentNode || div[0].parentNode.nodeType!=1)
					{
						clearInterval(div[0]._timer);
						div[0]._timer = null;
						div = null;
						items = null;
						return;
					}

					// If we're current in pause, don't run the animation.
					if (div[0]._pause) return;

					// Animate the elements.
					var moved = false;
					var count = 0;
					items.animate(div._css,div._speed,div._animation,function(){
						// Once we've animated the first element (css-wise) in the collection, move it to the end.
						if (div._pos==count && !moved)
						{
							// Once the animation is finished, reset the CSS to move this item to the end.
							items.eq(div._pos).css(div._last);
							// Move onto the next item.
							div._pos++;
							if (div._pos>=items.length) div._pos = 0;
							// Note that we've moved the item.
							moved = true;
						}
						count++;
					});
				};
			}(div,items);

			// Pause on mousenter.
			var pause = function(e){
				if (this._timer)
					clearInterval(this._timer);
				this._timer = null;
				this._pause = true;
			};
			
			// Restart on mouseleave
			var unpause = function(e){
				if (this._timer)
					clearInterval(this._timer);
				this._timer = setInterval(this._fn,this._delay);
				this._pause = false;
			};

			// Pause the scrolling on hover.
			div.hover(pause,unpause);

			// Start the animation.
			this._timer = setInterval(this._fn,this._delay);

		});
	};

})(jQuery);
