(function($){
  
  var defaults = {
    openDelay   : 400,
    closeDelay  : 100,
    fadeInSpeed : 300,
    getPopup    : function(elem) {},
    preShow     : function(elem, popup) {},
    closeCallback : function(elem, popup) {},
    log         : function(m) {},
    show        : function(popup, speed) {
      popup.fadeIn(speed);
    },
    hide        : function(popup) {
      popup.hide();
    }
  };
  
  function clear(t) {
    if(t) {
      clearTimeout(t);
    }
  }
  
  function closePop(conf, elem, popup) {
    conf.hide(popup);
    conf.closeCallback(elem, popup);
  }

  $.fn.hoverPopup = function(config) {
    var conf = $.extend( {}, defaults, config );
    $(this).mouseover(function() {
      ;;;conf.log('a mouseover');
      
      var elem = $(this);
      var data = elem.data('hoverPopup');
      if(!data) {
        data = {
          setupComplete : false,
          popup : null,
          hideTimer : null,
          showTimer : null
        };
        elem.data('hoverPopup', data);
      }
      clear(data.hideTimer);
      if(!data.setupComplete) {
        var popup = conf.getPopup(elem);
        popup.mouseover(function() {
          ;;;conf.log( 'popup mouseover');
          clear(data.hideTimer);
        }).mouseout(function() {
          ;;;conf.log('popup mouseout');
          clear(data.showTimer);
          clear(data.hideTimer);
          data.hideTimer = setTimeout(function() { closePop(conf, elem, popup); }, conf.closeDelay);
        });
        
        $(this).mouseout(function() {
          ;;;conf.log('a mouseout');
          clear(data.showTimer);
          clear(data.hideTimer);
          data.hideTimer = setTimeout(function() { closePop(conf, elem, popup); }, conf.closeDelay);
        });
        data.setupComplete = true;
        data.popup = popup;
      }
      data.showTimer = setTimeout(function() {
        conf.preShow(elem, data.popup);
        conf.show(data.popup, conf.fadeInSpeed);
      }, conf.openDelay);
    });
  };
  
})(jQuery);

