1 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
3 // Justin Palmer (http://encytemedia.com/)
4 // Mark Pilgrim (http://diveintomark.org/)
7 // See scriptaculous.js for full license.
9 // converts rgb() and #xxx to #xxxxxx format,
10 // returns self (or first argument) if not convertable
11 String
.prototype.parseColor = function() {
13 if(this.slice(0,4) == 'rgb(') {
14 var cols
= this.slice(4,this.length
-1).split(',');
15 var i
=0; do { color
+= parseInt(cols
[i
]).toColorPart() } while (++i
<3);
17 if(this.slice(0,1) == '#') {
18 if(this.length
==4) for(var i
=1;i
<4;i
++) color
+= (this.charAt(i
) + this.charAt(i
)).toLowerCase();
19 if(this.length
==7) color
= this.toLowerCase();
22 return(color
.length
==7 ? color
: (arguments
[0] || this));
25 /*--------------------------------------------------------------------------*/
27 Element
.collectTextNodes = function(element
) {
28 return $A($(element
).childNodes
).collect( function(node
) {
29 return (node
.nodeType
==3 ? node
.nodeValue
:
30 (node
.hasChildNodes() ? Element
.collectTextNodes(node
) : ''));
31 }).flatten().join('');
34 Element
.collectTextNodesIgnoreClass = function(element
, className
) {
35 return $A($(element
).childNodes
).collect( function(node
) {
36 return (node
.nodeType
==3 ? node
.nodeValue
:
37 ((node
.hasChildNodes() && !Element
.hasClassName(node
,className
)) ?
38 Element
.collectTextNodesIgnoreClass(node
, className
) : ''));
39 }).flatten().join('');
42 Element
.setContentZoom = function(element
, percent
) {
44 Element
.setStyle(element
, {fontSize
: (percent
/100) + 'em'});
45 if(navigator
.appVersion
.indexOf('AppleWebKit')>0) window
.scrollBy(0,0);
48 Element
.getOpacity = function(element
){
50 if (opacity
= Element
.getStyle(element
, 'opacity'))
51 return parseFloat(opacity
);
52 if (opacity
= (Element
.getStyle(element
, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
53 if(opacity
[1]) return parseFloat(opacity
[1]) / 100;
57 Element
.setOpacity = function(element
, value
){
60 Element
.setStyle(element
, { opacity
:
61 (/Gecko/.test(navigator
.userAgent
) && !/Konqueror|Safari|KHTML/.test(navigator
.userAgent
)) ?
63 if(/MSIE/.test(navigator
.userAgent
))
64 Element
.setStyle(element
, {filter
: Element
.getStyle(element
,'filter').replace(/alpha\([^\)]*\)/gi,'')});
66 if(value
< 0.00001) value
= 0;
67 Element
.setStyle(element
, {opacity
: value
});
68 if(/MSIE/.test(navigator
.userAgent
))
69 Element
.setStyle(element
,
70 { filter
: Element
.getStyle(element
,'filter').replace(/alpha\([^\)]*\)/gi,'') +
71 'alpha(opacity='+value
*100+')' });
75 Element
.getInlineOpacity = function(element
){
76 return $(element
).style
.opacity
|| '';
79 Element
.childrenWithClassName = function(element
, className
, findFirst
) {
80 var classNameRegExp
= new RegExp("(^|\\s)" + className
+ "(\\s|$)");
81 var results
= $A($(element
).getElementsByTagName('*'))[findFirst
? 'detect' : 'select']( function(c
) {
82 return (c
.className
&& c
.className
.match(classNameRegExp
));
84 if(!results
) results
= [];
88 Element
.forceRerendering = function(element
) {
91 var n
= document
.createTextNode(' ');
92 element
.appendChild(n
);
93 element
.removeChild(n
);
97 /*--------------------------------------------------------------------------*/
99 Array
.prototype.call = function() {
100 var args
= arguments
;
101 this.each(function(f
){ f
.apply(this, args
) });
104 /*--------------------------------------------------------------------------*/
107 tagifyText: function(element
) {
108 var tagifyStyle
= 'position:relative';
109 if(/MSIE/.test(navigator
.userAgent
)) tagifyStyle
+= ';zoom:1';
110 element
= $(element
);
111 $A(element
.childNodes
).each( function(child
) {
112 if(child
.nodeType
==3) {
113 child
.nodeValue
.toArray().each( function(character
) {
114 element
.insertBefore(
115 Builder
.node('span',{style
: tagifyStyle
},
116 character
== ' ' ? String
.fromCharCode(160) : character
),
119 Element
.remove(child
);
123 multiple: function(element
, effect
) {
125 if(((typeof element
== 'object') ||
126 (typeof element
== 'function')) &&
130 elements
= $(element
).childNodes
;
132 var options
= Object
.extend({
135 }, arguments
[2] || {});
136 var masterDelay
= options
.delay
;
138 $A(elements
).each( function(element
, index
) {
139 new effect(element
, Object
.extend(options
, { delay
: index
* options
.speed
+ masterDelay
}));
143 'slide': ['SlideDown','SlideUp'],
144 'blind': ['BlindDown','BlindUp'],
145 'appear': ['Appear','Fade']
147 toggle: function(element
, effect
) {
148 element
= $(element
);
149 effect
= (effect
|| 'appear').toLowerCase();
150 var options
= Object
.extend({
151 queue
: { position
:'end', scope
:(element
.id
|| 'global'), limit
: 1 }
152 }, arguments
[2] || {});
153 Effect
[element
.visible() ?
154 Effect
.PAIRS
[effect
][1] : Effect
.PAIRS
[effect
][0]](element
, options
);
158 var Effect2
= Effect
; // deprecated
160 /* ------------- transitions ------------- */
162 Effect
.Transitions
= {}
164 Effect
.Transitions
.linear = function(pos
) {
167 Effect
.Transitions
.sinoidal = function(pos
) {
168 return (-Math
.cos(pos
*Math
.PI
)/2) + 0.5;
170 Effect
.Transitions
.reverse = function(pos
) {
173 Effect
.Transitions
.flicker = function(pos
) {
174 return ((-Math
.cos(pos
*Math
.PI
)/4) + 0.75) + Math.random()/4;
176 Effect
.Transitions
.wobble = function(pos
) {
177 return (-Math
.cos(pos
*Math
.PI
*(9*pos
))/2) + 0.5;
179 Effect
.Transitions
.pulse = function(pos
) {
180 return (Math
.floor(pos
*10) % 2 == 0 ?
181 (pos
*10-Math
.floor(pos
*10)) : 1-(pos
*10-Math
.floor(pos
*10)));
183 Effect
.Transitions
.none = function(pos
) {
186 Effect
.Transitions
.full = function(pos
) {
190 /* ------------- core effects ------------- */
192 Effect
.ScopedQueue
= Class
.create();
193 Object
.extend(Object
.extend(Effect
.ScopedQueue
.prototype, Enumerable
), {
194 initialize: function() {
196 this.interval
= null;
198 _each: function(iterator
) {
199 this.effects
._each(iterator
);
201 add: function(effect
) {
202 var timestamp
= new Date().getTime();
204 var position
= (typeof effect
.options
.queue
== 'string') ?
205 effect
.options
.queue
: effect
.options
.queue
.position
;
209 // move unstarted effects after this effect
210 this.effects
.findAll(function(e
){ return e
.state
=='idle' }).each( function(e
) {
211 e
.startOn
+= effect
.finishOn
;
212 e
.finishOn
+= effect
.finishOn
;
216 // start effect after last queued effect has finished
217 timestamp
= this.effects
.pluck('finishOn').max() || timestamp
;
221 effect
.startOn
+= timestamp
;
222 effect
.finishOn
+= timestamp
;
224 if(!effect
.options
.queue
.limit
|| (this.effects
.length
< effect
.options
.queue
.limit
))
225 this.effects
.push(effect
);
228 this.interval
= setInterval(this.loop
.bind(this), 40);
230 remove: function(effect
) {
231 this.effects
= this.effects
.reject(function(e
) { return e
==effect
});
232 if(this.effects
.length
== 0) {
233 clearInterval(this.interval
);
234 this.interval
= null;
238 var timePos
= new Date().getTime();
239 this.effects
.invoke('loop', timePos
);
245 get: function(queueName
) {
246 if(typeof queueName
!= 'string') return queueName
;
248 if(!this.instances
[queueName
])
249 this.instances
[queueName
] = new Effect
.ScopedQueue();
251 return this.instances
[queueName
];
254 Effect
.Queue
= Effect
.Queues
.get('global');
256 Effect
.DefaultOptions
= {
257 transition
: Effect
.Transitions
.sinoidal
,
258 duration
: 1.0, // seconds
259 fps
: 25.0, // max. 25fps due to Effect.Queue implementation
260 sync
: false, // true for combining
267 Effect
.Base = function() {};
268 Effect
.Base
.prototype = {
270 start: function(options
) {
271 this.options
= Object
.extend(Object
.extend({},Effect
.DefaultOptions
), options
|| {});
272 this.currentFrame
= 0;
274 this.startOn
= this.options
.delay
*1000;
275 this.finishOn
= this.startOn
+ (this.options
.duration
*1000);
276 this.event('beforeStart');
277 if(!this.options
.sync
)
278 Effect
.Queues
.get(typeof this.options
.queue
== 'string' ?
279 'global' : this.options
.queue
.scope
).add(this);
281 loop: function(timePos
) {
282 if(timePos
>= this.startOn
) {
283 if(timePos
>= this.finishOn
) {
286 this.event('beforeFinish');
287 if(this.finish
) this.finish();
288 this.event('afterFinish');
291 var pos
= (timePos
- this.startOn
) / (this.finishOn
- this.startOn
);
292 var frame
= Math
.round(pos
* this.options
.fps
* this.options
.duration
);
293 if(frame
> this.currentFrame
) {
295 this.currentFrame
= frame
;
299 render: function(pos
) {
300 if(this.state
== 'idle') {
301 this.state
= 'running';
302 this.event('beforeSetup');
303 if(this.setup
) this.setup();
304 this.event('afterSetup');
306 if(this.state
== 'running') {
307 if(this.options
.transition
) pos
= this.options
.transition(pos
);
308 pos
*= (this.options
.to
-this.options
.from);
309 pos
+= this.options
.from;
311 this.event('beforeUpdate');
312 if(this.update
) this.update(pos
);
313 this.event('afterUpdate');
317 if(!this.options
.sync
)
318 Effect
.Queues
.get(typeof this.options
.queue
== 'string' ?
319 'global' : this.options
.queue
.scope
).remove(this);
320 this.state
= 'finished';
322 event: function(eventName
) {
323 if(this.options
[eventName
+ 'Internal']) this.options
[eventName
+ 'Internal'](this);
324 if(this.options
[eventName
]) this.options
[eventName
](this);
326 inspect: function() {
327 return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options
).inspect() + '>';
331 Effect
.Parallel
= Class
.create();
332 Object
.extend(Object
.extend(Effect
.Parallel
.prototype, Effect
.Base
.prototype), {
333 initialize: function(effects
) {
334 this.effects
= effects
|| [];
335 this.start(arguments
[1]);
337 update: function(position
) {
338 this.effects
.invoke('render', position
);
340 finish: function(position
) {
341 this.effects
.each( function(effect
) {
344 effect
.event('beforeFinish');
345 if(effect
.finish
) effect
.finish(position
);
346 effect
.event('afterFinish');
351 Effect
.Opacity
= Class
.create();
352 Object
.extend(Object
.extend(Effect
.Opacity
.prototype, Effect
.Base
.prototype), {
353 initialize: function(element
) {
354 this.element
= $(element
);
355 // make this work on IE on elements without 'layout'
356 if(/MSIE/.test(navigator
.userAgent
) && (!this.element
.hasLayout
))
357 this.element
.setStyle({zoom
: 1});
358 var options
= Object
.extend({
359 from: this.element
.getOpacity() || 0.0,
361 }, arguments
[1] || {});
364 update: function(position
) {
365 this.element
.setOpacity(position
);
369 Effect
.Move
= Class
.create();
370 Object
.extend(Object
.extend(Effect
.Move
.prototype, Effect
.Base
.prototype), {
371 initialize: function(element
) {
372 this.element
= $(element
);
373 var options
= Object
.extend({
377 }, arguments
[1] || {});
381 // Bug in Opera: Opera returns the "real" position of a static element or
382 // relative element that does not have top/left explicitly set.
383 // ==> Always set top and left for position relative elements in your stylesheets
384 // (to 0 if you do not need them)
385 this.element
.makePositioned();
386 this.originalLeft
= parseFloat(this.element
.getStyle('left') || '0');
387 this.originalTop
= parseFloat(this.element
.getStyle('top') || '0');
388 if(this.options
.mode
== 'absolute') {
389 // absolute movement, so we need to calc deltaX and deltaY
390 this.options
.x
= this.options
.x
- this.originalLeft
;
391 this.options
.y
= this.options
.y
- this.originalTop
;
394 update: function(position
) {
395 this.element
.setStyle({
396 left
: this.options
.x
* position
+ this.originalLeft
+ 'px',
397 top
: this.options
.y
* position
+ this.originalTop
+ 'px'
402 // for backwards compatibility
403 Effect
.MoveBy = function(element
, toTop
, toLeft
) {
404 return new Effect
.Move(element
,
405 Object
.extend({ x
: toLeft
, y
: toTop
}, arguments
[3] || {}));
408 Effect
.Scale
= Class
.create();
409 Object
.extend(Object
.extend(Effect
.Scale
.prototype, Effect
.Base
.prototype), {
410 initialize: function(element
, percent
) {
411 this.element
= $(element
)
412 var options
= Object
.extend({
416 scaleFromCenter
: false,
417 scaleMode
: 'box', // 'box' or 'contents' or {} with provided values
420 }, arguments
[2] || {});
424 this.restoreAfterFinish
= this.options
.restoreAfterFinish
|| false;
425 this.elementPositioning
= this.element
.getStyle('position');
427 this.originalStyle
= {};
428 ['top','left','width','height','fontSize'].each( function(k
) {
429 this.originalStyle
[k
] = this.element
.style
[k
];
432 this.originalTop
= this.element
.offsetTop
;
433 this.originalLeft
= this.element
.offsetLeft
;
435 var fontSize
= this.element
.getStyle('font-size') || '100%';
436 ['em','px','%'].each( function(fontSizeType
) {
437 if(fontSize
.indexOf(fontSizeType
)>0) {
438 this.fontSize
= parseFloat(fontSize
);
439 this.fontSizeType
= fontSizeType
;
443 this.factor
= (this.options
.scaleTo
- this.options
.scaleFrom
)/100;
446 if(this.options
.scaleMode
=='box')
447 this.dims
= [this.element
.offsetHeight
, this.element
.offsetWidth
];
448 if(/^content/.test(this.options
.scaleMode
))
449 this.dims
= [this.element
.scrollHeight
, this.element
.scrollWidth
];
451 this.dims
= [this.options
.scaleMode
.originalHeight
,
452 this.options
.scaleMode
.originalWidth
];
454 update: function(position
) {
455 var currentScale
= (this.options
.scaleFrom
/100.0) + (this.factor
* position
);
456 if(this.options
.scaleContent
&& this.fontSize
)
457 this.element
.setStyle({fontSize
: this.fontSize
* currentScale
+ this.fontSizeType
});
458 this.setDimensions(this.dims
[0] * currentScale
, this.dims
[1] * currentScale
);
460 finish: function(position
) {
461 if (this.restoreAfterFinish
) this.element
.setStyle(this.originalStyle
);
463 setDimensions: function(height
, width
) {
465 if(this.options
.scaleX
) d
.width
= width
+ 'px';
466 if(this.options
.scaleY
) d
.height
= height
+ 'px';
467 if(this.options
.scaleFromCenter
) {
468 var topd
= (height
- this.dims
[0])/2;
469 var leftd
= (width
- this.dims
[1])/2;
470 if(this.elementPositioning
== 'absolute') {
471 if(this.options
.scaleY
) d
.top
= this.originalTop
-topd
+ 'px';
472 if(this.options
.scaleX
) d
.left
= this.originalLeft
-leftd
+ 'px';
474 if(this.options
.scaleY
) d
.top
= -topd
+ 'px';
475 if(this.options
.scaleX
) d
.left
= -leftd
+ 'px';
478 this.element
.setStyle(d
);
482 Effect
.Highlight
= Class
.create();
483 Object
.extend(Object
.extend(Effect
.Highlight
.prototype, Effect
.Base
.prototype), {
484 initialize: function(element
) {
485 this.element
= $(element
);
486 var options
= Object
.extend({ startcolor
: '#ffff99' }, arguments
[1] || {});
490 // Prevent executing on elements not in the layout flow
491 if(this.element
.getStyle('display')=='none') { this.cancel(); return; }
492 // Disable background image during the effect
494 backgroundImage
: this.element
.getStyle('background-image') };
495 this.element
.setStyle({backgroundImage
: 'none'});
496 if(!this.options
.endcolor
)
497 this.options
.endcolor
= this.element
.getStyle('background-color').parseColor('#ffffff');
498 if(!this.options
.restorecolor
)
499 this.options
.restorecolor
= this.element
.getStyle('background-color');
500 // init color calculations
501 this._base
= $R(0,2).map(function(i
){ return parseInt(this.options
.startcolor
.slice(i
*2+1,i
*2+3),16) }.bind(this));
502 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));
504 update: function(position
) {
505 this.element
.setStyle({backgroundColor
: $R(0,2).inject('#',function(m
,v
,i
){
506 return m
+(Math
.round(this._base
[i
]+(this._delta
[i
]*position
)).toColorPart()); }.bind(this)) });
509 this.element
.setStyle(Object
.extend(this.oldStyle
, {
510 backgroundColor
: this.options
.restorecolor
515 Effect
.ScrollTo
= Class
.create();
516 Object
.extend(Object
.extend(Effect
.ScrollTo
.prototype, Effect
.Base
.prototype), {
517 initialize: function(element
) {
518 this.element
= $(element
);
519 this.start(arguments
[1] || {});
523 var offsets
= Position
.cumulativeOffset(this.element
);
524 if(this.options
.offset
) offsets
[1] += this.options
.offset
;
525 var max
= window
.innerHeight
?
526 window
.height
- window
.innerHeight
:
527 document
.body
.scrollHeight
-
528 (document
.documentElement
.clientHeight
?
529 document
.documentElement
.clientHeight
: document
.body
.clientHeight
);
530 this.scrollStart
= Position
.deltaY
;
531 this.delta
= (offsets
[1] > max
? max
: offsets
[1]) - this.scrollStart
;
533 update: function(position
) {
535 window
.scrollTo(Position
.deltaX
,
536 this.scrollStart
+ (position
*this.delta
));
540 /* ------------- combination effects ------------- */
542 Effect
.Fade = function(element
) {
543 element
= $(element
);
544 var oldOpacity
= element
.getInlineOpacity();
545 var options
= Object
.extend({
546 from: element
.getOpacity() || 1.0,
548 afterFinishInternal: function(effect
) {
549 if(effect
.options
.to
!=0) return;
550 effect
.element
.hide();
551 effect
.element
.setStyle({opacity
: oldOpacity
});
552 }}, arguments
[1] || {});
553 return new Effect
.Opacity(element
,options
);
556 Effect
.Appear = function(element
) {
557 element
= $(element
);
558 var options
= Object
.extend({
559 from: (element
.getStyle('display') == 'none' ? 0.0 : element
.getOpacity() || 0.0),
561 // force Safari to render floated elements properly
562 afterFinishInternal: function(effect
) {
563 effect
.element
.forceRerendering();
565 beforeSetup: function(effect
) {
566 effect
.element
.setOpacity(effect
.options
.from);
567 effect
.element
.show();
568 }}, arguments
[1] || {});
569 return new Effect
.Opacity(element
,options
);
572 Effect
.Puff = function(element
) {
573 element
= $(element
);
574 var oldStyle
= { opacity
: element
.getInlineOpacity(), position
: element
.getStyle('position') };
575 return new Effect
.Parallel(
576 [ new Effect
.Scale(element
, 200,
577 { sync
: true, scaleFromCenter
: true, scaleContent
: true, restoreAfterFinish
: true }),
578 new Effect
.Opacity(element
, { sync
: true, to
: 0.0 } ) ],
579 Object
.extend({ duration
: 1.0,
580 beforeSetupInternal: function(effect
) {
581 effect
.effects
[0].element
.setStyle({position
: 'absolute'}); },
582 afterFinishInternal: function(effect
) {
583 effect
.effects
[0].element
.hide();
584 effect
.effects
[0].element
.setStyle(oldStyle
); }
585 }, arguments
[1] || {})
589 Effect
.BlindUp = function(element
) {
590 element
= $(element
);
591 element
.makeClipping();
592 return new Effect
.Scale(element
, 0,
593 Object
.extend({ scaleContent
: false,
595 restoreAfterFinish
: true,
596 afterFinishInternal: function(effect
) {
597 effect
.element
.hide();
598 effect
.element
.undoClipping();
600 }, arguments
[1] || {})
604 Effect
.BlindDown = function(element
) {
605 element
= $(element
);
606 var elementDimensions
= element
.getDimensions();
607 return new Effect
.Scale(element
, 100,
608 Object
.extend({ scaleContent
: false,
611 scaleMode
: {originalHeight
: elementDimensions
.height
, originalWidth
: elementDimensions
.width
},
612 restoreAfterFinish
: true,
613 afterSetup: function(effect
) {
614 effect
.element
.makeClipping();
615 effect
.element
.setStyle({height
: '0px'});
616 effect
.element
.show();
618 afterFinishInternal: function(effect
) {
619 effect
.element
.undoClipping();
621 }, arguments
[1] || {})
625 Effect
.SwitchOff = function(element
) {
626 element
= $(element
);
627 var oldOpacity
= element
.getInlineOpacity();
628 return new Effect
.Appear(element
, {
631 transition
: Effect
.Transitions
.flicker
,
632 afterFinishInternal: function(effect
) {
633 new Effect
.Scale(effect
.element
, 1, {
634 duration
: 0.3, scaleFromCenter
: true,
635 scaleX
: false, scaleContent
: false, restoreAfterFinish
: true,
636 beforeSetup: function(effect
) {
637 effect
.element
.makePositioned();
638 effect
.element
.makeClipping();
640 afterFinishInternal: function(effect
) {
641 effect
.element
.hide();
642 effect
.element
.undoClipping();
643 effect
.element
.undoPositioned();
644 effect
.element
.setStyle({opacity
: oldOpacity
});
651 Effect
.DropOut = function(element
) {
652 element
= $(element
);
654 top
: element
.getStyle('top'),
655 left
: element
.getStyle('left'),
656 opacity
: element
.getInlineOpacity() };
657 return new Effect
.Parallel(
658 [ new Effect
.Move(element
, {x
: 0, y
: 100, sync
: true }),
659 new Effect
.Opacity(element
, { sync
: true, to
: 0.0 }) ],
662 beforeSetup: function(effect
) {
663 effect
.effects
[0].element
.makePositioned();
665 afterFinishInternal: function(effect
) {
666 effect
.effects
[0].element
.hide();
667 effect
.effects
[0].element
.undoPositioned();
668 effect
.effects
[0].element
.setStyle(oldStyle
);
670 }, arguments
[1] || {}));
673 Effect
.Shake = function(element
) {
674 element
= $(element
);
676 top
: element
.getStyle('top'),
677 left
: element
.getStyle('left') };
678 return new Effect
.Move(element
,
679 { x
: 20, y
: 0, duration
: 0.05, afterFinishInternal: function(effect
) {
680 new Effect
.Move(effect
.element
,
681 { x
: -40, y
: 0, duration
: 0.1, afterFinishInternal: function(effect
) {
682 new Effect
.Move(effect
.element
,
683 { x
: 40, y
: 0, duration
: 0.1, afterFinishInternal: function(effect
) {
684 new Effect
.Move(effect
.element
,
685 { x
: -40, y
: 0, duration
: 0.1, afterFinishInternal: function(effect
) {
686 new Effect
.Move(effect
.element
,
687 { x
: 40, y
: 0, duration
: 0.1, afterFinishInternal: function(effect
) {
688 new Effect
.Move(effect
.element
,
689 { x
: -20, y
: 0, duration
: 0.05, afterFinishInternal: function(effect
) {
690 effect
.element
.undoPositioned();
691 effect
.element
.setStyle(oldStyle
);
692 }}) }}) }}) }}) }}) }});
695 Effect
.SlideDown = function(element
) {
696 element
= $(element
);
697 element
.cleanWhitespace();
698 // SlideDown need to have the content of the element wrapped in a container element with fixed height!
699 var oldInnerBottom
= $(element
.firstChild
).getStyle('bottom');
700 var elementDimensions
= element
.getDimensions();
701 return new Effect
.Scale(element
, 100, Object
.extend({
705 scaleMode
: {originalHeight
: elementDimensions
.height
, originalWidth
: elementDimensions
.width
},
706 restoreAfterFinish
: true,
707 afterSetup: function(effect
) {
708 effect
.element
.makePositioned();
709 effect
.element
.firstChild
.makePositioned();
710 if(window
.opera
) effect
.element
.setStyle({top
: ''});
711 effect
.element
.makeClipping();
712 effect
.element
.setStyle({height
: '0px'});
713 effect
.element
.show(); },
714 afterUpdateInternal: function(effect
) {
715 effect
.element
.firstChild
.setStyle({bottom
:
716 (effect
.dims
[0] - effect
.element
.clientHeight
) + 'px' });
718 afterFinishInternal: function(effect
) {
719 effect
.element
.undoClipping();
720 // IE will crash if child is undoPositioned first
721 if(/MSIE/.test(navigator
.userAgent
)){
722 effect
.element
.undoPositioned();
723 effect
.element
.firstChild
.undoPositioned();
725 effect
.element
.firstChild
.undoPositioned();
726 effect
.element
.undoPositioned();
728 effect
.element
.firstChild
.setStyle({bottom
: oldInnerBottom
}); }
729 }, arguments
[1] || {})
733 Effect
.SlideUp = function(element
) {
734 element
= $(element
);
735 element
.cleanWhitespace();
736 var oldInnerBottom
= $(element
.firstChild
).getStyle('bottom');
737 return new Effect
.Scale(element
, 0,
738 Object
.extend({ scaleContent
: false,
742 restoreAfterFinish
: true,
743 beforeStartInternal: function(effect
) {
744 effect
.element
.makePositioned();
745 effect
.element
.firstChild
.makePositioned();
746 if(window
.opera
) effect
.element
.setStyle({top
: ''});
747 effect
.element
.makeClipping();
748 effect
.element
.show(); },
749 afterUpdateInternal: function(effect
) {
750 effect
.element
.firstChild
.setStyle({bottom
:
751 (effect
.dims
[0] - effect
.element
.clientHeight
) + 'px' }); },
752 afterFinishInternal: function(effect
) {
753 effect
.element
.hide();
754 effect
.element
.undoClipping();
755 effect
.element
.firstChild
.undoPositioned();
756 effect
.element
.undoPositioned();
757 effect
.element
.setStyle({bottom
: oldInnerBottom
}); }
758 }, arguments
[1] || {})
762 // Bug in opera makes the TD containing this element expand for a instance after finish
763 Effect
.Squish = function(element
) {
764 return new Effect
.Scale(element
, window
.opera
? 1 : 0,
765 { restoreAfterFinish
: true,
766 beforeSetup: function(effect
) {
767 effect
.element
.makeClipping(effect
.element
); },
768 afterFinishInternal: function(effect
) {
769 effect
.element
.hide(effect
.element
);
770 effect
.element
.undoClipping(effect
.element
); }
774 Effect
.Grow = function(element
) {
775 element
= $(element
);
776 var options
= Object
.extend({
778 moveTransition
: Effect
.Transitions
.sinoidal
,
779 scaleTransition
: Effect
.Transitions
.sinoidal
,
780 opacityTransition
: Effect
.Transitions
.full
781 }, arguments
[1] || {});
783 top
: element
.style
.top
,
784 left
: element
.style
.left
,
785 height
: element
.style
.height
,
786 width
: element
.style
.width
,
787 opacity
: element
.getInlineOpacity() };
789 var dims
= element
.getDimensions();
790 var initialMoveX
, initialMoveY
;
793 switch (options
.direction
) {
795 initialMoveX
= initialMoveY
= moveX
= moveY
= 0;
798 initialMoveX
= dims
.width
;
799 initialMoveY
= moveY
= 0;
803 initialMoveX
= moveX
= 0;
804 initialMoveY
= dims
.height
;
805 moveY
= -dims
.height
;
808 initialMoveX
= dims
.width
;
809 initialMoveY
= dims
.height
;
811 moveY
= -dims
.height
;
814 initialMoveX
= dims
.width
/ 2;
815 initialMoveY
= dims
.height
/ 2;
816 moveX
= -dims
.width
/ 2;
817 moveY
= -dims
.height
/ 2;
821 return new Effect
.Move(element
, {
825 beforeSetup: function(effect
) {
826 effect
.element
.hide();
827 effect
.element
.makeClipping();
828 effect
.element
.makePositioned();
830 afterFinishInternal: function(effect
) {
832 [ new Effect
.Opacity(effect
.element
, { sync
: true, to
: 1.0, from: 0.0, transition
: options
.opacityTransition
}),
833 new Effect
.Move(effect
.element
, { x
: moveX
, y
: moveY
, sync
: true, transition
: options
.moveTransition
}),
834 new Effect
.Scale(effect
.element
, 100, {
835 scaleMode
: { originalHeight
: dims
.height
, originalWidth
: dims
.width
},
836 sync
: true, scaleFrom
: window
.opera
? 1 : 0, transition
: options
.scaleTransition
, restoreAfterFinish
: true})
838 beforeSetup: function(effect
) {
839 effect
.effects
[0].element
.setStyle({height
: '0px'});
840 effect
.effects
[0].element
.show();
842 afterFinishInternal: function(effect
) {
843 effect
.effects
[0].element
.undoClipping();
844 effect
.effects
[0].element
.undoPositioned();
845 effect
.effects
[0].element
.setStyle(oldStyle
);
853 Effect
.Shrink = function(element
) {
854 element
= $(element
);
855 var options
= Object
.extend({
857 moveTransition
: Effect
.Transitions
.sinoidal
,
858 scaleTransition
: Effect
.Transitions
.sinoidal
,
859 opacityTransition
: Effect
.Transitions
.none
860 }, arguments
[1] || {});
862 top
: element
.style
.top
,
863 left
: element
.style
.left
,
864 height
: element
.style
.height
,
865 width
: element
.style
.width
,
866 opacity
: element
.getInlineOpacity() };
868 var dims
= element
.getDimensions();
871 switch (options
.direction
) {
888 moveX
= dims
.width
/ 2;
889 moveY
= dims
.height
/ 2;
893 return new Effect
.Parallel(
894 [ new Effect
.Opacity(element
, { sync
: true, to
: 0.0, from: 1.0, transition
: options
.opacityTransition
}),
895 new Effect
.Scale(element
, window
.opera
? 1 : 0, { sync
: true, transition
: options
.scaleTransition
, restoreAfterFinish
: true}),
896 new Effect
.Move(element
, { x
: moveX
, y
: moveY
, sync
: true, transition
: options
.moveTransition
})
898 beforeStartInternal: function(effect
) {
899 effect
.effects
[0].element
.makePositioned();
900 effect
.effects
[0].element
.makeClipping(); },
901 afterFinishInternal: function(effect
) {
902 effect
.effects
[0].element
.hide();
903 effect
.effects
[0].element
.undoClipping();
904 effect
.effects
[0].element
.undoPositioned();
905 effect
.effects
[0].element
.setStyle(oldStyle
); }
910 Effect
.Pulsate = function(element
) {
911 element
= $(element
);
912 var options
= arguments
[1] || {};
913 var oldOpacity
= element
.getInlineOpacity();
914 var transition
= options
.transition
|| Effect
.Transitions
.sinoidal
;
915 var reverser = function(pos
){ return transition(1-Effect
.Transitions
.pulse(pos
)) };
916 reverser
.bind(transition
);
917 return new Effect
.Opacity(element
,
918 Object
.extend(Object
.extend({ duration
: 3.0, from: 0,
919 afterFinishInternal: function(effect
) { effect
.element
.setStyle({opacity
: oldOpacity
}); }
920 }, options
), {transition
: reverser
}));
923 Effect
.Fold = function(element
) {
924 element
= $(element
);
926 top
: element
.style
.top
,
927 left
: element
.style
.left
,
928 width
: element
.style
.width
,
929 height
: element
.style
.height
};
930 Element
.makeClipping(element
);
931 return new Effect
.Scale(element
, 5, Object
.extend({
934 afterFinishInternal: function(effect
) {
935 new Effect
.Scale(element
, 1, {
938 afterFinishInternal: function(effect
) {
939 effect
.element
.hide();
940 effect
.element
.undoClipping();
941 effect
.element
.setStyle(oldStyle
);
943 }}, arguments
[1] || {}));
946 ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
947 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(
948 function(f
) { Element
.Methods
[f
] = Element
[f
]; }
951 Element
.Methods
.visualEffect = function(element
, effect
, options
) {
952 s
= effect
.gsub(/_
/, '-').camelize();
953 effect_class
= s
.charAt(0).toUpperCase() + s
.substring(1);
954 new Effect
[effect_class
](element
, options
);
958 Element
.addMethods();