function XBalloon()
{
    //=========================================
    //Public variables
	//=========================================
    
    var self=this;
  
    //Properties
    self.id=null;
    
	self.width=200;
	self.imagesDir="";
    
    self.title=null;
    self.showCloseButton=true;
    self.contentHtml=null;
    self.contentDivID=null;
    
    self.posX=null;
    self.posY=null;
    self.direction=null;
   
    self.autoHide=false;
	self.autoAway=false;
	
	self.autoHideInterval=8000;
			
    //Transitions (for IE)
    self.transShow=false;
	self.transHide=false;
	self.transShowFilter='progid:DXImageTransform.Microsoft.Fade(Overlap=1.00)';
	self.transHideFilter='progid:DXImageTransform.Microsoft.Fade(Overlap=1.00)';
	
	//Used only if contentDivID was provided
    self.allowContentClone=true;
    
    //Events
    self.onClose=null;
    
    //Need timer as property to use on current object
    self.timerHide=null;
    
    
  
    //=========================================
    //Internal variables
	//=========================================

    var _isIE = document.all ? true : false;
	var _childID;
	var _balloonDiv;

   
		
	//=========================================
    //Public methods
	//=========================================
   
	self.isVisible = function()
	{
	    if (_balloonDiv && (_balloonDiv.style.visibility!='hidden'))
		   	return true;
		else
	        return false;
	}
	
		
		
	//private functions used in XBalloon.show
	function initBalloonDiv()
	{
	    _balloonDiv= document.createElement("DIV");
	    
	    if (self.id)
	    {
	        var balloonDivID="balloonDiv_" + self.id;
    	    
	        if (document.getElementById(balloonDivID))
	             throw new Error ("XBalloon.initBalloonDiv div '" + balloonDivID + "' already exists");
   
	        _balloonDiv.id=balloonDivID;
        }
        
        _balloonDiv.style.position="absolute";
        _balloonDiv.style.visibility="hidden";
        _balloonDiv.style.zIndex=2001;
 	    _balloonDiv.style.width=self.width + "px";
	   
	    _childID=document.forms[0].appendChild(_balloonDiv);
	
	    TRACE("XBalloon.initBalloonDiv _childID=" + _childID + ", _balloonDiv.id=" + _balloonDiv.id);
	}
		
		
    
    function preRenderBalloon()
	{
	    TRACE("XBalloon.preRenderBalloon");
	
        // Assume SE
		if (!self.direction)
		    self.direction=getBalloonDirection();
		
		TRACE("XBalloon.preRenderBalloon self.direction=" + self.direction + ", document.getElementById(self.contentDivID)=" + document.getElementById(self.contentDivID));
	
		genBalloonHtml();
		
		var left=parseInt(_balloonDiv.style.left);
		var top=parseInt(_balloonDiv.style.top);
		  
		
		//East
		if (self.direction=="SE" || self.direction=="NE")
		   _balloonDiv.style.left = (left - 18) + "px";

        //West  	
		if (self.direction=="SW" || self.direction=="NW")
		    _balloonDiv.style.left = (left - _balloonDiv.offsetWidth + 15) + "px" ;
		
		//North
		if (self.direction=="NE" || self.direction=="NW")
		   _balloonDiv.style.top = (top + _balloonDiv.offsetHeight) + "px";

	}	
	
	
	function showBalloon()
	{
	    TRACE("XBalloon.showBalloon  left=" + _balloonDiv.style.left + "; top=" + _balloonDiv.style.top);

	    // Hide any overlapping selects
		//suppressSelects();
		
				
		// Show balloon
		if(_isIE && self.transShow)
		{
			_balloonDiv.style.filter=self.transShowFilter;
			TRACE("XBalloon.showBalloon _balloonDiv.style.filter=" + _balloonDiv.style.filter);
			
			if(_balloonDiv.filters(0).status==2 || _balloonDiv.filters(0).status==1)
			{
				_balloonDiv.filters(0).Stop();
			}
			_balloonDiv.filters(0).Apply();
			_balloonDiv.style.visibility='visible';
			_balloonDiv.filters(0).Play();
		}
		else
		{
			_balloonDiv.style.visibility='visible';
		}
					
		// Init autohide if true
		if(self.autoHide)
		{
			clearTimeout(self.timerHide);
			self.timerHide=setTimeout(self.hide, self.autoHideInterval);
		}
		
		if(self.autoAway)
			_balloonDiv.onmouseover=self.hide;
		else
			_balloonDiv.onmouseover='';
		
	}
	
	
		
	self.show = function()
	{
        TRACE("XBalloon.show");
    
        if (self.isVisible())
            throw new Error ("XBalloon.show  balloon '" + self.id  + "' is already visible");
        
        if ((!self.posX) || (!self.posY))
            throw new Error ("XBalloon.show posX and posY properties were not specified.");
        

        initBalloonDiv();
       
       
        preRenderBalloon();
        		
		showBalloon();
		
		g_xBalloonManager.addBalloon(self);
	}
	
		
	
	self.hide =function()
	{
	    TRACE("XBalloon.hide");
	    
		if(!self.isVisible)
		    throw new Error ("XBalloon.show  balloon '" + self.id  + "' is already hidden");
           

		if(_isIE && self.transHide)
		{
		    _balloonDiv.style.filter=self.transHideFilter;
		    
		    if(_balloonDiv.filters(0).status==2 || _balloonDiv.filters(0).status==1)
			{
				_balloonDiv.filters(0).Stop();
			}
			
			_balloonDiv.filters(0).Apply();
			_balloonDiv.style.visibility='hidden';
			_balloonDiv.filters(0).Play();
		}
		else
		{
			_balloonDiv.style.visibility='hidden';
		}	
		
		//restoreSelects();
				
		g_xBalloonManager.deleteBalloon(self);
	}
	
	
    self.onCloseButtonClick = function()
    {
        if (self.onClose!=null)
            self.onClose(self);
            
        self.hide();
    }
	
	//=========================================
    //Private methods
	//=========================================
   
	function suppressSelects()
	{
		var selObjects=document.getElementsByTagName("SELECT");
		
		for(var i=0;i<selObjects.length;i++)
		{	
			if(selObjects[i].balloonMember!='true')
			{
				if(selObjects[i].style.visibility=='visible' || selObjects[i].style.visibility=='')
				{
					if(objectOverlap(_balloonDiv, selObjects[i]))
					{
						selObjects[i].style.visibility='hidden';
						
						// Mark as ballonhidden
						selObjects[i].baloonHidden=true;
					}
				}
			}
		}
	}
	
	function restoreSelects()
	{
		var selObjects=document.getElementsByTagName("SELECT");
		
		for(var i=0;i<selObjects.length;i++)
		{	
			if(selObjects[i].baloonHidden)
			{
				selObjects[i].style.visibility='visible';
				// Mark as ballonhidden
				selObjects[i].baloonHidden=false;
			}
		}
	}
	
	
	
	function objectOverlap(obj1, obj2)
	{
		var obj1TopX = getLeft(obj1);
		var obj1TopY = getTop(obj1);
		var obj1BottomX = getLeft(obj1)+obj1.offsetWidth;
		var obj1BottomY = getTop(obj1)+obj1.offsetHeight;
		
		var obj2TopX = getLeft(obj2);
		var obj2TopY = getTop(obj2);
		var obj2BottomX = getLeft(obj2)+obj2.offsetWidth;
		var obj2BottomY = getTop(obj2)+obj2.offsetHeight;
		
		var overlapOnX = (obj1TopX < obj2BottomX && obj2TopX < obj1BottomX);
		var overlapOnY = (obj1TopY < obj2BottomY && obj2TopY < obj1BottomY);
		
		return (overlapOnX && overlapOnY);
	}



	//Positioning functions
	function getLeft(obj) 
	{
        return(obj.offsetParent ? (getLeft(obj.offsetParent) + obj.offsetLeft) : obj.offsetLeft);
	}
		 
		 
	function getTop(obj) 
	{
	    return(obj.offsetParent ? (getTop(obj.offsetParent) + obj.offsetTop) : obj.offsetTop); 
	}
		 
		 
		 

	//Rendering functions
    //Function gets called from show 
    
	function getBalloonDirection()
	{
	    genBalloonHtml("SE");
			 	   				
		TRACE("XBalloon.getBalloonDirection self.posX=" + self.posX + "; self.top=" + self.posY);
		
		var body = document.documentElement;
    		
        //Account for scrolling
	    var scrollTop = parseInt(body.scrollTop);
	    var scrollLeft = parseInt(body.scrollLeft);
    	    	
    	TRACE("XBalloon.getBalloonDirection  scrollTop=" + scrollTop  + "; scrollLeft=" + body.scrollLeft);
		
		var left=self.posX;
		var top=(self.posY - _balloonDiv.offsetHeight);
		
		_balloonDiv.style.left = left + "px";
		_balloonDiv.style.top =  top + "px";		
    	
	    TRACE("XBalloon.getBalloonDirection left=" + left + ", top=" + top);
		
		//Take scrolling into account
		var isEast=true;
		
		if((document.body.offsetWidth + scrollLeft) < (left + _balloonDiv.offsetWidth + 20))
		{		
		    //Balloon doesn't fit to the right
			isEast=false;
		}
		
		var direction;
		
		if(parseInt(top) < scrollTop)
		{
			if(isEast)
				direction='NE';
			else
				direction='NW';
		}		
		else
		{
			if(isEast)
				direction='SE';
			else
				direction='SW';
		}
		
			
		TRACE("XBalloon.getBalloonDirection direction=" + direction);
		return direction;
	}	
	

    function addContentHeader(content)
    {
        if ((self.title==null) && (!self.showCloseButton))
            return content;
      
        var newContent="";
        newContent=newContent + '<table width="100%" cellpadding="0" cellspacing="0" border="0">';
        newContent=newContent + '<tr>';
        
        var colspan=0;
        
        if (self.title!=null)
        {
            newContent=newContent + '<td><span class="title">' +  self.title + '</span></td>';        
            colspan++;
        }
        
        if (self.showCloseButton)
        {
            var closeImage='<img id="imgClose" src="' + self.imagesDir + '/close.jpg"' +
                           ' onmouseover="this.src=\'' + self.imagesDir + '/closeActive.jpg\'"' +
                           ' onmouseout="this.src=\'' + self.imagesDir + '/close.jpg\'"' +
                           ' onmouseup="this.src=\''+ self.imagesDir + '/closeActive.jpg\'"'+
                           ' onmousedown="this.src=\'' + self.imagesDir + '/closeDown.jpg\'"' +
                           ' title="Close">';
        
            newContent=newContent + '<td align="right" valign="top">' +  closeImage + '</td>';        
            colspan++;
        }
        
        
        newContent=newContent + '</tr>';
        newContent=newContent + '<tr><td colspan="' + colspan + '">' +  content + '</td></tr>';       

        newContent=newContent + '</table>';
        
        return newContent;

    }
    

	function genBalloonHtml(direction)
	{
	    if (!direction)
	        direction=self.direction;   
	
	    TRACE("XBalloon.genBalloonHtml self.direction=" + self.direction + ", self.direction=" + self.direction) ;
	 	
		var html="";
		var content="";
		
		var useContentDiv=((self.contentDivID!=null) && (self.contentDivID!=""))
		
		if (useContentDiv)
		    content='<div id="divContentPlaceholder"></div>';
		else
		    content='<div class="content">' + self.contentHtml + '<div>';
		    
				
	    //Add title and close button
	    //Body includes header and content
	    var body;
	    body=addContentHeader(content);
	    	
		switch(direction)
		{
			case 'SE':
				// South East	
				html ='<table class="XBalloon" border="0" cellpadding="0" cellspacing="0" >'+
					'  <tr>'+
					'    <td height="1" width="10">'+
					'    <img border="0" src="' + self.imagesDir + '/cLeftTop.gif" width="10" height="10"></td>'+
					'    <td height="7" colspan="4" width="100%" class="south_border_top" ></td>'+
					'    <td height="7"  width="10"><img border="0" src="' + self.imagesDir + '/cRightTop.gif" width="10" height="10"></td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td valign=top colspan="6" class="body">'+
					body +
					'    </td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td width="10" height="7">'+
					'    <img border="0" src="' + self.imagesDir + '/cLeftBottom.gif" width="10" height="10"></td>'+
					'    <td height="7" colspan="4" style="background-color: #FFFFEA"  width="280"></td>'+
					'    <td width="10" height="7"><img border="0" src="' + self.imagesDir + '/cRightBottom.gif" width="10" height="10"></td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td width="10" height="10"></td>'+
					'    <td width="1" class="south_border_bottom" height="10"></td>'+
					'    <td height="10"><img border="0" src="'+ self.imagesDir + '/aSouthEast.gif" width="67" height="18"></td>'+
					'    <td width="100%" height="10" class="south_border_bottom"></td>'+
					'    <td width="70" height="10" class="south_border_bottom"></td>'+
					'    <td width="10" height="10"></td>'+
					'  </tr>'+
					'</table>'
					break;

			case 'SW':					
				// South West
				html ='<table class="XBalloon" border="0" cellpadding="0" cellspacing="0">'+
					'  <tr>'+
					'    <td height="1" width="10">'+
					'    <img border="0" src="' + self.imagesDir + '/cLeftTop.gif" width="10" height="10"></td>'+
					'    <td height="7" width="179" class="south_border_top" colspan="4"></td>'+
					'    <td height="7"  width="11">'+
					'    <img border="0" src="' + self.imagesDir + '/cRightTop.gif" width="10" height="10"></td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td valign=top colspan="6" class="body">'+
					body +
					'    </td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td width="10" height="7">'+
					'    <img border="0" src="' + self.imagesDir + '/cLeftBottom.gif" width="10" height="10"></td>'+
					'    <td height="7" style="background-color: #FFFFEA" colspan="4" width="179"></td>'+
					'    <td width="11" height="7">'+
					'    <img border="0" src="' + self.imagesDir + '/cRightBottom.gif" width="10" height="10"></td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td width="10" height="10"></td>'+
					'    <td width="70" class="south_border_bottom" height="10"></td>'+
					'    <td  height="10" class="south_border_bottom" width="100%"></td>'+
					'    <td  align="right">'+
					'    <img border="0" src="' + self.imagesDir + '/aSouthWest.gif" width="67" height="18"></td>'+
					'    <td width="1" height="10" class="south_border_bottom"></td>'+
					'    <td width="10" height="10"></td>'+
					'  </tr>'+
					'</table>'
					break;
					
			case 'NE':	
					// North East
					html ='<table class="XBalloon" border="0" cellpadding="0" cellspacing="0">'+
					'   <tr>'+
					'    <td width="10" height="9"></td>' +
					'    <td width="1" class="north_border_top" height="9"></td>'+
					'    <td  height="9" valign="bottom">' +
					'    <img border="0" src=" '+ self.imagesDir + '/aNorthEast.gif" width="67" height="18"></td>'+
					'    <td width=100% height="9" class="north_border_top"></td>'+
					'    <td width="70" height="9" class="north_border_top"></td>'+
					'    <td width="10" height="9"></td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td height="1" width="10">'+
					'    <img border="0" src="' + self.imagesDir + '/cLeftTop.gif" width="10" height="10"></td>'+
					'    <td height="7" width=100% colspan="4" bgcolor="#FFFFEA"></td>'+
					'    <td height="7" width="10">'+
					'    <img border="0" src="' + self.imagesDir + '/cRightTop.gif" width="10" height="10"></td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td valign=top colspan="6" class="body">'+
					body +
					'    </td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td width="10" height="7">'+
					'    <img border="0" src="' + self.imagesDir + '/cLeftBottom.gif" width="10" height="10"></td>'+
					'    <td height="7" class="north_border_bottom" colspan="4" width="280"></td>'+
					'    <td width="10" height="7">'+
					'    <img border="0" src="' + self.imagesDir + '/cRightBottom.gif" width="10" height="10"></td>'+
					'  </tr>'+
					'</table>'
					break;
					
			case 'NW':	
					// North West			
					html ='<table class="XBalloon" border="0" cellpadding="0" cellspacing="0">'+
					'  <tr>'+
					'    <td width="10" height="10"></td>'+
					'    <td width="70" class="north_border_top" height="10"></td>'+
					'    <td  height="10" class="north_border_top" width="100%">'+
					'    </td>'+
					'    <td  align="right" valign="bottom">'+
					'    <img border="0" src="' + self.imagesDir + '/aNorthWest.gif" width="67" height="18"></td>'+
					'    <td width="1" height="10" class="north_border_top"></td>'+
					'    <td width="10" height="10"></td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td height="1" width="10">'+
					'    <img border="0" src="' + self.imagesDir + '/cLeftTop.gif" width="10" height="10"></td>'+
					'    <td height="7" width=179 colspan="4" bgcolor="#FFFFEA"></td>'+
					'    <td height="7"  width="11">'+
					'    <img border="0" src="' + self.imagesDir + '/cRightTop.gif" width="10" height="10"></td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td valign=top colspan="6" class="body">' +
					body +
					'    </td>'+
					'  </tr>'+
					'  <tr>'+
					'    <td width="10" height="7">'+
					'    <img border="0" src="' + self.imagesDir + '/cLeftBottom.gif" width="10" height="10"></td>'+
					'    <td height="7" class="north_border_bottom" colspan="4" width="179"></td>'+
					'    <td width="11" height="7">'+
					'    <img border="0" src="' + self.imagesDir + '/cRightBottom.gif" width="10" height="10"></td>'+
					'  </tr>'+
					'</table>'
					break;
		}
	
				
		if (useContentDiv)
		{
		    var divContent=document.getElementById(self.contentDivID);
		    if (divContent==null)
		        throw new Error ("XBalloon.genBalloonHtml contentDiv '" + self.contentDivID  + "' does not exist.");

            if (!self.allowContentClone) 
		    {
		        //Hide content and add it to body, otherwise it disappiars in IE
		        
		        divContent.style.display="none";
                document.forms[0].appendChild(divContent); 
            }
            
            _balloonDiv.innerHTML=html;
            
		    TRACE("XBalloon.genBalloonHtml self.contentDivID=" + self.contentDivID + ", divContent=" + divContent);
		    replaceContentPlaceholder(divContent);
		}
		else
		{
		    _balloonDiv.innerHTML=html;
		}
		
		if (self.showCloseButton)
        {
		    var imgClose=findChildNode(_balloonDiv, "IMG", "imgClose");
		    TRACE("XBalloon.genBalloonHtml imgClose=" + imgClose);
		    imgClose.onclick=self.onCloseButtonClick;
		}
	}
	
	
	
	function replaceContentPlaceholder(divContent)
	{
	    TRACE("replaceContentPlaceholder Start, divContent.id=" + divContent.id + ", self.allowContentClone=" + self.allowContentClone);

		var divContentPlaceholder=findChildNode(_balloonDiv, "DIV", "divContentPlaceholder");
		var parentNode=divContentPlaceholder.parentNode;

		parentNode.removeChild(divContentPlaceholder);
		
		if (self.allowContentClone) //Clone it
		    divContent=divContent.cloneNode(true);
				
		divContent.style.display="block";
		parentNode.appendChild(divContent);
        
        TRACE("replaceContentPlaceholder End");
	}
	
	
	
	function findChildNode(parentNode, tagName, id)
	{
	    var childNodes=parentNode.getElementsByTagName(tagName, id);
	    
	    TRACE("XBalloon:findChildNode tagName=" + tagName + ", childNodes.length=" + childNodes.length); 
	    
	    var count;
	    var node;
	    
	    for (count=0; count < childNodes.length; count++)
	    {
	        node=childNodes[count];
	        
	        if (node.id==id)
	        {
	            TRACE("XBalloon:findChildNode found node with id=" + id);
	            return node;
	        }
	    }
	    
	    return null;
	}
	
	 
    self.traceInnerHtml = function ()
    {
        TRACE(_balloonDiv.innerHTML);
    }
}


//===========================================
//Balloon manager
//
// -- Keeps track of all visible balloons.
//============================================

var g_xBalloonManager=new XBalloonManager();

function XBalloonManager()
{
    //=========================================
    //Private attributes
	//=========================================
   
   
    var _balloons=new Array();
    
    var self=this;
    
    
    //=========================================
    //Public methods
	//=========================================
       
    self.addBalloon = function(balloon)
    {      
        _balloons.push(balloon);
        TRACE("xBalloonManager.addBalloon index=" + _balloons.length-1);
    }


    self.deleteBalloon = function(balloon)
    {
        //array.indexOf is not supported in IE 6, so using my own
        var index = getIndexOf(balloon);
       
        if (index!=-1)
            _balloons.splice(index, 1);
                
        TRACE("xBalloonManager.deleteBalloon index=" + index);
    }


    self.hasBalloon = function(id)
    {
        var balloon;
        for (count=0; count < _balloons.length; count++)
        {
            balloon=_balloons[count];
            if (balloon.id==id)
                return true;
        }
        
        return false;
    }


    function getIndexOf(balloon)
    {
        for (count=0; count < _balloons.length; count++)
        {
            if (balloon==_balloons[count])
                return count;
        }
        
        return -1;
    }
}


