// SpryCollapsiblePanel.js - version 0.8 - Spry Pre-Release 1.6.1
//
// 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.

(function ()
{ // BeginSpryComponent

	if (typeof Spry == "undefined") window.Spry = {}; if (!Spry.Widget) Spry.Widget = {};

	Spry.Widget.CollapsiblePanel = function (element, opts)
	{
		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;

		this.openPanelKeyCode = Spry.Widget.CollapsiblePanel.KEY_DOWN;
		this.closePanelKeyCode = Spry.Widget.CollapsiblePanel.KEY_UP;

		Spry.Widget.CollapsiblePanel.setOptions(this, opts);

		this.attachBehaviors();
	};

	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 (e)
	{
		this.addClassName(this.getTab(), this.hoverClass);
		return false;
	};

	Spry.Widget.CollapsiblePanel.prototype.onTabMouseOut = function (e)
	{
		this.removeClassName(this.getTab(), this.hoverClass);
		return false;
	};

	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, { duration: this.duration, fps: this.fps, transition: this.transition });
			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, { duration: this.duration, fps: this.fps, transition: this.transition });
			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 (e)
	{
		if (this.isOpen())
			this.close();
		else
			this.open();

		this.focus();

		return this.stopPropagation(e);
	};

	Spry.Widget.CollapsiblePanel.prototype.onFocus = function (e)
	{
		this.hasFocus = true;
		this.addClassName(this.element, this.focusedClass);
		return false;
	};

	Spry.Widget.CollapsiblePanel.prototype.onBlur = function (e)
	{
		this.hasFocus = false;
		this.removeClassName(this.element, this.focusedClass);
		return false;
	};

	Spry.Widget.CollapsiblePanel.KEY_UP = 38;
	Spry.Widget.CollapsiblePanel.KEY_DOWN = 40;

	Spry.Widget.CollapsiblePanel.prototype.onKeyDown = function (e)
	{
		var key = e.keyCode;
		if (!this.hasFocus || (key != this.openPanelKeyCode && key != this.closePanelKeyCode))
			return true;

		if (this.isOpen() && key == this.closePanelKeyCode)
			this.close();
		else if (key == this.openPanelKeyCode)
			this.open();

		return this.stopPropagation(e);
	};

	Spry.Widget.CollapsiblePanel.prototype.stopPropagation = function (e)
	{
		if (e.preventDefault) e.preventDefault();
		else e.returnValue = false;
		if (e.stopPropagation) e.stopPropagation();
		else e.cancelBubble = true;
		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(e); }, false);
		Spry.Widget.CollapsiblePanel.addEventListener(tab, "mouseover", function (e) { return self.onTabMouseOver(e); }, false);
		Spry.Widget.CollapsiblePanel.addEventListener(tab, "mouseout", function (e) { return self.onTabMouseOut(e); }, 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.addClassName(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 ()
	{
		try
		{
			return this.getElementChildren(this.element)[0];
		}
		catch (error)
		{
			//
		}
	};

	Spry.Widget.CollapsiblePanel.prototype.getContent = function ()
	{
		try
		{
			return this.getElementChildren(this.element)[1];
		}
		catch (error)
		{
			//
		}
	};

	Spry.Widget.CollapsiblePanel.prototype.isOpen = function ()
	{
		return this.contentIsOpen;
	};

	Spry.Widget.CollapsiblePanel.prototype.getElementChildren = function (element)
	{
		try
		{
			var children = [];
			var child = element.firstChild;
			while (child)
			{
				if (child.nodeType == 1 /* Node.ELEMENT_NODE */)
					children.push(child);
				child = child.nextSibling;
			}
			return children;
		}
		catch (error)
		{
			//
		}
	};

	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.fps = 800;
		this.duration = 100;
		this.startTime = 0;

		this.transition = Spry.Widget.CollapsiblePanel.PanelAnimator.defaultTransition;

		this.onComplete = null;

		this.panel = panel;
		this.content = panel.getContent();
		this.doOpen = doOpen;

		Spry.Widget.CollapsiblePanel.setOptions(this, opts, true);

		this.interval = Math.floor(1000 / this.fps);

		var c = this.content;

		var curHeight = c.offsetHeight ? c.offsetHeight : 0;
		this.fromHeight = (doOpen && c.style.display == "none") ? 0 : 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";
			}

			// 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;
		}

		this.distance = this.toHeight - this.fromHeight;
		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.defaultTransition = function (time, begin, finish, duration) { time /= duration; return begin + ((2 - time) * time * finish); };

	Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.start = function ()
	{
		var self = this;
		this.startTime = (new Date).getTime();
		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 property.

			this.content.style.overflow = this.overflow;
		}

		this.timer = null;
	};

	Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.stepAnimation = function ()
	{
		var curTime = (new Date).getTime();
		var elapsedTime = curTime - this.startTime;

		if (elapsedTime >= this.duration)
		{
			if (!this.doOpen)
				this.content.style.display = "none";
			this.content.style.overflow = this.overflow;
			this.content.style.height = this.toHeight + "px";
			if (this.onComplete)
				this.onComplete();
			return;
		}

		var ht = this.transition(elapsedTime, this.fromHeight, this.distance, this.duration);

		this.content.style.height = ((ht < 0) ? 0 : ht) + "px";

		var self = this;
		this.timer = setTimeout(function () { self.stepAnimation(); }, this.interval);
	};

	Spry.Widget.CollapsiblePanelGroup = function (element, opts)
	{
		this.element = this.getElement(element);
		this.opts = opts;

		this.attachBehaviors();
	};

	Spry.Widget.CollapsiblePanelGroup.prototype.setOptions = Spry.Widget.CollapsiblePanel.prototype.setOptions;
	Spry.Widget.CollapsiblePanelGroup.prototype.getElement = Spry.Widget.CollapsiblePanel.prototype.getElement;
	Spry.Widget.CollapsiblePanelGroup.prototype.getElementChildren = Spry.Widget.CollapsiblePanel.prototype.getElementChildren;

	Spry.Widget.CollapsiblePanelGroup.prototype.setElementWidget = function (element, widget)
	{
		if (!element || !widget)
			return;
		if (!element.spry)
			element.spry = new Object;
		element.spry.collapsiblePanel = widget;
	};

	Spry.Widget.CollapsiblePanelGroup.prototype.getElementWidget = function (element)
	{
		return (element && element.spry && element.spry.collapsiblePanel) ? element.spry.collapsiblePanel : null;
	};

	Spry.Widget.CollapsiblePanelGroup.prototype.getPanels = function ()
	{
		if (!this.element)
			return [];
		return this.getElementChildren(this.element);
	};

	Spry.Widget.CollapsiblePanelGroup.prototype.getPanel = function (panelIndex)
	{
		return this.getPanels()[panelIndex];
	};

	Spry.Widget.CollapsiblePanelGroup.prototype.attachBehaviors = function ()
	{
		if (!this.element)
			return;

		var cpanels = this.getPanels();
		var numCPanels = cpanels.length;
		for (var i = 0; i < numCPanels; i++)
		{
			var cpanel = cpanels[i];
			this.setElementWidget(cpanel, new Spry.Widget.CollapsiblePanel(cpanel, this.opts));
		}
	};

	Spry.Widget.CollapsiblePanelGroup.prototype.openPanel = function (panelIndex)
	{
		var w = this.getElementWidget(this.getPanel(panelIndex));
		if (w && !w.isOpen())
			w.open();
	};

	Spry.Widget.CollapsiblePanelGroup.prototype.closePanel = function (panelIndex)
	{
		var w = this.getElementWidget(this.getPanel(panelIndex));
		if (w && w.isOpen())
			w.close();
	};

	Spry.Widget.CollapsiblePanelGroup.prototype.openAllPanels = function ()
	{
		var cpanels = this.getPanels();
		var numCPanels = cpanels.length;
		for (var i = 0; i < numCPanels; i++)
		{
			var w = this.getElementWidget(cpanels[i]);
			if (w && !w.isOpen())
				w.open();
		}
	};

	Spry.Widget.CollapsiblePanelGroup.prototype.closeAllPanels = function ()
	{
		var cpanels = this.getPanels();
		var numCPanels = cpanels.length;
		for (var i = 0; i < numCPanels; i++)
		{
			var w = this.getElementWidget(cpanels[i]);
			if (w && w.isOpen())
				w.close();
		}
	};

})();    // EndSpryComponent

