/**
* hoverTimer is similar to jQuery.hover() and also another plugin called "hoverIntent"
* 
* It waits for a specified amount of time to pass to prior to calling the "mouseover" and "mouseout" events 
* It times the events and triggers an update event that can be used as a listener. 
* 
* hoverTimer r1 // 2010.07.12 // jQuery 1.4.2+
* <http://dylanlawrence.com/hoverTimer>
* 
* hoverTimer is currently available for use in all personal or commercial 
* projects under both MIT and GPL licenses. This means that you can choose 
* the license that best suits your project, and use it accordingly.
* update : It times the events and triggers an update event that can be used as a listener. 
* 
* @param opt = {} - override _hoverTimer object.  The element attributes can be used to override as well. 
* @author    Dylan Lawrence <dylanslawrence@gmail.com>
*/

(function(jQuery) {

var groupId = 0

jQuery.fn.extend( {

	allTimers : [],
	hoverTimer : function(opt){
		var g = groupId++;
		this.allTimers[g] = this;
		this.elements = {};
		this.hTimers = {};
		var _this = this;
		this.each( function(i) {
		   _this.elements[i] = this;		
           _this.hTimers[i] = new _hTimer(g,i,this,opt);
        });
		return this;
	}
});

function _hTimer(g,i,el,opt) {
    this.init(g,i,el,opt);    
}

jQuery.extend(_hTimer.prototype, {
	px:2,		 // pixels travels before reset timeover
	rate:10,	 // rate of update
    waitover:200,// wait to over
    waitout:700, // wait to out
    over : function(ev){},
    out : function(ev){},
	update : function(){},
	othersOver : function(){},	
    cur:null, 	 // curr mouse posiiton
    pre:null, 	 // prev mouse posiiton
    index:0,	 // obj id
	el:"",	 	 // this element
	_others:[],  // timers other then this in the group
	timeover:0,  // amount of time that has elapsed since mouseover
	timeout:0,	 // amount of time that has elapsed since mouseout
	leave:0,	 // should we mouseout?
	duration:0,	 // amount of time user has spent over the element	

    init : function(g,i,el,opt) {
		
		this.el = el
		this.group = g;
		this.index = i;
        var _this = this;
		
        if(typeof opt == "object") {
             jQuery.extend(this, opt);
        }
		// Pull out the elements attibutes and see if they need to be functions 
		$(this.el.attributes).each(function(){
			switch(this.nodeName){ 
				case "over":
				case "out" : 
				case "update" :
				case "othersOver" :
					_this[this.nodeName] = eval(this.nodeValue);
				break;
				default : 
					_this[this.nodeName] = this.nodeValue;
			}
		});
		
		new jQuery.Event("hUp"+this.index);
		$(this.el).bind("hUp"+this.index, function(ev){ 
			_this.update.apply(_this);
		});

        $(this.el).mouseover( function(ev){ _this.start(ev); }).mouseout(  function(ev){ _this.start(ev); });
		
		jQuery.event.trigger("hUp"+this.index);
		return this;
    },
	start:function(ev) {
		
        var _this = this;
		this.getOthers();
        var p = (ev.type == "mouseover" ? ev.fromElement : ev.toElement) || ev.relatedTarget;
        while ( p && p != this ) { try { p = p.parentNode; } catch(ev) { p = this; } }
        if ( p == this ) { return false; }
        var _e = jQuery.extend({},ev);
        if(this._t) { 
			this._t = clearInterval(this._t); 
			this.timeout = 0;
			jQuery.event.trigger("hUp"+this.index);
		}
        if (ev.type == "mouseover") {
            this.pre = {x:_e.pageX, y:_e.pageY};			
			$(this.el).bind("mousemove", function(){_this.mouse(_e)});
			if (!this.leave) {
				_this.timeover = 0;					
				jQuery.event.trigger("hUp"+this.index);
				this._t = setInterval( function(ev){ _this.overInt(_e);} , this.rate );
            }
        } else {
            $(this.el).unbind("mousemove",function(){});
			_this.timeout = 0;
			jQuery.event.trigger("hUp"+this.index);
            if (this.leave == 1) { this._t = setInterval( function(ev){ _this.outInt(_e); } , this.rate );}
        }
    },	
    mouse:function(ev){
        this.cur = {x:ev.pageX, y:ev.pageY};
		if ((this.pre.x - this.cur.x + this.pre.y-this.cur.y) < this.px ){
			this.pre = this.cur;
		    this.timeover = this.timeout = 0;
		}
    },	
    overInt : function(ev) {
	    var _this = this;
		this.timeover += this.rate;
		jQuery.event.trigger("hUp"+this.index);
		if(this.timeover < this.waitover){return;}
		this._t = clearInterval(this._t);
        $(this.el).unbind("mousemove", function(ev){_this.mouse(ev)} );
		this.durOver = setInterval( function(ev){  _this.overTimer(ev) } , this.rate );
		this.othersOver.apply(this, [this._others]);
		this.leave = 1;		
        return this.over.apply(this.el,[{event:ev,obj:this}]);
    },
	overTimer : function(ev){
		this.duration += this.rate;		
		jQuery.event.trigger("hUp"+this.index);
	},	
    outInt : function(ev) {
		this.timeout += this.rate;
		jQuery.event.trigger("hUp"+this.index);
		if(this.timeout < this.waitout){return;}
		this.clear();
        return this.out.apply(this.el,[ev]);
    },
    getOthers : function(){
		this._others = [];
		var t = $(this).allTimers[this.group].hTimers;
        for(var e in t){
			if(e!= this.index){ this._others.push(t[e]); }
		}
    },
	clear : function(){		
		this._t = clearInterval(this._t);
		// set everything to 0;
		this.timeover = this.timeout = this.leave = this.duration = 0;
		// clear the over duration;
		this.durOver = clearInterval(this.durOver);
		// trigger the update once more;		
		jQuery.event.trigger("hUp"+this.index);
	}
});


})(jQuery);		
		
