/**
 * jQuery.timers - Timer abstractions for jQuery
 * Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com)
 * Licensed under the WTFPL (http://sam.zoy.org/wtfpl/).
 * Date: 2008/08/26
 *
 * @author Blair Mitchelmore
 * @version 1.0.0
 *
 **/

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);
	}
    });

