﻿		function nubbinHoverEffect(event) 
		{
			var nubbin = event.target.down("div.nubbin");

			if (!nubbin) return;

			var over = event.eventName == "hover:activated", memo = event.memo;
			var moving = memo.fromElement && memo.toElement;

			if (event.target.match(".busy") || event.target.up(".busy")) 
				return;

			if (nubbin.effect) 
			{
				nubbin.effect.cancel();
				nubbin.effect = null;
			}

			if (Prototype.Browser.IE || over || moving) 
			{
				nubbin.setOpacity(over ? 1.0 : 0.0);
				nubbin[over ? "show" : "hide"]();
			} 
			else 
			{
				nubbin.effect = new Effect.Opacity(nubbin, {duration: 0.3, from: 1.0, to: 0.0,
					afterFinish: function() 
					{
						nubbin.hide();
						nubbin.effect = null;
					}
					});
			}
		}
		
/*
Extra methods that were using Element.addMethods but IE threw a wobbly
*/

    function upwards(element, iterator) 
        {
            while (element = $(element)) 
            {
                if (iterator(element)) return element;
                element = element.parentNode;
            }
        }

    function downwards(element, iterator) 
        {
            while (element = $(element)) 
            {
                if (iterator(element)) return element;
                element = element.down();
            }
        }

    function nextwards(element, iterator) 
        {
            while (element = $(element)) 
            {
                if (iterator(element)) return element;
                    element = element.next();
            }
        }

        
/* ------------------------------------------------------------------------
 * HoverObserver.js
 * ------------------------------------------------------------------------ */

var HoverObserver = Class.create();

Object.extend(HoverObserver, {
  Options: {
    activationDelay:    0,
    deactivationDelay:  0.5,
    targetClassName:    "hover_target",
    containerClassName: "hover_container",
    containerAttribute: "hover_container",
    activeClassName:    "hover",
    activationEvent:    "hover:activated",
    deactivationEvent:  "hover:deactivated", 
    clickToHover:       false
  }
});

Object.extend(HoverObserver.prototype, {
  initialize: function(element, options) {
    this.element = $(element);
    this.options = Object.extend(Object.clone(HoverObserver.Options), options || {});
    this.start();
  },
  
  start: function() {
    if (!this.observers) {
      var events = $w(this.options.clickToHover ? "click" : "mouseover mouseout");
      this.observers = events.map(function(name) {
        var handler = this["on" + name.capitalize()].bind(this);
        this.element.observe(name, handler);
        return { name: name, handler: handler };
      }.bind(this));
    }
  },
  
  stop: function() {
    if (this.observers) {
      this.observers.each(function(info) {
        this.element.stopObserving(info.name, info.handler);
      }.bind(this));
      delete this.observers;
    }
  },
  
  onClick: function(event) {
    var element   = this.activeHoverElement = event.element();
    var container = this.getContainerForElement(element);
    
    if (container) {
      if (this.activeContainer && container == this.activeContainer) 
        return this.deactivateContainer();
      this.activateContainer(container);
    }
  },
  
  onMouseover: function(event) {
    var element   = this.activeHoverElement = event.element();
    var container = this.getContainerForElement(element);
    
    if (container) {
      if (this.activeContainer) {
        this.activateContainer(container);
      } else {
        this.startDelayedActivation(container);
      }
    } else {
      this.startDelayedDeactivation();
    }
  },
  
  onMouseout: function(event) {
    delete this.activeHoverElement;
    this.startDelayedDeactivation();
  },
  
  activateContainer: function(container) {
    var memo = { toElement: container };
    this.stopDelayedDeactivation();
    
    if (this.activeContainer) {
      if (this.activeContainer == container) return;
      memo.fromElement = this.activeContainer;
      this.deactivateContainer(memo);
    }
    
    this.activeContainer = container;
    this.activeContainer.fire(this.options.activationEvent, memo);
    this.activeContainer.addClassName(this.options.activeClassName);
  },
  
  deactivateContainer: function(memo) {
    if (this.activeContainer) {
      try {
        this.activeContainer.removeClassName(this.options.activeClassName);
        this.activeContainer.fire(this.options.deactivationEvent, memo);
      } catch (e) {
      }
      
      delete this.activeContainer;
    }
  },
  
  startDelayedActivation: function(container) {
    if (this.options.activationDelay) {
      (function() {
        if (container == this.getContainerForElement(this.activeHoverElement))
          this.activateContainer(container);
        
      }).bind(this).delay(this.options.activationDelay);
    } else {
      this.activateContainer(container);
    }
  },
  
  startDelayedDeactivation: function() {
    if (this.options.deactivationDelay) {
      this.deactivationTimeout = this.deactivationTimeout || function() {
        var container = this.getContainerForElement(this.activeHoverElement);
        if (!container || container != this.activeContainer)
          this.deactivateContainer();
        
      }.bind(this).delay(this.options.deactivationDelay);
    } else {
      this.deactivateContainer();
    }
  },
  
  stopDelayedDeactivation: function() {
    if (this.deactivationTimeout) {
      window.clearTimeout(this.deactivationTimeout);
      delete this.deactivationTimeout;
    }
  },
  
  getContainerForElement: function(element) {
    if (!element) return;
    
    if (element.hasAttribute && !element.hasAttribute(this.options.containerAttribute)) {
      var target    = this.getTargetForElement(element);
      var container = this.getContainerForTarget(target);
      this.cacheContainerFromElementToTarget(container, element, target);
    }
    
    return $(element.readAttribute(this.options.containerAttribute));
  },
  
  getTargetForElement: function(element) {
    if (!element) return;
    var targetClassName = this.options.targetClassName;
    return upwards(element, function(e) {
      if (e.hasClassName) 
        return e.hasClassName(targetClassName);
    });
  },
  
  getContainerForTarget: function(element) {
    if (!element) return;
    var containerClassName = this.options.containerClassName;
    return upwards(element, function(e) {
      if (e.hasClassName)
        return e.hasClassName(containerClassName);
    });
  },
  
  cacheContainerFromElementToTarget: function(container, element, target) {
    if (container && target) {
      upwards(element, function(e) {
        e.writeAttribute(this.options.containerAttribute, container.identify());
        if (e == target) return true;
      }.bind(this));
    }
  }
});
