// SpryAccordion.js - version 0.15 - 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.var Spry;if (!Spry) Spry = {};if (!Spry.Widget) Spry.Widget = {};Spry.Widget.Accordion = function(element, opts){this.element = this.getElement(element);this.defaultPanel = 0;this.hoverClass = "AccordionPanelTabHover";this.openClass = "AccordionPanelOpen";this.closedClass = "AccordionPanelClosed";this.focusedClass = "AccordionFocused";this.enableAnimation = true;this.enableKeyboardNavigation = true;this.currentPanel = null;this.animator = null;this.hasFocus = null;this.previousPanelKeyCode = Spry.Widget.Accordion.KEY_UP;this.nextPanelKeyCode = Spry.Widget.Accordion.KEY_DOWN;this.useFixedPanelHeights = true;this.fixedPanelHeight = 0;Spry.Widget.Accordion.setOptions(this, opts, true);this.attachBehaviors();};Spry.Widget.Accordion.prototype.getElement = function(ele){if (ele && typeof ele == "string")return document.getElementById(ele);return ele;};Spry.Widget.Accordion.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.Accordion.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.Accordion.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.Accordion.prototype.onPanelTabMouseOver = function(e, panel){if (panel)this.addClassName(this.getPanelTab(panel), this.hoverClass);return false;};Spry.Widget.Accordion.prototype.onPanelTabMouseOut = function(e, panel){if (panel)this.removeClassName(this.getPanelTab(panel), this.hoverClass);return false;};Spry.Widget.Accordion.prototype.openPanel = function(elementOrIndex){var panelA = this.currentPanel;var panelB;if (typeof elementOrIndex == "number")panelB = this.getPanels()[elementOrIndex];elsepanelB = this.getElement(elementOrIndex);if (!panelB || panelA == panelB)	return null;var contentA = panelA ? this.getPanelContent(panelA) : null;var contentB = this.getPanelContent(panelB);if (!contentB)return null;if (this.useFixedPanelHeights && !this.fixedPanelHeight)this.fixedPanelHeight = (contentA.offsetHeight) ? contentA.offsetHeight : contentA.scrollHeight;if (this.enableAnimation){if (this.animator)this.animator.stop();this.animator = new Spry.Widget.Accordion.PanelAnimator(this, panelB, { duration: this.duration, fps: this.fps, transition: this.transition });this.animator.start();}else{if(contentA){contentA.style.display = "none";contentA.style.height = "0px";}contentB.style.display = "block";contentB.style.height = this.useFixedPanelHeights ? this.fixedPanelHeight + "px" : "auto";}if(panelA){this.removeClassName(panelA, this.openClass);this.addClassName(panelA, this.closedClass);}this.removeClassName(panelB, this.closedClass);this.addClassName(panelB, this.openClass);this.currentPanel = panelB;return panelB;};Spry.Widget.Accordion.prototype.closePanel = function(){// The accordion can only ever have one panel open at any// give time, so this method only closes the current panel.// If the accordion is in fixed panel heights mode, this// method does nothing.if (!this.useFixedPanelHeights && this.currentPanel){var panel = this.currentPanel;var content = this.getPanelContent(panel);if (content){if (this.enableAnimation){if (this.animator)this.animator.stop();this.animator = new Spry.Widget.Accordion.PanelAnimator(this, null, { duration: this.duration, fps: this.fps, transition: this.transition });this.animator.start();}else{content.style.display = "none";content.style.height = "0px";}}		this.removeClassName(panel, this.openClass);this.addClassName(panel, this.closedClass);this.currentPanel = null;}};Spry.Widget.Accordion.prototype.openNextPanel = function(){return this.openPanel(this.getCurrentPanelIndex() + 1);};Spry.Widget.Accordion.prototype.openPreviousPanel = function(){return this.openPanel(this.getCurrentPanelIndex() - 1);};Spry.Widget.Accordion.prototype.openFirstPanel = function(){return this.openPanel(0);};Spry.Widget.Accordion.prototype.openLastPanel = function(){var panels = this.getPanels();return this.openPanel(panels[panels.length - 1]);};Spry.Widget.Accordion.prototype.onPanelTabClick = function(e, panel){if (panel != this.currentPanel)this.openPanel(panel);elsethis.closePanel();if (this.enableKeyboardNavigation)this.focus();if (e.preventDefault) e.preventDefault();else e.returnValue = false;if (e.stopPropagation) e.stopPropagation();else e.cancelBubble = true;return false;};Spry.Widget.Accordion.prototype.onFocus = function(e){this.hasFocus = true;this.addClassName(this.element, this.focusedClass);return false;};Spry.Widget.Accordion.prototype.onBlur = function(e){this.hasFocus = false;this.removeClassName(this.element, this.focusedClass);return false;};Spry.Widget.Accordion.KEY_UP = 38;Spry.Widget.Accordion.KEY_DOWN = 40;Spry.Widget.Accordion.prototype.onKeyDown = function(e){var key = e.keyCode;if (!this.hasFocus || (key != this.previousPanelKeyCode && key != this.nextPanelKeyCode))return true;var panels = this.getPanels();if (!panels || panels.length < 1)return false;var currentPanel = this.currentPanel ? this.currentPanel : panels[0];var nextPanel = (key == this.nextPanelKeyCode) ? currentPanel.nextSibling : currentPanel.previousSibling;while (nextPanel){if (nextPanel.nodeType == 1 /* Node.ELEMENT_NODE */)break;nextPanel = (key == this.nextPanelKeyCode) ? nextPanel.nextSibling : nextPanel.previousSibling;}if (nextPanel && currentPanel != nextPanel)this.openPanel(nextPanel);if (e.preventDefault) e.preventDefault();else e.returnValue = false;if (e.stopPropagation) e.stopPropagation();else e.cancelBubble = true;return false;};Spry.Widget.Accordion.prototype.attachPanelHandlers = function(panel){if (!panel)return;var tab = this.getPanelTab(panel);if (tab){var self = this;Spry.Widget.Accordion.addEventListener(tab, "click", function(e) { return self.onPanelTabClick(e, panel); }, false);Spry.Widget.Accordion.addEventListener(tab, "mouseover", function(e) { return self.onPanelTabMouseOver(e, panel); }, false);Spry.Widget.Accordion.addEventListener(tab, "mouseout", function(e) { return self.onPanelTabMouseOut(e, panel); }, false);}};Spry.Widget.Accordion.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.Accordion.prototype.initPanel = function(panel, isDefault){var content = this.getPanelContent(panel);if (isDefault){this.currentPanel = panel;this.removeClassName(panel, this.closedClass);this.addClassName(panel, this.openClass);// Attempt to set up the height of the default panel. We don't want to// do any dynamic panel height calculations here because our accordion// or one of its parent containers may be display:none.if (content){if (this.useFixedPanelHeights){// We are in fixed panel height mode and the user passed in// a panel height for us to use.if (this.fixedPanelHeight)content.style.height = this.fixedPanelHeight + "px";}else{// We are in variable panel height mode, but since we can't// calculate the panel height here, we just set the height to// auto so that it expands to show all of its content.content.style.height = "auto";}}}else{this.removeClassName(panel, this.openClass);this.addClassName(panel, this.closedClass);if (content){content.style.height = "0px";content.style.display = "none";}}this.attachPanelHandlers(panel);};Spry.Widget.Accordion.prototype.attachBehaviors = function(){var panels = this.getPanels();for (var i = 0; i < panels.length; i++) {if (typeof this.defaultPanel == "number")this.initPanel(panels[i], i == this.defaultPanel);elsethis.initPanel(panels[i], panels[i].id == this.defaultPanel);}// Advanced keyboard navigation requires the tabindex attribute// on the top-level element.this.enableKeyboardNavigation = (this.enableKeyboardNavigation && this.element.attributes.getNamedItem("tabindex"));if (this.enableKeyboardNavigation){var self = this;Spry.Widget.Accordion.addEventListener(this.element, "focus", function(e) { return self.onFocus(e); }, false);Spry.Widget.Accordion.addEventListener(this.element, "blur", function(e) { return self.onBlur(e); }, false);Spry.Widget.Accordion.addEventListener(this.element, "keydown", function(e) { return self.onKeyDown(e); }, false);}};Spry.Widget.Accordion.prototype.getPanels = function(){return this.getElementChildren(this.element);};Spry.Widget.Accordion.prototype.getCurrentPanel = function(){return this.currentPanel;};Spry.Widget.Accordion.prototype.getPanelIndex = function(panel){var panels = this.getPanels();for( var i = 0 ; i < panels.length; i++ ){if( panel == panels[i] )return i;}return -1;};Spry.Widget.Accordion.prototype.getCurrentPanelIndex = function(){return this.getPanelIndex(this.currentPanel);};Spry.Widget.Accordion.prototype.getPanelTab = function(panel){if (!panel)return null;return this.getElementChildren(panel)[0];};Spry.Widget.Accordion.prototype.getPanelContent = function(panel){if (!panel)return null;return this.getElementChildren(panel)[1];};Spry.Widget.Accordion.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.Accordion.prototype.focus = function(){if (this.element && this.element.focus)this.element.focus();};Spry.Widget.Accordion.prototype.blur = function(){if (this.element && this.element.blur)this.element.blur();};/////////////////////////////////////////////////////Spry.Widget.Accordion.PanelAnimator = function(accordion, panel, opts){this.timer = null;this.interval = 0;this.fps = 60;this.duration = 500;this.startTime = 0;this.transition = Spry.Widget.Accordion.PanelAnimator.defaultTransition;this.onComplete = null;this.panel = panel;this.panelToOpen = accordion.getElement(panel);this.panelData = [];this.useFixedPanelHeights = accordion.useFixedPanelHeights;Spry.Widget.Accordion.setOptions(this, opts, true);this.interval = Math.floor(1000 / this.fps);// Set up the array of panels we want to animate.var panels = accordion.getPanels();for (var i = 0; i < panels.length; i++){var p = panels[i];var c = accordion.getPanelContent(p);if (c){var h = c.offsetHeight;if (h == undefined)h = 0;if (p == panel && h == 0)c.style.display = "block";if (p == panel || h > 0){var obj = new Object;obj.panel = p;obj.content = c;obj.fromHeight = h;obj.toHeight = (p == panel) ? (accordion.useFixedPanelHeights ? accordion.fixedPanelHeight : c.scrollHeight) : 0;obj.distance = obj.toHeight - obj.fromHeight;obj.overflow = c.style.overflow;this.panelData.push(obj);c.style.overflow = "hidden";c.style.height = h + "px";}}}};Spry.Widget.Accordion.PanelAnimator.defaultTransition = function(time, begin, finish, duration) { time /= duration; return begin + ((2 - time) * time * finish); };Spry.Widget.Accordion.PanelAnimator.prototype.start = function(){var self = this;this.startTime = (new Date).getTime();this.timer = setTimeout(function() { self.stepAnimation(); }, this.interval);};Spry.Widget.Accordion.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!for (i = 0; i < this.panelData.length; i++){obj = this.panelData[i];obj.content.style.overflow = obj.overflow;}}this.timer = null;};Spry.Widget.Accordion.PanelAnimator.prototype.stepAnimation = function(){var curTime = (new Date).getTime();var elapsedTime = curTime - this.startTime;var i, obj;if (elapsedTime >= this.duration){for (i = 0; i < this.panelData.length; i++){obj = this.panelData[i];if (obj.panel != this.panel){obj.content.style.display = "none";obj.content.style.height = "0px";}obj.content.style.overflow = obj.overflow;obj.content.style.height = (this.useFixedPanelHeights || obj.toHeight == 0) ? obj.toHeight + "px" : "auto";}if (this.onComplete)this.onComplete();return;}for (i = 0; i < this.panelData.length; i++){obj = this.panelData[i];var ht = this.transition(elapsedTime, obj.fromHeight, obj.distance, this.duration);obj.content.style.height = ((ht < 0) ? 0 : ht) + "px";}var self = this;this.timer = setTimeout(function() { self.stepAnimation(); }, this.interval);};
