if (!window.Tips) var Tips = { };

Object.extend(Tips, 
{
	tips:			new Hash(),
	template:		null,
	body:			null,
	
	exists: function(id)
	{
		return(this.tips.get(id));
	},
	
	add: function(id, tip)
	{
		this.tips.set(id, tip);
	},
	
	createTipElement: function(id)
	{
		if(!this.body)
		{
			this.body = $$('body').first();
		}
		
		if(!this.template)
		{
			var element = $('tip');
			
			if(!element)
			{
				this.body.insert("<table id='tip' style='font-size:11px; background-color:ffff88; font-family: arial,helvetica; border:1px solid black'><tr><td class=\"tip_content\"></td></tr></table>");			
				
				element = $('tip');
			}
			
			element.hide();
			element.setStyle(
			{
				position: 'absolute',
				zIndex: '500'
			});
			
			this.template = element.wrap('tip').innerHTML;
		}
		
		var i = this.template.indexOf('tip');
		
		var html = this.template.substring(0, i) + id + this.template.substring(i + 3)
		
		this.body.insert(html);
		
		return $(id);			
	}
});

var Tip = Class.create(
{
	initialize: function(element, content, options)
	{
		this.element = $(element);
		
		if(!this.element)
		{
			return;
		}
		
		this.name = this.element.identify();
		
		if(Tips.exists(this.name))
		{
			return;
		}
		
		this.options = {
			delay:		700,
			maxWidth:	200,
			hideOn:		'out',
			decay:		5000
		};
		
		Object.extend(this.options, options || { });

		// --------
		
		this.tipElement = Tips.createTipElement("tip_" + this.name);

		this.tipElement.setStyle({cursor : 'default'});

		var descendants = this.tipElement.descendants();
	
		for(var i = 0; i < descendants.length; i++)
		{
			if(descendants[i].hasClassName('tip_content'))
			{
				if(Object.isElement(content))
				{
					content.show();
				}		
				
				descendants[i].update(content);
				
				this.contentElement = descendants[i];
			}
			else if(descendants[i].hasClassName('tip_top'))
			{
				this.topElement = descendants[i];
			}
			else if(descendants[i].hasClassName('tip_bottom'))
			{
				this.bottomElement = descendants[i];
			}
		}
		
		// --------
		
		this.element.observe('mouseover',	this.mouseOver.bindAsEventListener(this));
		this.element.observe('mouseout',	this.mouseOut.bindAsEventListener(this));
		this.element.observe('mousedown',	this.hide.bind(this));
		this.element.observe('keydown',		this.hide.bind(this));
		
		if(this.options.hideOn == 'click')
		{
			this.tipElement.observe('click', this.hide.bind(this));
			
			this.tipElement.setStyle(
			{
				zIndex: '400'
			});
		}
		
		// --------
		
		Tips.add(this.name, this);
	},
	
	mouseOver: function(event)
	{
		if(this.hiding)
		{
			window.clearTimeout(this.hiding);
			
			this.hiding = null;
		}
			
		if(this.showing == null)
		{
			this.showing = window.setTimeout(this.show.bind(this), this.options.delay);
		}				
	},
	
	mouseOut: function(event)
	{
		if(this.showing)
		{
			window.clearTimeout(this.showing);
			
			this.showing = null;
		}

		if((this.options.hideOn == 'out' || this.options.hideOn == 'decay') && this.hiding == null)
		{
			this.hiding = window.setTimeout(this.hide.bind(this), 100);
		}
	},
	
	show: function()
	{
		if(this.showing)
		{
			window.clearTimeout(this.showing);
		}
		
		this.showing = null;
		
		// ------------------
		
		if(this.options.hideOn == 'decay' && this.decaying == null)
		{
			this.decaying = window.setTimeout(this.hide.bind(this), this.options.decay);
		}
		
		// ------------------
		
		if(this.topElement)
		{
			this.topElement.show();
		}
		
		if(this.bottomElement)
		{
			this.bottomElement.hide();
		}
		
		// -----------------

		var viewPortBounds = document.viewport.getBounds();
	
		var elementDimensions = this.element.getDimensions();
			
		var elementPosition = this.element.positionedOffset();

		// ------------------
		
		if(this.tipDimensions == null)
		{
			this.tipElement.show();
			
			if(this.tipElement.getWidth() > this.options.maxWidth)
			{
				this.tipElement.setStyle({width: this.options.maxWidth});
			}

			var contentPosition = this.contentElement.positionedOffset();
			
			var contentDimensions = this.contentElement.getDimensions();
			
			this.tipDimensions = this.tipElement.getDimensions();

			this.leftWidth = contentPosition.left;
			
			this.contentWidth = contentDimensions.width;
			
			this.rightWidth = this.tipDimensions.width - this.leftWidth - contentDimensions.width;
			
			this.pointerOffset = 0;
			
			if(this.topElement)
			{
				this.pointerOffset = this.topElement.getDimensions().width / 2;
			}
		}
		
		var tipPosition =
		{
			left:	elementPosition.left + (elementDimensions.width / 2) - this.pointerOffset - this.leftWidth,
			top:	elementPosition.top + elementDimensions.height + 4
		};
		
		if(this.tipDimensions.width < elementDimensions.width)
		{
			tipPosition =
			{
				left:	elementPosition.left + (elementDimensions.width / 2) - (this.contentWidth / 2) - this.leftWidth,
				top:	elementPosition.top + elementDimensions.height + 4
			};
			
			if(this.topElement)
			{
				this.topElement.up().setStyle({textAlign: 'center'});
			}
			
			if(this.bottomElement)
			{
				this.bottomElement.up().setStyle({textAlign: 'center'});
			}
		}
		
		this.tipElement.setStyle(tipPosition);

		if(tipPosition.top + this.tipDimensions.height > viewPortBounds.bottom)
		{
			this.tipElement.setStyle({top: elementPosition.top - this.tipDimensions.height - 4});
			
			if(this.topElement)
			{
				this.topElement.hide();
			}
			
			if(this.bottomElement)
			{
				this.bottomElement.show();
			}
		}
		
		if((tipPosition.left + this.tipDimensions.width) > viewPortBounds.right)
		{
			var left = viewPortBounds.right - this.tipDimensions.width;
			
			if(left < viewPortBounds.left)
			{
				left = viewPortBounds.left;
			}
			
			this.tipElement.setStyle({left: left});
			
			if(this.topElement)
			{
				this.topElement.up().setStyle({textAlign: 'right'});
			}
			
			if(this.bottomElement)
			{
				this.bottomElement.up().setStyle({textAlign: 'right'});
			}
		}
		
		this.tipElement.show();
	},
	
	hide: function()
	{
		if(this.hiding)
		{
			window.clearTimeout(this.hiding);
		}
		
		this.hiding = null;
		
		if(this.decaying)
		{
			window.clearTimeout(this.decaying);
		}
		
		this.decaying = null;

		this.tipElement.hide();
	}
});

