/* 
 * jQuery Timers
 * http://jquery.offput.ca/every/
 *
 * Version 1.0
 * 
 */
 
 /**
 	USAGE
	--------------
	JAVASCRIPT:
	// Params: 
	$(document).ready(function(){
				var demos = $("div.wrapper div.demos");
				//===
				
				var active = false;
				$('.controlled-interval', demos).find('.start').css("cursor", "pointer").click(function() {
					if (!active) {
						active = !active;
						$(this).parents("div").find('ul').everyTime(1000, 'controlled', function() {
							$(this).append("<li>New One</li>");
						});
					}
				}).end().find('.stop').css("cursor", "pointer").click(function() {
					if (active) {
						active = !active;
						$(this).parents("div").find('ul').stopTime('controlled');
					}
				});
				
				$(".uncontrolled-interval p", demos).everyTime(1000,function(i) {
					$(this).html(i);
				});

				var event = function() {
					$(this).text("You did it!");
					$(this).stopTime();
				};
				$("div.controlled-timeout p", demos).click(event).oneTime("5s", function() {
					$(this).text("Oh noes, too late!");
					$(this).unbind('click', event);
				});
				
				$("div.uncontrolled-timeout p", demos).oneTime(2000, function() {
					$(this).html("See?");
				}).oneTime(5000, "soon", function() {
					$(this).html("I'm unstoppable");
				});
				//=== 		
 	}
	--------------
	XHTML:
	<div class="wrapper">
		<div class="demos">
				<h3>Controlled everyTime</h3>
				<div class="controlled-interval">
					<ul></ul>
					<p><span class="start">Start</span> | <span class="stop">Stop</span></p>

				</div>
				<h3>Unbounded everyTime</h3>
				<div class="uncontrolled-interval">
					<p>I am transient... *sigh*</p>
				</div>
				<h3>Controlled oneTime</h3>
				<div class="controlled-timeout">

					<p>Click here to stop me!</p>
				</div>
				<h3>Uncontrolled oneTime</h3>
				<div class="uncontrolled-timeout">
					<p>I'm gonna change whether you like it or not.</p>
				</div>
		</div>
	</div>
	
 */
jQuery.fn.extend({
	everyTime: function(interval, label, fn, times, belay) {
		return this.each(function() {
			jQuery.timer.add(this, interval, label, fn, times, belay);
		});
	},
	oneTime: function(interval, label, fn) {
		return this.each(function() {
			jQuery.timer.add(this, interval, label, fn, 1);
		});
	},
	stopTime: function(label, fn) {
		return this.each(function() {
			jQuery.timer.remove(this, label, fn);
		});
	}
});

jQuery.extend({
	timer: {
		guid: 1,
		global: {},
		regex: /^([0-9]+)\s*(.*s)?$/,
		powers: {
			// Yeah this is major overkill...
			'ms': 1,
			'cs': 10,
			'ds': 100,
			's': 1000,
			'das': 10000,
			'hs': 100000,
			'ks': 1000000
		},
		timeParse: function(value) {
			if (value == undefined || value == null)
				return null;
			var result = this.regex.exec(jQuery.trim(value.toString()));
			if (result[2]) {
				var num = parseInt(result[1], 10);
				var mult = this.powers[result[2]] || 1;
				return num * mult;
			} else {
				return value;
			}
		},
		add: function(element, interval, label, fn, times, belay) {
			var counter = 0;
			
			if (jQuery.isFunction(label)) {
				if (!times) 
					times = fn;
				fn = label;
				label = interval;
			}
			
			interval = jQuery.timer.timeParse(interval);

			if (typeof interval != 'number' || isNaN(interval) || interval <= 0)
				return;

			if (times && times.constructor != Number) {
				belay = !!times;
				times = 0;
			}
			
			times = times || 0;
			belay = belay || false;
			
			if (!element.$timers) 
				element.$timers = {};
			
			if (!element.$timers[label])
				element.$timers[label] = {};
			
			fn.$timerID = fn.$timerID || this.guid++;
			
			var handler = function() {
				if (belay && this.inProgress) 
					return;
				this.inProgress = true;
				if ((++counter > times && times !== 0) || fn.call(element, counter) === false)
					jQuery.timer.remove(element, label, fn);
				this.inProgress = false;
			};
			
			handler.$timerID = fn.$timerID;
			
			if (!element.$timers[label][fn.$timerID]) 
				element.$timers[label][fn.$timerID] = window.setInterval(handler,interval);
			
			if ( !this.global[label] )
				this.global[label] = [];
			this.global[label].push( element );
			
		},
		remove: function(element, label, fn) {
			var timers = element.$timers, ret;
			
			if ( timers ) {
				
				if (!label) {
					for ( label in timers )
						this.remove(element, label, fn);
				} else if ( timers[label] ) {
					if ( fn ) {
						if ( fn.$timerID ) {
							window.clearInterval(timers[label][fn.$timerID]);
							delete timers[label][fn.$timerID];
						}
					} else {
						for ( var fn in timers[label] ) {
							window.clearInterval(timers[label][fn]);
							delete timers[label][fn];
						}
					}
					
					for ( ret in timers[label] ) break;
					if ( !ret ) {
						ret = null;
						delete timers[label];
					}
				}
				
				for ( ret in timers ) break;
				if ( !ret ) 
					element.$timers = null;
			}
		}
	}
});

if (jQuery.browser.msie)
	jQuery(window).one("unload", function() {
		var global = jQuery.timer.global;
		for ( var label in global ) {
			var els = global[label], i = els.length;
			while ( --i )
				jQuery.timer.remove(els[i], label);
		}
	});


