
(function($) {
	
	
	var helper,
		
		tTitle,
		
		tBody,
		
		tUrl,
		
		current,
		
		oldTitle,
		
		tID;
	
	
	$.fn.Tooltip = function(settings) {
		
		settings = $.extend($.extend({}, arguments.callee.defaults), settings || {});
	
		
		if( !helper ) {
			
			helper = $('<div id="tooltip"><h3></h3><p class="body"></p><p class="url"></p></div>')
				
				.hide()
				
				.css({ position: 'absolute', zIndex: 3000 })
				
				.appendTo('body');
				
			
			tTitle = $('h3', helper);
			tBody = $('p:eq(0)', helper);
			tUrl = $('p:eq(1)', helper);
		}
		
		
		$(this).filter('[@title]')
			
			.each(function() {
				this.tSettings = settings;
			})
			
			.bind("mouseover", save)
			.bind(settings.event, handle);
		return this;
	};
	
	
	function handle(event) {
		
		if( this.tSettings.delay )
			tID = setTimeout(show, this.tSettings.delay);
		else
			show();
		
		
		if(this.tSettings.track)
			$('body').bind('mousemove', update);
			
		
		update(event);
		
		
		$(this).bind('mouseout', hide);
	}
	
	
	function save() {
		
		if(this == current || !this.title)
			return;
		
		current = this;
		
		var source = $(this),
			settings = this.tSettings;
			
		// save title, remove from element and set to helper
		oldTitle = title = source.attr('title');
		source.attr('title','');
		if(settings.showBody) {
			var parts = title.split(settings.showBody);
			tTitle.html(parts.shift());
			tBody.empty();
			for(var i = 0, part; part = parts[i]; i++) {
				if(i > 0)
					tBody.append("<br/>");
				tBody.append(part);
			}
			if(tBody.html())
				tBody.show();
			else
				tBody.hide();
		} else {
			tTitle.html(title);
			tBody.hide();
		}
		
		
		href = (source.attr('href') || source.attr('src'));
		if( settings.showURL && href )
			tUrl.html(href.replace('http://', '')).show();
		else 
			tUrl.hide();
		
		
		if( settings.extraClass ) {
			helper.addClass(settings.extraClass);
		}
		
		if (settings.fixPNG && $.browser.msie ) {
			helper.each(function () {
				if (this.currentStyle.backgroundImage != 'none') {
					var image = this.currentStyle.backgroundImage;
					image = image.substring(5, image.length - 2);
					$(this).css({
						'backgroundImage': 'none',
						'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
					});
				}
			});
		}
	}
	
	
	function show() {
		tID = null;
		helper.show();
		update();
	}
	
	
	function update(event)	{
		
		if( current == null ) {
			$('body').unbind('mousemove', update);
			return;	
		}
		
		var left = helper[0].offsetLeft;
		var top = helper[0].offsetTop;
		if(event) {
			
			function pos(c) {
				var p = c == 'X' ? 'Left' : 'Top';
				return event['page' + c] || (event['client' + c] + (document.documentElement['scroll' + p] || document.body['scroll' + p])) || 0;
			}
			
			left = pos('X') + 16;
			top = pos('Y') + 0;
			helper.css({
				left: left + 'px',
				top: top + 'px'
			});
		}
		
		var v = viewport(),
			h = helper[0];
		
		if(v.x + v.cx < h.offsetLeft + h.offsetWidth) {
			left -= h.offsetWidth + 20;
			helper.css({left: left + 'px'});
		}
		
		if(v.y + v.cy < h.offsetTop + h.offsetHeight) {
			top -= h.offsetHeight + 20;
			helper.css({top: top + 'px'});
		}
	}
	
	function viewport() {
		var e = document.documentElement || {},
			b = document.body || {},
			w = window;

		return {
			x: w.pageXOffset || e.scrollLeft || b.scrollLeft || 0,
			y: w.pageYOffset || e.scrollTop || b.scrollTop || 0,
			cx: min( e.clientWidth, b.clientWidth, w.innerWidth ),
			cy: min( e.clientHeight, b.clientHeight, w.innerHeight )
		};

		function min() {
			var v = Infinity;
			for( var i = 0;  i < arguments.length;  i++ ) {
				var n = arguments[i];
				if( n && n < v ) v = n;
			}
			return v;
		}
	}
	
	
	function hide() {
		
		if(tID)
			clearTimeout(tID);
		
		current = null;
		helper.hide();
		
		if( this.tSettings.extraClass ) {
			helper.removeClass( this.tSettings.extraClass);
		}
		
		
		$(this)
			.attr('title', oldTitle)
			.unbind('mouseout', hide);
			
		
		if( this.tSettings.fixPNG && $.browser.msie ) {
			helper.each(function () {
				$(this).css({'filter': '', backgroundImage: ''});
			});
		}
	}
	
	
	$.fn.Tooltip.defaults = {
		delay: 250,
		event: "mouseover",
		track: false,
		showURL: true,
		showBody: null,
		extraClass: null,
		fixPNG: false
	};

})(jQuery);
