
(function($)
{
   $.fn.tipsy = function(options)
   {
      options = $.extend(
      {
      }
      , $.fn.tipsy.defaults, options);
      return this.each(function()
      {
         var opts = $.fn.tipsy.elementOptions(this, options);
         $(this).hover(function()
         {
            $.data(this, 'cancel.tipsy', true);
            var tip = $.data(this, 'active.tipsy');
            if( ! tip)
            {
               tip = $('<div class="tipsy"><table border="0" cellpadding="0" cellspacing="0"><tr><td class="tb_tl"><img src="/images/space.gif"></td><td class="tb_tc"><img src="/images/space.gif"></td><td class="tb_tr"><img src="/images/space.gif"></td></tr><tr><td  class="tb_cl"><img src="/images/space.gif"></td><td class="tb_cc"><div class="tipsy-inner"></div></td><td class="tb_cr"><img src="/images/space.gif"></td></tr><tr><td class="tb_bl"><img src="/images/space.gif"></td><td class="tb_bc"><img src="/images/space.gif"></td><td class="tb_br"><img src="/images/space.gif"></td></tr></table><div class="bg-tipsy"></div></div>');
               tip.css(
               {
                  position : 'absolute', zIndex : 100000
               }
               );
               $.data(this, 'active.tipsy', tip);
            }
            if($(this).attr('title') || typeof($(this).attr('original-title')) != 'string')
            {
               $(this).attr('original-title', $(this).attr('title') || '').removeAttr('title');
            }
            var title;
            if(typeof opts.title == 'string')
            {
               title = $(this).attr(opts.title == 'title' ? 'original-title' : opts.title);
            }
            else if(typeof opts.title == 'function')
            {
               title = opts.title.call(this);
            }
            tip.find('.tipsy-inner')[opts.html ? 'html' : 'text'](title || opts.fallback);
            var pos = $.extend(
            {
            }
            , $(this).offset(),
            {
               width : this.offsetWidth, height : this.offsetHeight
            }
            );
            tip.get(0).className = 'tipsy';
            tip.remove().css(
            {
               top : 0, left : 0, visibility : 'hidden', display : 'block'
            }
            ).appendTo(document.body);
            var actualWidth = tip[0].offsetWidth, actualHeight = tip[0].offsetHeight;
            var gravity = (typeof opts.gravity == 'function') ? opts.gravity.call(this) : opts.gravity;
            switch(gravity.charAt(0))
            {
               case'n' :
                  tip.css(
                  {
                     top : pos.top + pos.height, left : pos.left + pos.width / 2 - actualWidth / 2
                  }
                  ).addClass('tipsy-north');
                  break;
               case's' :
                  tip.css(
                  {
                     top : pos.top - actualHeight, left : pos.left + pos.width / 2 - actualWidth / 2
                  }
                  ).addClass('tipsy-south');
                  break;
               case'e' :
                  tip.css(
                  {
                     top : pos.top + pos.height / 2 - actualHeight / 2, left : pos.left - actualWidth
                  }
                  ).addClass('tipsy-east');
                  break;
               case'w' :
                  tip.css(
                  {
                     top : pos.top + pos.height / 2 - actualHeight / 2, left : pos.left + pos.width
                  }
                  ).addClass('tipsy-west');
                  break;
            }
            if(opts.fade)
            {
               tip.css(
               {
                  opacity : 0, display : 'block', visibility : 'visible'
               }
               ).animate(
               {
                  opacity : 0.8
               }
               );
            }
            else
            {
               tip.css(
               {
                  visibility : 'visible'
               }
               );
            }
         }
         , function()
         {
            $.data(this, 'cancel.tipsy', false);
            var self = this;
            setTimeout(function()
            {
               if($.data(this, 'cancel.tipsy'))return;
               var tip = $.data(self, 'active.tipsy');
               if(opts.fade)
               {
                  tip.stop().fadeOut(function()
                  {
                     $(this).remove();
                  }
                  );
               }
               else
               {
                  tip.remove();
               }
            }
            , 100);
         }
         );
      }
      );
   }
   ;
   $.fn.tipsy.elementOptions = function(ele, options)
   {
      return $.metadata ? $.extend(
      {
      }
      , options, $(ele).metadata()) : options;
   }
   ;
   $.fn.tipsy.defaults =
   {
      fade : false, fallback : '', gravity : 'n', html : false, title : 'title'
   }
   ;
   $.fn.tipsy.autoNS = function()
   {
      return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
   }
   ;
   $.fn.tipsy.autoWE = function()
   {
      return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
   }
   ;
}
)(jQuery);

