;(function($) {
	var $jtip, $jtipInner, $jtipContainer;
	$.fn.jtip = function(js, options) {
		if (typeof js == 'object') {
			options = js;
			js = null;
		}
		return this.each(function() {
			var $t = $(this);

			var opts = $.extend(true, {}, $.fn.jtip.defaults, options || {});
			var isActive = false, closeOnDelay = 0;

			if (!$('#jtip').length) {
				$jtip = $( '<div id="jtip" class="' + opts.jtipClass + '-l"></div>' ).appendTo(document.body).hide();
				$jtipInner = $( '<div class="' + opts.jtipClass + '-a"></div>' ).appendTo($jtip);
				$( '<div class="' + opts.jtipClass + '-b"></div>' ).appendTo($jtip);
				$jtipContainer = $( '<p></p>' ).appendTo($jtipInner);
			}

			var tOffset = parseInt(opts.topOffset, 10), lOffset = parseInt(opts.leftOffset, 10);

			var tipHeight, wHeight;
			var defHeight = isNaN(parseInt(opts.height, 10)) ? 'auto' : (/\D/g).test(opts.height) ? opts.height : opts.height + 'px';
			var sTop, elTop, posY, tipY, mouseY, baseline;  // ?????????????

			var tipInnerWidth = isNaN(parseInt(opts.width, 10)) ? 326 : parseInt(opts.width, 10);
			var tipWidth = tipInnerWidth + (parseInt($jtip.css('paddingLeft'),10)||0) + (parseInt($jtip.css('paddingRight'),10)||0);
			var elWidth = this.offsetWidth;
			var elLeft, posX, tipX, mouseX, winWidth, fromTitle=false;	//???????????????

			var activate = function(event) {
				if (!opts.onActivate($t)) {
					return false;
				}
				isActive = true;
				elTop = posY = $t.offset().top;
				elLeft = $t.offset().left;
				mouseX = event.pageX;
				mouseY = event.pageY;
				if ($t[0].tagName.toLowerCase() != 'area') {
					sTop = $(document).scrollTop();
					winWidth = $(window).width();
				}

				if (opts.positionBy == 'fixed') {
					posX = elWidth + elLeft + lOffset;
					if( (elWidth > elLeft && elLeft > tipWidth)
						 || ( elLeft + elWidth + tipWidth + lOffset > winWidth ) ){
						posX = elLeft - tipWidth - lOffset
						$jtip.removeClass( opts.jtipClass + '-l' ).addClass( opts.jtipClass + '-r' );
					} else {
						$jtip.removeClass( opts.jtipClass + '-r' ).addClass( opts.jtipClass + '-l' );
					}
					$jtip.css({left: posX});
				} else {
					posX = elWidth + elLeft + lOffset;
					if( (elWidth > elLeft && elLeft > tipWidth)
						 || ( elLeft + elWidth + tipWidth + lOffset > winWidth ) ){
						posX = elLeft - tipWidth - lOffset
						$jtip.removeClass( opts.jtipClass + '-l' ).addClass( opts.jtipClass + '-r' );
					} else {
						$jtip.removeClass( opts.jtipClass + '-r' ).addClass( opts.jtipClass + '-l' );
					}
					if ( opts.positionBy == 'mouse' || elWidth + tipWidth > winWidth) { // position by mouse
						if (mouseX + 20 + tipWidth > winWidth) {
						posX = (mouseX - tipWidth - lOffset) >= 0 ? mouseX - tipWidth - lOffset - parseInt($jtip.css('marginLeft'),10) + parseInt($jtip.css('marginRight'),10) :  mouseX - (tipWidth/2);
						} else {
							posX = mouseX + lOffset;
						}
					}
					var pY = posX < 0 ? event.pageY + tOffset : event.pageY;
					$jtip.css({left: (posX > 0 && opts.positionBy != 'bottomTop') ? posX : (mouseX + (tipWidth/2) > winWidth) ? winWidth/2 - tipWidth/2 : Math.max(mouseX - (tipWidth/2),0)});
				}
				wHeight = $(window).height();

				if (js) {
					$jtipContainer.empty();
					if (typeof js == 'function') {
						js = js($t[0]);
					}
					$jtipContainer.html(js);
					jtipShow(pY);
				} else if( opts.content ) {
					$jtipContainer.empty();
					$jtipContainer.html( opts.content );
					jtipShow(pY);
				} else {
					$jtipContainer.empty();
					$jtipContainer.html( $t.attr('title') );
					$t.attr( 'title', '' );
					fromTitle=true;
					jtipShow(pY);
				}


			};

			var jtipShow = function(bpY) {
				if (opts.truncate) {
					var $truncloaded = $jtipContainer.text().slice(0,opts.truncate) + '...';
					$jtipContainer.html($truncloaded);
				}

				var direction = '';
				tipHeight = defHeight == 'auto' ? Math.max($jtip.outerHeight(),$jtip.height()) : parseInt(defHeight,10);
				tipY = posY;
				baseline = sTop + wHeight;
				if (opts.positionBy == 'fixed') {
					tipY = posY + tOffset;
				} else if ( (posX < mouseX && Math.max(posX, 0) + tipWidth > mouseX) || opts.positionBy == 'bottomTop') {
					if (posY + tipHeight + tOffset > baseline && mouseY - sTop > tipHeight + tOffset) {
						tipY = mouseY - tipHeight - tOffset;
						direction = 'top';
					} else {
						tipY = mouseY + tOffset;
						direction = 'bottom';
					}
				} else if ( posY + tipHeight + tOffset > baseline ) {
					tipY = (tipHeight >= wHeight) ? sTop : baseline - tipHeight - tOffset;
				} else if ($t.css('display') == 'block' || opts.positionBy == "mouse") {
					tipY = bpY - tOffset;
				} else {
					tipY = posY;
				}
				if (direction == '') {
					direction = posX < elLeft ? 'left' : 'right';
				}
				$jtip.css({top: tipY + 'px'});

				$jtip.hide()[opts.fx.open](opts.fx.open != 'show' && opts.fx.openSpeed);
				if (opts.delayedClose > 0) {
					closeOnDelay = setTimeout(jtipClose, opts.delayedClose);
				}
				opts.onShow($jtip, $jtipContainer);

			};

			var inactivate = function(event) {
				isActive = false;
				//if ( (/click|toggle/).test(opts.activation) ) {
					jtipClose();
					clearTimeout(closeOnDelay);
				//};

			};

			var jtipClose = function() {
				$jtip.hide();
				opts.onHide($jtip, $jtipContainer);
				if( fromTitle ){
					$t.attr( 'title', $jtipContainer.html() );
				}
			};


			if ( (/click|toggle/).test(opts.activation) ) {
				$t.click(function(event) {
					if ($jtip.is(':hidden')) {
						activate(event);
					} else {
						inactivate(event);
					}
					this.blur();
					return false;
				});
			} else if (opts.activation == 'focus') {
				$t.focus(function(event) {
					activate(event);
				});
				$t.blur(function(event) {
					inactivate(event);
				});
			} else {
				//set up mouse tracking
				var mouseTracks = function(evt) {
					if (opts.tracking == true) {
						var trackX = posX - evt.pageX;
						var trackY = tipY ? tipY - evt.pageY : posY - evt.pageY;
						$t.mousemove(function(evt) {
							$jtip.css({left: evt.pageX + trackX, top: evt.pageY + trackY });
						});
					}
				};

				$t.hover(function(event) {
					activate(event);
					mouseTracks(event);
				}, function(event) {
					inactivate(event);
					$t.unbind('mousemove');
				});
			}

		});

	};

	$.fn.jtip.defaults = {
		jtipClass: 'pride',
		content: '',
		positionBy: 'fixed',
		topOffset:  -10,
		leftOffset: 5,

		activation: 'hover',
		delayedClose: 0,
		truncate: 0,
		tracking: false,

		fx: {
			open:		'show', // can be 'show' or 'slideDown' or 'fadeIn'
			openSpeed: ''
		},

		onActivate: function(e) {return true;},
		onShow: function(jt, c){},
		onHide: function(jt, c){}

	};

})(jQuery);
