1 2String.prototype.parseColor = function() { 3 var color = '#'; 4 if(this.slice(0,4) == 'rgb(') { 5 var cols = this.slice(4,this.length-1).split(','); 6 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); 7 } else { 8 if(this.slice(0,1) == '#') { 9 if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); 10 if(this.length==7) color = this.toLowerCase(); 11 } 12 } 13 return(color.length==7 ? color : (arguments[0] || this)); 14} 15 16/*--------------------------------------------------------------------------*/ 17 18Element.collectTextNodes = function(element) { 19 return $A($(element).childNodes).collect( function(node) { 20 return (node.nodeType==3 ? node.nodeValue : 21 (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); 22 }).flatten().join(''); 23} 24 25Element.collectTextNodesIgnoreClass = function(element, className) { 26 return $A($(element).childNodes).collect( function(node) { 27 return (node.nodeType==3 ? node.nodeValue : 28 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 29 Element.collectTextNodesIgnoreClass(node, className) : '')); 30 }).flatten().join(''); 31} 32 33Element.setContentZoom = function(element, percent) { 34 element = $(element); 35 element.setStyle({fontSize: (percent/100) + 'em'}); 36 if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); 37 return element; 38} 39 40Element.getOpacity = function(element){ 41 return $(element).getStyle('opacity'); 42} 43 44Element.setOpacity = function(element, value){ 45 return $(element).setStyle({opacity:value}); 46} 47 48Element.getInlineOpacity = function(element){ 49 return $(element).style.opacity || ''; 50} 51 52Element.forceRerendering = function(element) { 53 try { 54 element = $(element); 55 var n = document.createTextNode(' '); 56 element.appendChild(n); 57 element.removeChild(n); 58 } catch(e) { } 59}; 60 61/*--------------------------------------------------------------------------*/ 62 63Array.prototype.call = function() { 64 var args = arguments; 65 this.each(function(f){ f.apply(this, args) }); 66} 67 68/*--------------------------------------------------------------------------*/ 69 70var Effect = { 71 _elementDoesNotExistError: { 72 name: 'ElementDoesNotExistError', 73 message: 'The specified DOM element does not exist, but is required for this effect to operate' 74 }, 75 tagifyText: function(element) { 76 if(typeof Builder == 'undefined') 77 throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); 78 79 var tagifyStyle = 'position:relative'; 80 if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1'; 81 82 element = $(element); 83 $A(element.childNodes).each( function(child) { 84 if(child.nodeType==3) { 85 child.nodeValue.toArray().each( function(character) { 86 element.insertBefore( 87 Builder.node('span',{style: tagifyStyle}, 88 character == ' ' ? String.fromCharCode(160) : character), 89 child); 90 }); 91 Element.remove(child); 92 } 93 }); 94 }, 95 multiple: function(element, effect) { 96 var elements; 97 if(((typeof element == 'object') || 98 (typeof element == 'function')) && 99 (element.length)) 100 elements = element; 101 else 102 elements = $(element).childNodes; 103 104 var options = Object.extend({ 105 speed: 0.1, 106 delay: 0.0 107 }, arguments[2] || {}); 108 var masterDelay = options.delay; 109 110 $A(elements).each( function(element, index) { 111 new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); 112 }); 113 }, 114 PAIRS: { 115 'slide': ['SlideDown','SlideUp'], 116 'blind': ['BlindDown','BlindUp'], 117 'appear': ['Appear','Fade'] 118 }, 119 toggle: function(element, effect) { 120 element = $(element); 121 effect = (effect || 'appear').toLowerCase(); 122 var options = Object.extend({ 123 queue: { position:'end', scope:(element.id || 'global'), limit: 1 } 124 }, arguments[2] || {}); 125 Effect[element.visible() ? 126 Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); 127 } 128}; 129 130var Effect2 = Effect; // deprecated 131 132/* ------------- transitions ------------- */ 133 134Effect.Transitions = { 135 linear: Prototype.K, 136 sinoidal: function(pos) { 137 return (-Math.cos(pos*Math.PI)/2) + 0.5; 138 }, 139 reverse: function(pos) { 140 return 1-pos; 141 }, 142 flicker: function(pos) { 143 return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; 144 }, 145 wobble: function(pos) { 146 return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; 147 }, 148 pulse: function(pos, pulses) { 149 pulses = pulses || 5; 150 return ( 151 Math.round((pos % (1/pulses)) * pulses) == 0 ? 152 ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 153 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) 154 ); 155 }, 156 none: function(pos) { 157 return 0; 158 }, 159 full: function(pos) { 160 return 1; 161 } 162}; 163 164/* ------------- core effects ------------- */ 165 166Effect.ScopedQueue = Class.create(); 167Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { 168 initialize: function() { 169 this.effects = []; 170 this.interval = null; 171 }, 172 _each: function(iterator) { 173 this.effects._each(iterator); 174 }, 175 add: function(effect) { 176 var timestamp = new Date().getTime(); 177 178 var position = (typeof effect.options.queue == 'string') ? 179 effect.options.queue : effect.options.queue.position; 180 181 switch(position) { 182 case 'front': 183 // move unstarted effects after this effect 184 this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { 185 e.startOn += effect.finishOn; 186 e.finishOn += effect.finishOn; 187 }); 188 break; 189 case 'with-last':
190 timestamp = this.effects.pluck('startOn').max() || timestamp; 191 break; 192 case 'end': 193 // start effect after last queued effect has finished 194 timestamp = this.effects.pluck('finishOn').max() || timestamp; 195 break; 196 } 197 198 effect.startOn += timestamp; 199 effect.finishOn += timestamp; 200 201 if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) 202 this.effects.push(effect); 203 204 if(!this.interval) 205 this.interval = setInterval(this.loop.bind(this), 15); 206 }, 207 remove: function(effect) { 208 this.effects = this.effects.reject(function(e) { return e==effect }); 209 if(this.effects.length == 0) { 210 clearInterval(this.interval); 211 this.interval = null; 212 } 213 }, 214 loop: function() { 215 var timePos = new Date().getTime(); 216 this.effects.invoke('loop', timePos); 217 } 218}); 219 220Effect.Queues = { 221 instances: $H(), 222 get: function(queueName) { 223 if(typeof queueName != 'string') return queueName; 224 225 if(!this.instances[queueName]) 226 this.instances[queueName] = new Effect.ScopedQueue(); 227 228 return this.instances[queueName]; 229 } 230} 231Effect.Queue = Effect.Queues.get('global'); 232 233Effect.DefaultOptions = { 234 transition: Effect.Transitions.sinoidal, 235 duration: 1.0, // seconds 236 fps: 60.0, // max. 60fps due to Effect.Queue implementation 237 sync: false, // true for combining 238 from: 0.0, 239 to: 1.0, 240 delay: 0.0, 241 queue: 'parallel' 242} 243 244Effect.Base = function() {}; 245Effect.Base.prototype = { 246 position: null, 247 start: function(options) { 248 this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {}); 249 this.currentFrame = 0; 250 this.state = 'idle'; 251 this.startOn = this.options.delay*1000; 252 this.finishOn = this.startOn + (this.options.duration*1000); 253 this.event('beforeStart'); 254 if(!this.options.sync) 255 Effect.Queues.get(typeof this.options.queue == 'string' ? 256 'global' : this.options.queue.scope).add(this); 257 }, 258 loop: function(timePos) { 259 if(timePos >= this.startOn) { 260 if(timePos >= this.finishOn) { 261 this.render(1.0); 262 this.cancel(); 263 this.event('beforeFinish'); 264 if(this.finish) this.finish(); 265 this.event('afterFinish'); 266 return; 267 } 268 var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); 269 var frame = Math.round(pos * this.options.fps * this.options.duration); 270 if(frame > this.currentFrame) { 271 this.render(pos); 272 this.currentFrame = frame; 273 } 274 } 275 }, 276 render: function(pos) { 277 if(this.state == 'idle') { 278 this.state = 'running'; 279 this.event('beforeSetup'); 280 if(this.setup) this.setup(); 281 this.event('afterSetup'); 282 } 283 if(this.state == 'running') { 284 if(this.options.transition) pos = this.options.transition(pos); 285 pos *= (this.options.to-this.options.from); 286 pos += this.options.from; 287 this.position = pos; 288 this.event('beforeUpdate'); 289 if(this.update) this.update(pos); 290 this.event('afterUpdate'); 291 } 292 }, 293 cancel: function() { 294 if(!this.options.sync) 295 Effect.Queues.get(typeof this.options.queue == 'string' ? 296 'global' : this.options.queue.scope).remove(this); 297 this.state = 'finished'; 298 }, 299 event: function(eventName) { 300 if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); 301 if(this.options[eventName]) this.options[eventName](this); 302 }, 303 inspect: function() { 304 return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>'; 305 } 306} 307 308Effect.Parallel = Class.create(); 309Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { 310 initialize: function(effects) { 311 this.effects = effects || []; 312 this.start(arguments[1]); 313 }, 314 update: function(position) { 315 this.effects.invoke('render', position); 316 }, 317 finish: function(position) { 318 this.effects.each( function(effect) { 319 effect.render(1.0); 320 effect.cancel(); 321 effect.event('beforeFinish');
322 if(effect.finish) effect.finish(position); 323 effect.event('afterFinish'); 324 }); 325 } 326}); 327 328Effect.Event = Class.create(); 329Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), { 330 initialize: function() { 331 var options = Object.extend({ 332 duration: 0 333 }, arguments[0] || {}); 334 this.start(options); 335 }, 336 update: Prototype.emptyFunction 337}); 338 339Effect.Opacity = Class.create(); 340Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { 341 initialize: function(element) { 342 this.element = $(element); 343 if(!this.element) throw(Effect._elementDoesNotExistError); 344 // make this work on IE on elements without 'layout' 345 if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout)) 346 this.element.setStyle({zoom: 1}); 347 var options = Object.extend({ 348 from: this.element.getOpacity() || 0.0, 349 to: 1.0 350 }, arguments[1] || {}); 351 this.start(options); 352 }, 353 update: function(position) { 354 this.element.setOpacity(position); 355 } 356}); 357 358Effect.Move = Class.create(); 359Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { 360 initialize: function(element) { 361 this.element = $(element); 362 if(!this.element) throw(Effect._elementDoesNotExistError); 363 var options = Object.extend({ 364 x: 0, 365 y: 0, 366 mode: 'relative' 367 }, arguments[1] || {}); 368 this.start(options); 369 }, 370 setup: function() { 371 // Bug in Opera: Opera returns the "real" position of a static element or 372 // relative element that does not have top/left explicitly set. 373 // ==> Always set top and left for position relative elements in your stylesheets 374 // (to 0 if you do not need them) 375 this.element.makePositioned(); 376 this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); 377 this.originalTop = parseFloat(this.element.getStyle('top') || '0'); 378 if(this.options.mode == 'absolute') { 379 // absolute movement, so we need to calc deltaX and deltaY 380 this.options.x = this.options.x - this.originalLeft; 381 this.options.y = this.options.y - this.originalTop; 382 } 383 }, 384 update: function(position) { 385 this.element.setStyle({ 386 left: Math.round(this.options.x * position + this.originalLeft) + 'px', 387 top: Math.round(this.options.y * position + this.originalTop) + 'px' 388 }); 389 } 390}); 391 392// for backwards compatibility 393Effect.MoveBy = function(element, toTop, toLeft) { 394 return new Effect.Move(element, 395 Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); 396}; 397 398Effect.Scale = Class.create(); 399Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { 400 initialize: function(element, percent) { 401 this.element = $(element); 402 if(!this.element) throw(Effect._elementDoesNotExistError); 403 var options = Object.extend({ 404 scaleX: true, 405 scaleY: true, 406 scaleContent: true, 407 scaleFromCenter: false, 408 scaleMode: 'box', // 'box' or 'contents' or {} with provided values 409 scaleFrom: 100.0, 410 scaleTo: percent 411 }, arguments[2] || {}); 412 this.start(options); 413 }, 414 setup: function() { 415 this.restoreAfterFinish = this.options.restoreAfterFinish || false; 416 this.elementPositioning = this.element.getStyle('position'); 417 418 this.originalStyle = {}; 419 ['top','left','width','height','fontSize'].each( function(k) { 420 this.originalStyle[k] = this.element.style[k]; 421 }.bind(this)); 422 423 this.originalTop = this.element.offsetTop; 424 this.originalLeft = this.element.offsetLeft; 425 426 var fontSize = this.element.getStyle('font-size') || '100%'; 427 ['em','px','%','pt'].each( function(fontSizeType) { 428 if(fontSize.indexOf(fontSizeType)>0) { 429 this.fontSize = parseFloat(fontSize); 430 this.fontSizeType = fontSizeType; 431 } 432 }.bind(this)); 433 434 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; 435 436 this.dims = null; 437 if(this.options.scaleMode=='box') 438 this.dims = [this.element.offsetHeight, this.element.offsetWidth]; 439 if(/^content/.test(this.options.scaleMode)) 440 this.dims = [this.element.scrollHeight, this.element.scrollWidth]; 441 if(!this.dims) 442 this.dims = [this.options.scaleMode.originalHeight, 443 this.options.scaleMode.originalWidth]; 444 }, 445 update: function(position) { 446 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); 447 if(this.options.scaleContent && this.fontSize) 448 this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); 449 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); 450 }, 451 finish: function(position) { 452 if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle); 453 }, 454 setDimensions: function(height, width) { 455 var d = {}; 456 if(this.options.scaleX) d.width = Math.round(width) + 'px'; 457 if(this.options.scaleY) d.height = Math.round(height) + 'px'; 458 if(this.options.scaleFromCenter) { 459 var topd = (height - this.dims[0])/2; 460 var leftd = (width - this.dims[1])/2; 461 if(this.elementPositioning == 'absolute') { 462 if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; 463 if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; 464 } else { 465 if(this.options.scaleY) d.top = -topd + 'px'; 466 if(this.options.scaleX) d.left = -leftd + 'px'; 467 } 468 } 469 this.element.setStyle(d); 470 } 471}); 472 473Effect.Highlight = Class.create(); 474Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { 475 initialize: function(element) { 476 this.element = $(element); 477 if(!this.element) throw(Effect._elementDoesNotExistError); 478 var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); 479 this.start(options); 480 }, 481 setup: function() { 482 // Prevent executing on elements not in the layout flow 483 if(this.element.getStyle('display')=='none') { this.cancel(); return; } 484 // Disable background image during the effect
485 this.oldStyle = { 486 backgroundImage: this.element.getStyle('background-image') }; 487 this.element.setStyle({backgroundImage: 'none'}); 488 if(!this.options.endcolor) 489 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); 490 if(!this.options.restorecolor) 491 this.options.restorecolor = this.element.getStyle('background-color'); 492 // init color calculations 493 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); 494 this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); 495 }, 496 update: function(position) { 497 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ 498 return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); 499 }, 500 finish: function() { 501 this.element.setStyle(Object.extend(this.oldStyle, { 502 backgroundColor: this.options.restorecolor 503 })); 504 } 505}); 506 507Effect.ScrollTo = Class.create(); 508Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { 509 initialize: function(element) { 510 this.element = $(element); 511 this.start(arguments[1] || {}); 512 }, 513 setup: function() { 514 Position.prepare(); 515 var offsets = Position.cumulativeOffset(this.element); 516 if(this.options.offset) offsets[1] += this.options.offset; 517 var max = window.innerHeight ? 518 window.height - window.innerHeight : 519 document.body.scrollHeight - 520 (document.documentElement.clientHeight ? 521 document.documentElement.clientHeight : document.body.clientHeight); 522 this.scrollStart = Position.deltaY; 523 this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; 524 }, 525 update: function(position) { 526 Position.prepare(); 527 window.scrollTo(Position.deltaX, 528 this.scrollStart + (position*this.delta)); 529 } 530}); 531 532/* ------------- combination effects ------------- */ 533 534Effect.Fade = function(element) { 535 element = $(element); 536 var oldOpacity = element.getInlineOpacity(); 537 var options = Object.extend({ 538 from: element.getOpacity() || 1.0, 539 to: 0.0, 540 afterFinishInternal: function(effect) { 541 if(effect.options.to!=0) return; 542 effect.element.hide().setStyle({opacity: oldOpacity}); 543 }}, arguments[1] || {}); 544 return new Effect.Opacity(element,options); 545} 546 547Effect.Appear = function(element) { 548 element = $(element); 549 var options = Object.extend({ 550 from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), 551 to: 1.0, 552 // force Safari to render floated elements properly 553 afterFinishInternal: function(effect) { 554 effect.element.forceRerendering(); 555 }, 556 beforeSetup: function(effect) { 557 effect.element.setOpacity(effect.options.from).show(); 558 }}, arguments[1] || {}); 559 return new Effect.Opacity(element,options); 560} 561 562Effect.Puff = function(element) { 563 element = $(element); 564 var oldStyle = { 565 opacity: element.getInlineOpacity(), 566 position: element.getStyle('position'), 567 top: element.style.top, 568 left: element.style.left, 569 width: element.style.width, 570 height: element.style.height 571 }; 572 return new Effect.Parallel( 573 [ new Effect.Scale(element, 200, 574 { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 575 new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 576 Object.extend({ duration: 1.0, 577 beforeSetupInternal: function(effect) { 578 Position.absolutize(effect.effects[0].element) 579 }, 580 afterFinishInternal: function(effect) { 581 effect.effects[0].element.hide().setStyle(oldStyle); } 582 }, arguments[1] || {}) 583 ); 584} 585 586Effect.BlindUp = function(element) { 587 element = $(element); 588 element.makeClipping(); 589 return new Effect.Scale(element, 0, 590 Object.extend({ scaleContent: false, 591 scaleX: false, 592 restoreAfterFinish: true, 593 afterFinishInternal: function(effect) { 594 effect.element.hide().undoClipping(); 595 } 596 }, arguments[1] || {}) 597 ); 598} 599 600Effect.BlindDown = function(element) { 601 element = $(element); 602 var elementDimensions = element.getDimensions(); 603 return new Effect.Scale(element, 100, Object.extend({ 604 scaleContent: false, 605 scaleX: false, 606 scaleFrom: 0, 607 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, 608 restoreAfterFinish: true, 609 afterSetup: function(effect) { 610 effect.element.makeClipping().setStyle({height: '0px'}).show(); 611 }, 612 afterFinishInternal: function(effect) { 613 effect.element.undoClipping(); 614 } 615 }, arguments[1] || {})); 616} 617 618Effect.SwitchOff = function(element) { 619 element = $(element); 620 var oldOpacity = element.getInlineOpacity(); 621 return new Effect.Appear(element, Object.extend({ 622 duration: 0.4, 623 from: 0, 624 transition: Effect.Transitions.flicker, 625 afterFinishInternal: function(effect) { 626 new Effect.Scale(effect.element, 1, { 627 duration: 0.3, scaleFromCenter: true, 628 scaleX: false, scaleContent: false, restoreAfterFinish: true, 629 beforeSetup: function(effect) { 630 effect.element.makePositioned().makeClipping(); 631 }, 632 afterFinishInternal: function(effect) { 633 effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); 634 } 635 }) 636 } 637 }, arguments[1] || {})); 638} 639 640Effect.DropOut = function(element) { 641 element = $(element); 642 var oldStyle = { 643 top: element.getStyle('top'), 644 left: element.getStyle('left'), 645 opacity: element.getInlineOpacity() }; 646 return new Effect.Parallel( 647 [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 648 new Effect.Opacity(element, { sync: true, to: 0.0 }) ], 649 Object.extend( 650 { duration: 0.5, 651 beforeSetup: function(effect) { 652 effect.effects[0].element.makePositioned(); 653 }, 654 afterFinishInternal: function(effect) { 655 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); 656 } 657 }, arguments[1] || {})); 658} 659 660Effect.Shake = function(element) { 661 element = $(element); 662 var oldStyle = { 663 top: element.getStyle('top'), 664 left: element.getStyle('left') }; 665 return new Effect.Move(element, 666 { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { 667 new Effect.Move(effect.element, 668 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { 669 new Effect.Move(effect.element, 670 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { 671 new Effect.Move(effect.element, 672 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { 673 new Effect.Move(effect.element, 674 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { 675 new Effect.Move(effect.element, 676 { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { 677 effect.element.undoPositioned().setStyle(oldStyle); 678 }}) }}) }}) }}) }}) }}); 679} 680 681Effect.SlideDown = function(element) { 682 element = $(element).cleanWhitespace(); 683 // SlideDown need to have the content of the element wrapped in a container element with fixed height! 684 var oldInnerBottom = element.down().getStyle('bottom'); 685 var elementDimensions = element.getDimensions(); 686 return new Effect.Scale(element, 100, Object.extend({ 687 scaleContent: false, 688 scaleX: false, 689 scaleFrom: window.opera ? 0 : 1, 690 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, 691 restoreAfterFinish: true, 692 afterSetup: function(effect) { 693 effect.element.makePositioned(); 694 effect.element.down().makePositioned(); 695 if(window.opera) effect.element.setStyle({top: ''}); 696 effect.element.makeClipping().setStyle({height: '0px'}).show(); 697 }, 698 afterUpdateInternal: function(effect) { 699 effect.element.down().setStyle({bottom: 700 (effect.dims[0] - effect.element.clientHeight) + 'px' }); 701 }, 702 afterFinishInternal: function(effect) { 703 effect.element.undoClipping().undoPositioned(); 704 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } 705 }, arguments[1] || {}) 706 ); 707} 708 709Effect.SlideUp = function(element) { 710 element = $(element).cleanWhitespace(); 711 var oldInnerBottom = element.down().getStyle('bottom'); 712 return new Effect.Scale(element, window.opera ? 0 : 1, 713 Object.extend({ scaleContent: false, 714 scaleX: false, 715 scaleMode: 'box', 716 scaleFrom: 100, 717 restoreAfterFinish: true, 718 beforeStartInternal: function(effect) { 719 effect.element.makePositioned(); 720 effect.element.down().makePositioned(); 721 if(window.opera) effect.element.setStyle({top: ''}); 722 effect.element.makeClipping().show(); 723 }, 724 afterUpdateInternal: function(effect) { 725 effect.element.down().setStyle({bottom: 726 (effect.dims[0] - effect.element.clientHeight) + 'px' }); 727 }, 728 afterFinishInternal: function(effect) { 729 effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom}); 730 effect.element.down().undoPositioned(); 731 } 732 }, arguments[1] || {}) 733 ); 734} 735 736// Bug in opera makes the TD containing this element expand for a instance after finish 737Effect.Squish = function(element) { 738 return new Effect.Scale(element, window.opera ? 1 : 0, { 739 restoreAfterFinish: true, 740 beforeSetup: function(effect) { 741 effect.element.makeClipping(); 742 }, 743 afterFinishInternal: function(effect) { 744 effect.element.hide().undoClipping(); 745 } 746 }); 747} 748 749Effect.Grow = function(element) { 750 element = $(element); 751 var options = Object.extend({ 752 direction: 'center', 753 moveTransition: Effect.Transitions.sinoidal, 754 scaleTransition: Effect.Transitions.sinoidal, 755 opacityTransition: Effect.Transitions.full 756 }, arguments[1] || {}); 757 var oldStyle = { 758 top: element.style.top, 759 left: element.style.left, 760 height: element.style.height, 761 width: element.style.width, 762 opacity: element.getInlineOpacity() }; 763 764 var dims = element.getDimensions(); 765 var initialMoveX, initialMoveY; 766 var moveX, moveY; 767 768 switch (options.direction) { 769 case 'top-left': 770 initialMoveX = initialMoveY = moveX = moveY = 0; 771 break; 772 case 'top-right': 773 initialMoveX = dims.width; 774 initialMoveY = moveY = 0; 775 moveX = -dims.width; 776 break; 777 case 'bottom-left': 778 initialMoveX = moveX = 0; 779 initialMoveY = dims.height; 780 moveY = -dims.height; 781 break; 782 case 'bottom-right': 783 initialMoveX = dims.width; 784 initialMoveY = dims.height; 785 moveX = -dims.width; 786 moveY = -dims.height; 787 break; 788 case 'center': 789 initialMoveX = dims.width / 2; 790 initialMoveY = dims.height / 2; 791 moveX = -dims.width / 2; 792 moveY = -dims.height / 2; 793 break; 794 } 795 796 return new Effect.Move(element, { 797 x: initialMoveX, 798 y: initialMoveY, 799 duration: 0.01, 800 beforeSetup: function(effect) { 801 effect.element.hide().makeClipping().makePositioned(); 802 }, 803 afterFinishInternal: function(effect) { 804 new Effect.Parallel( 805 [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), 806 new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), 807 new Effect.Scale(effect.element, 100, { 808 scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 809 sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) 810 ], Object.extend({ 811 beforeSetup: function(effect) { 812 effect.effects[0].element.setStyle({height: '0px'}).show(); 813 }, 814 afterFinishInternal: function(effect) { 815 effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 816 } 817 }, options) 818 ) 819 } 820 }); 821} 822 823Effect.Shrink = function(element) { 824 element = $(element); 825 var options = Object.extend({ 826 direction: 'center', 827 moveTransition: Effect.Transitions.sinoidal, 828 scaleTransition: Effect.Transitions.sinoidal, 829 opacityTransition: Effect.Transitions.none 830 }, arguments[1] || {}); 831 var oldStyle = { 832 top: element.style.top, 833 left: element.style.left, 834 height: element.style.height, 835 width: element.style.width, 836 opacity: element.getInlineOpacity() }; 837 838 var dims = element.getDimensions(); 839 var moveX, moveY; 840 841 switch (options.direction) { 842 case 'top-left': 843 moveX = moveY = 0; 844 break; 845 case 'top-right': 846 moveX = dims.width; 847 moveY = 0; 848 break; 849 case 'bottom-left': 850 moveX = 0; 851 moveY = dims.height; 852 break; 853 case 'bottom-right': 854 moveX = dims.width; 855 moveY = dims.height; 856 break; 857 case 'center': 858 moveX = dims.width / 2; 859 moveY = dims.height / 2; 860 break; 861 } 862 863 return new Effect.Parallel( 864 [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), 865 new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), 866 new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) 867 ], Object.extend({ 868 beforeStartInternal: function(effect) { 869 effect.effects[0].element.makePositioned().makeClipping(); 870 }, 871 afterFinishInternal: function(effect) { 872 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } 873 }, options) 874 ); 875} 876 877Effect.Pulsate = function(element) { 878 element = $(element); 879 var options = arguments[1] || {}; 880 var oldOpacity = element.getInlineOpacity();
881 var transition = options.transition || Effect.Transitions.sinoidal; 882 var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; 883 reverser.bind(transition); 884 return new Effect.Opacity(element, 885 Object.extend(Object.extend({ duration: 2.0, from: 0, 886 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } 887 }, options), {transition: reverser})); 888} 889 890Effect.Fold = function(element) { 891 element = $(element); 892 var oldStyle = { 893 top: element.style.top, 894 left: element.style.left, 895 width: element.style.width, 896 height: element.style.height }; 897 element.makeClipping(); 898 return new Effect.Scale(element, 5, Object.extend({ 899 scaleContent: false, 900 scaleX: false, 901 afterFinishInternal: function(effect) { 902 new Effect.Scale(element, 1, { 903 scaleContent: false, 904 scaleY: false, 905 afterFinishInternal: function(effect) { 906 effect.element.hide().undoClipping().setStyle(oldStyle); 907 } }); 908 }}, arguments[1] || {})); 909}; 910 911Effect.Morph = Class.create(); 912Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { 913 initialize: function(element) { 914 this.element = $(element); 915 if(!this.element) throw(Effect._elementDoesNotExistError); 916 var options = Object.extend({ 917 style: {} 918 }, arguments[1] || {}); 919 if (typeof options.style == 'string') { 920 if(options.style.indexOf(':') == -1) { 921 var cssText = '', selector = '.' + options.style; 922 $A(document.styleSheets).reverse().each(function(styleSheet) { 923 if (styleSheet.cssRules) cssRules = styleSheet.cssRules; 924 else if (styleSheet.rules) cssRules = styleSheet.rules; 925 $A(cssRules).reverse().each(function(rule) { 926 if (selector == rule.selectorText) { 927 cssText = rule.style.cssText; 928 throw $break; 929 } 930 }); 931 if (cssText) throw $break; 932 }); 933 this.style = cssText.parseStyle(); 934 options.afterFinishInternal = function(effect){ 935 effect.element.addClassName(effect.options.style); 936 effect.transforms.each(function(transform) { 937 if(transform.style != 'opacity') 938 effect.element.style[transform.style.camelize()] = ''; 939 }); 940 } 941 } else this.style = options.style.parseStyle(); 942 } else this.style = $H(options.style) 943 this.start(options); 944 }, 945 setup: function(){ 946 function parseColor(color){ 947 if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; 948 color = color.parseColor(); 949 return $R(0,2).map(function(i){ 950 return parseInt( color.slice(i*2+1,i*2+3), 16 ) 951 }); 952 } 953 this.transforms = this.style.map(function(pair){ 954 var property = pair[0].underscore().dasherize(), value = pair[1], unit = null; 955 956 if(value.parseColor('#zzzzzz') != '#zzzzzz') { 957 value = value.parseColor(); 958 unit = 'color'; 959 } else if(property == 'opacity') { 960 value = parseFloat(value); 961 if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout)) 962 this.element.setStyle({zoom: 1}); 963 } else if(Element.CSS_LENGTH.test(value)) 964 var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/), 965 value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null; 966 967 var originalValue = this.element.getStyle(property); 968 return $H({ 969 style: property, 970 originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 971 targetValue: unit=='color' ? parseColor(value) : value, 972 unit: unit 973 }); 974 }.bind(this)).reject(function(transform){ 975 return ( 976 (transform.originalValue == transform.targetValue) || 977 ( 978 transform.unit != 'color' && 979 (isNaN(transform.originalValue) || isNaN(transform.targetValue)) 980 ) 981 ) 982 }); 983 }, 984 update: function(position) { 985 var style = $H(), value = null; 986 this.transforms.each(function(transform){ 987 value = transform.unit=='color' ? 988 $R(0,2).inject('#',function(m,v,i){ 989 return m+(Math.round(transform.originalValue[i]+ 990 (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : 991 transform.originalValue + Math.round( 992 ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit; 993 style[transform.style] = value; 994 }); 995 this.element.setStyle(style); 996 } 997}); 998 999Effect.Transform = Class.create(); 1000Object.extend(Effect.Transform.prototype, { 1001 initialize: function(tracks){ 1002 this.tracks = []; 1003 this.options = arguments[1] || {}; 1004 this.addTracks(tracks); 1005 }, 1006 addTracks: function(tracks){ 1007 tracks.each(function(track){ 1008 var data = $H(track).values().first(); 1009 this.tracks.push($H({ 1010 ids: $H(track).keys().first(), 1011 effect: Effect.Morph, 1012 options: { style: data } 1013 })); 1014 }.bind(this)); 1015 return this; 1016 }, 1017 play: function(){ 1018 return new Effect.Parallel( 1019 this.tracks.map(function(track){ 1020 var elements = [$(track.ids) || $$(track.ids)].flatten(); 1021 return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) }); 1022 }).flatten(), 1023 this.options 1024 ); 1025 } 1026}); 1027 1028Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage', 1029 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle', 1030 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth', 1031 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor', 1032 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content', 1033 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction', 1034 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch', 1035 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight', 1036 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight', 1037 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity', 1038 'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY', 1039 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore', 1040 'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes', 1041 'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress', 1042 'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top', 1043 'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows', 1044 'width', 'wordSpacing', 'zIndex']; 1045 1046Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; 1047 1048String.prototype.parseStyle = function(){ 1049 var element = Element.extend(document.createElement('div')); 1050 element.innerHTML = '<div style="' + this + '"></div>'; 1051 var style = element.down().style, styleRules = $H(); 1052 1053 Element.CSS_PROPERTIES.each(function(property){ 1054 if(style[property]) styleRules[property] = style[property]; 1055 }); 1056 if(/MSIE/.test(navigator.userAgent) && !window.opera && this.indexOf('opacity') > -1) { 1057 styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]; 1058 } 1059 return styleRules; 1060}; 1061 1062Element.morph = function(element, style) { 1063 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {})); 1064 return element; 1065}; 1066 1067['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom', 1068 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( 1069 function(f) { Element.Methods[f] = Element[f]; } 1070); 1071 1072Element.Methods.visualEffect = function(element, effect, options) { 1073 s = effect.gsub(/_/, '-').camelize(); 1074 effect_class = s.charAt(0).toUpperCase() + s.substring(1); 1075 new Effect[effect_class](element, options); 1076 return $(element); 1077}; 1078 1079Element.addMethods();
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.