d3940a82b561362e7403ee04f5b10f15494e19a2
[euphorik.git] / lightbox / js / effects.js
1 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 // Contributors:
3 // Justin Palmer (http://encytemedia.com/)
4 // Mark Pilgrim (http://diveintomark.org/)
5 // Martin Bialasinki
6 //
7 // See scriptaculous.js for full license.
8
9 /* ------------- element ext -------------- */
10
11 // converts rgb() and #xxx to #xxxxxx format,
12 // returns self (or first argument) if not convertable
13 String.prototype.parseColor = function() {
14 var color = '#';
15 if(this.slice(0,4) == 'rgb(') {
16 var cols = this.slice(4,this.length-1).split(',');
17 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
18 } else {
19 if(this.slice(0,1) == '#') {
20 if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
21 if(this.length==7) color = this.toLowerCase();
22 }
23 }
24 return(color.length==7 ? color : (arguments[0] || this));
25 }
26
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('');
32 }
33
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.collectTextNodes(node) : ''));
39 }).flatten().join('');
40 }
41
42 Element.setStyle = function(element, style) {
43 element = $(element);
44 for(k in style) element.style[k.camelize()] = style[k];
45 }
46
47 Element.setContentZoom = function(element, percent) {
48 Element.setStyle(element, {fontSize: (percent/100) + 'em'});
49 if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
50 }
51
52 Element.getOpacity = function(element){
53 var opacity;
54 if (opacity = Element.getStyle(element, 'opacity'))
55 return parseFloat(opacity);
56 if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
57 if(opacity[1]) return parseFloat(opacity[1]) / 100;
58 return 1.0;
59 }
60
61 Element.setOpacity = function(element, value){
62 element= $(element);
63 if (value == 1){
64 Element.setStyle(element, { opacity:
65 (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
66 0.999999 : null });
67 if(/MSIE/.test(navigator.userAgent))
68 Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
69 } else {
70 if(value < 0.00001) value = 0;
71 Element.setStyle(element, {opacity: value});
72 if(/MSIE/.test(navigator.userAgent))
73 Element.setStyle(element,
74 { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
75 'alpha(opacity='+value*100+')' });
76 }
77 }
78
79 Element.getInlineOpacity = function(element){
80 return $(element).style.opacity || '';
81 }
82
83 Element.childrenWithClassName = function(element, className) {
84 return $A($(element).getElementsByTagName('*')).select(
85 function(c) { return Element.hasClassName(c, className) });
86 }
87
88 Array.prototype.call = function() {
89 var args = arguments;
90 this.each(function(f){ f.apply(this, args) });
91 }
92
93 /*--------------------------------------------------------------------------*/
94
95 var Effect = {
96 tagifyText: function(element) {
97 var tagifyStyle = 'position:relative';
98 if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
99 element = $(element);
100 $A(element.childNodes).each( function(child) {
101 if(child.nodeType==3) {
102 child.nodeValue.toArray().each( function(character) {
103 element.insertBefore(
104 Builder.node('span',{style: tagifyStyle},
105 character == ' ' ? String.fromCharCode(160) : character),
106 child);
107 });
108 Element.remove(child);
109 }
110 });
111 },
112 multiple: function(element, effect) {
113 var elements;
114 if(((typeof element == 'object') ||
115 (typeof element == 'function')) &&
116 (element.length))
117 elements = element;
118 else
119 elements = $(element).childNodes;
120
121 var options = Object.extend({
122 speed: 0.1,
123 delay: 0.0
124 }, arguments[2] || {});
125 var masterDelay = options.delay;
126
127 $A(elements).each( function(element, index) {
128 new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
129 });
130 },
131 PAIRS: {
132 'slide': ['SlideDown','SlideUp'],
133 'blind': ['BlindDown','BlindUp'],
134 'appear': ['Appear','Fade']
135 },
136 toggle: function(element, effect) {
137 element = $(element);
138 effect = (effect || 'appear').toLowerCase();
139 var options = Object.extend({
140 queue: { position:'end', scope:(element.id || 'global') }
141 }, arguments[2] || {});
142 Effect[Element.visible(element) ?
143 Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
144 }
145 };
146
147 var Effect2 = Effect; // deprecated
148
149 /* ------------- transitions ------------- */
150
151 Effect.Transitions = {}
152
153 Effect.Transitions.linear = function(pos) {
154 return pos;
155 }
156 Effect.Transitions.sinoidal = function(pos) {
157 return (-Math.cos(pos*Math.PI)/2) + 0.5;
158 }
159 Effect.Transitions.reverse = function(pos) {
160 return 1-pos;
161 }
162 Effect.Transitions.flicker = function(pos) {
163 return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
164 }
165 Effect.Transitions.wobble = function(pos) {
166 return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
167 }
168 Effect.Transitions.pulse = function(pos) {
169 return (Math.floor(pos*10) % 2 == 0 ?
170 (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
171 }
172 Effect.Transitions.none = function(pos) {
173 return 0;
174 }
175 Effect.Transitions.full = function(pos) {
176 return 1;
177 }
178
179 /* ------------- core effects ------------- */
180
181 Effect.ScopedQueue = Class.create();
182 Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
183 initialize: function() {
184 this.effects = [];
185 this.interval = null;
186 },
187 _each: function(iterator) {
188 this.effects._each(iterator);
189 },
190 add: function(effect) {
191 var timestamp = new Date().getTime();
192
193 var position = (typeof effect.options.queue == 'string') ?
194 effect.options.queue : effect.options.queue.position;
195
196 switch(position) {
197 case 'front':
198 // move unstarted effects after this effect
199 this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
200 e.startOn += effect.finishOn;
201 e.finishOn += effect.finishOn;
202 });
203 break;
204 case 'end':
205 // start effect after last queued effect has finished
206 timestamp = this.effects.pluck('finishOn').max() || timestamp;
207 break;
208 }
209
210 effect.startOn += timestamp;
211 effect.finishOn += timestamp;
212 this.effects.push(effect);
213 if(!this.interval)
214 this.interval = setInterval(this.loop.bind(this), 40);
215 },
216 remove: function(effect) {
217 this.effects = this.effects.reject(function(e) { return e==effect });
218 if(this.effects.length == 0) {
219 clearInterval(this.interval);
220 this.interval = null;
221 }
222 },
223 loop: function() {
224 var timePos = new Date().getTime();
225 this.effects.invoke('loop', timePos);
226 }
227 });
228
229 Effect.Queues = {
230 instances: $H(),
231 get: function(queueName) {
232 if(typeof queueName != 'string') return queueName;
233
234 if(!this.instances[queueName])
235 this.instances[queueName] = new Effect.ScopedQueue();
236
237 return this.instances[queueName];
238 }
239 }
240 Effect.Queue = Effect.Queues.get('global');
241
242 Effect.DefaultOptions = {
243 transition: Effect.Transitions.sinoidal,
244 duration: 1.0, // seconds
245 fps: 25.0, // max. 25fps due to Effect.Queue implementation
246 sync: false, // true for combining
247 from: 0.0,
248 to: 1.0,
249 delay: 0.0,
250 queue: 'parallel'
251 }
252
253 Effect.Base = function() {};
254 Effect.Base.prototype = {
255 position: null,
256 start: function(options) {
257 this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
258 this.currentFrame = 0;
259 this.state = 'idle';
260 this.startOn = this.options.delay*1000;
261 this.finishOn = this.startOn + (this.options.duration*1000);
262 this.event('beforeStart');
263 if(!this.options.sync)
264 Effect.Queues.get(typeof this.options.queue == 'string' ?
265 'global' : this.options.queue.scope).add(this);
266 },
267 loop: function(timePos) {
268 if(timePos >= this.startOn) {
269 if(timePos >= this.finishOn) {
270 this.render(1.0);
271 this.cancel();
272 this.event('beforeFinish');
273 if(this.finish) this.finish();
274 this.event('afterFinish');
275 return;
276 }
277 var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
278 var frame = Math.round(pos * this.options.fps * this.options.duration);
279 if(frame > this.currentFrame) {
280 this.render(pos);
281 this.currentFrame = frame;
282 }
283 }
284 },
285 render: function(pos) {
286 if(this.state == 'idle') {
287 this.state = 'running';
288 this.event('beforeSetup');
289 if(this.setup) this.setup();
290 this.event('afterSetup');
291 }
292 if(this.state == 'running') {
293 if(this.options.transition) pos = this.options.transition(pos);
294 pos *= (this.options.to-this.options.from);
295 pos += this.options.from;
296 this.position = pos;
297 this.event('beforeUpdate');
298 if(this.update) this.update(pos);
299 this.event('afterUpdate');
300 }
301 },
302 cancel: function() {
303 if(!this.options.sync)
304 Effect.Queues.get(typeof this.options.queue == 'string' ?
305 'global' : this.options.queue.scope).remove(this);
306 this.state = 'finished';
307 },
308 event: function(eventName) {
309 if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
310 if(this.options[eventName]) this.options[eventName](this);
311 },
312 inspect: function() {
313 return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
314 }
315 }
316
317 Effect.Parallel = Class.create();
318 Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
319 initialize: function(effects) {
320 this.effects = effects || [];
321 this.start(arguments[1]);
322 },
323 update: function(position) {
324 this.effects.invoke('render', position);
325 },
326 finish: function(position) {
327 this.effects.each( function(effect) {
328 effect.render(1.0);
329 effect.cancel();
330 effect.event('beforeFinish');
331 if(effect.finish) effect.finish(position);
332 effect.event('afterFinish');
333 });
334 }
335 });
336
337 Effect.Opacity = Class.create();
338 Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
339 initialize: function(element) {
340 this.element = $(element);
341 // make this work on IE on elements without 'layout'
342 if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
343 Element.setStyle(this.element, {zoom: 1});
344 var options = Object.extend({
345 from: Element.getOpacity(this.element) || 0.0,
346 to: 1.0
347 }, arguments[1] || {});
348 this.start(options);
349 },
350 update: function(position) {
351 Element.setOpacity(this.element, position);
352 }
353 });
354
355 Effect.Move = Class.create();
356 Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
357 initialize: function(element) {
358 this.element = $(element);
359 var options = Object.extend({
360 x: 0,
361 y: 0,
362 mode: 'relative'
363 }, arguments[1] || {});
364 this.start(options);
365 },
366 setup: function() {
367 // Bug in Opera: Opera returns the "real" position of a static element or
368 // relative element that does not have top/left explicitly set.
369 // ==> Always set top and left for position relative elements in your stylesheets
370 // (to 0 if you do not need them)
371 Element.makePositioned(this.element);
372 this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
373 this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
374 if(this.options.mode == 'absolute') {
375 // absolute movement, so we need to calc deltaX and deltaY
376 this.options.x = this.options.x - this.originalLeft;
377 this.options.y = this.options.y - this.originalTop;
378 }
379 },
380 update: function(position) {
381 Element.setStyle(this.element, {
382 left: this.options.x * position + this.originalLeft + 'px',
383 top: this.options.y * position + this.originalTop + 'px'
384 });
385 }
386 });
387
388 // for backwards compatibility
389 Effect.MoveBy = function(element, toTop, toLeft) {
390 return new Effect.Move(element,
391 Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
392 };
393
394 Effect.Scale = Class.create();
395 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
396 initialize: function(element, percent) {
397 this.element = $(element)
398 var options = Object.extend({
399 scaleX: true,
400 scaleY: true,
401 scaleContent: true,
402 scaleFromCenter: false,
403 scaleMode: 'box', // 'box' or 'contents' or {} with provided values
404 scaleFrom: 100.0,
405 scaleTo: percent
406 }, arguments[2] || {});
407 this.start(options);
408 },
409 setup: function() {
410 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
411 this.elementPositioning = Element.getStyle(this.element,'position');
412
413 this.originalStyle = {};
414 ['top','left','width','height','fontSize'].each( function(k) {
415 this.originalStyle[k] = this.element.style[k];
416 }.bind(this));
417
418 this.originalTop = this.element.offsetTop;
419 this.originalLeft = this.element.offsetLeft;
420
421 var fontSize = Element.getStyle(this.element,'font-size') || '100%';
422 ['em','px','%'].each( function(fontSizeType) {
423 if(fontSize.indexOf(fontSizeType)>0) {
424 this.fontSize = parseFloat(fontSize);
425 this.fontSizeType = fontSizeType;
426 }
427 }.bind(this));
428
429 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
430
431 this.dims = null;
432 if(this.options.scaleMode=='box')
433 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
434 if(/^content/.test(this.options.scaleMode))
435 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
436 if(!this.dims)
437 this.dims = [this.options.scaleMode.originalHeight,
438 this.options.scaleMode.originalWidth];
439 },
440 update: function(position) {
441 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
442 if(this.options.scaleContent && this.fontSize)
443 Element.setStyle(this.element, {fontSize: this.fontSize * currentScale + this.fontSizeType });
444 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
445 },
446 finish: function(position) {
447 if (this.restoreAfterFinish) Element.setStyle(this.element, this.originalStyle);
448 },
449 setDimensions: function(height, width) {
450 var d = {};
451 if(this.options.scaleX) d.width = width + 'px';
452 if(this.options.scaleY) d.height = height + 'px';
453 if(this.options.scaleFromCenter) {
454 var topd = (height - this.dims[0])/2;
455 var leftd = (width - this.dims[1])/2;
456 if(this.elementPositioning == 'absolute') {
457 if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
458 if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
459 } else {
460 if(this.options.scaleY) d.top = -topd + 'px';
461 if(this.options.scaleX) d.left = -leftd + 'px';
462 }
463 }
464 Element.setStyle(this.element, d);
465 }
466 });
467
468 Effect.Highlight = Class.create();
469 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
470 initialize: function(element) {
471 this.element = $(element);
472 var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
473 this.start(options);
474 },
475 setup: function() {
476 // Prevent executing on elements not in the layout flow
477 if(Element.getStyle(this.element, 'display')=='none') { this.cancel(); return; }
478 // Disable background image during the effect
479 this.oldStyle = {
480 backgroundImage: Element.getStyle(this.element, 'background-image') };
481 Element.setStyle(this.element, {backgroundImage: 'none'});
482 if(!this.options.endcolor)
483 this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff');
484 if(!this.options.restorecolor)
485 this.options.restorecolor = Element.getStyle(this.element, 'background-color');
486 // init color calculations
487 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
488 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));
489 },
490 update: function(position) {
491 Element.setStyle(this.element,{backgroundColor: $R(0,2).inject('#',function(m,v,i){
492 return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
493 },
494 finish: function() {
495 Element.setStyle(this.element, Object.extend(this.oldStyle, {
496 backgroundColor: this.options.restorecolor
497 }));
498 }
499 });
500
501 Effect.ScrollTo = Class.create();
502 Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
503 initialize: function(element) {
504 this.element = $(element);
505 this.start(arguments[1] || {});
506 },
507 setup: function() {
508 Position.prepare();
509 var offsets = Position.cumulativeOffset(this.element);
510 if(this.options.offset) offsets[1] += this.options.offset;
511 var max = window.innerHeight ?
512 window.height - window.innerHeight :
513 document.body.scrollHeight -
514 (document.documentElement.clientHeight ?
515 document.documentElement.clientHeight : document.body.clientHeight);
516 this.scrollStart = Position.deltaY;
517 this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
518 },
519 update: function(position) {
520 Position.prepare();
521 window.scrollTo(Position.deltaX,
522 this.scrollStart + (position*this.delta));
523 }
524 });
525
526 /* ------------- combination effects ------------- */
527
528 Effect.Fade = function(element) {
529 var oldOpacity = Element.getInlineOpacity(element);
530 var options = Object.extend({
531 from: Element.getOpacity(element) || 1.0,
532 to: 0.0,
533 afterFinishInternal: function(effect) { with(Element) {
534 if(effect.options.to!=0) return;
535 hide(effect.element);
536 setStyle(effect.element, {opacity: oldOpacity}); }}
537 }, arguments[1] || {});
538 return new Effect.Opacity(element,options);
539 }
540
541 Effect.Appear = function(element) {
542 var options = Object.extend({
543 from: (Element.getStyle(element, 'display') == 'none' ? 0.0 : Element.getOpacity(element) || 0.0),
544 to: 1.0,
545 beforeSetup: function(effect) { with(Element) {
546 setOpacity(effect.element, effect.options.from);
547 show(effect.element); }}
548 }, arguments[1] || {});
549 return new Effect.Opacity(element,options);
550 }
551
552 Effect.Puff = function(element) {
553 element = $(element);
554 var oldStyle = { opacity: Element.getInlineOpacity(element), position: Element.getStyle(element, 'position') };
555 return new Effect.Parallel(
556 [ new Effect.Scale(element, 200,
557 { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
558 new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
559 Object.extend({ duration: 1.0,
560 beforeSetupInternal: function(effect) { with(Element) {
561 setStyle(effect.effects[0].element, {position: 'absolute'}); }},
562 afterFinishInternal: function(effect) { with(Element) {
563 hide(effect.effects[0].element);
564 setStyle(effect.effects[0].element, oldStyle); }}
565 }, arguments[1] || {})
566 );
567 }
568
569 Effect.BlindUp = function(element) {
570 element = $(element);
571 Element.makeClipping(element);
572 return new Effect.Scale(element, 0,
573 Object.extend({ scaleContent: false,
574 scaleX: false,
575 restoreAfterFinish: true,
576 afterFinishInternal: function(effect) { with(Element) {
577 [hide, undoClipping].call(effect.element); }}
578 }, arguments[1] || {})
579 );
580 }
581
582 Effect.BlindDown = function(element) {
583 element = $(element);
584 var oldHeight = Element.getStyle(element, 'height');
585 var elementDimensions = Element.getDimensions(element);
586 return new Effect.Scale(element, 100,
587 Object.extend({ scaleContent: false,
588 scaleX: false,
589 scaleFrom: 0,
590 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
591 restoreAfterFinish: true,
592 afterSetup: function(effect) { with(Element) {
593 makeClipping(effect.element);
594 setStyle(effect.element, {height: '0px'});
595 show(effect.element);
596 }},
597 afterFinishInternal: function(effect) { with(Element) {
598 undoClipping(effect.element);
599 setStyle(effect.element, {height: oldHeight});
600 }}
601 }, arguments[1] || {})
602 );
603 }
604
605 Effect.SwitchOff = function(element) {
606 element = $(element);
607 var oldOpacity = Element.getInlineOpacity(element);
608 return new Effect.Appear(element, {
609 duration: 0.4,
610 from: 0,
611 transition: Effect.Transitions.flicker,
612 afterFinishInternal: function(effect) {
613 new Effect.Scale(effect.element, 1, {
614 duration: 0.3, scaleFromCenter: true,
615 scaleX: false, scaleContent: false, restoreAfterFinish: true,
616 beforeSetup: function(effect) { with(Element) {
617 [makePositioned,makeClipping].call(effect.element);
618 }},
619 afterFinishInternal: function(effect) { with(Element) {
620 [hide,undoClipping,undoPositioned].call(effect.element);
621 setStyle(effect.element, {opacity: oldOpacity});
622 }}
623 })
624 }
625 });
626 }
627
628 Effect.DropOut = function(element) {
629 element = $(element);
630 var oldStyle = {
631 top: Element.getStyle(element, 'top'),
632 left: Element.getStyle(element, 'left'),
633 opacity: Element.getInlineOpacity(element) };
634 return new Effect.Parallel(
635 [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
636 new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
637 Object.extend(
638 { duration: 0.5,
639 beforeSetup: function(effect) { with(Element) {
640 makePositioned(effect.effects[0].element); }},
641 afterFinishInternal: function(effect) { with(Element) {
642 [hide, undoPositioned].call(effect.effects[0].element);
643 setStyle(effect.effects[0].element, oldStyle); }}
644 }, arguments[1] || {}));
645 }
646
647 Effect.Shake = function(element) {
648 element = $(element);
649 var oldStyle = {
650 top: Element.getStyle(element, 'top'),
651 left: Element.getStyle(element, 'left') };
652 return new Effect.Move(element,
653 { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
654 new Effect.Move(effect.element,
655 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
656 new Effect.Move(effect.element,
657 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
658 new Effect.Move(effect.element,
659 { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
660 new Effect.Move(effect.element,
661 { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
662 new Effect.Move(effect.element,
663 { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
664 undoPositioned(effect.element);
665 setStyle(effect.element, oldStyle);
666 }}}) }}) }}) }}) }}) }});
667 }
668
669 Effect.SlideDown = function(element) {
670 element = $(element);
671 Element.cleanWhitespace(element);
672 // SlideDown need to have the content of the element wrapped in a container element with fixed height!
673 var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
674 var elementDimensions = Element.getDimensions(element);
675 return new Effect.Scale(element, 100, Object.extend({
676 scaleContent: false,
677 scaleX: false,
678 scaleFrom: 0,
679 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
680 restoreAfterFinish: true,
681 afterSetup: function(effect) { with(Element) {
682 makePositioned(effect.element);
683 makePositioned(effect.element.firstChild);
684 if(window.opera) setStyle(effect.element, {top: ''});
685 makeClipping(effect.element);
686 setStyle(effect.element, {height: '0px'});
687 show(element); }},
688 afterUpdateInternal: function(effect) { with(Element) {
689 setStyle(effect.element.firstChild, {bottom:
690 (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
691 afterFinishInternal: function(effect) { with(Element) {
692 undoClipping(effect.element);
693 undoPositioned(effect.element.firstChild);
694 undoPositioned(effect.element);
695 setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
696 }, arguments[1] || {})
697 );
698 }
699
700 Effect.SlideUp = function(element) {
701 element = $(element);
702 Element.cleanWhitespace(element);
703 var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
704 return new Effect.Scale(element, 0,
705 Object.extend({ scaleContent: false,
706 scaleX: false,
707 scaleMode: 'box',
708 scaleFrom: 100,
709 restoreAfterFinish: true,
710 beforeStartInternal: function(effect) { with(Element) {
711 makePositioned(effect.element);
712 makePositioned(effect.element.firstChild);
713 if(window.opera) setStyle(effect.element, {top: ''});
714 makeClipping(effect.element);
715 show(element); }},
716 afterUpdateInternal: function(effect) { with(Element) {
717 setStyle(effect.element.firstChild, {bottom:
718 (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
719 afterFinishInternal: function(effect) { with(Element) {
720 [hide, undoClipping].call(effect.element);
721 undoPositioned(effect.element.firstChild);
722 undoPositioned(effect.element);
723 setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
724 }, arguments[1] || {})
725 );
726 }
727
728 // Bug in opera makes the TD containing this element expand for a instance after finish
729 Effect.Squish = function(element) {
730 return new Effect.Scale(element, window.opera ? 1 : 0,
731 { restoreAfterFinish: true,
732 beforeSetup: function(effect) { with(Element) {
733 makeClipping(effect.element); }},
734 afterFinishInternal: function(effect) { with(Element) {
735 hide(effect.element);
736 undoClipping(effect.element); }}
737 });
738 }
739
740 Effect.Grow = function(element) {
741 element = $(element);
742 var options = Object.extend({
743 direction: 'center',
744 moveTransistion: Effect.Transitions.sinoidal,
745 scaleTransition: Effect.Transitions.sinoidal,
746 opacityTransition: Effect.Transitions.full
747 }, arguments[1] || {});
748 var oldStyle = {
749 top: element.style.top,
750 left: element.style.left,
751 height: element.style.height,
752 width: element.style.width,
753 opacity: Element.getInlineOpacity(element) };
754
755 var dims = Element.getDimensions(element);
756 var initialMoveX, initialMoveY;
757 var moveX, moveY;
758
759 switch (options.direction) {
760 case 'top-left':
761 initialMoveX = initialMoveY = moveX = moveY = 0;
762 break;
763 case 'top-right':
764 initialMoveX = dims.width;
765 initialMoveY = moveY = 0;
766 moveX = -dims.width;
767 break;
768 case 'bottom-left':
769 initialMoveX = moveX = 0;
770 initialMoveY = dims.height;
771 moveY = -dims.height;
772 break;
773 case 'bottom-right':
774 initialMoveX = dims.width;
775 initialMoveY = dims.height;
776 moveX = -dims.width;
777 moveY = -dims.height;
778 break;
779 case 'center':
780 initialMoveX = dims.width / 2;
781 initialMoveY = dims.height / 2;
782 moveX = -dims.width / 2;
783 moveY = -dims.height / 2;
784 break;
785 }
786
787 return new Effect.Move(element, {
788 x: initialMoveX,
789 y: initialMoveY,
790 duration: 0.01,
791 beforeSetup: function(effect) { with(Element) {
792 hide(effect.element);
793 makeClipping(effect.element);
794 makePositioned(effect.element);
795 }},
796 afterFinishInternal: function(effect) {
797 new Effect.Parallel(
798 [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
799 new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
800 new Effect.Scale(effect.element, 100, {
801 scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
802 sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
803 ], Object.extend({
804 beforeSetup: function(effect) { with(Element) {
805 setStyle(effect.effects[0].element, {height: '0px'});
806 show(effect.effects[0].element); }},
807 afterFinishInternal: function(effect) { with(Element) {
808 [undoClipping, undoPositioned].call(effect.effects[0].element);
809 setStyle(effect.effects[0].element, oldStyle); }}
810 }, options)
811 )
812 }
813 });
814 }
815
816 Effect.Shrink = function(element) {
817 element = $(element);
818 var options = Object.extend({
819 direction: 'center',
820 moveTransistion: Effect.Transitions.sinoidal,
821 scaleTransition: Effect.Transitions.sinoidal,
822 opacityTransition: Effect.Transitions.none
823 }, arguments[1] || {});
824 var oldStyle = {
825 top: element.style.top,
826 left: element.style.left,
827 height: element.style.height,
828 width: element.style.width,
829 opacity: Element.getInlineOpacity(element) };
830
831 var dims = Element.getDimensions(element);
832 var moveX, moveY;
833
834 switch (options.direction) {
835 case 'top-left':
836 moveX = moveY = 0;
837 break;
838 case 'top-right':
839 moveX = dims.width;
840 moveY = 0;
841 break;
842 case 'bottom-left':
843 moveX = 0;
844 moveY = dims.height;
845 break;
846 case 'bottom-right':
847 moveX = dims.width;
848 moveY = dims.height;
849 break;
850 case 'center':
851 moveX = dims.width / 2;
852 moveY = dims.height / 2;
853 break;
854 }
855
856 return new Effect.Parallel(
857 [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
858 new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
859 new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
860 ], Object.extend({
861 beforeStartInternal: function(effect) { with(Element) {
862 [makePositioned, makeClipping].call(effect.effects[0].element) }},
863 afterFinishInternal: function(effect) { with(Element) {
864 [hide, undoClipping, undoPositioned].call(effect.effects[0].element);
865 setStyle(effect.effects[0].element, oldStyle); }}
866 }, options)
867 );
868 }
869
870 Effect.Pulsate = function(element) {
871 element = $(element);
872 var options = arguments[1] || {};
873 var oldOpacity = Element.getInlineOpacity(element);
874 var transition = options.transition || Effect.Transitions.sinoidal;
875 var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
876 reverser.bind(transition);
877 return new Effect.Opacity(element,
878 Object.extend(Object.extend({ duration: 3.0, from: 0,
879 afterFinishInternal: function(effect) { Element.setStyle(effect.element, {opacity: oldOpacity}); }
880 }, options), {transition: reverser}));
881 }
882
883 Effect.Fold = function(element) {
884 element = $(element);
885 var oldStyle = {
886 top: element.style.top,
887 left: element.style.left,
888 width: element.style.width,
889 height: element.style.height };
890 Element.makeClipping(element);
891 return new Effect.Scale(element, 5, Object.extend({
892 scaleContent: false,
893 scaleX: false,
894 afterFinishInternal: function(effect) {
895 new Effect.Scale(element, 1, {
896 scaleContent: false,
897 scaleY: false,
898 afterFinishInternal: function(effect) { with(Element) {
899 [hide, undoClipping].call(effect.element);
900 setStyle(effect.element, oldStyle);
901 }} });
902 }}, arguments[1] || {}));
903 }