1/* SpryCollapsiblePanel.js - Revision: Spry Preview Release 1.4 */ 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.CollapsiblePanel = function(element, opts) 35{ 36 this.init(element); 37 38 Spry.Widget.CollapsiblePanel.setOptions(this, opts); 39 40 this.attachBehaviors(); 41}; 42 43Spry.Widget.CollapsiblePanel.prototype.init = function(element) 44{ 45 this.element = this.getElement(element); 46 this.focusElement = null; 47 this.hoverClass = "CollapsiblePanelTabHover"; 48 this.openClass = "CollapsiblePanelOpen"; 49 this.closedClass = "CollapsiblePanelClosed"; 50 this.focusedClass = "CollapsiblePanelFocused"; 51 this.enableAnimation = true; 52 this.enableKeyboardNavigation = true; 53 this.animator = null; 54 this.hasFocus = false; 55 this.contentIsOpen = true; 56}; 57 58Spry.Widget.CollapsiblePanel.prototype.getElement = function(ele) 59{ 60 if (ele && typeof ele == "string") 61 return document.getElementById(ele); 62 return ele; 63}; 64 65Spry.Widget.CollapsiblePanel.prototype.addClassName = function(ele, className) 66{ 67 if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1)) 68 return; 69 ele.className += (ele.className ? " " : "") + className; 70}; 71 72Spry.Widget.CollapsiblePanel.prototype.removeClassName = function(ele, className) 73{ 74 if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) == -1)) 75 return; 76 ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), ""); 77}; 78 79Spry.Widget.CollapsiblePanel.prototype.hasClassName = function(ele, className) 80{ 81 if (!ele || !className || !ele.className || ele.className.search(new RegExp("\\b" + className + "\\b")) == -1) 82 return false; 83 return true; 84}; 85 86Spry.Widget.CollapsiblePanel.prototype.setDisplay = function(ele, display) 87{ 88 if( ele ) 89 ele.style.display = display; 90}; 91 92Spry.Widget.CollapsiblePanel.setOptions = function(obj, optionsObj, ignoreUndefinedProps) 93{ 94 if (!optionsObj) 95 return; 96 for (var optionName in optionsObj) 97 { 98 if (ignoreUndefinedProps && optionsObj[optionName] == undefined) 99 continue; 100 obj[optionName] = optionsObj[optionName]; 101 } 102}; 103 104Spry.Widget.CollapsiblePanel.prototype.onTabMouseOver = function() 105{ 106 this.addClassName(this.getTab(), this.hoverClass); 107}; 108 109Spry.Widget.CollapsiblePanel.prototype.onTabMouseOut = function() 110{ 111 this.removeClassName(this.getTab(), this.hoverClass); 112}; 113 114Spry.Widget.CollapsiblePanel.prototype.open = function() 115{ 116 this.contentIsOpen = true; 117 if (this.enableAnimation) 118 { 119 if (this.animator) 120 this.animator.stop(); 121 this.animator = new Spry.Widget.CollapsiblePanel.PanelAnimator(this, true); 122 this.animator.start(); 123 } 124 else 125 this.setDisplay(this.getContent(), "block"); 126 127 this.removeClassName(this.element, this.closedClass); 128 this.addClassName(this.element, this.openClass); 129}; 130 131Spry.Widget.CollapsiblePanel.prototype.close = function() 132{ 133 this.contentIsOpen = false; 134 if (this.enableAnimation) 135 { 136 if (this.animator) 137 this.animator.stop(); 138 this.animator = new Spry.Widget.CollapsiblePanel.PanelAnimator(this, false); 139 this.animator.start(); 140 } 141 else 142 this.setDisplay(this.getContent(), "none"); 143 144 this.removeClassName(this.element, this.openClass); 145 this.addClassName(this.element, this.closedClass); 146}; 147 148Spry.Widget.CollapsiblePanel.prototype.onTabClick = function() 149{ 150 if (this.isOpen()) 151 this.close(); 152 else
153 this.open(); 154 this.focus(); 155}; 156 157Spry.Widget.CollapsiblePanel.prototype.onFocus = function(e) 158{ 159 this.hasFocus = true; 160 this.addClassName(this.element, this.focusedClass); 161}; 162 163Spry.Widget.CollapsiblePanel.prototype.onBlur = function(e) 164{ 165 this.hasFocus = false; 166 this.removeClassName(this.element, this.focusedClass); 167}; 168 169Spry.Widget.CollapsiblePanel.ENTER_KEY = 13; 170Spry.Widget.CollapsiblePanel.SPACE_KEY = 32; 171 172Spry.Widget.CollapsiblePanel.prototype.onKeyDown = function(e) 173{ 174 var key = e.keyCode; 175 if (!this.hasFocus || (key != Spry.Widget.CollapsiblePanel.ENTER_KEY && key != Spry.Widget.CollapsiblePanel.SPACE_KEY)) 176 return true; 177 178 if (this.isOpen()) 179 this.close(); 180 else 181 this.open(); 182 183 if (e.stopPropagation) 184 e.stopPropagation(); 185 if (e.preventDefault) 186 e.preventDefault(); 187 188 return false; 189}; 190 191Spry.Widget.CollapsiblePanel.prototype.attachPanelHandlers = function() 192{ 193 var tab = this.getTab(); 194 if (!tab) 195 return; 196 197 var self = this; 198 Spry.Widget.CollapsiblePanel.addEventListener(tab, "click", function(e) { return self.onTabClick(); }, false); 199 Spry.Widget.CollapsiblePanel.addEventListener(tab, "mouseover", function(e) { return self.onTabMouseOver(); }, false); 200 Spry.Widget.CollapsiblePanel.addEventListener(tab, "mouseout", function(e) { return self.onTabMouseOut(); }, false); 201 202 if (this.enableKeyboardNavigation) 203 { 204 // XXX: IE doesn't allow the setting of tabindex dynamically. This means we can't 205 // rely on adding the tabindex attribute if it is missing to enable keyboard navigation 206 // by default. 207 208 // Find the first element within the tab container that has a tabindex or the first 209 // anchor tag. 210 211 var tabIndexEle = null; 212 var tabAnchorEle = null; 213 214 this.preorderTraversal(tab, function(node) { 215 if (node.nodeType == 1 /* NODE.ELEMENT_NODE */) 216 { 217 var tabIndexAttr = tab.attributes.getNamedItem("tabindex"); 218 if (tabIndexAttr) 219 { 220 tabIndexEle = node; 221 return true; 222 } 223 if (!tabAnchorEle && node.nodeName.toLowerCase() == "a") 224 tabAnchorEle = node; 225 } 226 return false; 227 }); 228 229 if (tabIndexEle) 230 this.focusElement = tabIndexEle; 231 else if (tabAnchorEle) 232 this.focusElement = tabAnchorEle; 233 234 if (this.focusElement) 235 { 236 Spry.Widget.CollapsiblePanel.addEventListener(this.focusElement, "focus", function(e) { return self.onFocus(e); }, false); 237 Spry.Widget.CollapsiblePanel.addEventListener(this.focusElement, "blur", function(e) { return self.onBlur(e); }, false); 238 Spry.Widget.CollapsiblePanel.addEventListener(this.focusElement, "keydown", function(e) { return self.onKeyDown(e); }, false); 239 } 240 } 241}; 242 243Spry.Widget.CollapsiblePanel.addEventListener = function(element, eventType, handler, capture) 244{ 245 try 246 { 247 if (element.addEventListener) 248 element.addEventListener(eventType, handler, capture); 249 else if (element.attachEvent) 250 element.attachEvent("on" + eventType, handler); 251 } 252 catch (e) {} 253}; 254 255Spry.Widget.CollapsiblePanel.prototype.preorderTraversal = function(root, func) 256{ 257 var stopTraversal = false; 258 if (root) 259 { 260 stopTraversal = func(root); 261 if (root.hasChildNodes()) 262 { 263 var child = root.firstChild; 264 while (!stopTraversal && child) 265 { 266 stopTraversal = this.preorderTraversal(child, func); 267 try { child = child.nextSibling; } catch (e) { child = null; } 268 } 269 } 270 } 271 return stopTraversal; 272}; 273 274Spry.Widget.CollapsiblePanel.prototype.attachBehaviors = function() 275{ 276 var panel = this.element; 277 var tab = this.getTab(); 278 var content = this.getContent(); 279 280 if (this.contentIsOpen || this.hasClassName(panel, this.openClass)) 281 { 282 this.removeClassName(panel, this.closedClass); 283 this.setDisplay(content, "block"); 284 this.contentIsOpen = true; 285 } 286 else 287 { 288 this.removeClassName(panel, this.openClass); 289 this.addClassName(panel, this.closedClass); 290 this.setDisplay(content, "none"); 291 this.contentIsOpen = false; 292 } 293 294 this.attachPanelHandlers(); 295}; 296 297Spry.Widget.CollapsiblePanel.prototype.getTab = function() 298{ 299 return this.getElementChildren(this.element)[0]; 300}; 301 302Spry.Widget.CollapsiblePanel.prototype.getContent = function() 303{ 304 return this.getElementChildren(this.element)[1]; 305}; 306 307Spry.Widget.CollapsiblePanel.prototype.isOpen = function() 308{ 309 return this.contentIsOpen; 310}; 311 312Spry.Widget.CollapsiblePanel.prototype.getElementChildren = function(element) 313{ 314 var children = []; 315 var child = element.firstChild; 316 while (child) 317 { 318 if (child.nodeType == 1 /* Node.ELEMENT_NODE */) 319 children.push(child); 320 child = child.nextSibling; 321 } 322 return children; 323}; 324
325Spry.Widget.CollapsiblePanel.prototype.focus = function() 326{ 327 if (this.focusElement && this.focusElement.focus) 328 this.focusElement.focus(); 329}; 330 331///////////////////////////////////////////////////// 332 333Spry.Widget.CollapsiblePanel.PanelAnimator = function(panel, doOpen, opts) 334{ 335 this.timer = null; 336 this.interval = 0; 337 this.stepCount = 0; 338 339 this.fps = 0; 340 this.steps = 10; 341 this.duration = 500; 342 this.onComplete = null; 343 344 this.panel = panel; 345 this.content = panel.getContent(); 346 this.panelData = []; 347 this.doOpen = doOpen; 348 349 Spry.Widget.CollapsiblePanel.setOptions(this, opts); 350 351 352 // If caller specified speed in terms of frames per second, 353 // convert them into steps. 354 355 if (this.fps > 0) 356 { 357 this.interval = Math.floor(1000 / this.fps); 358 this.steps = parseInt((this.duration + (this.interval - 1)) / this.interval); 359 } 360 else if (this.steps > 0) 361 this.interval = this.duration / this.steps; 362 363 var c = this.content; 364 365 var curHeight = c.offsetHeight ? c.offsetHeight : 0; 366 367 if (doOpen && c.style.display == "none") 368 this.fromHeight = 0; 369 else 370 this.fromHeight = curHeight; 371 372 if (!doOpen) 373 this.toHeight = 0; 374 else 375 { 376 if (c.style.display == "none") 377 { 378 // The content area is not displayed so in order to calculate the extent 379 // of the content inside it, we have to set its display to block. 380 381 c.style.visibility = "hidden"; 382 c.style.display = "block"; 383 } 384 385 // Unfortunately in Mozilla/Firefox, fetching the offsetHeight seems to cause 386 // the browser to synchronously re-layout and re-display content on the page, 387 // so we see a brief flash of content that is *after* the panel being positioned 388 // where it should when the panel is fully expanded. To get around this, we 389 // temporarily position the content area of the panel absolutely off-screen. 390 // This has the effect of taking the content out-of-flow, so nothing shifts around. 391 392 // var oldPos = c.style.position; 393 // var oldLeft = c.style.left; 394 // c.style.position = "absolute"; 395 // c.style.left = "-2000em"; 396 397 // Clear the height property so we can calculate 398 // the full height of the content we are going to show. 399 c.style.height = ""; 400 this.toHeight = c.offsetHeight; 401 402 // Now restore the position and offset to what it was! 403 // c.style.position = oldPos; 404 // c.style.left = oldLeft; 405 } 406 407 this.increment = (this.toHeight - this.fromHeight) / this.steps; 408 this.overflow = c.style.overflow; 409 410 c.style.height = this.fromHeight + "px"; 411 c.style.visibility = "visible"; 412 c.style.overflow = "hidden"; 413 c.style.display = "block"; 414}; 415 416Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.start = function() 417{ 418 var self = this; 419 this.timer = setTimeout(function() { self.stepAnimation(); }, this.interval); 420}; 421 422Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.stop = function() 423{ 424 if (this.timer) 425 { 426 clearTimeout(this.timer); 427 428 // If we're killing the timer, restore the overflow 429 // properties on the panels we were animating! 430 431 if (this.stepCount < this.steps) 432 this.content.style.overflow = this.overflow; 433 } 434 435 this.timer = null; 436}; 437 438Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.stepAnimation = function() 439{ 440 ++this.stepCount; 441 442 this.animate(); 443 444 if (this.stepCount < this.steps) 445 this.start(); 446 else if (this.onComplete) 447 this.onComplete(); 448}; 449 450Spry.Widget.CollapsiblePanel.PanelAnimator.prototype.animate = function() 451{ 452 if (this.stepCount >= this.steps) 453 { 454 if (!this.doOpen) 455 this.content.style.display = "none"; 456 this.content.style.overflow = this.overflow; 457 this.content.style.height = this.toHeight + "px"; 458 } 459 else 460 { 461 this.fromHeight += this.increment; 462 this.content.style.height = this.fromHeight + "px"; 463 } 464}; 465
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.