/* SpryCollapsiblePanel.js - Revision: Spry Preview Release 1.4 */



// Copyright (c) 2006. Adobe Systems Incorporated.

// All rights reserved.

//

// Redistribution and use in source and binary forms, with or without

// modification, are permitted provided that the following conditions are met:

//

//   * Redistributions of source code must retain the above copyright notice,

//     this list of conditions and the following disclaimer.

//   * Redistributions in binary form must reproduce the above copyright notice,

//     this list of conditions and the following disclaimer in the documentation

//     and/or other materials provided with the distribution.

//   * Neither the name of Adobe Systems Incorporated nor the names of its

//     contributors may be used to endorse or promote products derived from this

//     software without specific prior written permission.

//

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"

// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE

// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR

// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF

// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)

// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE

// POSSIBILITY OF SUCH DAMAGE.



var Spry;

if (!Spry) Spry = {};

if (!Spry.Widget) Spry.Widget = {};



Spry.Widget.CollapsiblePanel = function(element, opts)

{

	this.init(element);



	Spry.Widget.CollapsiblePanel.setOptions(this, opts);



	this.attachBehaviors();

};



Spry.Widget.CollapsiblePanel.prototype.init = function(element)

{

	this.element = this.getElement(element);

	this.focusElement = null;

	this.hoverClass = "CollapsiblePanelTabHover";

	this.openClass = "CollapsiblePanelOpen";

	this.closedClass = "CollapsiblePanelClosed";

	this.focusedClass = "CollapsiblePanelFocused";

	this.enableAnimation = true;

	this.enableKeyboardNavigation = true;

	this.animator = null;

	this.hasFocus = false;

	this.contentIsOpen = true;

};



Spry.Widget.CollapsiblePanel.prototype.getElement = function(ele)

{

	if (ele && typeof ele == "string")

		return document.getElementById(ele);

	return ele;

};



Spry.Widget.CollapsiblePanel.prototype.addClassName = function(ele, className)

{

	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1))

		return;

	ele.className += (ele.className ? " " : "") + className;

};



Spry.Widget.CollapsiblePanel.prototype.removeClassName = function(ele, className)

{

	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) == -1))

		return;

	ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), "");

};



Spry.Widget.CollapsiblePanel.prototype.hasClassName = function(ele, className)

{

	if (!ele || !className || !ele.className || ele.className.search(new RegExp("\\b" + className + "\\b")) == -1)

		return false;

	return true;

};



Spry.Widget.CollapsiblePanel.prototype.setDisplay = function(ele, display)

{

	if( ele )

		ele.style.display = display;

};



Spry.Widget.CollapsiblePanel.setOptions = function(obj, optionsObj, ignoreUndefinedProps)

{

	if (!optionsObj)

		return;

	for (var optionName in optionsObj)

	{

		if (ignoreUndefinedProps && optionsObj[optionName] == undefined)

			continue;

		obj[optionName] = optionsObj[optionName];

	}

};



Spry.Widget.CollapsiblePanel.prototype.onTabMouseOver = function()

{

	this.addClassName(this.getTab(), this.hoverClass);

};



Spry.Widget.CollapsiblePanel.prototype.onTabMouseOut = function()

{

	this.removeClassName(this.getTab(), this.hoverClass);

};



Spry.Widget.CollapsiblePanel.prototype.open = function()

{

	this.contentIsOpen = true;

	if (this.enableAnimation)

	{

		if (this.animator)

			this.animator.stop();

		this.animator = new Spry.Widget.CollapsiblePanel.PanelAnimator(this, true);

		this.animator.start();

	}

	else

		this.setDisplay(this.getContent(), "block");



	this.removeClassName(this.element, this.closedClass);

	this.addClassName(this.element, this.openClass);

};



Spry.Widget.CollapsiblePanel.prototype.close = function()

{

	this.contentIsOpen = false;

	if (this.enableAnimation)

	{

		if (this.animator)

			this.animator.stop();

		this.animator = new Spry.Widget.CollapsiblePanel.PanelAnimator(this, false);

		this.animator.start();

	}

	else

		this.setDisplay(this.getContent(), "none");



	this.removeClassName(this.element, this.openClass);

	this.addClassName(this.element, this.closedClass);

};



Spry.Widget.CollapsiblePanel.prototype.onTabClick = function()

{

	if (this.isOpen())

		this.close();

	else

		this.open();

	this.focus();

};



Spry.Widget.CollapsiblePanel.prototype.onFocus = function(e)

{

	this.hasFocus = true;

	this.addClassName(this.element, this.focusedClass);

};



Spry.Widget.CollapsiblePanel.prototype.onBlur = function(e)

{

	this.hasFocus = false;

	this.removeClassName(this.element, this.focusedClass);

};



Spry.Widget.CollapsiblePanel.ENTER_KEY = 13;

Spry.Widget.CollapsiblePanel.SPACE_KEY = 32;



Spry.Widget.CollapsiblePanel.prototype.onKeyDown = function(e)

{

	var key = e.keyCode;

	if (!this.hasFocus || (key != Spry.Widget.CollapsiblePanel.ENTER_KEY && key != Spry.Widget.CollapsiblePanel.SPACE_KEY))

		return true;

	

	if (this.isOpen())

		this.close();

	else

		this.open();



	if (e.stopPropagation)

		e.stopPropagation();

	if (e.preventDefault)

		e.preventDefault();



	return false;

};



Spry.Widget.CollapsiblePanel.prototype.attachPanelHandlers = function()

{

	var tab = this.getTab();

	if (!tab)

		return;



	var self = this;

	Spry.Widget.CollapsiblePanel.addEventListener(tab, "click", function(e) { return self.onTabClick(); }, false);

	Spry.Widget.CollapsiblePanel.addEventListener(tab, "mouseover", function(e) { return self.onTabMouseOver(); }, false);

	Spry.Widget.CollapsiblePanel.addEventListener(tab, "mouseout", function(e) { return self.onTabMouseOut(); }, false);



	if (this.enableKeyboardNavigation)

	{

		// XXX: IE doesn't allow the setting of tabindex dynamically. This means we can't

		// rely on adding the tabindex attribute if it is missing to enable keyboard navigation

		// by default.



		// Find the first element within the tab container that has a tabindex or the first

		// anchor tag.

		

		var tabIndexEle = null;

		var tabAnchorEle = null;



		this.preorderTraversal(tab, function(node) {

			if (node.nodeType == 1 /* NODE.ELEMENT_NODE */)

			{

				var tabIndexAttr = tab.attributes.getNamedItem("tabindex");

				if (tabIndexAttr)

				{

					tabIndexEle = node;

					return true;

				}

				if (!tabAnchorEle && node.nodeName.toLowerCase() == "a")

					tabAnchorEle = node;

			}

			return false;

		});



		if (tabIndexEle)

			this.focusElement = tabIndexEle;

		else if (tabAnchorEle)

			this.focusElement = tabAnchorEle;



		if (this.focusElement)

		{

			Spry.Widget.CollapsiblePanel.addEventListener(this.focusElement, "focus", function(e) { return self.onFocus(e); }, false);

			Spry.Widget.CollapsiblePanel.addEventListener(this.focusElement, "blur", function(e) { return self.onBlur(e); }, false);

			Spry.Widget.CollapsiblePanel.addEventListener(this.focusElement, "keydown", function(e) { return self.onKeyDown(e); }, false);

		}

	}

};



Spry.Widget.CollapsiblePanel.addEventListener = function(element, eventType, handler, capture)

{

	try

	{

		if (element.addEventListener)

			element.addEventListener(eventType, handler, capture);

		else if (element.attachEvent)

			element.attachEvent("on" + eventType, handler);

	}

	catch (e) {}

};



Spry.Widget.CollapsiblePanel.prototype.preorderTraversal = function(root, func)

{

	var stopTraversal = false;

	if (root)

	{

		stopTraversal = func(root);

		if (root.hasChildNodes())

		{

			var child = root.firstChild;

			while (!stopTraversal && child)

			{

				stopTraversal = this.preorderTraversal(child, func);

				try { child = child.nextSibling; } catch (e) { child = null; }

			}

		}

	}

	return stopTraversal;

};



Spry.Widget.CollapsiblePanel.prototype.attachBehaviors = function()

{

	var panel = this.element;

	var tab = this.getTab();

	var content = this.getContent();



	if (this.contentIsOpen || this.hasClassName(panel, this.openClass))

	{

		this.removeClassName(panel, this.closedClass);

		this.setDisplay(content, "block");

		this.contentIsOpen = true;

	}

	else

	{

		this.removeClassName(panel, this.openClass);

		this.addClassName(panel, this.closedClass);

		this.setDisplay(content, "none");

		this.contentIsOpen = false;

	}



	this.attachPanelHandlers();

};



Spry.Widget.CollapsiblePanel.prototype.getTab = function()

{

	return this.getElementChildren(this.element)[0];

};



Spry.Widget.CollapsiblePanel.prototype.getContent = function()

{

	return this.getElementChildren(this.element)[1];

};



Spry.Widget.CollapsiblePanel.prototype.isOpen = function()

{

	return this.contentIsOpen;

};



Spry.Widget.CollapsiblePanel.prototype.getElementChildren = function(element)

{

	var children = [];

	var child = element.firstChild;

	while (child)

	{

		if (child.nodeType == 1 /* Node.ELEMENT_NODE */)

			children.push(child);

		child = child.nextSibling;

	}

	return children;

};



Spry.Widget.CollapsiblePanel.prototype.focus = function()

{

	if (this.focusElement && this.focusElement.focus)

		this.focusElement.focus();

};



/////////////////////////////////////////////////////



Spry.Widget.CollapsiblePanel.PanelAnimator = function(panel, doOpen, opts)

{

	this.timer = null;

	this.interval = 0;

	this.stepCount = 0;



	this.fps = 0;

	this.steps = 10;

	this.duration = 500;

	this.onComplete = null;



	this.panel = panel;

	this.content = panel.getContent();

	this.panelData = [];

	this.doOpen = doOpen;



	Spry.Widget.CollapsiblePanel.setOptions(this, opts);





	// If caller specified speed in terms of frames per second,

	// convert them into steps.



	if (this.fps > 0)

	{

		this.interval = Math.floor(1000 / this.fps);

		this.steps = parseInt((this.duration + (this.interval - 1)) / this.interval);

	}

	else if (this.steps > 0)

		this.interval = this.duration / this.steps;



	var c = this.content;



	var curHeight = c.offsetHeight ? c.offsetHeight : 0;

	

	if (doOpen && c.style.display == "none")

		this.fromHeight = 0;

	else

		this.fromHeight = curHeight;



	if (!doOpen)

		this.toHeight = 0;

	else

	{

		if (c.style.display == "none")

		{

			// The content area is not displayed so in order to calculate the extent

			// of the content inside it, we have to set its display to block.



			c.style.visibility = "hidden";

			c.style.display = "block";

		}



		// Unfortunately in Mozilla/Firefox, fetching the offsetHeight seems to cause

		// the browser to synchronously re-layout and re-display content on the page,

		// so we see a brief flash of content that is *after* the panel being positioned

		// where it should when the panel is fully expanded. To get around this, we

		// temporarily position the content area of the panel absolutely off-screen.

		// This has the effect of taking the content out-of-flow, so nothing shifts around.



		// var oldPos = c.style.position;

		// var oldLeft = c.style.left;

		// c.style.position = "absolute";

		// c.style.left = "-2000em";



		// Clear the height property so we can calculate

		// the full height of the content we are going to show.

		c.style.height = "";

		this.toHeight = c.offsetHeight;



		// Now restore the position and offset to what it was!

		// c.style.position = oldPos;

		// c.style.left = oldLeft;

	}



	this.increment = (this.toHeight - this.fromHeight) / this.steps;

	this.overflow = c.style.overflow;



	c.style.height = this.fromHeight + "px";

	c.style.visibility = "visible";

	c.style.overflow = "hidden";

	c.style.display = "block";

};



Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.start = function()

{

	var self = this;

	this.timer = setTimeout(function() { self.stepAnimation(); }, this.interval);

};



Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.stop = function()

{

	if (this.timer)

	{

		clearTimeout(this.timer);



		// If we're killing the timer, restore the overflow

		// properties on the panels we were animating!



		if (this.stepCount < this.steps)

			this.content.style.overflow = this.overflow;

	}



	this.timer = null;

};



Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.stepAnimation = function()

{

	++this.stepCount;



	this.animate();



	if (this.stepCount < this.steps)

		this.start();

	else if (this.onComplete)

		this.onComplete();

};



Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.animate = function()

{

	if (this.stepCount >= this.steps)

	{

		if (!this.doOpen)

			this.content.style.display = "none";

		this.content.style.overflow = this.overflow;

		this.content.style.height = this.toHeight + "px";

	}

	else

	{

		this.fromHeight += this.increment;

		this.content.style.height = this.fromHeight + "px";

	}

};



