// sprycollapsiblepanel.js - version 0.7 - 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.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 = false; 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() { 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.fps = 60; this.duration = 500; 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(); } };