
// Provides hover functionality to make IE6 easier to deal with.
// Calls onHoverOn onHoverOff to elements that are matched.

var HoverEvent = Class.create();
HoverEvent.prototype = {
  options: null,
  listener: null,
  onHoverOn: function() {},
  onHoverOff: function() {},
  initialize: function( options ) {
    options.counter = 0;
    options.onHoverOn = options.onHoverOn || this.onHoverOn;
    options.onHoverOff = options.onHoverOff || this.onHoverOff;
    options.ignore = options.ignore || [];
    this.options = options;
    this.listener = this._listener.bind(this).curry( options );
    Event.observe( options.element, 'mouseover', this.listener );
    Event.observe( options.element, 'mouseout', this.listener );
  },
  stop: function() {
    Event.stopObserving( this.options.element, 'mouseover', this.listener );
    Event.stopObserving( this.options.element, 'mousout', this.listener );
  },
  // this routine is longer than you'd expect, because some browsers seem to drop events (sometimes).
  // (namely opera and safari)
  // thus, when we get any event, don't assume we will get the oposite event.
  // additionally, we keep track of the last element we fired hover on, and make sure we
  // hover off for that element on every event too.
  _listener: function( options, e ) {  
    var element = $(e.target).up_inclusive( options.match );
    var relatedElement = e.relatedTarget ? $(e.relatedTarget).up_inclusive( options.match ) : null;

    if( e.type == 'mouseover' ) {
    	// do nothing
    } else if( e.type == 'mouseout' ) {
    	var tmp = element;
    	element = relatedElement;
    	relatedElement = tmp;
    } else {
    	return;
    }

    if( relatedElement && relatedElement != element && relatedElement != options.lastOff ) {
      options.onHoverOff( e, relatedElement );
      options.lastOff = relatedElement;
      if( options.lastOn == options.lastOff )
        options.lastOn = null;
    }
      
    if( element && element != options.lastOn ) {
      options.onHoverOn( e, element );
  
      if( options.lastOn ) {
        options.onHoverOff( e, options.lastOn );
      }
        
      options.lastOn = element;
  
      if( options.lastOff == options.lastOn )
        options.lastOff = null;
    }	
  }
}
Element.addMethods({
  hover: function( element, match, options ) {
    options = options || {};
    options.classname = options.classname || 'hover';
    var onHoverOn = function( e, element ) {
      element.addClassName( options.classname );
    }
    var onHoverOff = function( e, element ) {
      element.removeClassName( options.classname );
    }
    Element.hoverEvent( element, match, onHoverOn, onHoverOff, options );
  },
  hoverEvent: function( element, match, onHoverOn, onHoverOff, options ) {
    if( !element._hoverEvents )
      element._hoverEvents = [];
    options = options || {};
    element._hoverEvents.push( [ match, new HoverEvent( Object.extend( options, { element: element, match: match, onHoverOn: onHoverOn, onHoverOff: onHoverOff })) ] );
  },
  stopHoverEvent: function( element, match ) {
    element._hoverEvents = element._hoverEvents.select(function( pair ) {
      if( match && pair[0] != match )
        return true;
      pair[1].stop();
      return false;
    });
  }
});

