home *** CD-ROM | disk | FTP | other *** search
/ mcgregor.k12.mn.us / www.mcgregor.k12.mn.us.tar / www.mcgregor.k12.mn.us / SpryAssets / SpryAccordion.js < prev    next >
Text File  |  2010-08-31  |  15KB  |  560 lines

  1. // SpryAccordion.js - version 0.15 - Spry Pre-Release 1.6.1
  2. //
  3. // Copyright (c) 2006. Adobe Systems Incorporated.
  4. // All rights reserved.
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions are met:
  8. //
  9. //   * Redistributions of source code must retain the above copyright notice,
  10. //     this list of conditions and the following disclaimer.
  11. //   * Redistributions in binary form must reproduce the above copyright notice,
  12. //     this list of conditions and the following disclaimer in the documentation
  13. //     and/or other materials provided with the distribution.
  14. //   * Neither the name of Adobe Systems Incorporated nor the names of its
  15. //     contributors may be used to endorse or promote products derived from this
  16. //     software without specific prior written permission.
  17. //
  18. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21. // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  22. // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23. // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24. // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26. // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. // POSSIBILITY OF SUCH DAMAGE.
  29.  
  30. var Spry;
  31. if (!Spry) Spry = {};
  32. if (!Spry.Widget) Spry.Widget = {};
  33.  
  34. Spry.Widget.Accordion = function(element, opts)
  35. {
  36.     this.element = this.getElement(element);
  37.     this.defaultPanel = 0;
  38.     this.hoverClass = "AccordionPanelTabHover";
  39.     this.openClass = "AccordionPanelOpen";
  40.     this.closedClass = "AccordionPanelClosed";
  41.     this.focusedClass = "AccordionFocused";
  42.     this.enableAnimation = true;
  43.     this.enableKeyboardNavigation = true;
  44.     this.currentPanel = null;
  45.     this.animator = null;
  46.     this.hasFocus = null;
  47.  
  48.     this.previousPanelKeyCode = Spry.Widget.Accordion.KEY_UP;
  49.     this.nextPanelKeyCode = Spry.Widget.Accordion.KEY_DOWN;
  50.  
  51.     this.useFixedPanelHeights = false;
  52.     this.fixedPanelHeight = 0;
  53.  
  54.     Spry.Widget.Accordion.setOptions(this, opts, true);
  55.  
  56.     this.attachBehaviors();
  57. };
  58.  
  59. Spry.Widget.Accordion.prototype.getElement = function(ele)
  60. {
  61.     if (ele && typeof ele == "string")
  62.         return document.getElementById(ele);
  63.     return ele;
  64. };
  65.  
  66. Spry.Widget.Accordion.prototype.addClassName = function(ele, className)
  67. {
  68.     if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1))
  69.         return;
  70.     ele.className += (ele.className ? " " : "") + className;
  71. };
  72.  
  73. Spry.Widget.Accordion.prototype.removeClassName = function(ele, className)
  74. {
  75.     if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) == -1))
  76.         return;
  77.     ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), "");
  78. };
  79.  
  80. Spry.Widget.Accordion.setOptions = function(obj, optionsObj, ignoreUndefinedProps)
  81. {
  82.     if (!optionsObj)
  83.         return;
  84.     for (var optionName in optionsObj)
  85.     {
  86.         if (ignoreUndefinedProps && optionsObj[optionName] == undefined)
  87.             continue;
  88.         obj[optionName] = optionsObj[optionName];
  89.     }
  90. };
  91.  
  92. Spry.Widget.Accordion.prototype.onPanelTabMouseOver = function(e, panel)
  93. {
  94.     if (panel)
  95.         this.addClassName(this.getPanelTab(panel), this.hoverClass);
  96.     return false;
  97. };
  98.  
  99. Spry.Widget.Accordion.prototype.onPanelTabMouseOut = function(e, panel)
  100. {
  101.     if (panel)
  102.         this.removeClassName(this.getPanelTab(panel), this.hoverClass);
  103.     return false;
  104. };
  105.  
  106. Spry.Widget.Accordion.prototype.openPanel = function(elementOrIndex)
  107. {
  108.     var panelA = this.currentPanel;
  109.     var panelB;
  110.  
  111.     if (typeof elementOrIndex == "number")
  112.         panelB = this.getPanels()[elementOrIndex];
  113.     else
  114.         panelB = this.getElement(elementOrIndex);
  115.     
  116.     if (!panelB || panelA == panelB)    
  117.         return null;
  118.  
  119.     var contentA = panelA ? this.getPanelContent(panelA) : null;
  120.     var contentB = this.getPanelContent(panelB);
  121.  
  122.     if (!contentB)
  123.         return null;
  124.  
  125.     if (this.useFixedPanelHeights && !this.fixedPanelHeight)
  126.         this.fixedPanelHeight = (contentA.offsetHeight) ? contentA.offsetHeight : contentA.scrollHeight;
  127.  
  128.     if (this.enableAnimation)
  129.     {
  130.         if (this.animator)
  131.             this.animator.stop();
  132.         this.animator = new Spry.Widget.Accordion.PanelAnimator(this, panelB, { duration: this.duration, fps: this.fps, transition: this.transition });
  133.         this.animator.start();
  134.     }
  135.     else
  136.     {
  137.         if(contentA)
  138.         {
  139.             contentA.style.display = "none";
  140.             contentA.style.height = "0px";
  141.         }
  142.         contentB.style.display = "block";
  143.         contentB.style.height = this.useFixedPanelHeights ? this.fixedPanelHeight + "px" : "auto";
  144.     }
  145.  
  146.     if(panelA)
  147.     {
  148.         this.removeClassName(panelA, this.openClass);
  149.         this.addClassName(panelA, this.closedClass);
  150.     }
  151.  
  152.     this.removeClassName(panelB, this.closedClass);
  153.     this.addClassName(panelB, this.openClass);
  154.  
  155.     this.currentPanel = panelB;
  156.  
  157.     return panelB;
  158. };
  159.  
  160. Spry.Widget.Accordion.prototype.closePanel = function()
  161. {
  162.     // The accordion can only ever have one panel open at any
  163.     // give time, so this method only closes the current panel.
  164.     // If the accordion is in fixed panel heights mode, this
  165.     // method does nothing.
  166.  
  167.     if (!this.useFixedPanelHeights && this.currentPanel)
  168.     {
  169.         var panel = this.currentPanel;
  170.         var content = this.getPanelContent(panel);
  171.         if (content)
  172.         {
  173.             if (this.enableAnimation)
  174.             {
  175.                 if (this.animator)
  176.                     this.animator.stop();
  177.                 this.animator = new Spry.Widget.Accordion.PanelAnimator(this, null, { duration: this.duration, fps: this.fps, transition: this.transition });
  178.                 this.animator.start();
  179.             }
  180.             else
  181.             {
  182.                 content.style.display = "none";
  183.                 content.style.height = "0px";
  184.             }
  185.         }        
  186.         this.removeClassName(panel, this.openClass);
  187.         this.addClassName(panel, this.closedClass);
  188.         this.currentPanel = null;
  189.     }
  190. };
  191.  
  192. Spry.Widget.Accordion.prototype.openNextPanel = function()
  193. {
  194.     return this.openPanel(this.getCurrentPanelIndex() + 1);
  195. };
  196.  
  197. Spry.Widget.Accordion.prototype.openPreviousPanel = function()
  198. {
  199.     return this.openPanel(this.getCurrentPanelIndex() - 1);
  200. };
  201.  
  202. Spry.Widget.Accordion.prototype.openFirstPanel = function()
  203. {
  204.     return this.openPanel(0);
  205. };
  206.  
  207. Spry.Widget.Accordion.prototype.openLastPanel = function()
  208. {
  209.     var panels = this.getPanels();
  210.     return this.openPanel(panels[panels.length - 1]);
  211. };
  212.  
  213. Spry.Widget.Accordion.prototype.onPanelTabClick = function(e, panel)
  214. {
  215.     if (panel != this.currentPanel)
  216.         this.openPanel(panel);
  217.     else
  218.         this.closePanel();
  219.  
  220.     if (this.enableKeyboardNavigation)
  221.         this.focus();
  222.  
  223.     if (e.preventDefault) e.preventDefault();
  224.     else e.returnValue = false;
  225.     if (e.stopPropagation) e.stopPropagation();
  226.     else e.cancelBubble = true;
  227.  
  228.     return false;
  229. };
  230.  
  231. Spry.Widget.Accordion.prototype.onFocus = function(e)
  232. {
  233.     this.hasFocus = true;
  234.     this.addClassName(this.element, this.focusedClass);
  235.     return false;
  236. };
  237.  
  238. Spry.Widget.Accordion.prototype.onBlur = function(e)
  239. {
  240.     this.hasFocus = false;
  241.     this.removeClassName(this.element, this.focusedClass);
  242.     return false;
  243. };
  244.  
  245. Spry.Widget.Accordion.KEY_UP = 38;
  246. Spry.Widget.Accordion.KEY_DOWN = 40;
  247.  
  248. Spry.Widget.Accordion.prototype.onKeyDown = function(e)
  249. {
  250.     var key = e.keyCode;
  251.     if (!this.hasFocus || (key != this.previousPanelKeyCode && key != this.nextPanelKeyCode))
  252.         return true;
  253.     
  254.     var panels = this.getPanels();
  255.     if (!panels || panels.length < 1)
  256.         return false;
  257.     var currentPanel = this.currentPanel ? this.currentPanel : panels[0];
  258.     var nextPanel = (key == this.nextPanelKeyCode) ? currentPanel.nextSibling : currentPanel.previousSibling;
  259.  
  260.     while (nextPanel)
  261.     {
  262.         if (nextPanel.nodeType == 1 /* Node.ELEMENT_NODE */)
  263.             break;
  264.         nextPanel = (key == this.nextPanelKeyCode) ? nextPanel.nextSibling : nextPanel.previousSibling;
  265.     }
  266.  
  267.     if (nextPanel && currentPanel != nextPanel)
  268.         this.openPanel(nextPanel);
  269.  
  270.     if (e.preventDefault) e.preventDefault();
  271.     else e.returnValue = false;
  272.     if (e.stopPropagation) e.stopPropagation();
  273.     else e.cancelBubble = true;
  274.  
  275.     return false;
  276. };
  277.  
  278. Spry.Widget.Accordion.prototype.attachPanelHandlers = function(panel)
  279. {
  280.     if (!panel)
  281.         return;
  282.  
  283.     var tab = this.getPanelTab(panel);
  284.  
  285.     if (tab)
  286.     {
  287.         var self = this;
  288.         Spry.Widget.Accordion.addEventListener(tab, "click", function(e) { return self.onPanelTabClick(e, panel); }, false);
  289.         Spry.Widget.Accordion.addEventListener(tab, "mouseover", function(e) { return self.onPanelTabMouseOver(e, panel); }, false);
  290.         Spry.Widget.Accordion.addEventListener(tab, "mouseout", function(e) { return self.onPanelTabMouseOut(e, panel); }, false);
  291.     }
  292. };
  293.  
  294. Spry.Widget.Accordion.addEventListener = function(element, eventType, handler, capture)
  295. {
  296.     try
  297.     {
  298.         if (element.addEventListener)
  299.             element.addEventListener(eventType, handler, capture);
  300.         else if (element.attachEvent)
  301.             element.attachEvent("on" + eventType, handler);
  302.     }
  303.     catch (e) {}
  304. };
  305.  
  306. Spry.Widget.Accordion.prototype.initPanel = function(panel, isDefault)
  307. {
  308.     var content = this.getPanelContent(panel);
  309.     if (isDefault)
  310.     {
  311.         this.currentPanel = panel;
  312.         this.removeClassName(panel, this.closedClass);
  313.         this.addClassName(panel, this.openClass);
  314.  
  315.         // Attempt to set up the height of the default panel. We don't want to
  316.         // do any dynamic panel height calculations here because our accordion
  317.         // or one of its parent containers may be display:none.
  318.  
  319.         if (content)
  320.         {
  321.             if (this.useFixedPanelHeights)
  322.             {
  323.                 // We are in fixed panel height mode and the user passed in
  324.                 // a panel height for us to use.
  325.     
  326.                 if (this.fixedPanelHeight)
  327.                     content.style.height = this.fixedPanelHeight + "px";
  328.             }
  329.             else
  330.             {
  331.                 // We are in variable panel height mode, but since we can't
  332.                 // calculate the panel height here, we just set the height to
  333.                 // auto so that it expands to show all of its content.
  334.     
  335.                 content.style.height = "auto";
  336.             }
  337.         }
  338.     }
  339.     else
  340.     {
  341.         this.removeClassName(panel, this.openClass);
  342.         this.addClassName(panel, this.closedClass);
  343.  
  344.         if (content)
  345.         {
  346.             content.style.height = "0px";
  347.             content.style.display = "none";
  348.         }
  349.     }
  350.     
  351.     this.attachPanelHandlers(panel);
  352. };
  353.  
  354. Spry.Widget.Accordion.prototype.attachBehaviors = function()
  355. {
  356.     var panels = this.getPanels();
  357.     for (var i = 0; i < panels.length; i++)
  358.         this.initPanel(panels[i], i == this.defaultPanel);
  359.  
  360.     // Advanced keyboard navigation requires the tabindex attribute
  361.     // on the top-level element.
  362.  
  363.     this.enableKeyboardNavigation = (this.enableKeyboardNavigation && this.element.attributes.getNamedItem("tabindex"));
  364.     if (this.enableKeyboardNavigation)
  365.     {
  366.         var self = this;
  367.         Spry.Widget.Accordion.addEventListener(this.element, "focus", function(e) { return self.onFocus(e); }, false);
  368.         Spry.Widget.Accordion.addEventListener(this.element, "blur", function(e) { return self.onBlur(e); }, false);
  369.         Spry.Widget.Accordion.addEventListener(this.element, "keydown", function(e) { return self.onKeyDown(e); }, false);
  370.     }
  371. };
  372.  
  373. Spry.Widget.Accordion.prototype.getPanels = function()
  374. {
  375.     return this.getElementChildren(this.element);
  376. };
  377.  
  378. Spry.Widget.Accordion.prototype.getCurrentPanel = function()
  379. {
  380.     return this.currentPanel;
  381. };
  382.  
  383. Spry.Widget.Accordion.prototype.getPanelIndex = function(panel)
  384. {
  385.     var panels = this.getPanels();
  386.     for( var i = 0 ; i < panels.length; i++ )
  387.     {
  388.         if( panel == panels[i] )
  389.             return i;
  390.     }
  391.     return -1;
  392. };
  393.  
  394. Spry.Widget.Accordion.prototype.getCurrentPanelIndex = function()
  395. {
  396.     return this.getPanelIndex(this.currentPanel);
  397. };
  398.  
  399. Spry.Widget.Accordion.prototype.getPanelTab = function(panel)
  400. {
  401.     if (!panel)
  402.         return null;
  403.     return this.getElementChildren(panel)[0];
  404. };
  405.  
  406. Spry.Widget.Accordion.prototype.getPanelContent = function(panel)
  407. {
  408.     if (!panel)
  409.         return null;
  410.     return this.getElementChildren(panel)[1];
  411. };
  412.  
  413. Spry.Widget.Accordion.prototype.getElementChildren = function(element)
  414. {
  415.     var children = [];
  416.     var child = element.firstChild;
  417.     while (child)
  418.     {
  419.         if (child.nodeType == 1 /* Node.ELEMENT_NODE */)
  420.             children.push(child);
  421.         child = child.nextSibling;
  422.     }
  423.     return children;
  424. };
  425.  
  426. Spry.Widget.Accordion.prototype.focus = function()
  427. {
  428.     if (this.element && this.element.focus)
  429.         this.element.focus();
  430. };
  431.  
  432. Spry.Widget.Accordion.prototype.blur = function()
  433. {
  434.     if (this.element && this.element.blur)
  435.         this.element.blur();
  436. };
  437.  
  438. /////////////////////////////////////////////////////
  439.  
  440. Spry.Widget.Accordion.PanelAnimator = function(accordion, panel, opts)
  441. {
  442.     this.timer = null;
  443.     this.interval = 0;
  444.  
  445.     this.fps = 60;
  446.     this.duration = 500;
  447.     this.startTime = 0;
  448.  
  449.     this.transition = Spry.Widget.Accordion.PanelAnimator.defaultTransition;
  450.  
  451.     this.onComplete = null;
  452.  
  453.     this.panel = panel;
  454.     this.panelToOpen = accordion.getElement(panel);
  455.     this.panelData = [];
  456.     this.useFixedPanelHeights = accordion.useFixedPanelHeights;
  457.  
  458.     Spry.Widget.Accordion.setOptions(this, opts, true);
  459.  
  460.     this.interval = Math.floor(1000 / this.fps);
  461.  
  462.     // Set up the array of panels we want to animate.
  463.  
  464.     var panels = accordion.getPanels();
  465.     for (var i = 0; i < panels.length; i++)
  466.     {
  467.         var p = panels[i];
  468.         var c = accordion.getPanelContent(p);
  469.         if (c)
  470.         {
  471.             var h = c.offsetHeight;
  472.             if (h == undefined)
  473.                 h = 0;
  474.  
  475.             if (p == panel && h == 0)
  476.                 c.style.display = "block";
  477.  
  478.             if (p == panel || h > 0)
  479.             {
  480.                 var obj = new Object;
  481.                 obj.panel = p;
  482.                 obj.content = c;
  483.                 obj.fromHeight = h;
  484.                 obj.toHeight = (p == panel) ? (accordion.useFixedPanelHeights ? accordion.fixedPanelHeight : c.scrollHeight) : 0;
  485.                 obj.distance = obj.toHeight - obj.fromHeight;
  486.                 obj.overflow = c.style.overflow;
  487.                 this.panelData.push(obj);
  488.  
  489.                 c.style.overflow = "hidden";
  490.                 c.style.height = h + "px";
  491.             }
  492.         }
  493.     }
  494. };
  495.  
  496. Spry.Widget.Accordion.PanelAnimator.defaultTransition = function(time, begin, finish, duration) { time /= duration; return begin + ((2 - time) * time * finish); };
  497.  
  498. Spry.Widget.Accordion.PanelAnimator.prototype.start = function()
  499. {
  500.     var self = this;
  501.     this.startTime = (new Date).getTime();
  502.     this.timer = setTimeout(function() { self.stepAnimation(); }, this.interval);
  503. };
  504.  
  505. Spry.Widget.Accordion.PanelAnimator.prototype.stop = function()
  506. {
  507.     if (this.timer)
  508.     {
  509.         clearTimeout(this.timer);
  510.  
  511.         // If we're killing the timer, restore the overflow
  512.         // properties on the panels we were animating!
  513.  
  514.         for (i = 0; i < this.panelData.length; i++)
  515.         {
  516.             obj = this.panelData[i];
  517.             obj.content.style.overflow = obj.overflow;
  518.         }
  519.     }
  520.  
  521.     this.timer = null;
  522. };
  523.  
  524. Spry.Widget.Accordion.PanelAnimator.prototype.stepAnimation = function()
  525. {
  526.     var curTime = (new Date).getTime();
  527.     var elapsedTime = curTime - this.startTime;
  528.  
  529.     var i, obj;
  530.  
  531.     if (elapsedTime >= this.duration)
  532.     {
  533.         for (i = 0; i < this.panelData.length; i++)
  534.         {
  535.             obj = this.panelData[i];
  536.             if (obj.panel != this.panel)
  537.             {
  538.                 obj.content.style.display = "none";
  539.                 obj.content.style.height = "0px";
  540.             }
  541.             obj.content.style.overflow = obj.overflow;
  542.             obj.content.style.height = (this.useFixedPanelHeights || obj.toHeight == 0) ? obj.toHeight + "px" : "auto";
  543.         }
  544.         if (this.onComplete)
  545.             this.onComplete();
  546.         return;
  547.     }
  548.  
  549.     for (i = 0; i < this.panelData.length; i++)
  550.     {
  551.         obj = this.panelData[i];
  552.         var ht = this.transition(elapsedTime, obj.fromHeight, obj.distance, this.duration);
  553.         obj.content.style.height = ((ht < 0) ? 0 : ht) + "px";
  554.     }
  555.     
  556.     var self = this;
  557.     this.timer = setTimeout(function() { self.stepAnimation(); }, this.interval);
  558. };
  559.  
  560.