(function($){

    $.widget("ui.bubble", {
        _init: function(){
            var self = this, o = this.options;
            
            this.tip = null;
			this.offset = null;
            this.showDelayTimer = null;
            this.hideDelayTimer = null;
            this.beingShown = false;
            this.shown = false;
            
			var show = function(e){
                if (self.showDelayTimer) 
                    clearTimeout(self.showDelayTimer);
                if (self.hideDelayTimer) 
                    clearTimeout(self.hideDelayTimer);
                
                if (self.beingShown || self.shown) {
                    return;
                }
                else {
                    self.showDelayTimer = setTimeout(function(){
                        var target = self.element.closest(o.selector);
                        self.beingShown = true;
                        self.tip = $('<div class="ui-bubble ui-widget ui-widget-content"/>')
							.addClass(o.bubbleClass)
							.css(o.bubbleStyle)
							.html('<span>' + o.text + '</span>')
							.hide()
							.appendTo(target);
						
						var targetOffset = target.offset();
						
                        self.tip
							.show(1, function(){
								var $this = $(this);
								var textWidth = $('span', $this).innerWidth();
								if (textWidth > $this.innerWidth()){
			                        $this
										.css('width', textWidth);
								}
								
								self.offset = self._getOffset();
								$this
									.css('top', self.offset.top - targetOffset.top)
									.css('left', self.offset.left - targetOffset.left)
									.animate({
		    	                        top: self.offset.position.match(/^t/)? '-=3px' : '+=3px',
		        	                    opacity: o.opacity
		            	            }, 250, 'swing', function(){
			                            // once the animation is complete, set the tracker variables
		    	                        self.beingShown = false;
		        	                    self.shown = true;
		            	            });
							});
                    }, 500);
                }
			};
			
			var hide = function(e){
                if (self.showDelayTimer) 
                    clearTimeout(self.showDelayTimer);
                if (self.hideDelayTimer) 
                    clearTimeout(self.hideDelayTimer);
                
                // store the timer so that it can be cleared in the mouseover if required
                self.hideDelayTimer = setTimeout(function(){
                    if (self.tip) 
                        self.tip.animate({
                            top: self.offset.position.match(/^t/)? '+=3px' : '-=3px',
                            opacity: 0
                        }, 250, 'swing', function(){
                            self.shown = false;
                            $(this).remove();
                            self.tip = null;
							self.offset = null;
                        });
                }, 50);
            };
			
            this.element.hover(show, hide);
			this.element.click(hide);
        },
        
        setText: function(text){
            this.options.text = text;
        },
		
		_getOffset: function() {
			var o = this.options;
			
			var element = this.element;
			var elementOffset = element.offset();
			var elementHeight = element.outerHeight();
			var elementWidth = element.outerWidth();
			
			var tip = this.tip;
			// above position
			var tipOffset = {
				top: elementOffset.top - tip.outerHeight(),
				left: elementOffset.left + (element.outerWidth() - tip.outerWidth()) / 2
			};
			var tipHeight = tip.outerHeight();
			var tipWidth = tip.outerWidth();

			var target = o.target? this.element.closest(o.target) : null;			
			var targetOffset = null;

			if (!target) {
				target = $(window);
				targetOffset = {
					top: target.scrollTop(),
					left: target.scrollLeft()
				};
			}
			else {
				targetOffset = target.offset();
			}
			var targetHeight = target.height();
			var targetWidth = target.width();
			
			var position = '';
			if (tipOffset.top  < targetOffset.top){
				if (tipOffset.left < targetOffset.left){
					position = 'b-r';
				}
				else if (tipOffset.left + tipWidth > targetOffset.left + targetWidth) {
					position = 'b-l';
				}
				else {
					position = 'b'
				}
			}
			else if (tipOffset.top + tipHeight > targetOffset.top + targetHeight){
				if (tipOffset.left < targetOffset.left){
					position = 't-r';
				}
				else if (tipOffset.left + tipWidth > targetOffset.left + targetWidth) {
					position = 't-l';
				}
				else {
					position = 't';
				}
			}
			else {
				if (tipOffset.left < targetOffset.left){
					position = 't-r';
				}
				else if (tipOffset.left + tipWidth > targetOffset.left + targetWidth){
					position = 't-l';
				}
				else {
					position = 't';
				}
			}
			
			var offset = {position: position};
			if (position.match(/^t/)){
				offset.top = tipOffset.top;
			}
			else {
				offset.top = elementOffset.top + elementHeight;
			}
			
			if (position == 't' || position == 'b'){
				offset.left = tipOffset.left;	
			}
			else if (position.match(/-r$/)){
				offset.left = targetOffset.left + 5; //elementOffset.left + elementWidth;
			}
			else { // this is /-l$/
				offset.left = targetOffset.left + targetWidth - (tipWidth + 5);// elementOffset.left - tipWidth;
			}
			
			return offset;
		}
    });
    
    $.ui.bubble.defaults = {
        text: 'Default text',
        bubbleClass: 'bubble',
		bubbleSkinClass: 'bubble-skin',
		bubbleStyle: {position: 'absolute'},
        selector: 'body',
		opacity: 1
    };
	
	$.fn.addBubble = function(bubbleText, selector){
		if (bubbleText){
			this.bubble({
				text: bubbleText,
				bubbleClass: 'ui-corner-all', 
				//bubbleStyle: style,
				selector: selector,
				target: selector
			});
		}
		
		return this;
	};
}(jQuery));
