Team:Queens-Canada/js/effects

From 2010.igem.org

Revision as of 19:33, 21 June 2010 by Glh (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

// script.aculo.us effects.js v1.7.0_beta1, Tue Nov 21 10:25:25 CET 2006

// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) // Contributors: // Justin Palmer (http://encytemedia.com/) // Mark Pilgrim (http://diveintomark.org/) // Martin Bialasinki // // script.aculo.us is freely distributable under the terms of an MIT-style license. // For details, see the script.aculo.us web site: http://script.aculo.us/

// converts rgb() and #xxx to #xxxxxx format, // returns self (or first argument) if not convertable String.prototype.parseColor = function() {

 var color = '#';
 if(this.slice(0,4) == 'rgb(') {  
   var cols = this.slice(4,this.length-1).split(',');  
   var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
 } else {  
   if(this.slice(0,1) == '#') {  
     if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
     if(this.length==7) color = this.toLowerCase();  
   }  
 }  
 return(color.length==7 ? color : (arguments[0] || this));  

}

/*--------------------------------------------------------------------------*/

Element.collectTextNodes = function(element) {

 return $A($(element).childNodes).collect( function(node) {
   return (node.nodeType==3 ? node.nodeValue : 
     (node.hasChildNodes() ? Element.collectTextNodes(node) : ));
 }).flatten().join();

}

Element.collectTextNodesIgnoreClass = function(element, className) {

 return $A($(element).childNodes).collect( function(node) {
   return (node.nodeType==3 ? node.nodeValue : 
     ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
       Element.collectTextNodesIgnoreClass(node, className) : ));
 }).flatten().join();

}

Element.setContentZoom = function(element, percent) {

 element = $(element);  
 element.setStyle({fontSize: (percent/100) + 'em'});   
 if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
 return element;

}

Element.getOpacity = function(element){

 element = $(element);
 var opacity;
 if (opacity = element.getStyle('opacity'))  
   return parseFloat(opacity);  
 if (opacity = (element.getStyle('filter') || ).match(/alpha\(opacity=(.*)\)/))  
   if(opacity[1]) return parseFloat(opacity[1]) / 100;  
 return 1.0;  

}

Element.setOpacity = function(element, value){

 element= $(element);  
 if (value == 1){
   element.setStyle({ opacity: 
     (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
     0.999999 : 1.0 });
   if(/MSIE/.test(navigator.userAgent) && !window.opera)  
     element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,)});  
 } else {  
   if(value < 0.00001) value = 0;  
   element.setStyle({opacity: value});
   if(/MSIE/.test(navigator.userAgent) && !window.opera)  
     element.setStyle(
       { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,) +
           'alpha(opacity='+value*100+')' });  
 }
 return element;

}

Element.getInlineOpacity = function(element){

 return $(element).style.opacity || ;

}

Element.forceRerendering = function(element) {

 try {
   element = $(element);
   var n = document.createTextNode(' ');
   element.appendChild(n);
   element.removeChild(n);
 } catch(e) { }

};

/*--------------------------------------------------------------------------*/

Array.prototype.call = function() {

 var args = arguments;
 this.each(function(f){ f.apply(this, args) });

}

/*--------------------------------------------------------------------------*/

var Effect = {

 _elementDoesNotExistError: {
   name: 'ElementDoesNotExistError',
   message: 'The specified DOM element does not exist, but is required for this effect to operate'
 },
 tagifyText: function(element) {
   if(typeof Builder == 'undefined')
     throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
     
   var tagifyStyle = 'position:relative';
   if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
   
   element = $(element);
   $A(element.childNodes).each( function(child) {
     if(child.nodeType==3) {
       child.nodeValue.toArray().each( function(character) {
         element.insertBefore(
           Builder.node('span',{style: tagifyStyle},
             character == ' ' ? String.fromCharCode(160) : character), 
             child);
       });
       Element.remove(child);
     }
   });
 },
 multiple: function(element, effect) {
   var elements;
   if(((typeof element == 'object') || 
       (typeof element == 'function')) && 
      (element.length))
     elements = element;
   else
     elements = $(element).childNodes;
     
   var options = Object.extend({
     speed: 0.1,
     delay: 0.0
   }, arguments[2] || {});
   var masterDelay = options.delay;
   $A(elements).each( function(element, index) {
     new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
   });
 },
 PAIRS: {
   'slide':  ['SlideDown','SlideUp'],
   'blind':  ['BlindDown','BlindUp'],
   'appear': ['Appear','Fade']
 },
 toggle: function(element, effect) {
   element = $(element);
   effect = (effect || 'appear').toLowerCase();
   var options = Object.extend({
     queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
   }, arguments[2] || {});
   Effect[element.visible() ? 
     Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
 }

};

var Effect2 = Effect; // deprecated

/* ------------- transitions ------------- */

Effect.Transitions = {

 linear: Prototype.K,
 sinoidal: function(pos) {
   return (-Math.cos(pos*Math.PI)/2) + 0.5;
 },
 reverse: function(pos) {
   return 1-pos;
 },
 flicker: function(pos) {
   return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
 },
 wobble: function(pos) {
   return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
 },
 pulse: function(pos, pulses) { 
   pulses = pulses || 5; 
   return (
     Math.round((pos % (1/pulses)) * pulses) == 0 ? 
           ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 
       1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
     );
 },
 none: function(pos) {
   return 0;
 },
 full: function(pos) {
   return 1;
 }

};

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create(); Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {

 initialize: function() {
   this.effects  = [];
   this.interval = null;
 },
 _each: function(iterator) {
   this.effects._each(iterator);
 },
 add: function(effect) {
   var timestamp = new Date().getTime();
   
   var position = (typeof effect.options.queue == 'string') ? 
     effect.options.queue : effect.options.queue.position;
   
   switch(position) {
     case 'front':
       // move unstarted effects after this effect  
       this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
           e.startOn  += effect.finishOn;
           e.finishOn += effect.finishOn;
         });
       break;
     case 'with-last':
       timestamp = this.effects.pluck('startOn').max() || timestamp;
       break;
     case 'end':
       // start effect after last queued effect has finished
       timestamp = this.effects.pluck('finishOn').max() || timestamp;
       break;
   }
   
   effect.startOn  += timestamp;
   effect.finishOn += timestamp;
   if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
     this.effects.push(effect);
   
   if(!this.interval) 
     this.interval = setInterval(this.loop.bind(this), 40);
 },
 remove: function(effect) {
   this.effects = this.effects.reject(function(e) { return e==effect });
   if(this.effects.length == 0) {
     clearInterval(this.interval);
     this.interval = null;
   }
 },
 loop: function() {
   var timePos = new Date().getTime();
   this.effects.invoke('loop', timePos);
 }

});

Effect.Queues = {

 instances: $H(),
 get: function(queueName) {
   if(typeof queueName != 'string') return queueName;
   
   if(!this.instances[queueName])
     this.instances[queueName] = new Effect.ScopedQueue();
     
   return this.instances[queueName];
 }

} Effect.Queue = Effect.Queues.get('global');

Effect.DefaultOptions = {

 transition: Effect.Transitions.sinoidal,
 duration:   1.0,   // seconds
 fps:        25.0,  // max. 25fps due to Effect.Queue implementation
 sync:       false, // true for combining
 from:       0.0,
 to:         1.0,
 delay:      0.0,
 queue:      'parallel'

}

Effect.Base = function() {}; Effect.Base.prototype = {

 position: null,
 start: function(options) {
   this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
   this.currentFrame = 0;
   this.state        = 'idle';
   this.startOn      = this.options.delay*1000;
   this.finishOn     = this.startOn + (this.options.duration*1000);
   this.event('beforeStart');
   if(!this.options.sync)
     Effect.Queues.get(typeof this.options.queue == 'string' ? 
       'global' : this.options.queue.scope).add(this);
 },
 loop: function(timePos) {
   if(timePos >= this.startOn) {
     if(timePos >= this.finishOn) {
       this.render(1.0);
       this.cancel();
       this.event('beforeFinish');
       if(this.finish) this.finish(); 
       this.event('afterFinish');
       return;  
     }
     var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
     var frame = Math.round(pos * this.options.fps * this.options.duration);
     if(frame > this.currentFrame) {
       this.render(pos);
       this.currentFrame = frame;
     }
   }
 },
 render: function(pos) {
   if(this.state == 'idle') {
     this.state = 'running';
     this.event('beforeSetup');
     if(this.setup) this.setup();
     this.event('afterSetup');
   }
   if(this.state == 'running') {
     if(this.options.transition) pos = this.options.transition(pos);
     pos *= (this.options.to-this.options.from);
     pos += this.options.from;
     this.position = pos;
     this.event('beforeUpdate');
     if(this.update) this.update(pos);
     this.event('afterUpdate');
   }
 },
 cancel: function() {
   if(!this.options.sync)
     Effect.Queues.get(typeof this.options.queue == 'string' ? 
       'global' : this.options.queue.scope).remove(this);
   this.state = 'finished';
 },
 event: function(eventName) {
   if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
   if(this.options[eventName]) this.options[eventName](this);
 },
 inspect: function() {
   return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
 }

}

Effect.Parallel = Class.create(); Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {

 initialize: function(effects) {
   this.effects = effects || [];
   this.start(arguments[1]);
 },
 update: function(position) {
   this.effects.invoke('render', position);
 },
 finish: function(position) {
   this.effects.each( function(effect) {
     effect.render(1.0);
     effect.cancel();
     effect.event('beforeFinish');
     if(effect.finish) effect.finish(position);
     effect.event('afterFinish');
   });
 }

});

Effect.Event = Class.create(); Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {

 initialize: function() {
   var options = Object.extend({
     duration: 0
   }, arguments[0] || {});
   this.start(options);
 },
 update: Prototype.emptyFunction

});

Effect.Opacity = Class.create(); Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {

 initialize: function(element) {
   this.element = $(element);
   if(!this.element) throw(Effect._elementDoesNotExistError);
   // make this work on IE on elements without 'layout'
   if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
     this.element.setStyle({zoom: 1});
   var options = Object.extend({
     from: this.element.getOpacity() || 0.0,
     to:   1.0
   }, arguments[1] || {});
   this.start(options);
 },
 update: function(position) {
   this.element.setOpacity(position);
 }

});

Effect.Move = Class.create(); Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {

 initialize: function(element) {
   this.element = $(element);
   if(!this.element) throw(Effect._elementDoesNotExistError);
   var options = Object.extend({
     x:    0,
     y:    0,
     mode: 'relative'
   }, arguments[1] || {});
   this.start(options);
 },
 setup: function() {
   // Bug in Opera: Opera returns the "real" position of a static element or
   // relative element that does not have top/left explicitly set.
   // ==> Always set top and left for position relative elements in your stylesheets 
   // (to 0 if you do not need them) 
   this.element.makePositioned();
   this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
   this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
   if(this.options.mode == 'absolute') {
     // absolute movement, so we need to calc deltaX and deltaY
     this.options.x = this.options.x - this.originalLeft;
     this.options.y = this.options.y - this.originalTop;
   }
 },
 update: function(position) {
   this.element.setStyle({
     left: Math.round(this.options.x  * position + this.originalLeft) + 'px',
     top:  Math.round(this.options.y  * position + this.originalTop)  + 'px'
   });
 }

});

// for backwards compatibility Effect.MoveBy = function(element, toTop, toLeft) {

 return new Effect.Move(element, 
   Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));

};

Effect.Scale = Class.create(); Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {

 initialize: function(element, percent) {
   this.element = $(element);
   if(!this.element) throw(Effect._elementDoesNotExistError);
   var options = Object.extend({
     scaleX: true,
     scaleY: true,
     scaleContent: true,
     scaleFromCenter: false,
     scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
     scaleFrom: 100.0,
     scaleTo:   percent
   }, arguments[2] || {});
   this.start(options);
 },
 setup: function() {
   this.restoreAfterFinish = this.options.restoreAfterFinish || false;
   this.elementPositioning = this.element.getStyle('position');
   
   this.originalStyle = {};
   ['top','left','width','height','fontSize'].each( function(k) {
     this.originalStyle[k] = this.element.style[k];
   }.bind(this));
     
   this.originalTop  = this.element.offsetTop;
   this.originalLeft = this.element.offsetLeft;
   
   var fontSize = this.element.getStyle('font-size') || '100%';
   ['em','px','%','pt'].each( function(fontSizeType) {
     if(fontSize.indexOf(fontSizeType)>0) {
       this.fontSize     = parseFloat(fontSize);
       this.fontSizeType = fontSizeType;
     }
   }.bind(this));
   
   this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
   
   this.dims = null;
   if(this.options.scaleMode=='box')
     this.dims = [this.element.offsetHeight, this.element.offsetWidth];
   if(/^content/.test(this.options.scaleMode))
     this.dims = [this.element.scrollHeight, this.element.scrollWidth];
   if(!this.dims)
     this.dims = [this.options.scaleMode.originalHeight,
                  this.options.scaleMode.originalWidth];
 },
 update: function(position) {
   var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
   if(this.options.scaleContent && this.fontSize)
     this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
   this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
 },
 finish: function(position) {
   if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
 },
 setDimensions: function(height, width) {
   var d = {};
   if(this.options.scaleX) d.width = Math.round(width) + 'px';
   if(this.options.scaleY) d.height = Math.round(height) + 'px';
   if(this.options.scaleFromCenter) {
     var topd  = (height - this.dims[0])/2;
     var leftd = (width  - this.dims[1])/2;
     if(this.elementPositioning == 'absolute') {
       if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
       if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
     } else {
       if(this.options.scaleY) d.top = -topd + 'px';
       if(this.options.scaleX) d.left = -leftd + 'px';
     }
   }
   this.element.setStyle(d);
 }

});

Effect.Highlight = Class.create(); Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {

 initialize: function(element) {
   this.element = $(element);
   if(!this.element) throw(Effect._elementDoesNotExistError);
   var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
   this.start(options);
 },
 setup: function() {
   // Prevent executing on elements not in the layout flow
   if(this.element.getStyle('display')=='none') { this.cancel(); return; }
   // Disable background image during the effect
   this.oldStyle = {
     backgroundImage: this.element.getStyle('background-image') };
   this.element.setStyle({backgroundImage: 'none'});
   if(!this.options.endcolor)
     this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
   if(!this.options.restorecolor)
     this.options.restorecolor = this.element.getStyle('background-color');
   // init color calculations
   this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
   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));
 },
 update: function(position) {
   this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
     return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
 },
 finish: function() {
   this.element.setStyle(Object.extend(this.oldStyle, {
     backgroundColor: this.options.restorecolor
   }));
 }

});

Effect.ScrollTo = Class.create(); Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {

 initialize: function(element) {
   this.element = $(element);
   this.start(arguments[1] || {});
 },
 setup: function() {
   Position.prepare();
   var offsets = Position.cumulativeOffset(this.element);
   if(this.options.offset) offsets[1] += this.options.offset;
   var max = window.innerHeight ? 
     window.height - window.innerHeight :
     document.body.scrollHeight - 
       (document.documentElement.clientHeight ? 
         document.documentElement.clientHeight : document.body.clientHeight);
   this.scrollStart = Position.deltaY;
   this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
 },
 update: function(position) {
   Position.prepare();
   window.scrollTo(Position.deltaX, 
     this.scrollStart + (position*this.delta));
 }

});

/* ------------- combination effects ------------- */

Effect.Fade = function(element) {

 element = $(element);
 var oldOpacity = element.getInlineOpacity();
 var options = Object.extend({
 from: element.getOpacity() || 1.0,
 to:   0.0,
 afterFinishInternal: function(effect) { 
   if(effect.options.to!=0) return;
   effect.element.hide().setStyle({opacity: oldOpacity}); 
 }}, arguments[1] || {});
 return new Effect.Opacity(element,options);

}

Effect.Appear = function(element) {

 element = $(element);
 var options = Object.extend({
 from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
 to:   1.0,
 // force Safari to render floated elements properly
 afterFinishInternal: function(effect) {
   effect.element.forceRerendering();
 },
 beforeSetup: function(effect) {
   effect.element.setOpacity(effect.options.from).show(); 
 }}, arguments[1] || {});
 return new Effect.Opacity(element,options);

}

Effect.Puff = function(element) {

 element = $(element);
 var oldStyle = { 
   opacity: element.getInlineOpacity(), 
   position: element.getStyle('position'),
   top:  element.style.top,
   left: element.style.left,
   width: element.style.width,
   height: element.style.height
 };
 return new Effect.Parallel(
  [ new Effect.Scale(element, 200, 
     { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
    new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
    Object.extend({ duration: 1.0, 
     beforeSetupInternal: function(effect) {
       Position.absolutize(effect.effects[0].element)
     },
     afterFinishInternal: function(effect) {
        effect.effects[0].element.hide().setStyle(oldStyle); }
    }, arguments[1] || {})
  );

}

Effect.BlindUp = function(element) {

 element = $(element);
 element.makeClipping();
 return new Effect.Scale(element, 0,
   Object.extend({ scaleContent: false, 
     scaleX: false, 
     restoreAfterFinish: true,
     afterFinishInternal: function(effect) {
       effect.element.hide().undoClipping();
     } 
   }, arguments[1] || {})
 );

}

Effect.BlindDown = function(element) {

 element = $(element);
 var elementDimensions = element.getDimensions();
 return new Effect.Scale(element, 100, Object.extend({ 
   scaleContent: false, 
   scaleX: false,
   scaleFrom: 0,
   scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
   restoreAfterFinish: true,
   afterSetup: function(effect) {
     effect.element.makeClipping().setStyle({height: '0px'}).show(); 
   },  
   afterFinishInternal: function(effect) {
     effect.element.undoClipping();
   }
 }, arguments[1] || {}));

}

Effect.SwitchOff = function(element) {

 element = $(element);
 var oldOpacity = element.getInlineOpacity();
 return new Effect.Appear(element, Object.extend({
   duration: 0.4,
   from: 0,
   transition: Effect.Transitions.flicker,
   afterFinishInternal: function(effect) {
     new Effect.Scale(effect.element, 1, { 
       duration: 0.3, scaleFromCenter: true,
       scaleX: false, scaleContent: false, restoreAfterFinish: true,
       beforeSetup: function(effect) { 
         effect.element.makePositioned().makeClipping();
       },
       afterFinishInternal: function(effect) {
         effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
       }
     })
   }
 }, arguments[1] || {}));

}

Effect.DropOut = function(element) {

 element = $(element);
 var oldStyle = {
   top: element.getStyle('top'),
   left: element.getStyle('left'),
   opacity: element.getInlineOpacity() };
 return new Effect.Parallel(
   [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
     new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
   Object.extend(
     { duration: 0.5,
       beforeSetup: function(effect) {
         effect.effects[0].element.makePositioned(); 
       },
       afterFinishInternal: function(effect) {
         effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
       } 
     }, arguments[1] || {}));

}

Effect.Shake = function(element) {

 element = $(element);
 var oldStyle = {
   top: element.getStyle('top'),
   left: element.getStyle('left') };
   return new Effect.Move(element, 
     { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
   new Effect.Move(effect.element,
     { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
   new Effect.Move(effect.element,
     { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
   new Effect.Move(effect.element,
     { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
   new Effect.Move(effect.element,
     { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
   new Effect.Move(effect.element,
     { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
       effect.element.undoPositioned().setStyle(oldStyle);
 }}) }}) }}) }}) }}) }});

}

Effect.SlideDown = function(element) {

 element = $(element).cleanWhitespace();
 // SlideDown need to have the content of the element wrapped in a container element with fixed height!
 var oldInnerBottom = element.down().getStyle('bottom');
 var elementDimensions = element.getDimensions();
 return new Effect.Scale(element, 100, Object.extend({ 
   scaleContent: false, 
   scaleX: false, 
   scaleFrom: window.opera ? 0 : 1,
   scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
   restoreAfterFinish: true,
   afterSetup: function(effect) {
     effect.element.makePositioned();
     effect.element.down().makePositioned();
     if(window.opera) effect.element.setStyle({top: });
     effect.element.makeClipping().setStyle({height: '0px'}).show(); 
   },
   afterUpdateInternal: function(effect) {
     effect.element.down().setStyle({bottom:
       (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
   },
   afterFinishInternal: function(effect) {
     effect.element.undoClipping().undoPositioned();
     effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
   }, arguments[1] || {})
 );

}

Effect.SlideUp = function(element) {

 element = $(element).cleanWhitespace();
 var oldInnerBottom = element.down().getStyle('bottom');
 return new Effect.Scale(element, window.opera ? 0 : 1,
  Object.extend({ scaleContent: false, 
   scaleX: false, 
   scaleMode: 'box',
   scaleFrom: 100,
   restoreAfterFinish: true,
   beforeStartInternal: function(effect) {
     effect.element.makePositioned();
     effect.element.down().makePositioned();
     if(window.opera) effect.element.setStyle({top: });
     effect.element.makeClipping().show();
   },  
   afterUpdateInternal: function(effect) {
     effect.element.down().setStyle({bottom:
       (effect.dims[0] - effect.element.clientHeight) + 'px' });
   },
   afterFinishInternal: function(effect) {
     effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
     effect.element.down().undoPositioned();
   }
  }, arguments[1] || {})
 );

}

// Bug in opera makes the TD containing this element expand for a instance after finish Effect.Squish = function(element) {

 return new Effect.Scale(element, window.opera ? 1 : 0, { 
   restoreAfterFinish: true,
   beforeSetup: function(effect) {
     effect.element.makeClipping(); 
   },  
   afterFinishInternal: function(effect) {
     effect.element.hide().undoClipping(); 
   }
 });

}

Effect.Grow = function(element) {

 element = $(element);
 var options = Object.extend({
   direction: 'center',
   moveTransition: Effect.Transitions.sinoidal,
   scaleTransition: Effect.Transitions.sinoidal,
   opacityTransition: Effect.Transitions.full
 }, arguments[1] || {});
 var oldStyle = {
   top: element.style.top,
   left: element.style.left,
   height: element.style.height,
   width: element.style.width,
   opacity: element.getInlineOpacity() };
 var dims = element.getDimensions();    
 var initialMoveX, initialMoveY;
 var moveX, moveY;
 
 switch (options.direction) {
   case 'top-left':
     initialMoveX = initialMoveY = moveX = moveY = 0; 
     break;
   case 'top-right':
     initialMoveX = dims.width;
     initialMoveY = moveY = 0;
     moveX = -dims.width;
     break;
   case 'bottom-left':
     initialMoveX = moveX = 0;
     initialMoveY = dims.height;
     moveY = -dims.height;
     break;
   case 'bottom-right':
     initialMoveX = dims.width;
     initialMoveY = dims.height;
     moveX = -dims.width;
     moveY = -dims.height;
     break;
   case 'center':
     initialMoveX = dims.width / 2;
     initialMoveY = dims.height / 2;
     moveX = -dims.width / 2;
     moveY = -dims.height / 2;
     break;
 }
 
 return new Effect.Move(element, {
   x: initialMoveX,
   y: initialMoveY,
   duration: 0.01, 
   beforeSetup: function(effect) {
     effect.element.hide().makeClipping().makePositioned();
   },
   afterFinishInternal: function(effect) {
     new Effect.Parallel(
       [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
         new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
         new Effect.Scale(effect.element, 100, {
           scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
           sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
       ], Object.extend({
            beforeSetup: function(effect) {
              effect.effects[0].element.setStyle({height: '0px'}).show(); 
            },
            afterFinishInternal: function(effect) {
              effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
            }
          }, options)
     )
   }
 });

}

Effect.Shrink = function(element) {

 element = $(element);
 var options = Object.extend({
   direction: 'center',
   moveTransition: Effect.Transitions.sinoidal,
   scaleTransition: Effect.Transitions.sinoidal,
   opacityTransition: Effect.Transitions.none
 }, arguments[1] || {});
 var oldStyle = {
   top: element.style.top,
   left: element.style.left,
   height: element.style.height,
   width: element.style.width,
   opacity: element.getInlineOpacity() };
 var dims = element.getDimensions();
 var moveX, moveY;
 
 switch (options.direction) {
   case 'top-left':
     moveX = moveY = 0;
     break;
   case 'top-right':
     moveX = dims.width;
     moveY = 0;
     break;
   case 'bottom-left':
     moveX = 0;
     moveY = dims.height;
     break;
   case 'bottom-right':
     moveX = dims.width;
     moveY = dims.height;
     break;
   case 'center':  
     moveX = dims.width / 2;
     moveY = dims.height / 2;
     break;
 }
 
 return new Effect.Parallel(
   [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
     new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
     new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
   ], Object.extend({            
        beforeStartInternal: function(effect) {
          effect.effects[0].element.makePositioned().makeClipping(); 
        },
        afterFinishInternal: function(effect) {
          effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
      }, options)
 );

}

Effect.Pulsate = function(element) {

 element = $(element);
 var options    = arguments[1] || {};
 var oldOpacity = element.getInlineOpacity();
 var transition = options.transition || Effect.Transitions.sinoidal;
 var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
 reverser.bind(transition);
 return new Effect.Opacity(element, 
   Object.extend(Object.extend({  duration: 2.0, from: 0,
     afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
   }, options), {transition: reverser}));

}

Effect.Fold = function(element) {

 element = $(element);
 var oldStyle = {
   top: element.style.top,
   left: element.style.left,
   width: element.style.width,
   height: element.style.height };
 element.makeClipping();
 return new Effect.Scale(element, 5, Object.extend({   
   scaleContent: false,
   scaleX: false,
   afterFinishInternal: function(effect) {
   new Effect.Scale(element, 1, { 
     scaleContent: false, 
     scaleY: false,
     afterFinishInternal: function(effect) {
       effect.element.hide().undoClipping().setStyle(oldStyle);
     } });
 }}, arguments[1] || {}));

};

Effect.Morph = Class.create(); Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {

 initialize: function(element) {
   this.element = $(element);
   if(!this.element) throw(Effect._elementDoesNotExistError);
   var options = Object.extend({
     style: 
   }, arguments[1] || {});
   this.start(options);
 },
 setup: function(){
   function parseColor(color){
     if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
     color = color.parseColor();
     return $R(0,2).map(function(i){
       return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
     });
   }
   this.transforms = this.options.style.parseStyle().map(function(property){
     var originalValue = this.element.getStyle(property[0]);
     return $H({ 
       style: property[0], 
       originalValue: property[1].unit=='color' ? 
         parseColor(originalValue) : parseFloat(originalValue || 0), 
       targetValue: property[1].unit=='color' ? 
         parseColor(property[1].value) : property[1].value,
       unit: property[1].unit
     });
   }.bind(this)).reject(function(transform){
     return (
       (transform.originalValue == transform.targetValue) ||
       (
         transform.unit != 'color' &&
         (isNaN(transform.originalValue) || isNaN(transform.targetValue))
       )
     )
   });
 },
 update: function(position) {
   var style = $H(), value = null;
   this.transforms.each(function(transform){
     value = transform.unit=='color' ?
       $R(0,2).inject('#',function(m,v,i){
         return m+(Math.round(transform.originalValue[i]+
           (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : 
       transform.originalValue + Math.round(
         ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
     style[transform.style] = value;
   });
   this.element.setStyle(style);
 }

});

Effect.Transform = Class.create(); Object.extend(Effect.Transform.prototype, {

 initialize: function(tracks){
   this.tracks  = [];
   this.options = arguments[1] || {};
   this.addTracks(tracks);
 },
 addTracks: function(tracks){
   tracks.each(function(track){
     var data = $H(track).values().first();
     this.tracks.push($H({
       ids:     $H(track).keys().first(),
       effect:  Effect.Morph,
       options: { style: data }
     }));
   }.bind(this));
   return this;
 },
 play: function(){
   return new Effect.Parallel(
     this.tracks.map(function(track){
       var elements = [$(track.ids) || $$(track.ids)].flatten();
       return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
     }).flatten(),
     this.options
   );
 }

});

Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage',

 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle', 
 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
 'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
 'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
 'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
 'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
 'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
 'width', 'wordSpacing', 'zIndex'];
 

Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.prototype.parseStyle = function(){

 var element = Element.extend(document.createElement('div'));
element.innerHTML = '
';
 var style = element.down().style, styleRules = $H();
 
 Element.CSS_PROPERTIES.each(function(property){
  if(style[property]) styleRules[property] = style[property]; 
 });
 
 var result = $H();
 
 styleRules.each(function(pair){
   var property = pair[0], value = pair[1], unit = null;
   
   if(value.parseColor('#zzzzzz') != '#zzzzzz') {
     value = value.parseColor();
     unit  = 'color';
   } else if(Element.CSS_LENGTH.test(value)) 
     var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
         value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
   
   result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
 }.bind(this));
 
 return result;

};

Element.morph = function(element, style) {

 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
 return element;

};

['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',

'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( 
 function(f) { Element.Methods[f] = Element[f]; }

);

Element.Methods.visualEffect = function(element, effect, options) {

 s = effect.gsub(/_/, '-').camelize();
 effect_class = s.charAt(0).toUpperCase() + s.substring(1);
 new Effect[effect_class](element, options);
 return $(element);

};

Element.addMethods();