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 30var Spry; 31if (!Spry) Spry = {}; 32if (!Spry.Widget) Spry.Widget = {}; 33 34Spry.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 = true; 52 this.fixedPanelHeight = 0; 53 54 Spry.Widget.Accordion.setOptions(this, opts, true); 55 56 this.attachBehaviors(); 57}; 58 59Spry.Widget.Accordion.prototype.getElement = function(ele) 60{ 61 if (ele && typeof ele == "string") 62 return document.getElementById(ele); 63 return ele; 64}; 65 66Spry.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 73Spry.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 80Spry.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 92Spry.Widget.Accordion.prototype.onPanelTabMouseOver = function(e, panel) 93{ 94 if (panel) 95 this.addClassName(this.getPanelTab(panel), this.hoverClass); 96 return false; 97}; 98 99Spry.Widget.Accordion.prototype.onPanelTabMouseOut = function(e, panel) 100{ 101 if (panel) 102 this.removeClassName(this.getPanelTab(panel), this.hoverClass); 103 return false; 104}; 105 106Spry.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 160Spry.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 192Spry.Widget.Accordion.prototype.openNextPanel = function() 193{ 194 return this.openPanel(this.getCurrentPanelIndex() + 1); 195}; 196 197Spry.Widget.Accordion.prototype.openPreviousPanel = function() 198{ 199 return this.openPanel(this.getCurrentPanelIndex() - 1); 200}; 201 202Spry.Widget.Accordion.prototype.openFirstPanel = function() 203{ 204 return this.openPanel(0); 205}; 206 207Spry.Widget.Accordion.prototype.openLastPanel = function() 208{ 209 var panels = this.getPanels(); 210 return this.openPanel(panels[panels.length - 1]); 211}; 212 213Spry.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 231Spry.Widget.Accordion.prototype.onFocus = function(e) 232{ 233 this.hasFocus = true; 234 this.addClassName(this.element, this.focusedClass); 235 return false; 236}; 237 238Spry.Widget.Accordion.prototype.onBlur = function(e) 239{ 240 this.hasFocus = false; 241 this.removeClassName(this.element, this.focusedClass); 242 return false; 243}; 244 245Spry.Widget.Accordion.KEY_UP = 38; 246Spry.Widget.Accordion.KEY_DOWN = 40; 247 248Spry.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 278Spry.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 294Spry.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 306Spry.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 354Spry.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 373Spry.Widget.Accordion.prototype.getPanels = function() 374{ 375 return this.getElementChildren(this.element); 376}; 377 378Spry.Widget.Accordion.prototype.getCurrentPanel = function() 379{ 380 return this.currentPanel; 381}; 382 383Spry.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 394Spry.Widget.Accordion.prototype.getCurrentPanelIndex = function() 395{ 396 return this.getPanelIndex(this.currentPanel); 397}; 398 399Spry.Widget.Accordion.prototype.getPanelTab = function(panel) 400{ 401 if (!panel) 402 return null; 403 return this.getElementChildren(panel)[0]; 404}; 405 406Spry.Widget.Accordion.prototype.getPanelContent = function(panel) 407{ 408 if (!panel) 409 return null; 410 return this.getElementChildren(panel)[1]; 411}; 412 413Spry.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 426Spry.Widget.Accordion.prototype.focus = function() 427{ 428 if (this.element && this.element.focus) 429 this.element.focus(); 430}; 431 432Spry.Widget.Accordion.prototype.blur = function() 433{ 434 if (this.element && this.element.blur) 435 this.element.blur(); 436}; 437 438///////////////////////////////////////////////////// 439 440Spry.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 496Spry.Widget.Accordion.PanelAnimator.defaultTransition = function(time, begin, finish, duration) { time /= duration; return begin + ((2 - time) * time * finish); }; 497 498Spry.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 505Spry.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 524Spry.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
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.