//# ControlTabs.js -- Control Tabs Javascript
//#
//# Copyright (C) Tom Nelson, 2003
//#
//# Maybe used freely for personal use if this heading is not removed
//# or modified. For commerical use, contact developer for licensing.
//#
//# Source:	ControlTabs.js
//# Email:	developer@moondew.com
//# Web:	http://www.moondew.com/javascript
//#
//# --end heading--


// Revision History:
//		2003/08/05 - Script created. Version 0.10
//		2003/08/09 - Tested with: IE 5.5,6.0; Netscape 7.1; Mozilla 1.4
//		2003/08/10 - Added ControlTabs.Create(). 
//					 ControlTabs() no longer accepts element name. Version 0.20
//		2004/10/31 - Updated to work with IE 5.0
//					 Fixed multi-line tab height bug in Mozilla/Firefox
//
// If you have any comments, suggestions or requests, please email me.
//
//	Only 'ControlTabs', 'Color' and 'Hover' are added to the public namespace
//
// Naming conventions:
//		eName	- DHTML element
//		oName	- Javascript object
//		iName	- Index of
//		nName	- Number of
//		sName	- Size of
//		aName	- Array of
//		stName	- String
//		jsName()- Global Javascript function
//
//	This is one of my first Javascript projects and it is still very much a work in progress.
// - Tom Nelson 8/7/2003
//
// --end notes--


// OBJECT: ControlTabs - Version
//
// Read as 'version.revision'
ControlTabs.version = 0;
ControlTabs.revision = 20;

// OBJECT: ControlTabs - Default configuration
//
ControlTabs.config = { 
	color:'#D4D0C8', colorSelect:'#D4D0C8',
	invert:false, stretch:false, rise:3, overlap:3,
	textColor:'#000000', textColorSelect:'#000000', textPad:2, textPadV:0, 
	fontSize:12, fontWeight:'bold', fontFamily:'Verdana,Arial,Helvetica,sans-serif',	//'Arial,Tahoma,Helv',
	basePad:5, baseLine:true, baseHeight:0, baseWidth:0,
	cursor: (navigator.appName == "Microsoft Internet Explorer" ? "hand" : "pointer")
};

// OBJECT: ControlTabs.Create()
//
// Creates ControlTabs object and replaces content of specified element with it.
// 
// Reference to ControlTabs object is saved in specified element. This will cause
// the ControlTabs object to be deleted if content of specified element is replaced.
//
// If tabConfig.baseWidth has not been defined, it will be set to .style.width of
// the specified element.
//
// Returns ControlTabs object but can be ignored
//
ControlTabs.Create = jsCtCreate;
//*DEBUG* put in global namespace
function jsCtCreate(idParentElement,tabConfig,aTabDefs,iSelectTab) {
	// Tab Control container element
	var eTabContainer = document.getElementById(idParentElement);
	eTabContainer.innerHTML = '';
	
	// Note: Changed from tabConfig.hasOwnProperty(prop) to tabConfig[prop] for IE 5.0.
	if (tabConfig != null && !tabConfig['baseWidth'] != null)
		tabConfig.baseWidth = parseInt(eTabContainer.style.width);
		
	eTabContainer.oTabCtrl = new ControlTabs(tabConfig,aTabDefs,iSelectTab);
	eTabContainer.appendChild(eTabContainer.oTabCtrl.eTabCtrl);
	
	return eTabContainer.oTabCtrl;
}

// OBJECT: ControlTabs() - constructor
//
// idParentElement = "id of parent element to contain controls";
// tabConfig = { /* any combination of properties set in the default configuration */ };
// aTabDefs = [ ["Tab Label","Hover Help",jsCallback /*, ignored */], ... ];
// iSelectTab = indexOfSelectedTab;
//
// Returns ControlTabs object containing a new unappended .oTabControl element
//
function ControlTabs(tabConfig,aTabDefs,iSelectTab) {
	if (typeof aTabDefs != "object" || aTabDefs.length == 0)
		return null;
		
	// Copy properties from default. Using a simple assignment causes all
	// properties copied from tabConfig to also end up in the default!
	//
	this.config = new Object;
	for (var prop in ControlTabs.config)
		this.config[prop] = ControlTabs.config[prop];

	// Copy specified configuration properties
	if (tabConfig != null && typeof tabConfig == "object") {
		for (var prop in this.config) {
			if (tabConfig[prop] != null)
				this.config[prop] = tabConfig[prop];
		}
	}
	this.nTabs = aTabDefs.length;
	this.iTab = -1;	// No tab selected
	
	// Tab Control container element
	this.eTabCtrl = document.createElement("div");
	this.eTabCtrl.className = 'tabCtrl';
	this.eTabCtrl.style.position = 'relative';
	this.eTabCtrl.style.overflow = 'hidden';
	//this.eTabCtrl.style.border = '1px solid black'; //*DEBUG**
	
	// Because Netscape refuses to set the width of the container elements, we can only look
	// at the .style width, not the actual width.
	var tabWidth= (!this.config.stretch ? 0 : Math.floor(Math.max(this.config.baseWidth-this.config.basePad*2-this.config.overlap*2, 0) / this.nTabs) );
	
	this.aeTabs = new Array();
	
	var width,indent = this.config.basePad + this.config.overlap;	// Includes space for zoom
	for (var ii=0; ii < this.nTabs; ii++) {
		var tabDef = aTabDefs[ii];
		if (tabDef.length > 3)	//*HACK*
			this.config.colorSelect = tabDef[3];
		
		// <div> Single Tab container element
		// Sized to unselected tab graphics. Selected tab graphics will overflow element.
		var eTab = document.createElement("div");
		eTab.id = "tab"+ii;
		eTab.style.position = 'absolute';
		eTab.style.zIndex = 1;
		eTab.style.cursor = this.config.cursor;
		eTab.style.top = (this.config.invert ? 0 : this.config.rise);
		eTab.style.left = indent;
		
		eTab.iTab = ii;	// Maybe should point to oTab instead?
		
		this.aeTabs[ii] = eTab;	// save it for a rainy day
		this.eTabCtrl.appendChild(eTab);

		// <div> Tab Label Text
		// Text positioned over unselected tab graphic
		var eText = document.createElement('div')
		eText.className = 'tabText';
		eText.align = 'left';
		eText.style.position = 'absolute';
		eText.style.margin = eText.style.padding = 0;	// We will do our own
		eText.style.fontSize = this.config.fontSize;
		eText.style.fontWeight = this.config.fontWeight;
		eText.style.fontFamily = this.config.fontFamily;
		eText.style.color = this.config.textColor;
		eText.style.zIndex = 3;
		eText.style.top = this.config.textPadV;
		eText.style.left = this.config.textPad;
		//oText.style.border = '1px solid black';	//*DEBUG*
		eText.innerHTML = tabDef[0];
	
		// Compute the text size in pixels. It is crazy that there is not an easy way to do this!
		// The width of oText is not set until it is linked into the active document in IE, and then
		// it is highly dependent on the style.position setting. It does not work at all in Netscape!
		// We can't trust the oText height either since it is not set until linked into document.
		eText.tabTextSize = ControlTabs.GetElementTextSize(eText);
		
		// Incase of word-wrap
		eText.style.height = eText.tabTextSize.height;
		eText.style.overflow = 'hidden';
		
		eTab.eText = eText;
		eTab.tabText = tabDef[0];
		eTab.tabCallback = tabDef[2];
		eTab.appendChild(eText);

		if (tabWidth > 0) {
			eText.style.textOverflow = 'ellipsis';	// IE6 only. Hmmm, doesn't work, imagine that.
			eText.style.width = tabWidth-this.config.textPad*2;
			eText.align = 'center';
		}
			
		var oThis = this;	// Used to trick the function literals
		eTab.onclick = function() { oThis.OnClickTab(this); };
		//eTab.onmouseover = function() { oThis.OnMouseOverTab(this); };
		//eTab.onmouseout = function() { oThis.OnMouseOutTab(this); };
		//eTab.onmousemove = Hover.OnMouseMove;
		eTab.oHover = new Hover(eTab,tabDef[1]);
		
		eTab.left = indent;
		eTab.width = (tabWidth > 0 ? tabWidth : eText.tabTextSize.width + this.config.textPad*2);
		eTab.height = eText.tabTextSize.height+this.config.textPadV*2;
		eTab.style.height = eTab.height;
		eTab.style.width = eTab.width;

		eTab.eTabImg = ControlTabs.MakeGraphic(eTab.width,eTab.height,this.config.color,this.config.invert);
		eTab.eTabImg.style.visibility = 'visible';

		eTab.appendChild(eTab.eTabImg);
		
		eTab.eTabImgBig = ControlTabs.MakeGraphic(eTab.width+this.config.overlap*2,eTab.height+this.config.rise,this.config.colorSelect,this.config.invert);
		eTab.eTabImgBig.style.top = (this.config.invert ? 0 : -this.config.rise);
		eTab.eTabImgBig.style.left = -this.config.overlap;
		eTab.eTabImgBig.style.visibility = 'hidden';

		eTab.appendChild(eTab.eTabImgBig);

		indent += eTab.width;
	}
	
	var baseWidth = Math.max(indent+this.config.basePad+this.config.overlap, this.config.baseWidth);
	
	if (this.config.baseLine) {
	// Tab Baseline Container Element
	// Show of baseline overlaps upright tabs by 1 pixel, inverted tabs by 2 pixels
	var eTabImg = document.createElement("div");
	eTabImg.style.position = 'absolute';
	eTabImg.style.zIndex = 2;
	eTabImg.style.top = (this.config.invert != 0 ? 0 : this.aeTabs[0].height+this.config.rise-1);
	eTabImg.style.left = 0;
	eTabImg.style.width = baseWidth;

	var color = new Color(this.config.colorSelect);
	if (!this.config.invert) {
		eTabImg.style.height= (this.config.baseHeight+1);

		// Baseline Top Highlight
		var highlight = color.AlphaBlend(50,"#FFFFFF");	// '#EAE8E4';

		// Always get the baseline highlight/show line
		var ePart = ControlTabs.createElmtPart(0,1,baseWidth-2,1,highlight);
		eTabImg.appendChild(ePart);

		if (this.config.baseHeight > 0) {
			var ePart = ControlTabs.createElmtPart(1,0,baseWidth,this.config.baseHeight,this.config.colorSelect);
			eTabImg.appendChild(ePart);
		}
	}
	else {
		eTabImg.style.height= (this.config.baseHeight+2);

		if (this.config.baseHeight > 0) {
			var ePart = ControlTabs.createElmtPart(0,0,baseWidth,this.config.baseHeight,this.config.colorSelect);
			eTabImg.appendChild(ePart);
		}
		// Baseline Top Highlight
		// Note: These should somehow reference the tabDef used by .MakeGraphic
		var light = color.AlphaBlend(160,'#000000');	// light shadow
		var dark =  color.AlphaBlend(0,'#000000');		// dark shadow

		// Always get the baseline highlight/shadow line
		var ePart = ControlTabs.createElmtPart(this.config.baseHeight,1,baseWidth-2,1,light);
		eTabImg.appendChild(ePart);
		var ePart = ControlTabs.createElmtPart(this.config.baseHeight+1,0,baseWidth-1,1,dark);
		eTabImg.appendChild(ePart);
	}
	this.eTabCtrl.appendChild(eTabImg);
	}
	
	// Must be height of tab + rise + baseLine + baseHeight
	this.eTabCtrl.style.width = baseWidth;
	this.eTabCtrl.style.height = (this.aeTabs[0].height+this.config.baseHeight+this.config.rise);
	
	this.Select(iSelectTab);
	
	return this;
}

// OBJECT: ControlTabs.MakeGraphic()
//
// width,height of text on tab
ControlTabs.MakeGraphic = jsCtMakeGraphic;
//*DEBUG* put in global namespace
function jsCtMakeGraphic(width,height,tabColor, fInvert) {
	var color = new Color(tabColor);
		 
	// Tab Graphic Container Element
	var eTabImg = document.createElement("div");
	eTabImg.style.position = 'absolute';
	eTabImg.style.overflow = 'hidden';
	eTabImg.style.zIndex = 0;
	eTabImg.style.top = 0;
	eTabImg.style.left = 0;
	eTabImg.style.width=(width);
	eTabImg.style.height=(height);
		
	// Alpha is amount of tab color to keep (0-255)
	// Blend is background color to alpha blend tabColor with.
	// Top,Left if negative then heigth-top,width-left.
	// Width,Height if negative or zero then tabWidth-width,tabHeight-height.
	// Shade is index into shading definitions. For tabColor alpha=255.
	var tabDefUp = [	// Upright Tabs
			{ alpha:255, blend: "#000000",	// no shading
			  elements: [
					{ top: 1, left: 1, width:-3, height:-1 }	// Tab Center
				]
			},
			{ alpha: 50, blend: "#FFFFFF",	// highlight
			  elements: [
					{ top: 1, left: 1, width: 1, height: 1 },	// Upper left corner highlight
					{ top: 0, left: 2, width:-5, height: 1 },	// Top line highlight
					{ top: 2, left: 0, width: 1, height:-2 }	// Left side highlight			
				]
			},
			{ alpha:160, blend: "#000000",	// light shadow
			  elements: [	
					{ top: 2, left:-2, width: 1, height:-2 }	// Right edge light shadow
				]
			},
			{ alpha:110, blend: "#000000",	// dark shadow
			  elements: [
					{ top: 1, left:-2, width: 1, height: 1 },	// Upper right corner dark pixel
					{ top: 2, left:-1, width: 1, height:-2 }	// Right edge dark shadow
				]
			}
		];
	var tabDefInv = [	// Inverted Tabs
			{ alpha:255, blend: "#000000",	// no shading
			  elements: [
					{ top: 0, left: 1, width:-3, height:-2 }	// Tab Center
				]
			},
			{ alpha: 50, blend: "#FFFFFF",	// highlight
			  elements: [
					{ top: 0, left: 0, width: 1, height:-2 }	// Left side highlight			
				]
			},
			{ alpha:160, blend: "#000000",	// light shadow
			  elements: [	
					{ top: 0, left:-2, width: 1, height:-2 },	// Right edge light shadow
					{ top:-3, left:-3, width: 1, height: 1 },	// Lower right corner light pixel
					{ top:-3, left: 1, width: 1, height: 1 },	// Lower left corner light pixel
					{ top:-2, left: 1, width:-3, height: 1 }	// Bottom edge light shadow
				]
			},
			{ alpha: 0, blend: "#000000",	// dark shadow
			  elements: [
					{ top: 1, left:-1, width: 1, height:-3 },	// Right edge dark shadow
					{ top:-2, left:-2, width: 1, height: 1 },	// Lower right corner dark pixel
					{ top:-1, left: 2, width:-4, height: 1 }	// Bottom edge dark shadow
				]
			}
		];
	tabDef = (!fInvert ? tabDefUp : tabDefInv );
		
	eTabImg.tabParts = new Array();
	var iPart = 0;
	for (var iShade=0; iShade < tabDef.length; iShade++) {	// shading
		var oShade = tabDef[iShade];
		var partColor = color.AlphaBlend(oShade.alpha,oShade.blend);
		var aElmt=oShade.elements;
		for (var iElmt=0; iElmt<aElmt.length; iElmt++) {
			var ePart = document.createElement("div"); 
			ePart.style.position = 'absolute';
			ePart.style.overflow = 'hidden';
			ePart.style.top = (aElmt[iElmt].top >= 0 ? aElmt[iElmt].top : height+aElmt[iElmt].top);
			ePart.style.left = (aElmt[iElmt].left >= 0 ? aElmt[iElmt].left : width+aElmt[iElmt].left);
			ePart.style.width = (aElmt[iElmt].width > 0 ? aElmt[iElmt].width : width+aElmt[iElmt].width);
			ePart.style.height = (aElmt[iElmt].height > 0 ? aElmt[iElmt].height : height+aElmt[iElmt].height);
			ePart.style.backgroundColor = partColor;
			ePart.tabShade = { alpha: oShade.alpha, blend: oShade.blend };
				
			eTabImg.appendChild(ePart);	
			eTabImg.tabParts[iPart++] = ePart;
		}	
	}
	return eTabImg;
}

// OBJECT: ControlTabs.GetElementTextSize()
//
// Compute the element text width in pixels. It is crazy there is not an easy way to do this!
// The width of an element is not set until it is linked into the active document in IE, and then
// it is highly depented on the style.position setting. It does not work at all in Netscape!
//
// Rather than keep fighting with it, I am using this "hack" to compute the width & height.
//
ControlTabs.GetElementTextSize = function(eElement) {
	var eDiv = document.createElement("DIV");
	eDiv.style.position='absolute';
	eDiv.style.visibility = "hidden";
	eDiv.style.overflow='hidden';
	eDiv.style.fontSize = eElement.style.fontSize;
	eDiv.style.fontWeight = eElement.style.fontWeight;
	eDiv.style.fontFamily = eElement.style.fontFamily;
	eDiv.style.fontStyle = eElement.style.fontStyle;
	eDiv.style.fontVariant = eElement.style.fontVariant;
	//oDiv.style.height = '1em';
	document.body.insertBefore(eDiv,document.body.firstChild);
	
	eDiv.innerHTML = eElement.innerHTML;
	
	// Note: .offsetWidth/.offsetHeight work in IE 5.5/6.x.
	// Requires .scrollWidth/.scrollHeight in IE 5.0/Mozilla/Firefox
	var size = { width: eDiv.scrollWidth, height: eDiv.scrollHeight };
	
	document.body.removeChild(eDiv);
	
	return size;
}

// OBJECT: ControlTabs.createElmtPart()
//
ControlTabs.createElmtPart = function(top,left,width,height,bgColor) {
	var ePart = document.createElement("div"); 
	ePart.style.position = 'absolute';
	ePart.style.overflow = 'hidden';
	ePart.style.top = top;
	ePart.style.left = left;
	ePart.style.width = width;
	ePart.style.height = height;
	ePart.style.backgroundColor = bgColor;

	return ePart;
}

// OBJECT: ControlTabs.prototyle.Select()
//
ControlTabs.prototype.Select = jsCtSelectTab;
//*DEBUG* put in global namespace
function jsCtSelectTab(iTab) {
	if (this.iTab != iTab && (iTab >= 0 && iTab < this.aeTabs.length)) {
		if (this.iTab >= 0) {
			var eTab = this.aeTabs[this.iTab];
			// Zoom-Out previous tab selection
			eTab.style.zIndex = 1;	
			eTab.eTabImgBig.style.visibility = 'hidden';
			eTab.eTabImg.style.visibility = 'visible';
			eTab.eText.style.top = this.config.textPadV
			eTab.eText.style.color = this.config.textColor;
		}
		// Zoom-In new tab selection
		var eSelectTab = this.aeTabs[iTab];
		eSelectTab.style.zIndex = 3;
		eSelectTab.eTabImg.style.visibility = 'hidden';
		eSelectTab.eTabImgBig.style.visibility = 'visible';
		eSelectTab.eText.style.top = (this.config.invert ? this.config.rise : -this.config.rise)+this.config.textPadV;
		eSelectTab.eText.style.color = this.config.textColorSelect;

		this.iTab = eSelectTab.iTab;

		if (eSelectTab.tabCallback != null)
			eSelectTab.tabCallback(eSelectTab.iTab);
	}
}


// OBJECT: ControlTabs.prototype.OnClickTab()
//
// context: ControlTab object instance
//
ControlTabs.prototype.OnClickTab = jsCtOnClickTab;
//*DEBUG* put in global namespace
function jsCtOnClickTab(eSelectTab) {
	this.Select(eSelectTab.iTab);
//		jsStopHoverTimer();
//		jsLoadViewerSubject(iTab);
};


// OBJECT: ControlTabs.prototype.OnMouseOverTab()
//
// context: ControlTabs
//
ControlTabs.prototype.OnMouseOverTab = jsCtOnMouseOver;
//*DEBUG* put in global namespace
function jsCtOnMouseOver(eTab) {
	Hover.StartTimer(this,eTab.stHover);
};

// OBJECT: ControlTabs.prototype.OnMouseOutTab()
//
// context: ControlTabs
ControlTabs.prototype.OnMouseOutTab = function(eTab) {
	Hover.StopTimer(this);
};

// OBJECT: ControlTabs.prototype.GetHoverText()
//
ControlTabs.prototype.GetHoverText = function() {
	return aeTabs[iTab].stHover;
};


// OBJECT: Color - constructor
//
// Note: Does not support color names, eg: red,yellow,blue
//
// color must be formatted as: '#rrggbb'
//
function Color(color) {
	this.setRGB(color);
}

// Set instance rgb color string
Color.prototype.setRGB = function(newRGB) {
	this.rgb = (Color.IsColor(newRGB) ? newRGB : '#000000');
};

// Returns the instance rgb color string
Color.prototype.getRGB = function()  {
	return this.rgb;
};

// Returns decimal value of Red(),Green(),Blue()
Color.prototype.Red = function()	{ return parseInt('0x'+this.rgb.substring(1,3)); }
Color.prototype.Green = function()	{ return parseInt('0x'+this.rgb.substring(3,5)); }
Color.prototype.Blue = function()	{ return parseInt('0x'+this.rgb.substring(5,7)); }

// alpha is amount to keep (0-255) of instance color, remainder is blendColor
Color.prototype.AlphaBlend = function(alpha,blendColor) {
	if (alpha >= 255)
		return this.rgb;

	var bcRGB = new Color(blendColor);
	var comp = 255-alpha;

	var red = (bcRGB.Red() * comp + this.Red() * alpha) >> 8;
	var green=(bcRGB.Green() * comp + this.Green() * alpha) >> 8;
	var blue =(bcRGB.Blue() * comp + this.Blue() * alpha) >> 8;
	
	rgb = Color.MakeRGB(red,green,blue);
	
	return rgb;
}

// Over-ride inherited toString() method to return the rgb string
Color.prototype.toString = function() { 
	return (this.rgb);
};

// Returns RGB string from decimal R,G,B values.
Color.MakeRGB = function(red,green,blue)	{
	return ('#' + Color.ToHEX(red) + Color.ToHEX(green) + Color.ToHEX(blue)); 
}

// Returns HEX string from decimal
Color.ToHEX = function(dec) {
	return('' + Color.HEXchars[Math.floor(dec / 16)] + Color.HEXchars[Math.floor(dec % 16)]);
}

// HEX characters
Color.HEXchars = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F');

// Determine if a string looks like a color value
Color.IsColor = function(string) {

	// Color values must have 7 characters with a '#' as the first character
	if (string.length != 7 || string.substring(0, 1) != '#')
		return false;
	
	// Check that all characters after the first are a valid hex digit
	for (var i = 1; i < 7; i++) {
		for (var j = 0; j < 16; j++) {
			if (string.substring(i, i+1) == Color.HEXchars[j])
				break;
		}
		if (j == 16)
			return false;
	}
	return true;
} 



// OBJECT: Hover() - constructor
//
function Hover(eOver,stHover) {
	this.eOver = eOver;
	this.stHover = stHover;
	
	var oThis = this;
	if (document.addEventListener)
		eOver.addEventListener("mouseover",function(){ Hover.OnMouseOver(oThis); },true);
	else
		eOver.attachEvent("onmouseover", function(){ Hover.OnMouseOver(oThis); });
}

// OBJECT: static variables
//
Hover.fLinked = false;
Hover.timeoutId = 0;
Hover.timeCount = 0;
Hover.mouseX = 0;
Hover.mouseY = 0;

Hover.Interval = 50;	// In 1/100 sec.

Hover.eBubble;

// OBJECT: Hover.OnMouseOver()
//
Hover.OnMouseOver = function(oHover) {
	if (!Hover.fLinked) {
		Hover.eBubble = document.createElement("div");
		Hover.eBubble.style.cssText="position:absolute;z-index:20;visibility:hidden;padding:1 2 1 3;font-size:11;font-family:sans-serif,Arial,Tahoma,Helv;color:000000;background-color:lightyellow;border:1px black solid";
		document.body.insertBefore(Hover.eBubble,document.body.firstChild);
		Hover.fLinked = true;
	}
	Hover.SetHTML(oHover.stHover);

	if (document.addEventListener) {
		oHover.eOver.addEventListener("mouseout",function(){ Hover.OnMouseOut(oHover);},true);
		oHover.eOver.addEventListener("mousemove",Hover.OnMouseMove,true);
	}
	else {
		oHover.eOver.attachEvent("onmouseout",  function(){ Hover.OnMouseOut(oHover);} );
		oHover.eOver.attachEvent("onmousemove", Hover.OnMouseMove);
	}
	Hover.timeCount = 0;
	Hover.timeoutId = setTimeout("Hover.Timeout()",Hover.Interval);
}

// OBJECT: Hover.OnMouseOut()
//
Hover.OnMouseOut = function(oHover) {
	clearTimeout(Hover.timeoutId);
	
	if (document.removeEventListener) {
		oHover.eOver.removeEventListener("mouseout",function(){ Hover.OnMouseOut(oHover);},true);
		oHover.eOver.removeEventListener("mousemove",Hover.OnMouseMove,true);
	}
	else {
		oHover.eOver.detachEvent("onmouseout",  function(){ Hover.OnMouseOut(oHover);} );
		oHover.eOver.detachEvent("onmousemove", Hover.OnMouseMove);
	}
	Hover.timeCount = 0;
	Hover.eBubble.style.visibility = "hidden";
}

// OBJECT: Hover.SetHTML()
//
Hover.SetHTML = function(html) {
	Hover.eBubble.innerHTML = html;
}

// OBJECT: Hover.Timeout()
//
// Timeout Processing
Hover.Timeout = function() {
	if (Hover.timeCount < 10) {
		++Hover.timeCount;
	}
	else {
		Hover.eBubble.style.left = Hover.mouseX;
		Hover.eBubble.style.top = Hover.mouseY+20;
		Hover.eBubble.style.visibility = "visible";
		Hover.timeCount = 0;
	}
	Hover.timeoutId = setTimeout("Hover.Timeout()",Hover.Interval);
}

// OBJECT: Hover.OnMouseMove()
//
Hover.OnMouseMove = function(evt) {
	evt = (evt) ? evt : event;

	if (evt.layerX) {
		x = evt.clientX;//-oThumbView.offsetLeft;	//evt.layerX;
		y = evt.clientY;//-oThumbView.offsetTop;	//layerY;
	}
	else if (evt.offsetX) {
		x = evt.clientX;//-oThumbView.offsetLeft;	//evt.offsetX;
		y = evt.clientY;//-oThumbView.offsetTop;	//evt.offsetY;
	}
	else
		x = y = 0;

	// Ignore mouse jitter
	if (Math.abs(Hover.mouseX-x) > 1 || Math.abs(Hover.mouseY-y) > 1) {
		Hover.timeCount = 0;
		Hover.eBubble.style.visibility = "hidden";
	}
	Hover.mouseX = x;
	Hover.mouseY = y;
}



