source: branches/1.2/workflow/js/scriptaculous/effects.js @ 1349

Revision 1349, 32.0 KB checked in by niltonneto, 15 years ago (diff)

Ticket #561 - Inclusão do módulo Workflow faltante nessa versão.

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