1/* 2 Animator.js 1.1.11 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// Applies a sequence of numbers between 0 and 1 to a number of subjects 34// construct - see setOptions for parameters 35function Animator(options) 36{ 37 try 38 { 39 this.setOptions(options); 40 var _this = this; 41 this.timerDelegate = function() 42 { 43 _this.onTimerEvent() 44 } 45 try 46 { 47 this.animFrameStart = null; 48 this.animFrameActive = false; 49 this.animFrameRequestID = null; 50 this.animFrameDelegate = function(timestamp) 51 { 52 var progress; 53 if (_this.animFrameStart === null) 54 _this.animFrameStart = timestamp; 55 56 progress = timestamp - _this.animFrameStart; 57// if (progress >= _this.options.interval) 58 { 59 _this.animFrameStart = null; 60 _this.onTimerEvent(); 61 } 62 if (_this.animFrameActive) 63 { 64 if (window.requestAnimationFrame != null) 65 _this.animFrameRequestID = window.requestAnimationFrame(_this.animFrameDelegate); 66 else if (window.mozRequestAnimationFrame != null) 67 _this.animFrameRequestID = window.mozRequestAnimationFrame(_this.animFrameDelegate); 68 else if (window.webkitRequestAnimationFrame != null) 69 _this.animFrameRequestID = window.webkitRequestAnimationFrame(_this.animFrameDelegate); 70 else if (window.msRequestAnimationFrame != null) 71 _this.animFrameRequestID = window.msRequestAnimationFrame(_this.animFrameDelegate); 72 } 73 } 74 } 75 catch (e) 76 { 77 if (typeof(HRLogError) == 'function') 78 HRLogError(e); 79 } 80 this.subjects = []; 81 this.target = 0; 82 this.state = 0; 83 this.lastTime = null; 84 } 85 catch (e) 86 { 87 if (typeof(HRLogError) == 'function') 88 HRLogError(e); 89 } 90}; 91 92Animator.prototype = 93{ 94 // apply defaults 95 setOptions : function(options) 96 { 97 this.options = Animator.applyDefaults( 98 { 99 interval : 20, // time between animation frames 100 duration : 400, // length of animation 101 onComplete : function() 102 { 103 }, 104 onStep : function() 105 { 106 }, 107 transition : Animator.tx.easeInOut 108 }, options); 109 }, 110 // animate from the current state to provided value 111 seekTo : function(to) 112 { 113 this.seekFromTo(this.state, to); 114 }, 115 // animate from the current state to provided value 116 seekFromTo : function(from, to) 117 { 118 this.target = Math.max(0, Math.min(1, to)); 119 this.state = Math.max(0, Math.min(1, from)); 120 this.lastTime = new Date().getTime(); 121 if (window.requestAnimationFrame == null && window.mozRequestAnimationFrame == null && window.webkitRequestAnimationFrame == null && window.msRequestAnimationFrame == null) 122 { 123 if (!this.intervalId) 124 { 125 this.intervalId = window.setInterval(this.timerDelegate, this.options.interval); 126 } 127 } 128 else 129 { 130 if (!this.animFrameActive) 131 { 132 this.animFrameActive = true; 133 if (window.requestAnimationFrame != null) 134 this.animFrameRequestID = window.requestAnimationFrame(this.animFrameDelegate); 135 else if (window.mozRequestAnimationFrame != null) 136 this.animFrameRequestID = window.mozRequestAnimationFrame(this.animFrameDelegate); 137 else if (window.webkitRequestAnimationFrame != null) 138 this.animFrameRequestID = window.webkitRequestAnimationFrame(this.animFrameDelegate); 139 else if (window.msRequestAnimationFrame != null) 140 this.animFrameRequestID = window.msRequestAnimationFrame(this.animFrameDelegate); 141 } 142 } 143 }, 144 // animate from the current state to provided value 145 jumpTo : function(to) 146 { 147 this.target = this.state = Math.max(0, Math.min(1, to));
148 this.propagate(); 149 }, 150 // seek to the opposite of the current target 151 toggle : function() 152 { 153 this.seekTo(1 - this.target); 154 }, 155 // add a function or an object with a method setState(state) that will be 156 // called with a number 157 // between 0 and 1 on each frame of the animation 158 addSubject : function(subject) 159 { 160 this.subjects[this.subjects.length] = subject; 161 return this; 162 }, 163 // remove all subjects 164 clearSubjects : function() 165 { 166 this.subjects = []; 167 }, 168 // forward the current state to the animation subjects 169 propagate : function() 170 { 171 var value = this.options.transition(this.state); 172 for ( var i = 0; i < this.subjects.length; i++) 173 { 174 if (this.subjects[i].setState) 175 { 176 this.subjects[i].setState(value); 177 } 178 else 179 { 180 this.subjects[i](value); 181 } 182 } 183 }, 184 // called once per frame to update the current state 185 onTimerEvent : function() 186 { 187 var now = new Date().getTime(); 188 var timePassed = now - this.lastTime; 189 this.lastTime = now; 190 var movement = (timePassed / this.options.duration) * (this.state < this.target ? 1 : -1); 191 if (Math.abs(movement) >= Math.abs(this.state - this.target)) 192 { 193 this.state = this.target; 194 } 195 else 196 { 197 this.state += movement; 198 } 199 200 try 201 { 202 this.propagate(); 203 } 204 finally 205 { 206 this.options.onStep.call(this); 207 if (this.target == this.state) 208 { 209 this.stop(); 210 } 211 } 212 }, 213 stop : function() 214 { 215 if (window.requestAnimationFrame == null && window.mozRequestAnimationFrame == null && window.webkitRequestAnimationFrame == null && window.msRequestAnimationFrame == null) 216 { 217 if (this.intervalId) 218 { 219 window.clearInterval(this.intervalId); 220 this.intervalId = null; 221 this.options.onComplete.call(this); 222 } 223 } 224 else 225 { 226 if (this.animFrameActive) 227 { 228 this.animFrameActive = false; 229 if (this.animFrameRequestID != null) 230 { 231 if (window.cancelAnimationFrame != null) 232 window.cancelAnimationFrame(this.animFrameRequestID); 233 else if (window.mozCancelAnimationFrame != null) 234 window.mozCancelAnimationFrame(this.animFrameRequestID); 235 } 236 this.animFrameRequestID = null; 237 this.options.onComplete.call(this); 238 } 239 } 240 }, 241 // shortcuts 242 play : function() 243 { 244 this.seekFromTo(0, 1) 245 }, 246 reverse : function() 247 { 248 this.seekFromTo(1, 0) 249 }, 250 // return a string describing this Animator, for debugging 251 inspect : function() 252 { 253 var str = "#<Animator:\n"; 254 for ( var i = 0; i < this.subjects.length; i++) 255 { 256 str += this.subjects[i].inspect(); 257 } 258 str += ">"; 259 return str; 260 } 261} 262// merge the properties of two objects 263Animator.applyDefaults = function(defaults, prefs) 264{ 265 prefs = prefs || {}; 266 var prop, result = {}; 267 for (prop in defaults) 268 result[prop] = prefs[prop] !== undefined ? prefs[prop] : defaults[prop]; 269 270 return result; 271} 272// make an array from any object 273Animator.makeArrayOfElements = function(o) 274{ 275 if (o == null) 276 return []; 277 if ("string" == typeof o) 278 { 279 return [ document.getElementById(o) ]; 280 } 281 if (!o.length) 282 return [ o ]; 283 var result = []; 284 for ( var i = 0; i < o.length; i++) 285 { 286 if ("string" == typeof o[i]) 287 { 288 result[i] = document.getElementById(o[i]); 289 } 290 else 291 { 292 result[i] = o[i]; 293 } 294 } 295 return result; 296} 297// convert a dash-delimited-property to a camelCaseProperty (c/o Prototype, thanks Sam!) 298Animator.camelize = function(string) 299{ 300 var oStringList = string.split('-'); 301 if (oStringList.length == 1) 302 return oStringList[0]; 303 304 var camelizedString = string.indexOf('-') == 0 ? 305 oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : 306 oStringList[0]; 307 308 for ( var i = 1, len = oStringList.length; i < len; i++) 309 { 310 var s = oStringList[i]; 311 camelizedString += s.charAt(0).toUpperCase() + s.substring(1); 312 } 313 return camelizedString; 314} 315// syntactic sugar for creating CSSStyleSubjects 316Animator.apply = function(el, style, options) 317{ 318 if (style instanceof Array) 319 { 320 return new Animator(options).addSubject(new CSSStyleSubject(el, style[0], style[1])); 321 } 322 return new Animator(options).addSubject(new CSSStyleSubject(el, style)); 323} 324// make a transition function that gradually accelerates. pass a=1 for smooth 325// gravitational acceleration, higher values for an exaggerated effect 326Animator.makeEaseIn = function(a) 327{ 328 return function(state) 329 { 330 return Math.pow(state, a * 2); 331 } 332} 333// as makeEaseIn but for deceleration 334Animator.makeEaseOut = function(a) 335{ 336 return function(state) 337 { 338 return 1 - Math.pow(1 - state, a * 2); 339 } 340} 341// make a transition function that, like an object with momentum being attracted 342// to a point, 343// goes past the target then returns
344Animator.makeElastic = function(bounces) 345{ 346 return function(state) 347 { 348 state = Animator.tx.easeInOut(state); 349 return ((1 - Math.cos(state * Math.PI * bounces)) * (1 - state)) + state; 350 } 351} 352// make an Attack Decay Sustain Release envelope that starts and finishes on the same level 353// 354Animator.makeADSR = function(attackEnd, decayEnd, sustainEnd, sustainLevel) 355{ 356 if (sustainLevel == null) 357 sustainLevel = 0.5; 358 359 return function(state) 360 { 361 if (state < attackEnd) 362 { 363 return state / attackEnd; 364 } 365 if (state < decayEnd) 366 { 367 return 1 - ((state - attackEnd) / (decayEnd - attackEnd) * (1 - sustainLevel)); 368 } 369 if (state < sustainEnd) 370 { 371 return sustainLevel; 372 } 373 return sustainLevel * (1 - ((state - sustainEnd) / (1 - sustainEnd))); 374 } 375} 376// make a transition function that, like a ball falling to floor, reaches the target and/ 377// bounces back again 378Animator.makeBounce = function(bounces) 379{ 380 var fn = Animator.makeElastic(bounces); 381 return function(state) 382 { 383 state = fn(state); 384 return state <= 1 ? state : 2 - state; 385 } 386} 387 388 389// pre-made transition functions to use with the 'transition' option 390Animator.tx = 391{ 392 easeInOut : function(pos) 393 { 394 return ((-Math.cos(pos * Math.PI) / 2) + 0.5); 395 }, 396 linear : function(x) 397 { 398 return x; 399 }, 400 easeInOutQuad : function(pos) 401 { 402 if ((pos /= 0.5) < 1) 403 return 0.5 * pos * pos + 0; 404 return -0.5 * ((--pos) * (pos - 2) - 1) + 0; 405 }, 406 easeInOutCubic : function(pos) 407 { 408 if ((pos /= 0.5) < 1) 409 return 0.5 * pos * pos * pos + 0; 410 return 0.5 * ((pos -= 2) * pos * pos + 2) + 0; 411 }, 412 easeInOutQuart : function(pos) 413 { 414 if ((pos /= 0.5) < 1) 415 return 0.5 * pos * pos * pos * pos + 0; 416 return -0.5 * ((pos -= 2) * pos * pos * pos - 2) + 0; 417 }, 418 easeInOutQuint : function(pos) 419 { 420 if ((pos /= 0.5) < 1) 421 return 0.5 * pos * pos * pos * pos * pos + 0; 422 return 0.5 * ((pos -= 2) * pos * pos * pos * pos + 2) + 0; 423 }, 424 easeIn : Animator.makeEaseIn(1.5), 425 easeOut : Animator.makeEaseOut(1.5), 426 strongEaseIn : Animator.makeEaseIn(2.5), 427 strongEaseOut : Animator.makeEaseOut(2.5), 428 elastic : Animator.makeElastic(1), 429 veryElastic : Animator.makeElastic(3), 430 bouncy : Animator.makeBounce(1), 431 veryBouncy : Animator.makeBounce(3) 432} 433 434// animates a pixel-based style property between two integer values 435function NumericalStyleSubject(els, property, from, to, units) 436{ 437 this.els = Animator.makeArrayOfElements(els); 438 var ieVersion = getIEVersionNr(); 439 if (property == 'opacity' && window.ActiveXObject && ieVersion <= 9 && ieVersion > 0) 440 { 441 this.property = 'filter'; 442 } 443 else 444 { 445 this.property = Animator.camelize(property); 446 } 447 this.from = parseFloat(from); 448 this.to = parseFloat(to); 449 this.units = units != null ? units : 'px'; 450} 451NumericalStyleSubject.prototype = 452{ 453 setState : function(state) 454 { 455 var style = this.getStyle(state); 456 var visibility = (this.property == 'opacity' && state == 0) ? 'hidden' : ''; 457 var j = 0; 458 for ( var i = 0; i < this.els.length; i++) 459 { 460 try 461 { 462 this.els[i].style[this.property] = style; 463 } 464 catch (e) 465 { 466 // ignore fontWeight - intermediate numerical values cause exeptions 467 // in firefox 468 if (this.property != 'fontWeight') 469 throw e; 470 } 471 if (j++ > 20) 472 return; 473 } 474 }, 475 getStyle : function(state) 476 { 477 state = this.from + ((this.to - this.from) * state); 478 if (this.property == 'filter') 479 return "alpha(opacity=" + Math.round(state * 100) + ")"; 480 if (this.property == 'opacity') 481 return state; 482 return Math.round(state) + this.units; 483 }, 484 inspect : function() 485 { 486 return "\t" + this.property + "(" + this.from + this.units + " to " + this.to + this.units 487 + ")\n"; 488 } 489} 490 491// animates a colour based style property between two hex values 492function ColorStyleSubject(els, property, from, to) 493{ 494 this.els = Animator.makeArrayOfElements(els); 495 this.property = Animator.camelize(property); 496 this.to = this.expandColor(to); 497 this.from = this.expandColor(from); 498 this.origFrom = from; 499 this.origTo = to; 500} 501 502ColorStyleSubject.prototype = { 503 // parse "#FFFF00" to [256, 256, 0] 504 expandColor: function(color) { 505 var hexColor, red, green, blue; 506 hexColor = ColorStyleSubject.parseColor(color); 507 if (hexColor) { 508 red = parseInt(hexColor.slice(1, 3), 16); 509 green = parseInt(hexColor.slice(3, 5), 16); 510 blue = parseInt(hexColor.slice(5, 7), 16); 511 return [red,green,blue] 512 } 513 if (window.ANIMATOR_DEBUG) { 514 alert("Invalid colour: '" + color + "'"); 515 } 516 }, 517 getValueForState: function(color, state) { 518 return Math.round(this.from[color] + ((this.to[color] - this.from[color]) * state)); 519 }, 520 setState: function(state) { 521 var color = '#' 522 + ColorStyleSubject.toColorPart(this.getValueForState(0, state)) 523 + ColorStyleSubject.toColorPart(this.getValueForState(1, state)) 524 + ColorStyleSubject.toColorPart(this.getValueForState(2, state)); 525 for (var i=0; i<this.els.length; i++) { 526 this.els[i].style[this.property] = color; 527 } 528 }, 529 inspect: function() { 530 return "\t" + this.property + "(" + this.origFrom + " to " + this.origTo + ")\n"; 531 } 532} 533 534// return a properly formatted 6-digit hex colour spec, or false 535ColorStyleSubject.parseColor = function(string) 536{ 537 var color = '#', match; 538 if (match = ColorStyleSubject.parseColor.rgbRe.exec(string)) 539 { 540 var part; 541 for ( var i = 1; i <= 3; i++) 542 { 543 part = Math.max(0, Math.min(255, parseInt(match[i]))); 544 color += ColorStyleSubject.toColorPart(part); 545 } 546 return color; 547 } 548 if (match = ColorStyleSubject.parseColor.hexRe.exec(string)) 549 { 550 if (match[1].length == 3) 551 { 552 for ( var i = 0; i < 3; i++) 553 { 554 color += match[1].charAt(i) + match[1].charAt(i); 555 } 556 return color; 557 } 558 return '#' + match[1]; 559 } 560 return false; 561} 562// convert a number to a 2 digit hex string 563ColorStyleSubject.toColorPart = function(number) 564{ 565 if (number > 255) 566 number = 255; 567 var digits = number.toString(16); 568 if (number < 16) 569 return '0' + digits; 570 return digits; 571} 572ColorStyleSubject.parseColor.rgbRe = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i; 573ColorStyleSubject.parseColor.hexRe = /^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/; 574 575// Animates discrete styles, i.e. ones that do not scale but have discrete values 576// that can't be interpolated 577function DiscreteStyleSubject(els, property, from, to, threshold) 578{ 579 this.els = Animator.makeArrayOfElements(els); 580 this.property = Animator.camelize(property); 581 this.from = from; 582 this.to = to; 583 this.threshold = threshold || 0.5; 584} 585 586DiscreteStyleSubject.prototype = 587{ 588 setState : function(state) 589 { 590 var j = 0; 591 for ( var i = 0; i < this.els.length; i++) 592 { 593 this.els[i].style[this.property] = state <= this.threshold ? this.from : this.to; 594 } 595 }, 596 inspect : function() 597 { 598 return "\t" + this.property + "(" + this.from + " to " + this.to + " @ " + this.threshold + ")\n"; 599 } 600} 601 602//Animates numerical properties, i.e. scrollTop 603function NumericalPropertySubject(els, property, from, to) 604{ 605 this.els = Animator.makeArrayOfElements(els); 606 this.property = Animator.camelize(property); 607 this.from = parseFloat(from); 608 this.to = parseFloat(to); 609} 610 611NumericalPropertySubject.prototype = 612{ 613 setState : function(state) 614 { 615 var j = 0; 616 state = this.from + ((this.to - this.from) * state); 617 for ( var i = 0; i < this.els.length; i++) 618 { 619 this.els[i][this.property] = Math.round(state); 620 } 621 }, 622 inspect : function() 623 { 624 return "\t" + this.property + "(" + this.from + " to " + this.to + ")\n"; 625 } 626} 627 628// animates between two styles defined using CSS. 629// if style1 and style2 are present, animate between them, if only style1 630// is present, animate between the element's current style and style1 631function CSSStyleSubject(els, style1, style2) 632{ 633 els = Animator.makeArrayOfElements(els); 634 this.subjects = []; 635 if (els.length == 0) 636 return; 637 638 var prop, toStyle, fromStyle; 639 if (style2) 640 { 641 fromStyle = this.parseStyle(style1, els[0]); 642 toStyle = this.parseStyle(style2, els[0]); 643 } 644 else 645 { 646 toStyle = this.parseStyle(style1, els[0]); 647 fromStyle = 648 {}; 649 for (prop in toStyle) 650 { 651 fromStyle[prop] = CSSStyleSubject.getStyle(els[0], prop); 652 } 653 } 654 // remove unchanging properties 655 var prop; 656 for (prop in fromStyle) 657 { 658 if (fromStyle[prop] == toStyle[prop]) 659 { 660 delete fromStyle[prop]; 661 delete toStyle[prop]; 662 } 663 } 664 // discover the type (numerical or colour) of each style 665 var prop, units, match, type, from, to; 666 for (prop in fromStyle) 667 { 668 var fromProp = String(fromStyle[prop]); 669 var toProp = String(toStyle[prop]); 670 if (toStyle[prop] == null) 671 { 672 if (window.ANIMATOR_DEBUG) 673 alert("No to style provided for '" + prop + '"'); 674 continue; 675 } 676 677 if (from = ColorStyleSubject.parseColor(fromProp)) 678 { 679 to = ColorStyleSubject.parseColor(toProp); 680 type = ColorStyleSubject; 681 } 682 else if (fromProp.match(CSSStyleSubject.numericalRe) 683 && toProp.match(CSSStyleSubject.numericalRe)) 684 { 685 from = parseFloat(fromProp); 686 to = parseFloat(toProp); 687 type = NumericalStyleSubject; 688 match = CSSStyleSubject.numericalRe.exec(fromProp); 689 var reResult = CSSStyleSubject.numericalRe.exec(toProp); 690 if (match[1] != null) 691 { 692 units = match[1]; 693 } 694 else if (reResult[1] != null) 695 { 696 units = reResult[1]; 697 } 698 else 699 { 700 units = reResult; 701 } 702 } 703 else if ( fromProp.match(CSSStyleSubject.discreteRe) 704 && toProp.match(CSSStyleSubject.discreteRe)) 705 { 706 from = fromProp; 707 to = toProp; 708 type = DiscreteStyleSubject; 709 units = 0; // hack - how to get an animator option down to here 710 } 711 else 712 { 713 if (window.ANIMATOR_DEBUG) 714 { 715 alert("Unrecognised format for value of " + prop + ": '" + fromStyle[prop] + "'"); 716 } 717 continue; 718 } 719 this.subjects[this.subjects.length] = new type(els, prop, from, to, units); 720 } 721} 722 723CSSStyleSubject.prototype = 724{ 725 // parses "width: 400px; color: #FFBB2E" to {width: "400px", color: 726 // "#FFBB2E"} 727 parseStyle : function(style, el) 728 { 729 var rtn = 730 {}; 731 // if style is a rule set 732 if (style.indexOf(":") != -1) 733 { 734 var styles = style.split(";"); 735 for ( var i = 0; i < styles.length; i++) 736 { 737 var parts = CSSStyleSubject.ruleRe.exec(styles[i]); 738 if (parts) 739 { 740 rtn[parts[1]] = parts[2]; 741 } 742 } 743 } 744 // else assume style is a class name 745 else 746 { 747 var prop, value, oldClass; 748 oldClass = el.className; 749 el.className = style; 750 for ( var i = 0; i < CSSStyleSubject.cssProperties.length; i++) 751 { 752 prop = CSSStyleSubject.cssProperties[i];
753 value = CSSStyleSubject.getStyle(el, prop); 754 if (value != null) 755 { 756 rtn[prop] = value; 757 } 758 } 759 el.className = oldClass; 760 } 761 return rtn; 762 763 }, 764 setState : function(state) 765 { 766 for ( var i = 0; i < this.subjects.length; i++) 767 { 768 this.subjects[i].setState(state); 769 } 770 }, 771 inspect : function() 772 { 773 var str = ""; 774 for ( var i = 0; i < this.subjects.length; i++) 775 { 776 str += this.subjects[i].inspect(); 777 } 778 return str; 779 } 780} 781// get the current value of a css property, 782CSSStyleSubject.getStyle = function(el, property) 783{ 784 var style; 785 if (document.defaultView && document.defaultView.getComputedStyle) 786 { 787 style = document.defaultView.getComputedStyle(el, "").getPropertyValue(property); 788 if (style) 789 { 790 return style; 791 } 792 } 793 property = Animator.camelize(property); 794 if (el.currentStyle) 795 { 796 style = el.currentStyle[property]; 797 } 798 return style || el.style[property] 799} 800 801 802CSSStyleSubject.ruleRe = /^\s*([a-zA-Z\-]+)\s*:\s*(\S(.+\S)?)\s*$/; 803CSSStyleSubject.numericalRe = /^-?\d+(?:\.\d+)?(%|[a-zA-Z]{2})?$/; 804CSSStyleSubject.discreteRe = /^\w+$/; 805 806// required because the style object of elements isn't enumerable in Safari 807/* 808CSSStyleSubject.cssProperties = ['background-color','border','border-color','border-spacing', 809'border-style','border-top','border-right','border-bottom','border-left','border-top-color', 810'border-right-color','border-bottom-color','border-left-color','border-top-width','border-right-width', 811'border-bottom-width','border-left-width','border-width','bottom','color','font-size','font-size-adjust', 812'font-stretch','font-style','height','left','letter-spacing','line-height','margin','margin-top', 813'margin-right','margin-bottom','margin-left','marker-offset','max-height','max-width','min-height', 814'min-width','orphans','outline','outline-color','outline-style','outline-width','overflow','padding', 815'padding-top','padding-right','padding-bottom','padding-left','quotes','right','size','text-indent', 816'top','width','word-spacing','z-index','opacity','outline-offset'];*/ 817 818 819CSSStyleSubject.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','opacity','outline-offset','overflow-x','overflow-y']; 820 821 822// chains several Animator objects together 823function AnimatorChain(animators, options) 824{ 825 this.animators = animators; 826 this.setOptions(options); 827 for ( var i = 0; i < this.animators.length; i++) 828 { 829 this.listenTo(this.animators[i]); 830 } 831 this.forwards = false; 832 this.current = 0; 833} 834 835AnimatorChain.prototype = 836{ 837 // apply defaults 838 setOptions : function(options) 839 { 840 this.options = Animator.applyDefaults( 841 { 842 // by default, each call to AnimatorChain.play() calls jumpTo(0) of 843 // each animator 844 // before playing, which can cause flickering if you have multiple 845 // animators all
846 // targeting the same element. Set this to false to avoid this. 847 resetOnPlay : true 848 }, options); 849 }, 850 // play each animator in turn 851 play : function() 852 { 853 this.forwards = true; 854 this.current = -1; 855 if (this.options.resetOnPlay) 856 { 857 for ( var i = 0; i < this.animators.length; i++) 858 { 859 this.animators[i].jumpTo(0); 860 } 861 } 862 this.advance(); 863 }, 864 // play all animators backwards 865 reverse : function() 866 { 867 this.forwards = false; 868 this.current = this.animators.length; 869 if (this.options.resetOnPlay) 870 { 871 for ( var i = 0; i < this.animators.length; i++) 872 { 873 this.animators[i].jumpTo(1); 874 } 875 } 876 this.advance(); 877 }, 878 // if we have just play()'d, then call reverse(), and vice versa 879 toggle : function() 880 { 881 if (this.forwards) 882 { 883 this.seekTo(0); 884 } 885 else 886 { 887 this.seekTo(1); 888 } 889 }, 890 // internal: install an event listener on an animator's onComplete option 891 // to trigger the next animator 892 listenTo : function(animator) 893 { 894 var oldOnComplete = animator.options.onComplete; 895 var _this = this; 896 animator.options.onComplete = function() 897 { 898 if (oldOnComplete) 899 oldOnComplete.call(animator); 900 _this.advance(); 901 } 902 }, 903 // play the next animator 904 advance : function() 905 { 906 if (this.forwards) 907 { 908 if (this.animators[this.current + 1] == null) 909 return; 910 this.current++; 911 this.animators[this.current].play(); 912 } 913 else 914 { 915 if (this.animators[this.current - 1] == null) 916 return; 917 this.current--; 918 this.animators[this.current].reverse(); 919 } 920 }, 921 // this function is provided for drop-in compatibility with Animator objects, 922 // but only accepts 0 and 1 as target values 923 seekTo : function(target) 924 { 925 if (target <= 0) 926 { 927 this.forwards = false; 928 this.animators[this.current].seekTo(0); 929 } 930 else 931 { 932 this.forwards = true; 933 this.animators[this.current].seekTo(1); 934 } 935 } 936} 937 938// an Accordion is a class that creates and controls a number of Animators. An 939// array of elements is passed in, 940// and for each element an Animator and a activator button is created. When an 941// Animator's activator button is 942// clicked, the Animator and all before it seek to 0, and all Animators after it 943// seek to 1. This can be used to 944// create the classic Accordion effect, hence the name. 945// see setOptions for arguments 946function Accordion(options) 947{ 948 this.setOptions(options); 949 var selected = this.options.initialSection, current; 950 if (this.options.rememberance) 951 { 952 current = document.location.hash.substring(1); 953 } 954 this.rememberanceTexts = []; 955 this.ans = []; 956 var _this = this; 957 for ( var i = 0; i < this.options.sections.length; i++) 958 { 959 var el = this.options.sections[i]; 960 var an = new Animator(this.options.animatorOptions); 961 var from = this.options.from + (this.options.shift * i); 962 var to = this.options.to + (this.options.shift * i); 963 an.addSubject(new NumericalStyleSubject(el, this.options.property, from, to, 964 this.options.units)); 965 an.jumpTo(0); 966 var activator = this.options.getActivator(el); 967 activator.index = i; 968 activator.onclick = function() 969 { 970 _this.show(this.index) 971 }; 972 this.ans[this.ans.length] = an; 973 this.rememberanceTexts[i] = activator.innerHTML.replace(/\s/g, ""); 974 if (this.rememberanceTexts[i] === current) 975 { 976 selected = i; 977 } 978 } 979 this.show(selected); 980} 981 982Accordion.prototype = 983{ 984 // apply defaults 985 setOptions : function(options) 986 { 987 this.options = Object.extend( 988 { 989 // REQUIRED: an array of elements to use as the accordion sections 990 sections : null, 991 // a function that locates an activator button element given a section 992 // element. 993 // by default it takes a button id from the section's "activator" 994 // attibute 995 getActivator : function(el) 996 { 997 return document.getElementById(el.getAttribute("activator")) 998 }, 999 // shifts each animator's range, for example with options 1000 // {from:0,to:100,shift:20} 1001 // the animators' ranges will be 0-100, 20-120, 40-140 etc. 1002 shift : 0, 1003 // the first page to show 1004 initialSection : 0, 1005 // if set to true, document.location.hash will be used to preserve the 1006 // open section across page reloads 1007 rememberance : true, 1008 // constructor arguments to the Animator objects 1009 animatorOptions : 1010 {} 1011 }, options || 1012 {}); 1013 }, 1014 show : function(section) 1015 { 1016 for ( var i = 0; i < this.ans.length; i++) 1017 { 1018 this.ans[i].seekTo(i > section ? 1 : 0); 1019 } 1020 if (this.options.rememberance) 1021 { 1022 document.location.hash = this.rememberanceTexts[section]; 1023 } 1024 } 1025} 1026 1027// 1028// Ein paar Hilfsfunktionen, bis wir uns sicher sein können, dass immer die HRSysLib zuvor geladen ist. 1029// 1030function getIEVersionNr() 1031{ 1032 var ieVersion = -1; // Default kein IE! 1033 try 1034 { 1035 // Wenn wir unser Browser-Objekt am Start haben, fragen wir das. 1036 // 1037 if (typeof(HRBrowser) != 'undefined' && typeof(HRBrowser.getIEVersionsNr) == 'function') 1038 return HRBrowser.getIEVersionsNr(); 1039 1040 var agentStr = navigator.userAgent.toLowerCase(); 1041 var ieKennung = 'msie'; 1042 var indexOfIE = agentStr.indexOf(ieKennung); 1043 if (indexOfIE >= 0) 1044 { 1045 agentStr = agentStr.substring(indexOfIE + ieKennung.length); 1046 ieVersion = parseFloat(agentStr); 1047 } 1048 } 1049 catch (e) 1050 {} 1051 return ieVersion; 1052}
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.