(function($){

 $.jatt = function(options){

  var opt, o = $.extend({},$.jatt.defaultOptions, options);
  var init = function(){
  var evt = (o.live) ? 'live' : 'bind';

   $(o.tooltip)
    [evt](o.activate,function(e){
     var obj = $(this),
         meta = (o.metadata.toString() == 'false') ? [o, ''] : $.jatt.getMeta(obj);
     opt = meta[0];
     var tt = (obj.attr(opt.content) === '') ? obj.data('tooltip') || '' : obj.attr(opt.content) || '',
         rel = obj.attr('rel') || '',
         url = obj.attr('href') || '';
     obj.data('tooltip', tt);
     obj.attr('title', '');

     if (tt === ''){
      if (rel !== '') {
       tt = $(rel).html() || 'No tooltip found';
      } else if (url !== '') {
       tt = 'Loading...';

       var ttloader = $('<div />');
       ttloader.load(url, function(){
        $('#' + o.tooltipId).html( ttloader.html() );
       });
      }
     }

     var tmp = '<div id="' + o.tooltipId + '" style="position:absolute;z-index:' + opt.zIndex + ';' + meta[1] + '">' + tt + '</div>';
     if (opt.local){
      obj.before(tmp);
     } else {
      $('body').append(tmp);
     }
     $.jatt.ttrelocate(e, '#' + o.tooltipId);
     $('#' + o.tooltipId).fadeIn(opt.speed);
    })
    [evt](o.deactivate,function(e) {
     $('#' + o.tooltipId).remove();
    })
    [evt]('mousemove',function(e) {
     if (opt.followMouse) { $.jatt.ttrelocate(e, '#' + o.tooltipId); }
    });

   $(o.preview + ',' + o.screenshot)
    [evt](o.activate,function(e){
     var obj = $(this),
         meta = (o.metadata.toString() == 'false') ? [o, ''] : $.jatt.getMeta(obj);
     opt = meta[0];
     var tt = (obj.attr(opt.content) === '') ? obj.data('tooltip') || '' : obj.attr(opt.content) || '';
     obj.data('tooltip', tt);
     if (opt.content == 'title') { obj.attr(opt.content, ''); } // leave title attr empty
     var tmp = '<div id="' + o.previewId + '" style="position:absolute;z-index:' + opt.zIndex + ';' + meta[1] + '"><img src="';
     var c = (tt !== '') ? '<br/>' + tt : '';
     var ss = (obj.is(o.screenshot) && this.rel == '#') ? 'http://images.websnapr.com/?url=' + this.href : this.rel;
     tmp += (obj.is(o.preview)) ? this.href + '" alt="Image preview" />' : ss + '" alt="URL preview: ' + this.href + '" />';
     tmp += c + '</div>';
     if (opt.local){
      obj.before(tmp);
     } else {
      $('body').append(tmp);
     }
     $('#' + o.previewId).data('options',opt);
     $.jatt.ttrelocate(e, '#' + o.previewId);
     $('#' + o.previewId).fadeIn(opt.speed);
    })
    [evt](o.deactivate,function(e){
     $('#' + o.previewId).remove();
    })
    [evt]('mousemove',function(e){
     if (opt.followMouse) { $.jatt.ttrelocate(e, '#' + o.previewId); }
    });

  };

  $.jatt.ttrelocate = function(e, ttid){
   var ttw = $(ttid).outerWidth(),
    tth = $(ttid).outerHeight(),
    tip = {
     e  : [ opt.xOffset, -tth/2, ttw+opt.xOffset, tth/2 ],
     se : [ opt.xOffset, opt.yOffset, ttw+opt.xOffset, tth+opt.yOffset ],
     s  : [ -ttw/2, opt.yOffset, ttw/2, tth+opt.yOffset ],
     sw : [ -ttw-opt.xOffset, opt.yOffset, -opt.xOffset, tth+opt.yOffset ],
     w  : [ -ttw-opt.xOffset, -tth/2, -opt.xOffset, tth/2 ],
     nw : [ -ttw-opt.xOffset, -tth-opt.yOffset, -opt.xOffset, -opt.yOffset ],
     n  : [ -ttw/2, -tth-opt.yOffset, ttw/2, -opt.yOffset ],
     ne : [ opt.xOffset, -tth-opt.yOffset, ttw+opt.xOffset, -opt.yOffset ]
    },
    dir = tip[opt.direction],
    wscrY = $(window).scrollTop(),
    wscrX = $(window).scrollLeft(),
    curX = e.pageX,
    curY = e.pageY;

   if (!opt.followMouse) {
    var tar = $(e.target),
     objw = tar.outerWidth(),
     objh = tar.outerHeight(),
     obj = {
      e  : [ objw, objh/2 ],
      se : [ objw, objh ],
      s  : [ objw/2, objh ],
      sw : [ 0, objh ],
      w  : [ 0, objh/2 ],
      nw : [ 0, 0 ],
      n  : [ objw/2, 0 ],
      ne : [ objw, 0 ]
     };
    curX = tar.offset().left + obj[opt.direction][0];
    curY = tar.offset().top + obj[opt.direction][1];
   }
   var ttleft = curX + dir[0],
       tttop = curY + dir[1];

   if ( curX + dir[2] > wscrX + $(window).width() - opt.xOffset ) { ttleft = $(window).width() - ttw - opt.xOffset; }
   if ( curY + dir[3] > wscrY + $(window).height() - opt.yOffset ) { tttop = curY - tth - opt.yOffset; }
   if ( ttleft < wscrX + opt.xOffset ) { ttleft = wscrX + opt.xOffset; }
   if ( tttop < wscrY + opt.yOffset ) {  tttop = curY + opt.yOffset; }

   if ( curX > ttleft && curX < ttleft + ttw && curY > tttop && curY < tttop + tth ) {
    tttop += ( (tttop - tth/2 - opt.yOffset) < wscrY + opt.yOffset ) ? tth/2 + opt.yOffset : -tth/2 - opt.yOffset;
   }
   $(ttid).css({ left : ttleft + 'px', top : tttop + 'px' });
  };

  $.jatt.getMeta = function(el){
   opt = $.extend({}, o);
   var t, m = [], meta = el.attr(o.metadata).match(/(\{.*\})/g) || '';
   if (meta !== '') {
    var opts = 'direction|followMouse|content|speed|local|xOffset|yOffset|zIndex';
    meta = meta[0].replace(/(\{|\'|\"|\})/g,'');
    if (meta.match(opts)) {
     $.each( meta.split(';'), function(i,val){
      t = val.split(':');
      if (t[0].match(opts)) {
       var k = $.trim(t[0]), v = $.trim(t[1]);
       if (v == 'true' || v == 'false') {
        opt[k] = (v == 'true') ? true : false;
       } else {
        opt[k] = (isNaN(v)) ? v : parseFloat(v);
       }
      } else {
       m.push(val);
      }
     });
     meta = m.join(';');
    }
   }

   t = el.attr('rel') || '';
   if (t !== '' && !/^[#|\.]|[\/]/.test(t)) {
    t = t.split(',');
    meta += ';width:' + t[0] + 'px;';
    if (typeof(t[1]) != 'undefined') { meta += 'background:' + t[1]; }
   }
   return [opt, meta];
  };

  init();
 };

 $.jatt.defaultOptions = {
  direction      : 'nw',     // direction of tooltip
  followMouse    : true,    // tooltip follows mouse movement
  content        : 'title', // attribute containing tooltip text
  speed          : 300,     // tooltip fadein speed
  local          : false,   // if true, the script attachs the tooltip locally; if false, the tooltip is added to the body
  xOffset        : 10,      // x distance from mouse (no negative values)
  yOffset        : 10,      // y distance from mouse (no negative values)
  zIndex         : 1000,    // z-index of tooltip
  // options not supported by metadata
  live           : false,                 // use live event support?
  metadata       : 'class',               // attribute that contains the metadata, use "false" (no quotes) to disable the metadata.
  activate       : 'mouseenter focusin',  // how tooltip is activated
  deactivate     : 'mouseleave focusout', // how tooltip is deactivated
  // change tooltip, screenshot and preview class
  tooltip        : '.tooltip',            // tooltip class 
  screenshot     : 'a.screenshot',        // screenshot class
  preview        : 'a.preview',           // preview class 
  // tooltip & preview ID (div that contains the tooltip)
  tooltipId      : 'tooltip',             // ID of actual tooltip
  previewId      : 'preview'              // ID of screenshot/preview tooltip 
 };

})(jQuery);
