/*************************************************** (c)2008 by Jennifer Simonds
A class for an expandable pane which drops down when the chevron on the pane's 
header is clicked.

TO USE: 
	1. Create a <div> on your HTML page for each dropdown pane, containing a title 
		and an <img width=19 height=19> for its chevron.
	2. Create a <div> for each expanding pane.
	3. In onload, create a new DropdownPane ("header id", "chevron id", "panel id" [,callback] [,bOpen=false])
		for each pane.

CONSTRUCTOR
	oDP = new DropdownPane (idHeader, idChevron, idPane [, onOpen=null, onClose=null]);

PROPERTIES
	oDP.isOpen  // Is the dropdown panel visible?

METHODS
	bOK = oDP.openPanel  ();
	bOK = oDP.closePanel ();

EVENT HANDLERS
	oDP.onOpen  // User clicked the chevron or header. Triggered before showing the panel.
	oDP.onClose // User clicked the chevron or header to close it. Triggered after hiding the panel.

CLASS PROPERTIES (= default values)
	DropdownPane.prototype.dirImages	 = "/images";
	DropdownPane.prototype.aimgChevrons = // hastable of the pre-loaded images & their pathnames:
		images: ["open-up"], ["open-down"], ["close-up"], ["close-down"]
		pathnames: ["open-up"].src, etc.


   Date	  Who Changes
--------- --- ----------------------------------------------------------------
10may2008 jls Split off & refactored from chartable project.
12may2008 jls 
******************************************************************************/

// REQUIRES: <script src="domutils.js"></script>

// Class properties.
DropdownPane.prototype.dirChevronImages = "/images/cdp";
DropdownPane.prototype.aimgChevrons = new Object (); // Hashtable array


/*****************************************************************************/
function DropdownPane (p_idHeader, p_idChevron, p_idPane, p_onOpen, p_onClose)
	{
	var id;
	
	// Pre-load our images once.
	if (!this.aimgChevrons["open-up"])
		{
		this.aimgChevrons["open-up"]        = new Image (19,19);
		this.aimgChevrons["open-up"].src    = this.dirChevronImages + "/chevronopen-up.gif";
		this.aimgChevrons["open-down"]      = new Image (19,19);
		this.aimgChevrons["open-down"].src  = this.dirChevronImages + "/chevronopen-down.gif";
		this.aimgChevrons["close-up"]       = new Image (19,19);
		this.aimgChevrons["close-up"].src   = this.dirChevronImages + "/chevronclose-up.gif";
		this.aimgChevrons["close-down"]     = new Image (19,19);
		this.aimgChevrons["close-down"].src = this.dirChevronImages + "/chevronclose-down.gif";
		}
	
	this.divHeader	= document.getElementById (p_idHeader);
	this.imgChevron	= document.getElementById (p_idChevron);
	this.divPane	= document.getElementById (p_idPane);
	this.isOpen		= false;
	
	// Optional params & their default values.
	this.onOpen  = arguments.length>3 ? p_onOpen  : null;
	this.onClose = arguments.length>3 ? p_onClose : null;
	
	// Initialize our behavior.
	if (this.imgChevron)
		{
		this.imgChevron.src = this.aimgChevrons["open-up"].src;
		this.imgChevron.onmousedown	= this._bindMethodToEvent ("_onMouseDownChevron");
		this.imgChevron.onmouseup	= this._bindMethodToEvent ("_onMouseUpChevron");
		this.imgChevron.onmouseout	= this._bindMethodToEvent ("_onMouseOutChevron")
		}
	
	if (this.divHeader)
		{// Clicking on the header also activates the dropdown, not just the button.
		this.divHeader.style.zIndex = 1;
		this.divHeader.style.cursor = "pointer";
		this.divHeader.onclick = this._bindMethodToEvent ("_onClickHeader");
		}
	this.divPane.style.zIndex=0;
	}


/*****************************************************************************/
DropdownPane.prototype._onMouseDownChevron = function ()
	{
	var sSrc;
	
	sSrc = "" + this.src;
	
	if (sSrc.search(/[.]*chevronopen[.]*/) != -1)
		{sSrc = this.aimgChevrons["open-down"].src;
		}
	else
		{sSrc = this.aimgChevrons["close-down"].src;
		}
	}
/*****************************************************************************/
DropdownPane.prototype._onMouseUpChevron = function ()
	{
	var sSrc;
	
	sSrc = "" + this.src;
	
	if (sSrc.search(/[.]*chevronopen[.]*/) != -1)
		{sSrc = this.aimgChevrons["open-up"].src;
		}
	else
		{sSrc = this.aimgChevrons["close-up"].src;
		}
	}
/*****************************************************************************/
DropdownPane.prototype._onMouseOutChevron = function ()
	{
	var sSrc;
	
	sSrc = "" + this.src;
	
	if (sSrc.search(/[.]*chevronopen[.]*/) != -1)
		{sSrc = this.aimgChevrons["open-up"].src;
		}
	else
		{sSrc = this.aimgChevrons["close-up"].src;
		}
	}

/******************************************************************************
If the user clicked on a chevron, the event bubbles up here after onMouseUpChevron.
******************************************************************************/
DropdownPane.prototype._onClickHeader = function ()
	{
	if (!this.divPane.style.display || this.divPane.style.display=="none")
		{
		if (this.onOpen)
			{this.onOpen ();
			}
		
		this.divPane.style.display = "block";
		if (this.imgChevron)
			{this.imgChevron.src = this.aimgChevrons["close-up"].src;
			}
		}
	else
		{
		this.divPane.style.display = "none";
		if (this.imgChevron)
			{this.imgChevron.src = this.aimgChevrons["open-up"].src;
			}
		
		// Using anon func lets us callback a member method:
		var _this = this;
		setTimeout (function () {_this._afterClose();}, 50);
		}
	}

/*****************************************************************************/
DropdownPane.prototype._afterClose = function ()
	{
	if (this.onClose)
		{this.onClose ();
		}
	}

	
/*****************************************************************************
Uses closures to link a DOM event to an object's method.
See: http://www.jibbering.com/faq/faq_notes/closures.html#clObjI

Params: (name of the object's method [, extra data])

Event handler: CMyClass.prototype.onMyEvent (p_event [, p_extra]);
*****************************************************************************/
DropdownPane.prototype._bindMethodToEvent = function (p_sMethodName, p_extraData)
	{
	var oThis, extraData;
	
	// Parameter checks.
	if (!p_sMethodName)
		{return null;
		}
	
	oThis = this;
	extraData = arguments.length>2 ? p_extraData : null;
	
	return (function (p_e)
		{
		p_e = p_e || window.event;
		if (window.event)
			{// Try to consistencize some properties.
			p_e.pageX  = p_e.clientX;
			p_e.pageY  = p_e.clientY;
			p_e.target = p_e.srcElement;
			}
		p_e.related = p_e.relatedTarget ? p_e.relatedTarget : p_e.toElement;
		
		return oThis[p_sMethodName] (p_e, extraData);
		});
	}
