1/** @license 2 Animator.js 1.1.9 3 4 This library is released under the BSD license: 5 6 Copyright (c) 2006, Bernard Sumption. All rights reserved. 7 8 Redistribution and use in source and binary forms, with or without 9 modification, are permitted provided that the following conditions are met: 10 11 Redistributions of source code must retain the above copyright notice, this 12 list of conditions and the following disclaimer. Redistributions in binary 13 form must reproduce the above copyright notice, this list of conditions and 14 the following disclaimer in the documentation and/or other materials 15 provided with the distribution. Neither the name BernieCode nor 16 the names of its contributors may be used to endorse or promote products 17 derived from this software without specific prior written permission. 18 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 23 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 29 DAMAGE. 30 31*/ 32 33// http://www.berniecode.com/writing/animator.html 34 35// Applies a sequence of numbers between 0 and 1 to a number of subjects 36// construct - see setOptions for parameters 37function Animator(options) { 38 this.setOptions(options); 39 var _this = this; 40 this.timerDelegate = function(){_this.onTimerEvent()}; 41 this.subjects = []; 42 this.subjectScopes = []; 43 this.target = 0; 44 this.state = 0; 45 this.lastTime = null; 46}; 47Animator.prototype = { 48 // apply defaults 49 setOptions: function(options) { 50 this.options = Animator.applyDefaults({ 51 interval: 20, // time between animation frames 52 duration: 400, // length of animation 53 onComplete: function(){}, 54 onStep: function(){}, 55 transition: Animator.tx.easeInOut 56 }, options); 57 }, 58 // animate from the current state to provided value 59 seekTo: function(to) { 60 this.seekFromTo(this.state, to); 61 }, 62 // animate from the current state to provided value 63 seekFromTo: function(from, to) { 64 this.target = Math.max(0, Math.min(1, to)); 65 this.state = Math.max(0, Math.min(1, from)); 66 this.lastTime = new Date().getTime(); 67 if (!this.intervalId) { 68 this.intervalId = window.setInterval(this.timerDelegate, this.options.interval); 69 } 70 }, 71 // animate from the current state to provided value 72 jumpTo: function(to) { 73 this.target = this.state = Math.max(0, Math.min(1, to)); 74 this.propagate(); 75 }, 76 // seek to the opposite of the current target 77 toggle: function() { 78 this.seekTo(1 - this.target); 79 }, 80 // add a function or an object with a method setState(state) that will be called with a number 81 // between 0 and 1 on each frame of the animation 82 addSubject: function(subject,scope) { 83 this.subjects[this.subjects.length] = subject; 84 this.subjectScopes[this.subjectScopes.length] = scope; 85 return this; 86 }, 87 // remove all subjects 88 clearSubjects: function() { 89 this.subjects = []; 90 this.subjectScopes = []; 91 }, 92 // forward the current state to the animation subjects 93 propagate: function() { 94 var value = this.options.transition(this.state); 95 for (var i=0; i<this.subjects.length; i++) { 96 if (this.subjects[i].setState) { 97 this.subjects[i].setState(value); 98 } else { 99 this.subjects[i].apply(this.subjectScopes[i],[value]); 100 } 101 } 102 }, 103 // called once per frame to update the current state 104 onTimerEvent: function() { 105 var now = new Date().getTime(); 106 var timePassed = now - this.lastTime; 107 this.lastTime = now; 108 var movement = (timePassed / this.options.duration) * (this.state < this.target ? 1 : -1); 109 if (Math.abs(movement) >= Math.abs(this.state - this.target)) { 110 this.state = this.target; 111 } else { 112 this.state += movement; 113 } 114 115 try {
116 this.propagate(); 117 } finally { 118 this.options.onStep.call(this); 119 if (this.target == this.state) { 120 window.clearInterval(this.intervalId); 121 this.intervalId = null; 122 this.options.onComplete.call(this); 123 } 124 } 125 }, 126 // shortcuts 127 play: function() {this.seekFromTo(0, 1)}, 128 reverse: function() {this.seekFromTo(1, 0)}, 129 // return a string describing this Animator, for debugging 130 inspect: function() { 131 var str = "#<Animator:\n"; 132 for (var i=0; i<this.subjects.length; i++) { 133 str += this.subjects[i].inspect(); 134 } 135 str += ">"; 136 return str; 137 } 138} 139// merge the properties of two objects 140Animator.applyDefaults = function(defaults, prefs) { 141 prefs = prefs || {}; 142 var prop, result = {}; 143 for (prop in defaults) result[prop] = prefs[prop] !== undefined ? prefs[prop] : defaults[prop]; 144 return result; 145} 146// make an array from any object 147Animator.makeArray = function(o) { 148 if (o == null) return []; 149 if (!o.length) return [o]; 150 var result = []; 151 for (var i=0; i<o.length; i++) result[i] = o[i]; 152 return result; 153} 154// convert a dash-delimited-property to a camelCaseProperty (c/o Prototype, thanks Sam!) 155Animator.camelize = function(string) { 156 var oStringList = string.split('-'); 157 if (oStringList.length == 1) return oStringList[0]; 158 159 var camelizedString = string.indexOf('-') == 0 160 ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) 161 : oStringList[0]; 162 163 for (var i = 1, len = oStringList.length; i < len; i++) { 164 var s = oStringList[i]; 165 camelizedString += s.charAt(0).toUpperCase() + s.substring(1); 166 } 167 return camelizedString; 168} 169// syntactic sugar for creating CSSStyleSubjects 170Animator.apply = function(el, style, options) { 171 if (style instanceof Array) { 172 return new Animator(options).addSubject(new CSSStyleSubject(el, style[0], style[1])); 173 } 174 return new Animator(options).addSubject(new CSSStyleSubject(el, style)); 175} 176// make a transition function that gradually accelerates. pass a=1 for smooth 177// gravitational acceleration, higher values for an exaggerated effect 178Animator.makeEaseIn = function(a) { 179 return function(state) { 180 return Math.pow(state, a*2); 181 } 182} 183// as makeEaseIn but for deceleration 184Animator.makeEaseOut = function(a) { 185 return function(state) { 186 return 1 - Math.pow(1 - state, a*2); 187 } 188} 189// make a transition function that, like an object with momentum being attracted to a point, 190// goes past the target then returns 191Animator.makeElastic = function(bounces) { 192 return function(state) { 193 state = Animator.tx.easeInOut(state); 194 return ((1-Math.cos(state * Math.PI * bounces)) * (1 - state)) + state; 195 } 196} 197// make an Attack Decay Sustain Release envelope that starts and finishes on the same level 198// 199Animator.makeADSR = function(attackEnd, decayEnd, sustainEnd, sustainLevel) { 200 if (sustainLevel == null) sustainLevel = 0.5; 201 return function(state) { 202 if (state < attackEnd) { 203 return state / attackEnd; 204 } 205 if (state < decayEnd) { 206 return 1 - ((state - attackEnd) / (decayEnd - attackEnd) * (1 - sustainLevel)); 207 } 208 if (state < sustainEnd) { 209 return sustainLevel; 210 } 211 return sustainLevel * (1 - ((state - sustainEnd) / (1 - sustainEnd))); 212 } 213} 214// make a transition function that, like a ball falling to floor, reaches the target and/ 215// bounces back again 216Animator.makeBounce = function(bounces) { 217 var fn = Animator.makeElastic(bounces); 218 return function(state) { 219 state = fn(state); 220 return state <= 1 ? state : 2-state; 221 } 222} 223 224// pre-made transition functions to use with the 'transition' option 225Animator.tx = { 226 easeInOut: function(pos){ 227 return ((-Math.cos(pos*Math.PI)/2) + 0.5); 228 }, 229 linear: function(x) { 230 return x; 231 }, 232 easeIn: Animator.makeEaseIn(1.5), 233 easeOut: Animator.makeEaseOut(1.5), 234 strongEaseIn: Animator.makeEaseIn(2.5), 235 strongEaseOut: Animator.makeEaseOut(2.5), 236 elastic: Animator.makeElastic(1), 237 veryElastic: Animator.makeElastic(3), 238 bouncy: Animator.makeBounce(1), 239 veryBouncy: Animator.makeBounce(3) 240} 241 242// animates a pixel-based style property between two integer values 243function NumericalStyleSubject(els, property, from, to, units) { 244 this.els = Animator.makeArray(els); 245 if (property == 'opacity' && window.ActiveXObject) { 246 this.property = 'filter'; 247 } else { 248 this.property = Animator.camelize(property); 249 } 250 this.from = parseFloat(from); 251 this.to = parseFloat(to); 252 this.units = units != null ? units : 'px'; 253} 254NumericalStyleSubject.prototype = { 255 setState: function(state) {
256 var style = this.getStyle(state); 257 var visibility = (this.property == 'opacity' && state == 0) ? 'hidden' : ''; 258 var j=0; 259 for (var i=0; i<this.els.length; i++) { 260 try { 261 this.els[i].style[this.property] = style; 262 } catch (e) { 263 // ignore fontWeight - intermediate numerical values cause exeptions in firefox 264 if (this.property != 'fontWeight') throw e; 265 } 266 if (j++ > 20) return; 267 } 268 }, 269 getStyle: function(state) { 270 state = this.from + ((this.to - this.from) * state); 271 if (this.property == 'filter') return "alpha(opacity=" + Math.round(state*100) + ")"; 272 if (this.property == 'opacity') return state; 273 return Math.round(state) + this.units; 274 }, 275 inspect: function() { 276 return "\t" + this.property + "(" + this.from + this.units + " to " + this.to + this.units + ")\n"; 277 } 278} 279 280// animates a colour based style property between two hex values 281function ColorStyleSubject(els, property, from, to) { 282 this.els = Animator.makeArray(els); 283 this.property = Animator.camelize(property); 284 this.to = this.expandColor(to); 285 this.from = this.expandColor(from); 286 this.origFrom = from; 287 this.origTo = to; 288} 289 290ColorStyleSubject.prototype = { 291 // parse "#FFFF00" to [256, 256, 0] 292 expandColor: function(color) { 293 var hexColor, red, green, blue; 294 hexColor = ColorStyleSubject.parseColor(color); 295 if (hexColor) { 296 red = parseInt(hexColor.slice(1, 3), 16); 297 green = parseInt(hexColor.slice(3, 5), 16); 298 blue = parseInt(hexColor.slice(5, 7), 16); 299 return [red,green,blue] 300 } 301 if (window.DEBUG) { 302 alert("Invalid colour: '" + color + "'"); 303 } 304 }, 305 getValueForState: function(color, state) { 306 return Math.round(this.from[color] + ((this.to[color] - this.from[color]) * state)); 307 }, 308 setState: function(state) { 309 var color = '#' 310 + ColorStyleSubject.toColorPart(this.getValueForState(0, state)) 311 + ColorStyleSubject.toColorPart(this.getValueForState(1, state)) 312 + ColorStyleSubject.toColorPart(this.getValueForState(2, state)); 313 for (var i=0; i<this.els.length; i++) { 314 this.els[i].style[this.property] = color; 315 } 316 }, 317 inspect: function() { 318 return "\t" + this.property + "(" + this.origFrom + " to " + this.origTo + ")\n"; 319 } 320} 321 322// return a properly formatted 6-digit hex colour spec, or false 323ColorStyleSubject.parseColor = function(string) { 324 var color = '#', match; 325 if(match = ColorStyleSubject.parseColor.rgbRe.exec(string)) { 326 var part; 327 for (var i=1; i<=3; i++) { 328 part = Math.max(0, Math.min(255, parseInt(match[i]))); 329 color += ColorStyleSubject.toColorPart(part); 330 } 331 return color; 332 } 333 if (match = ColorStyleSubject.parseColor.hexRe.exec(string)) { 334 if(match[1].length == 3) { 335 for (var i=0; i<3; i++) { 336 color += match[1].charAt(i) + match[1].charAt(i); 337 } 338 return color; 339 } 340 return '#' + match[1]; 341 } 342 return false; 343} 344// convert a number to a 2 digit hex string 345ColorStyleSubject.toColorPart = function(number) { 346 if (number > 255) number = 255; 347 var digits = number.toString(16); 348 if (number < 16) return '0' + digits; 349 return digits; 350} 351ColorStyleSubject.parseColor.rgbRe = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i; 352ColorStyleSubject.parseColor.hexRe = /^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/; 353 354// Animates discrete styles, i.e. ones that do not scale but have discrete values 355// that can't be interpolated 356function DiscreteStyleSubject(els, property, from, to, threshold) { 357 this.els = Animator.makeArray(els); 358 this.property = Animator.camelize(property); 359 this.from = from; 360 this.to = to; 361 this.threshold = threshold || 0.5; 362} 363 364DiscreteStyleSubject.prototype = { 365 setState: function(state) { 366 var j=0; 367 for (var i=0; i<this.els.length; i++) { 368 this.els[i].style[this.property] = state <= this.threshold ? this.from : this.to; 369 } 370 }, 371 inspect: function() { 372 return "\t" + this.property + "(" + this.from + " to " + this.to + " @ " + this.threshold + ")\n"; 373 } 374} 375 376// animates between two styles defined using CSS. 377// if style1 and style2 are present, animate between them, if only style1 378// is present, animate between the element's current style and style1 379function CSSStyleSubject(els, style1, style2) { 380 els = Animator.makeArray(els); 381 this.subjects = []; 382 if (els.length == 0) return; 383 var prop, toStyle, fromStyle; 384 if (style2) { 385 fromStyle = this.parseStyle(style1, els[0]); 386 toStyle = this.parseStyle(style2, els[0]); 387 } else { 388 toStyle = this.parseStyle(style1, els[0]); 389 fromStyle = {}; 390 for (prop in toStyle) { 391 fromStyle[prop] = CSSStyleSubject.getStyle(els[0], prop); 392 } 393 } 394 // remove unchanging properties 395 var prop; 396 for (prop in fromStyle) { 397 if (fromStyle[prop] == toStyle[prop]) { 398 delete fromStyle[prop]; 399 delete toStyle[prop]; 400 } 401 } 402 // discover the type (numerical or colour) of each style 403 var prop, units, match, type, from, to; 404 for (prop in fromStyle) { 405 var fromProp = String(fromStyle[prop]); 406 var toProp = String(toStyle[prop]); 407 if (toStyle[prop] == null) { 408 if (window.DEBUG) alert("No to style provided for '" + prop + '"');
409 continue; 410 } 411 412 if (from = ColorStyleSubject.parseColor(fromProp)) { 413 to = ColorStyleSubject.parseColor(toProp); 414 type = ColorStyleSubject; 415 } else if (fromProp.match(CSSStyleSubject.numericalRe) 416 && toProp.match(CSSStyleSubject.numericalRe)) { 417 from = parseFloat(fromProp); 418 to = parseFloat(toProp); 419 type = NumericalStyleSubject; 420 match = CSSStyleSubject.numericalRe.exec(fromProp); 421 var reResult = CSSStyleSubject.numericalRe.exec(toProp); 422 if (match[1] != null) { 423 units = match[1]; 424 } else if (reResult[1] != null) { 425 units = reResult[1]; 426 } else { 427 units = reResult; 428 } 429 } else if (fromProp.match(CSSStyleSubject.discreteRe) 430 && toProp.match(CSSStyleSubject.discreteRe)) { 431 from = fromProp; 432 to = toProp; 433 type = DiscreteStyleSubject; 434 units = 0; // hack - how to get an animator option down to here 435 } else { 436 if (window.DEBUG) { 437 alert("Unrecognised format for value of " 438 + prop + ": '" + fromStyle[prop] + "'"); 439 } 440 continue; 441 } 442 this.subjects[this.subjects.length] = new type(els, prop, from, to, units); 443 } 444} 445 446CSSStyleSubject.prototype = { 447 // parses "width: 400px; color: #FFBB2E" to {width: "400px", color: "#FFBB2E"} 448 parseStyle: function(style, el) { 449 var rtn = {}; 450 // if style is a rule set 451 if (style.indexOf(":") != -1) { 452 var styles = style.split(";"); 453 for (var i=0; i<styles.length; i++) { 454 var parts = CSSStyleSubject.ruleRe.exec(styles[i]); 455 if (parts) { 456 rtn[parts[1]] = parts[2]; 457 } 458 } 459 } 460 // else assume style is a class name 461 else { 462 var prop, value, oldClass; 463 oldClass = el.className; 464 el.className = style; 465 for (var i=0; i<CSSStyleSubject.cssProperties.length; i++) { 466 prop = CSSStyleSubject.cssProperties[i]; 467 value = CSSStyleSubject.getStyle(el, prop); 468 if (value != null) { 469 rtn[prop] = value; 470 } 471 } 472 el.className = oldClass; 473 } 474 return rtn; 475 476 }, 477 setState: function(state) { 478 for (var i=0; i<this.subjects.length; i++) { 479 this.subjects[i].setState(state); 480 } 481 }, 482 inspect: function() { 483 var str = ""; 484 for (var i=0; i<this.subjects.length; i++) { 485 str += this.subjects[i].inspect(); 486 } 487 return str; 488 } 489} 490// get the current value of a css property, 491CSSStyleSubject.getStyle = function(el, property){ 492 var style; 493 if(document.defaultView && document.defaultView.getComputedStyle){ 494 style = document.defaultView.getComputedStyle(el, "").getPropertyValue(property); 495 if (style) { 496 return style; 497 } 498 } 499 property = Animator.camelize(property); 500 if(el.currentStyle){ 501 style = el.currentStyle[property]; 502 } 503 return style || el.style[property] 504} 505 506 507CSSStyleSubject.ruleRe = /^\s*([a-zA-Z\-]+)\s*:\s*(\S(.+\S)?)\s*$/; 508CSSStyleSubject.numericalRe = /^-?\d+(?:\.\d+)?(%|[a-zA-Z]{2})?$/; 509CSSStyleSubject.discreteRe = /^\w+$/; 510 511// required because the style object of elements isn't enumerable in Safari 512/* 513CSSStyleSubject.cssProperties = ['background-color','border','border-color','border-spacing', 514'border-style','border-top','border-right','border-bottom','border-left','border-top-color', 515'border-right-color','border-bottom-color','border-left-color','border-top-width','border-right-width', 516'border-bottom-width','border-left-width','border-width','bottom','color','font-size','font-size-adjust', 517'font-stretch','font-style','height','left','letter-spacing','line-height','margin','margin-top', 518'margin-right','margin-bottom','margin-left','marker-offset','max-height','max-width','min-height', 519'min-width','orphans','outline','outline-color','outline-style','outline-width','overflow','padding', 520'padding-top','padding-right','padding-bottom','padding-left','quotes','right','size','text-indent', 521'top','width','word-spacing','z-index','opacity','outline-offset'];*/ 522 523 524CSSStyleSubject.cssProperties = ['azimuth','background','background-attachment','background-color','background-image','background-position','background-repeat','border-collapse','border-color','border-spacing','border-style','border-top','border-top-color','border-right-color','border-bottom-color','border-left-color','border-top-style','border-right-style','border-bottom-style','border-left-style','border-top-width','border-right-width','border-bottom-width','border-left-width','border-width','bottom','clear','clip','color','content','cursor','direction','display','elevation','empty-cells','css-float','font','font-family','font-size','font-size-adjust','font-stretch','font-style','font-variant','font-weight','height','left','letter-spacing','line-height','list-style','list-style-image','list-style-position','list-style-type','margin','margin-top','margin-right','margin-bottom','margin-left','max-height','max-width','min-height','min-width','orphans','outline','outline-color','outline-style','outline-width','overflow','padding','padding-top','padding-right','padding-bottom','padding-left','pause','position','right','size','table-layout','text-align','text-decoration','text-indent','text-shadow','text-transform','top','vertical-align','visibility','white-space','width','word-spacing','z-index','o
524pacity','outline-offset','overflow-x','overflow-y']; 525 526 527// chains several Animator objects together 528function AnimatorChain(animators, options) { 529 this.animators = animators; 530 this.setOptions(options); 531 for (var i=0; i<this.animators.length; i++) { 532 this.listenTo(this.animators[i]); 533 } 534 this.forwards = false; 535 this.current = 0; 536} 537 538AnimatorChain.prototype = { 539 // apply defaults 540 setOptions: function(options) { 541 this.options = Animator.applyDefaults({ 542 // by default, each call to AnimatorChain.play() calls jumpTo(0) of each animator 543 // before playing, which can cause flickering if you have multiple animators all 544 // targeting the same element. Set this to false to avoid this. 545 resetOnPlay: true 546 }, options); 547 }, 548 // play each animator in turn 549 play: function() { 550 this.forwards = true; 551 this.current = -1; 552 if (this.options.resetOnPlay) { 553 for (var i=0; i<this.animators.length; i++) { 554 this.animators[i].jumpTo(0); 555 } 556 } 557 this.advance(); 558 }, 559 // play all animators backwards 560 reverse: function() { 561 this.forwards = false; 562 this.current = this.animators.length; 563 if (this.options.resetOnPlay) { 564 for (var i=0; i<this.animators.length; i++) { 565 this.animators[i].jumpTo(1); 566 } 567 } 568 this.advance(); 569 }, 570 // if we have just play()'d, then call reverse(), and vice versa 571 toggle: function() { 572 if (this.forwards) { 573 this.seekTo(0); 574 } else { 575 this.seekTo(1); 576 } 577 }, 578 // internal: install an event listener on an animator's onComplete option 579 // to trigger the next animator 580 listenTo: function(animator) { 581 var oldOnComplete = animator.options.onComplete; 582 var _this = this; 583 animator.options.onComplete = function() { 584 if (oldOnComplete) oldOnComplete.call(animator); 585 _this.advance(); 586 } 587 }, 588 // play the next animator 589 advance: function() { 590 if (this.forwards) { 591 if (this.animators[this.current + 1] == null) return; 592 this.current++; 593 this.animators[this.current].play(); 594 } else { 595 if (this.animators[this.current - 1] == null) return; 596 this.current--; 597 this.animators[this.current].reverse(); 598 } 599 }, 600 // this function is provided for drop-in compatibility with Animator objects, 601 // but only accepts 0 and 1 as target values 602 seekTo: function(target) { 603 if (target <= 0) { 604 this.forwards = false; 605 this.animators[this.current].seekTo(0); 606 } else { 607 this.forwards = true; 608 this.animators[this.current].seekTo(1); 609 } 610 } 611} 612 613// an Accordion is a class that creates and controls a number of Animators. An array of elements is passed in, 614// and for each element an Animator and a activator button is created. When an Animator's activator button is 615// clicked, the Animator and all before it seek to 0, and all Animators after it seek to 1. This can be used to 616// create the classic Accordion effect, hence the name. 617// see setOptions for arguments 618function Accordion(options) { 619 this.setOptions(options); 620 var selected = this.options.initialSection, current; 621 if (this.options.rememberance) { 622 current = document.location.hash.substring(1); 623 } 624 this.rememberanceTexts = []; 625 this.ans = []; 626 var _this = this; 627 for (var i=0; i<this.options.sections.length; i++) { 628 var el = this.options.sections[i]; 629 var an = new Animator(this.options.animatorOptions); 630 var from = this.options.from + (this.options.shift * i); 631 var to = this.options.to + (this.options.shift * i); 632 an.addSubject(new NumericalStyleSubject(el, this.options.property, from, to, this.options.units)); 633 an.jumpTo(0); 634 var activator = this.options.getActivator(el); 635 activator.index = i; 636 activator.onclick = function(){_this.show(this.index)}; 637 this.ans[this.ans.length] = an; 638 this.rememberanceTexts[i] = activator.innerHTML.replace(/\s/g, ""); 639 if (this.rememberanceTexts[i] === current) { 640 selected = i; 641 } 642 } 643 this.show(selected); 644} 645 646Accordion.prototype = { 647 // apply defaults 648 setOptions: function(options) { 649 this.options = Object.extend({ 650 // REQUIRED: an array of elements to use as the accordion sections 651 sections: null, 652 // a function that locates an activator button element given a section element. 653 // by default it takes a button id from the section's "activator" attibute 654 getActivator: function(el) {return document.getElementById(el.getAttribute("activator"))}, 655 // shifts each animator's range, for example with options {from:0,to:100,shift:20} 656 // the animators' ranges will be 0-100, 20-120, 40-140 etc. 657 shift: 0, 658 // the first page to show 659 initialSection: 0, 660 // if set to true, document.location.hash will be used to preserve the open section across page reloads 661 rememberance: true, 662 // constructor arguments to the Animator objects 663 animatorOptions: {} 664 }, options || {}); 665 }, 666 show: function(section) { 667 for (var i=0; i<this.ans.length; i++) { 668 this.ans[i].seekTo(i > section ? 1 : 0); 669 } 670 if (this.options.rememberance) { 671 document.location.hash = this.rememberanceTexts[section]; 672 } 673 } 674}
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.