(function($) {
// .pulse() plugin
	var isNumber = function(value) {
		return typeof value === 'number' && isFinite(value);
	};
	
	$.fn.pulse = function(fadeInTime, fadeOutTime, easing, n) {
		var inTime = isNumber(fadeInTime) && fadeInTime >= 0 ? fadeInTime : 1000;
		var outTime = isNumber(fadeOutTime) && fadeOutTime >= 0 ? fadeOutTime : 1000;
		var myEasing = typeof easing ==='string' ? easing : 'swing';
		
		if (isNumber(n) && n <= 0) {
			return;
		}
		return this.fadeIn(inTime, myEasing, function() {
			$(this).fadeOut(outTime, myEasing, function() {
				$(this).pulse(inTime, outTime, myEasing, n-1);
			});
		});
	};
})(jQuery);


/*
TODO
allow .move() and .stretch() to animate multiple properties at once like:
$('#foo').stretch({
	up: 200,
	toRight: 400
}, 1000, 'swing', complete);

First arg is object with direction:delta pairs. animationPropsMethods are called for each pair and aggregate top, left, height, and width 
	in a shared props object which is then sent to .animate()
*/


(function($) {
// .move() and .stretch() plugins
	var that
		defaults = {
		stretch: false,
		direction: "toRight",
		delta: 0,
		duration: 0,
		easing: 'swing',
		complete: $.noop
	},
	
	animationPropsMethods = {
		up: function(o) {
			return {
				top: '-=' + (o.delta),
				height: '+=' + (o.stretch ? o.delta : 0)
			};
		},
		down: function(o) {
			return {
				top: '+=' + (o.stretch ? 0 : o.delta),
				height: '+=' + (o.stretch ? o.delta : 0)
			};
		},
		toLeft: function(o) {
			return {
				left: '-=' + o.delta,
				width: '+=' + (o.stretch ? o.delta : 0)
			};
		},
		toRight: function(o) {
			return {
				left: '+=' + (o.stretch ? 0 : o.delta),
				width: '+=' + (o.stretch ? o.delta : 0)
			};
		}
	},
	
	transform = function(that, o) {
		var animationProperties = animationPropsMethods[o.direction](o);
		if ( animationProperties ) {
			return that.animate( animationProperties, o);
		} else {
			$.error( o.direction + ' is a bad argument' );
		}
	},
	
	makeArgObj = function(direction, delta, duration, easing, complete, stretch) {
		if ( typeof direction === 'string' ) {
			return $.extend( {}, defaults, {
				'direction': direction,
				'delta': delta,
				'duration': duration,
				'easing': easing,
				'complete': complete,
				'stretch': stretch
			});
		} else if ( typeof direction === 'object' || ! direction ) {
			return $.extend( {}, defaults, direction, {'stretch': stretch} );
		} else {
			$.error( direction + ' is a bad argument' );
		}
	};
	
	
	$.fn.move = function(direction, delta, duration, easing, complete) {
		// .move() may be invoked with the args above
		// or with a single options object with properties named like the args
		
		return transform( this, makeArgObj(direction, delta, duration, easing, complete, false) );
	};
	
	
	$.fn.stretch = function(direction, delta, duration, easing, complete) {
		// .stretch() may be invoked with the args above
		// or with a single options object with properties named like the args
		
		return transform( this, makeArgObj(direction, delta, duration, easing, complete, true) );
	};
})(jQuery);
