var Bubbles = 
{
	ajaxCache: [],
	defaultOptions: {
		showEvent: 'mouseover',
		showCallback: null,
		hideCallback: null,
		appearDuration: 0,
		fadeDuration: 0,
		align: 'left',
		offsetX: 0,
		offsetY: 0,
		
		//ajaxove naplnanie bublinky
		ajaxUse: false,
		ajaxScript: null,
		ajaxBubbleId: null, //kontainer ktory sa ma naplnit
		ajaxInvokerIdPrefix: null //id linky ktora vyvola ajax sa sklada z prefixu a id, ktore sa posle ajaxom
	},
	
	//nastavia sa eventy pre zobrazenie a schovanie bublinky pre vsetky prvky danej triedy
	create: function(className, params)
	{
		//pretazia sa defaultne nastavenia
		var options = Object.extend(Bubbles.defaultOptions, params);
		$$('.' + className).each(
			function(element)
			{
				element.observe(options.showEvent, Bubbles.show.bind(null, element, options));
				element.onmouseout = Bubbles.invokerOutDelayed.bind(null, element, options);
			}
		)
	},
	
	show: function(invoker, options)
	{
		var bubble = options.ajaxUse ? $(options.ajaxBubbleId) : $(invoker.identify() + 'Bubble');
		if(bubble)
		{
			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 bubble = options.ajaxUse ? $(options.ajaxBubbleId) : $(invoker.identify() + 'Bubble');
				Bubbles.hideBubble(bubble, options);
				Bubbles.invokerId = null;
			}
			
			//ulozi sa novy invoker pre ktoreho sa zobrazuje bublinka
			Bubbles.invokerId = invoker.identify();
			
			//NASTAVENIE POZICIE BUBLINKY
			//vertikalna pozicia
			bubble.style.top = invoker.offsetTop + invoker.getHeight() + options.offsetY + "px";	
			//horizontalna pozicia
			var left = invoker.offsetLeft + options.offsetX;	
			if(options.align == 'right')
			{
				left -= bubble.getWidth() - invoker.getWidth();
			}
			if(options.align == 'middle')
			{
				left -= Math.round((bubble.getWidth() - invoker.getWidth()) / 2);
			}
			bubble.style.left = left + "px";
			

			//ajaxove naplnenie bublinky
			if(options.ajaxUse)
			{
				//do ajaxoveho volania sa posle id, ktore sa vyparsuje z id-ivokera
				var id = invoker.identify().replace(options.ajaxInvokerIdPrefix, '');
				
				//ajaxove volanie este nie je v cache
				if(!Bubbles.ajaxCache[id])
				{
					ajax(options.ajaxScript, {id: id}, null, Bubbles.ajaxOnSuccess.bind(null, id, options));
					return;
				}
				
				//ajaxove volanie vratilo 0 - nulovy vystup - bublinka sa nezobrazuje
				else if(Bubbles.ajaxCache[id] == '0')
				{
					return;
				}
				
				//pouzije sa cache
				else
				{
					bubble.update(Bubbles.ajaxCache[id]);
				}
			}
			
			//zobrazenie bublinky
			Bubbles.displayBubble(bubble, options);
		}
	},
	
	displayBubble: function(bubble, options)
	{
		if(!Bubbles.invokerId)
		{
			//ak nie je nastaveny invoker bublinka sa nemoze zobrazit
			return;
		}
		
		//zobrazenie bublinky
		if(options.appearDuration > 0)
		{
			Effect.Appear(bubble.identify(), {duration: options.appearDuration})
		}
		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)
	},
	
	ajaxOnSuccess: function(id, options, transport)
	{
		var bubble = $(options.ajaxBubbleId);
		Bubbles.ajaxCache[id] = transport.responseText;
		
		if(transport.responseText != '0')
		{
			bubble.update(transport.responseText);
			Bubbles.displayBubble(bubble, options);
		}
	},
	
	//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.1, invoker, options);
	},
	
	invokerOut: function(invoker, options)
	{
		var bubble = options.ajaxUse ? $(options.ajaxBubbleId) : $(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);
			Bubbles.invokerId = null;
		}
	},
	
	
	hideBubble: function(bubble, options)
	{
		if(bubble)
		{
			if(options.fadeDuration > 0)
			{
				Effect.Fade(bubble.identify(), {duration: options.fadeDuration})
			}
			else
			{
				bubble.hide();
			}
			
			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
		if (reltg.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);
	}
};
