var Bubbles = 
{
	ajaxCache: [],
	defaultOptions: {
		showEvent: 'mouseover',
		showCallback: null,
		hideCallback: null,
		appearDuration: 0,
		fadeDuration: 0,
		align: 'left',
		valign: 'bottom',
		referencePointAlign:'left',
		referencePointVAlign:'top',
		useCSSPosition: false, //ak tru offsetX a offsetY sa preber z CSS bubliny top a left
		offsetX: 30,
		offsetY: 1
		
	},
	
	//nastavia sa eventy pre zobrazenie a schovanie bublinky pre vsetky prvky danej triedy
	create: function(className, params)
	{
		//pretazia sa defaultne nastavenia
		var tmpOptions = Object.clone(Bubbles.defaultOptions);
		var options = Object.extend(tmpOptions, params);
		$$('.' + className).each(
			function(element)
			{
				if (options.useCSSPosition)
				{
					var bubble = $(element.identify() + '_Bubble');					
					
					bubble.style.visibility = "hidden";
					bubble.show();					
					options.offsetX = bubble.offsetLeft;
					options.offsetY = bubble.offsetTop;
					bubble.style.visibility = "visible";
					bubble.hide();					
				}
				
				element.observe(options.showEvent, Bubbles.show.bind(null, element, options));
				element.onmouseout = Bubbles.invokerOutDelayed.bind(null, element, options);
			}
		)
	},
	
	show: function(invoker, options)
	{
		var bubble = $(invoker.identify() + '_Bubble');
		
		$(invoker).addClassName('over');
		if(bubble)
		{
			var appearEffect = true;
			if(Bubbles.invokerId && Bubbles.invokerId == invoker.identify())
			{	
				return;
			}			
			
			//pri prechode z jedneho invokera na druheho treba skryt bublinku povodneho
			if(Bubbles.invokerId && Bubbles.invokerId != invoker.identify())
			{
						
				var oldBubble = $(Bubbles.invokerId + '_Bubble');
				Bubbles.hideBubble(oldBubble, options, false);
				Bubbles.invokerId = null;
				appearEffect = false;
			}
			
			//ulozi sa novy invoker pre ktoreho sa zobrazuje bublinka
			Bubbles.invokerId = invoker.identify();
			
			//NASTAVENIE POZICIE BUBLINKY
			//vertikalna pozicia
			var referencePointOffsetY;
			var referencePointOffsetX;
			
			if (options.useCSSPosition)
			{
				bubble.style.left = options.offsetX + "px";
				bubble.style.top = options.offsetY + "px";
			
			}
			else
			{
			
				switch(options.referencePointVAlign)
				{
					case 'top':referencePointOffsetY = invoker.offsetTop;
								break;
					case 'bottom':referencePointOffsetY = invoker.offsetTop + invoker.getHeight();
								break;
					case 'middle': referencePointOffsetY = invoker.offsetTop + Math.round(invoker.getHeight() / 2);
									break;
					default:referencePointOffsetY = invoker.offsetTop;
								break;
				}
				
				switch(options.referencePointAlign)
				{
					case 'left':referencePointOffsetX = invoker.offsetLeft;
								break;
					case 'right':referencePointOffsetX = invoker.offsetLeft + invoker.getWidth();
								break;
					case 'middle':referencePointOffsetX = invoker.offsetLeft + Math.round(invoker.getWidth() / 2);
								break;
					default:referencePointOffsetX = invoker.offsetLeft;
								break;
				}
				
				
				var bubbleOffsetX;	
				var bubbleOffsetY;				
				switch (options.valign)
				{
					case 'top': bubbleOffsetY = 0;
								break;
					case 'bottom': bubbleOffsetY = -bubble.getHeight();
								break;
					case 'middle': bubbleOffsetY = - Math.round(bubble.getHeight() / 2);
								break;
					case 'default': bubbleOffsetY = 0;
								break;
				}
					
				switch (options.align)
				{
					case 'left': bubbleOffsetX = 0;
								break;
					case 'right': bubbleOffsetX = - bubble.getWidth();
								break;
					case 'right': bubbleOffsetX = - Math.round(bubble.getWidth() / 2);
								break;
					case 'default': bubbleOffsetX = 0;
								break;
				}				
				
				bubble.style.left = bubbleOffsetX + referencePointOffsetX + options.offsetX + "px";
				bubble.style.top = bubbleOffsetY + referencePointOffsetY + options.offsetY + "px";
			}
			
			
			//zobrazenie bublinky
			Bubbles.displayBubble(bubble, options, appearEffect);
		}
	},
	
	displayBubble: function(bubble, options, appearEffect )
	{
		if(!Bubbles.invokerId)
		{
			//ak nie je nastaveny invoker bublinka sa nemoze zobrazit
			return;
		}
		
		//zobrazenie bublinky
		if(appearEffect)
		{
			bubble.addClassName('animation');
			Effect.Appear(bubble.identify(), {duration: options.appearDuration, afterFinish: Bubbles.animationFinished.bind(null,bubble,options)})
		}
		else
		{
			bubble.show();
		}
		
		//user callback function
		if(options.showCallback) options.showCallback(Bubbles.invokerId);
		
		//eventy bublinky
		bubble.observe('mouseover', Bubbles.bubbleOver.bind(null, bubble, options));
		bubble.onmouseout = Bubbles.bubbleOut.bind(null, bubble, options)
	},
	
	animationFinished: function (bubble, options)
	{
		bubble.removeClassName('animation');
	
	},
		
	//pozdrzanie volania...aby sa bublina neschovala pri prechode z invokera na nu
	invokerOutDelayed: function(invoker, options, event)
	{
		if (!event) var event = window.event;
		var reltg = $((event.relatedTarget) ? event.relatedTarget : event.toElement);

		//postupne sa prachadzaju rodicia cieloveho elementu
		//hlada sa medzi rodicmi invoker
		while (reltg.nodeName != 'BODY')
		{
			if(reltg.identify() == invoker.identify()) return;
			reltg= $(reltg.parentNode);
		}	
		
		Bubbles.invokerOut.delay(0.2, invoker, options);
	},
	
	invokerOut: function(invoker, options)
	{
		
		var bubble = $(invoker.identify() + '_Bubble');
		if(Bubbles.bubbleOverId != bubble.identify() && Bubbles.invokerId == invoker.identify())
		{
			//bublinka sa skryje ak sa mysou opustil invoker a nepreslo sa nad jeho bublinku
			Bubbles.hideBubble(bubble, options, true);
			Bubbles.invokerId = null;
		}
	},
	
	
	hideBubble: function(bubble, options, fadeEffect)
	{
		
		if(bubble)
		{
			if(fadeEffect)
			{
				bubble.addClassName('animation'); 
				Effect.Fade(bubble.identify(), {duration: options.fadeDuration,afterFinish: Bubbles.animationFinished.bind(null,bubble,options)})
			}
			else
			{
				bubble.hide();
			}
			
			if ($(Bubbles.invokerId)) $(Bubbles.invokerId).removeClassName('over');
			if(options.hideCallback) options.hideCallback(Bubbles.invokerId);
			Bubbles.invokerId = null;
		}
	},
	
	//zapamata sa bublinka ked sa mysou nad nu prejde
	bubbleOver: function(bubble, options)
	{
		Bubbles.bubbleOverId = bubble.identify();
	},
	
	
	bubbleOut: function(bubble, options, event)
	{
		if (!event) var event = window.event;
		var reltg = $((event.relatedTarget) ? event.relatedTarget : event.toElement);
      
	  
	    
		
		
		//z bublinky sa preslo spat na jej invokera - toto  nejak nefunguje
	
			if (reltg.identify() == Bubbles.invokerId || $(reltg.parentNode).identify() == Bubbles.invokerId) 
			{
				Bubbles.bubbleOverId = null;
				return;
			}
	
		
		//postupne sa prachadzaju rodicia cieloveho elementu
		//hlada sa medzi rodicmi samotna bublinka
		//tym sa zabrani schovaniu bublinky ak sa mysou chodi po elementoch bublinky
		while (reltg.nodeName != 'BODY')
		{
			if(reltg.identify() == Bubbles.bubbleOverId) return;
			reltg = $(reltg.parentNode);
		}
		Bubbles.bubbleOverId = null;
	
		Bubbles.hideBubble(bubble, options, true);
	}
	
	
	
};
