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