home *** CD-ROM | disk | FTP | other *** search
/ CrystalVision Software Se… Wiki Wonder - Wikipedia / CrystalVision Software Services 703: The Wiki Wonder - Wikipedia.iso / 0703 / Business / CodeX Apps / CodeX.exe / CodeX / html / lib / Web 2.0 / JavaScripts / effects.js < prev    next >
Encoding:
JavaScript  |  2006-09-05  |  33.0 KB  |  977 lines

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