web/wp-includes/js/scriptaculous/effects.js
changeset 204 09a1c134465b
parent 203 f507feede89a
child 205 a4f7897e21a9
equal deleted inserted replaced
203:f507feede89a 204:09a1c134465b
     1 // script.aculo.us effects.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009
       
     2 
       
     3 // Copyright (c) 2005-2009 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
       
    14 String.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 
       
    30 Element.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 
       
    37 Element.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 
       
    45 Element.setContentZoom = function(element, percent) {
       
    46   element = $(element);
       
    47   element.setStyle({fontSize: (percent/100) + 'em'});
       
    48   if (Prototype.Browser.WebKit) window.scrollBy(0,0);
       
    49   return element;
       
    50 };
       
    51 
       
    52 Element.getInlineOpacity = function(element){
       
    53   return $(element).style.opacity || '';
       
    54 };
       
    55 
       
    56 Element.forceRerendering = function(element) {
       
    57   try {
       
    58     element = $(element);
       
    59     var n = document.createTextNode(' ');
       
    60     element.appendChild(n);
       
    61     element.removeChild(n);
       
    62   } catch(e) { }
       
    63 };
       
    64 
       
    65 /*--------------------------------------------------------------------------*/
       
    66 
       
    67 var Effect = {
       
    68   _elementDoesNotExistError: {
       
    69     name: 'ElementDoesNotExistError',
       
    70     message: 'The specified DOM element does not exist, but is required for this effect to operate'
       
    71   },
       
    72   Transitions: {
       
    73     linear: Prototype.K,
       
    74     sinoidal: function(pos) {
       
    75       return (-Math.cos(pos*Math.PI)/2) + .5;
       
    76     },
       
    77     reverse: function(pos) {
       
    78       return 1-pos;
       
    79     },
       
    80     flicker: function(pos) {
       
    81       var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
       
    82       return pos > 1 ? 1 : pos;
       
    83     },
       
    84     wobble: function(pos) {
       
    85       return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
       
    86     },
       
    87     pulse: function(pos, pulses) {
       
    88       return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
       
    89     },
       
    90     spring: function(pos) {
       
    91       return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
       
    92     },
       
    93     none: function(pos) {
       
    94       return 0;
       
    95     },
       
    96     full: function(pos) {
       
    97       return 1;
       
    98     }
       
    99   },
       
   100   DefaultOptions: {
       
   101     duration:   1.0,   // seconds
       
   102     fps:        100,   // 100= assume 66fps max.
       
   103     sync:       false, // true for combining
       
   104     from:       0.0,
       
   105     to:         1.0,
       
   106     delay:      0.0,
       
   107     queue:      'parallel'
       
   108   },
       
   109   tagifyText: function(element) {
       
   110     var tagifyStyle = 'position:relative';
       
   111     if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
       
   112 
       
   113     element = $(element);
       
   114     $A(element.childNodes).each( function(child) {
       
   115       if (child.nodeType==3) {
       
   116         child.nodeValue.toArray().each( function(character) {
       
   117           element.insertBefore(
       
   118             new Element('span', {style: tagifyStyle}).update(
       
   119               character == ' ' ? String.fromCharCode(160) : character),
       
   120               child);
       
   121         });
       
   122         Element.remove(child);
       
   123       }
       
   124     });
       
   125   },
       
   126   multiple: function(element, effect) {
       
   127     var elements;
       
   128     if (((typeof element == 'object') ||
       
   129         Object.isFunction(element)) &&
       
   130        (element.length))
       
   131       elements = element;
       
   132     else
       
   133       elements = $(element).childNodes;
       
   134 
       
   135     var options = Object.extend({
       
   136       speed: 0.1,
       
   137       delay: 0.0
       
   138     }, arguments[2] || { });
       
   139     var masterDelay = options.delay;
       
   140 
       
   141     $A(elements).each( function(element, index) {
       
   142       new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
       
   143     });
       
   144   },
       
   145   PAIRS: {
       
   146     'slide':  ['SlideDown','SlideUp'],
       
   147     'blind':  ['BlindDown','BlindUp'],
       
   148     'appear': ['Appear','Fade']
       
   149   },
       
   150   toggle: function(element, effect, options) {
       
   151     element = $(element);
       
   152     effect  = (effect || 'appear').toLowerCase();
       
   153     
       
   154     return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({
       
   155       queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
       
   156     }, options || {}));
       
   157   }
       
   158 };
       
   159 
       
   160 Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
       
   161 
       
   162 /* ------------- core effects ------------- */
       
   163 
       
   164 Effect.ScopedQueue = Class.create(Enumerable, {
       
   165   initialize: function() {
       
   166     this.effects  = [];
       
   167     this.interval = null;
       
   168   },
       
   169   _each: function(iterator) {
       
   170     this.effects._each(iterator);
       
   171   },
       
   172   add: function(effect) {
       
   173     var timestamp = new Date().getTime();
       
   174 
       
   175     var position = Object.isString(effect.options.queue) ?
       
   176       effect.options.queue : effect.options.queue.position;
       
   177 
       
   178     switch(position) {
       
   179       case 'front':
       
   180         // move unstarted effects after this effect
       
   181         this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
       
   182             e.startOn  += effect.finishOn;
       
   183             e.finishOn += effect.finishOn;
       
   184           });
       
   185         break;
       
   186       case 'with-last':
       
   187         timestamp = this.effects.pluck('startOn').max() || timestamp;
       
   188         break;
       
   189       case 'end':
       
   190         // start effect after last queued effect has finished
       
   191         timestamp = this.effects.pluck('finishOn').max() || timestamp;
       
   192         break;
       
   193     }
       
   194 
       
   195     effect.startOn  += timestamp;
       
   196     effect.finishOn += timestamp;
       
   197 
       
   198     if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
       
   199       this.effects.push(effect);
       
   200 
       
   201     if (!this.interval)
       
   202       this.interval = setInterval(this.loop.bind(this), 15);
       
   203   },
       
   204   remove: function(effect) {
       
   205     this.effects = this.effects.reject(function(e) { return e==effect });
       
   206     if (this.effects.length == 0) {
       
   207       clearInterval(this.interval);
       
   208       this.interval = null;
       
   209     }
       
   210   },
       
   211   loop: function() {
       
   212     var timePos = new Date().getTime();
       
   213     for(var i=0, len=this.effects.length;i<len;i++)
       
   214       this.effects[i] && this.effects[i].loop(timePos);
       
   215   }
       
   216 });
       
   217 
       
   218 Effect.Queues = {
       
   219   instances: $H(),
       
   220   get: function(queueName) {
       
   221     if (!Object.isString(queueName)) return queueName;
       
   222 
       
   223     return this.instances.get(queueName) ||
       
   224       this.instances.set(queueName, new Effect.ScopedQueue());
       
   225   }
       
   226 };
       
   227 Effect.Queue = Effect.Queues.get('global');
       
   228 
       
   229 Effect.Base = Class.create({
       
   230   position: null,
       
   231   start: function(options) {
       
   232     if (options && options.transition === false) options.transition = Effect.Transitions.linear;
       
   233     this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
       
   234     this.currentFrame = 0;
       
   235     this.state        = 'idle';
       
   236     this.startOn      = this.options.delay*1000;
       
   237     this.finishOn     = this.startOn+(this.options.duration*1000);
       
   238     this.fromToDelta  = this.options.to-this.options.from;
       
   239     this.totalTime    = this.finishOn-this.startOn;
       
   240     this.totalFrames  = this.options.fps*this.options.duration;
       
   241 
       
   242     this.render = (function() {
       
   243       function dispatch(effect, eventName) {
       
   244         if (effect.options[eventName + 'Internal'])
       
   245           effect.options[eventName + 'Internal'](effect);
       
   246         if (effect.options[eventName])
       
   247           effect.options[eventName](effect);
       
   248       }
       
   249 
       
   250       return function(pos) {
       
   251         if (this.state === "idle") {
       
   252           this.state = "running";
       
   253           dispatch(this, 'beforeSetup');
       
   254           if (this.setup) this.setup();
       
   255           dispatch(this, 'afterSetup');
       
   256         }
       
   257         if (this.state === "running") {
       
   258           pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
       
   259           this.position = pos;
       
   260           dispatch(this, 'beforeUpdate');
       
   261           if (this.update) this.update(pos);
       
   262           dispatch(this, 'afterUpdate');
       
   263         }
       
   264       };
       
   265     })();
       
   266 
       
   267     this.event('beforeStart');
       
   268     if (!this.options.sync)
       
   269       Effect.Queues.get(Object.isString(this.options.queue) ?
       
   270         'global' : this.options.queue.scope).add(this);
       
   271   },
       
   272   loop: function(timePos) {
       
   273     if (timePos >= this.startOn) {
       
   274       if (timePos >= this.finishOn) {
       
   275         this.render(1.0);
       
   276         this.cancel();
       
   277         this.event('beforeFinish');
       
   278         if (this.finish) this.finish();
       
   279         this.event('afterFinish');
       
   280         return;
       
   281       }
       
   282       var pos   = (timePos - this.startOn) / this.totalTime,
       
   283           frame = (pos * this.totalFrames).round();
       
   284       if (frame > this.currentFrame) {
       
   285         this.render(pos);
       
   286         this.currentFrame = frame;
       
   287       }
       
   288     }
       
   289   },
       
   290   cancel: function() {
       
   291     if (!this.options.sync)
       
   292       Effect.Queues.get(Object.isString(this.options.queue) ?
       
   293         'global' : this.options.queue.scope).remove(this);
       
   294     this.state = 'finished';
       
   295   },
       
   296   event: function(eventName) {
       
   297     if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
       
   298     if (this.options[eventName]) this.options[eventName](this);
       
   299   },
       
   300   inspect: function() {
       
   301     var data = $H();
       
   302     for(property in this)
       
   303       if (!Object.isFunction(this[property])) data.set(property, this[property]);
       
   304     return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
       
   305   }
       
   306 });
       
   307 
       
   308 Effect.Parallel = Class.create(Effect.Base, {
       
   309   initialize: function(effects) {
       
   310     this.effects = effects || [];
       
   311     this.start(arguments[1]);
       
   312   },
       
   313   update: function(position) {
       
   314     this.effects.invoke('render', position);
       
   315   },
       
   316   finish: function(position) {
       
   317     this.effects.each( function(effect) {
       
   318       effect.render(1.0);
       
   319       effect.cancel();
       
   320       effect.event('beforeFinish');
       
   321       if (effect.finish) effect.finish(position);
       
   322       effect.event('afterFinish');
       
   323     });
       
   324   }
       
   325 });
       
   326 
       
   327 Effect.Tween = Class.create(Effect.Base, {
       
   328   initialize: function(object, from, to) {
       
   329     object = Object.isString(object) ? $(object) : object;
       
   330     var args = $A(arguments), method = args.last(),
       
   331       options = args.length == 5 ? args[3] : null;
       
   332     this.method = Object.isFunction(method) ? method.bind(object) :
       
   333       Object.isFunction(object[method]) ? object[method].bind(object) :
       
   334       function(value) { object[method] = value };
       
   335     this.start(Object.extend({ from: from, to: to }, options || { }));
       
   336   },
       
   337   update: function(position) {
       
   338     this.method(position);
       
   339   }
       
   340 });
       
   341 
       
   342 Effect.Event = Class.create(Effect.Base, {
       
   343   initialize: function() {
       
   344     this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
       
   345   },
       
   346   update: Prototype.emptyFunction
       
   347 });
       
   348 
       
   349 Effect.Opacity = Class.create(Effect.Base, {
       
   350   initialize: function(element) {
       
   351     this.element = $(element);
       
   352     if (!this.element) throw(Effect._elementDoesNotExistError);
       
   353     // make this work on IE on elements without 'layout'
       
   354     if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
       
   355       this.element.setStyle({zoom: 1});
       
   356     var options = Object.extend({
       
   357       from: this.element.getOpacity() || 0.0,
       
   358       to:   1.0
       
   359     }, arguments[1] || { });
       
   360     this.start(options);
       
   361   },
       
   362   update: function(position) {
       
   363     this.element.setOpacity(position);
       
   364   }
       
   365 });
       
   366 
       
   367 Effect.Move = Class.create(Effect.Base, {
       
   368   initialize: function(element) {
       
   369     this.element = $(element);
       
   370     if (!this.element) throw(Effect._elementDoesNotExistError);
       
   371     var options = Object.extend({
       
   372       x:    0,
       
   373       y:    0,
       
   374       mode: 'relative'
       
   375     }, arguments[1] || { });
       
   376     this.start(options);
       
   377   },
       
   378   setup: function() {
       
   379     this.element.makePositioned();
       
   380     this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
       
   381     this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
       
   382     if (this.options.mode == 'absolute') {
       
   383       this.options.x = this.options.x - this.originalLeft;
       
   384       this.options.y = this.options.y - this.originalTop;
       
   385     }
       
   386   },
       
   387   update: function(position) {
       
   388     this.element.setStyle({
       
   389       left: (this.options.x  * position + this.originalLeft).round() + 'px',
       
   390       top:  (this.options.y  * position + this.originalTop).round()  + 'px'
       
   391     });
       
   392   }
       
   393 });
       
   394 
       
   395 // for backwards compatibility
       
   396 Effect.MoveBy = function(element, toTop, toLeft) {
       
   397   return new Effect.Move(element,
       
   398     Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
       
   399 };
       
   400 
       
   401 Effect.Scale = Class.create(Effect.Base, {
       
   402   initialize: function(element, percent) {
       
   403     this.element = $(element);
       
   404     if (!this.element) throw(Effect._elementDoesNotExistError);
       
   405     var options = Object.extend({
       
   406       scaleX: true,
       
   407       scaleY: true,
       
   408       scaleContent: true,
       
   409       scaleFromCenter: false,
       
   410       scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
       
   411       scaleFrom: 100.0,
       
   412       scaleTo:   percent
       
   413     }, arguments[2] || { });
       
   414     this.start(options);
       
   415   },
       
   416   setup: function() {
       
   417     this.restoreAfterFinish = this.options.restoreAfterFinish || false;
       
   418     this.elementPositioning = this.element.getStyle('position');
       
   419 
       
   420     this.originalStyle = { };
       
   421     ['top','left','width','height','fontSize'].each( function(k) {
       
   422       this.originalStyle[k] = this.element.style[k];
       
   423     }.bind(this));
       
   424 
       
   425     this.originalTop  = this.element.offsetTop;
       
   426     this.originalLeft = this.element.offsetLeft;
       
   427 
       
   428     var fontSize = this.element.getStyle('font-size') || '100%';
       
   429     ['em','px','%','pt'].each( function(fontSizeType) {
       
   430       if (fontSize.indexOf(fontSizeType)>0) {
       
   431         this.fontSize     = parseFloat(fontSize);
       
   432         this.fontSizeType = fontSizeType;
       
   433       }
       
   434     }.bind(this));
       
   435 
       
   436     this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
       
   437 
       
   438     this.dims = null;
       
   439     if (this.options.scaleMode=='box')
       
   440       this.dims = [this.element.offsetHeight, this.element.offsetWidth];
       
   441     if (/^content/.test(this.options.scaleMode))
       
   442       this.dims = [this.element.scrollHeight, this.element.scrollWidth];
       
   443     if (!this.dims)
       
   444       this.dims = [this.options.scaleMode.originalHeight,
       
   445                    this.options.scaleMode.originalWidth];
       
   446   },
       
   447   update: function(position) {
       
   448     var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
       
   449     if (this.options.scaleContent && this.fontSize)
       
   450       this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
       
   451     this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
       
   452   },
       
   453   finish: function(position) {
       
   454     if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
       
   455   },
       
   456   setDimensions: function(height, width) {
       
   457     var d = { };
       
   458     if (this.options.scaleX) d.width = width.round() + 'px';
       
   459     if (this.options.scaleY) d.height = height.round() + 'px';
       
   460     if (this.options.scaleFromCenter) {
       
   461       var topd  = (height - this.dims[0])/2;
       
   462       var leftd = (width  - this.dims[1])/2;
       
   463       if (this.elementPositioning == 'absolute') {
       
   464         if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
       
   465         if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
       
   466       } else {
       
   467         if (this.options.scaleY) d.top = -topd + 'px';
       
   468         if (this.options.scaleX) d.left = -leftd + 'px';
       
   469       }
       
   470     }
       
   471     this.element.setStyle(d);
       
   472   }
       
   473 });
       
   474 
       
   475 Effect.Highlight = Class.create(Effect.Base, {
       
   476   initialize: function(element) {
       
   477     this.element = $(element);
       
   478     if (!this.element) throw(Effect._elementDoesNotExistError);
       
   479     var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
       
   480     this.start(options);
       
   481   },
       
   482   setup: function() {
       
   483     // Prevent executing on elements not in the layout flow
       
   484     if (this.element.getStyle('display')=='none') { this.cancel(); return; }
       
   485     // Disable background image during the effect
       
   486     this.oldStyle = { };
       
   487     if (!this.options.keepBackgroundImage) {
       
   488       this.oldStyle.backgroundImage = this.element.getStyle('background-image');
       
   489       this.element.setStyle({backgroundImage: 'none'});
       
   490     }
       
   491     if (!this.options.endcolor)
       
   492       this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
       
   493     if (!this.options.restorecolor)
       
   494       this.options.restorecolor = this.element.getStyle('background-color');
       
   495     // init color calculations
       
   496     this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
       
   497     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));
       
   498   },
       
   499   update: function(position) {
       
   500     this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
       
   501       return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
       
   502   },
       
   503   finish: function() {
       
   504     this.element.setStyle(Object.extend(this.oldStyle, {
       
   505       backgroundColor: this.options.restorecolor
       
   506     }));
       
   507   }
       
   508 });
       
   509 
       
   510 Effect.ScrollTo = function(element) {
       
   511   var options = arguments[1] || { },
       
   512   scrollOffsets = document.viewport.getScrollOffsets(),
       
   513   elementOffsets = $(element).cumulativeOffset();
       
   514 
       
   515   if (options.offset) elementOffsets[1] += options.offset;
       
   516 
       
   517   return new Effect.Tween(null,
       
   518     scrollOffsets.top,
       
   519     elementOffsets[1],
       
   520     options,
       
   521     function(p){ scrollTo(scrollOffsets.left, p.round()); }
       
   522   );
       
   523 };
       
   524 
       
   525 /* ------------- combination effects ------------- */
       
   526 
       
   527 Effect.Fade = function(element) {
       
   528   element = $(element);
       
   529   var oldOpacity = element.getInlineOpacity();
       
   530   var options = Object.extend({
       
   531     from: element.getOpacity() || 1.0,
       
   532     to:   0.0,
       
   533     afterFinishInternal: function(effect) {
       
   534       if (effect.options.to!=0) return;
       
   535       effect.element.hide().setStyle({opacity: oldOpacity});
       
   536     }
       
   537   }, arguments[1] || { });
       
   538   return new Effect.Opacity(element,options);
       
   539 };
       
   540 
       
   541 Effect.Appear = function(element) {
       
   542   element = $(element);
       
   543   var options = Object.extend({
       
   544   from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
       
   545   to:   1.0,
       
   546   // force Safari to render floated elements properly
       
   547   afterFinishInternal: function(effect) {
       
   548     effect.element.forceRerendering();
       
   549   },
       
   550   beforeSetup: function(effect) {
       
   551     effect.element.setOpacity(effect.options.from).show();
       
   552   }}, arguments[1] || { });
       
   553   return new Effect.Opacity(element,options);
       
   554 };
       
   555 
       
   556 Effect.Puff = function(element) {
       
   557   element = $(element);
       
   558   var oldStyle = {
       
   559     opacity: element.getInlineOpacity(),
       
   560     position: element.getStyle('position'),
       
   561     top:  element.style.top,
       
   562     left: element.style.left,
       
   563     width: element.style.width,
       
   564     height: element.style.height
       
   565   };
       
   566   return new Effect.Parallel(
       
   567    [ new Effect.Scale(element, 200,
       
   568       { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
       
   569      new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
       
   570      Object.extend({ duration: 1.0,
       
   571       beforeSetupInternal: function(effect) {
       
   572         Position.absolutize(effect.effects[0].element);
       
   573       },
       
   574       afterFinishInternal: function(effect) {
       
   575          effect.effects[0].element.hide().setStyle(oldStyle); }
       
   576      }, arguments[1] || { })
       
   577    );
       
   578 };
       
   579 
       
   580 Effect.BlindUp = function(element) {
       
   581   element = $(element);
       
   582   element.makeClipping();
       
   583   return new Effect.Scale(element, 0,
       
   584     Object.extend({ scaleContent: false,
       
   585       scaleX: false,
       
   586       restoreAfterFinish: true,
       
   587       afterFinishInternal: function(effect) {
       
   588         effect.element.hide().undoClipping();
       
   589       }
       
   590     }, arguments[1] || { })
       
   591   );
       
   592 };
       
   593 
       
   594 Effect.BlindDown = function(element) {
       
   595   element = $(element);
       
   596   var elementDimensions = element.getDimensions();
       
   597   return new Effect.Scale(element, 100, Object.extend({
       
   598     scaleContent: false,
       
   599     scaleX: false,
       
   600     scaleFrom: 0,
       
   601     scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
       
   602     restoreAfterFinish: true,
       
   603     afterSetup: function(effect) {
       
   604       effect.element.makeClipping().setStyle({height: '0px'}).show();
       
   605     },
       
   606     afterFinishInternal: function(effect) {
       
   607       effect.element.undoClipping();
       
   608     }
       
   609   }, arguments[1] || { }));
       
   610 };
       
   611 
       
   612 Effect.SwitchOff = function(element) {
       
   613   element = $(element);
       
   614   var oldOpacity = element.getInlineOpacity();
       
   615   return new Effect.Appear(element, Object.extend({
       
   616     duration: 0.4,
       
   617     from: 0,
       
   618     transition: Effect.Transitions.flicker,
       
   619     afterFinishInternal: function(effect) {
       
   620       new Effect.Scale(effect.element, 1, {
       
   621         duration: 0.3, scaleFromCenter: true,
       
   622         scaleX: false, scaleContent: false, restoreAfterFinish: true,
       
   623         beforeSetup: function(effect) {
       
   624           effect.element.makePositioned().makeClipping();
       
   625         },
       
   626         afterFinishInternal: function(effect) {
       
   627           effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
       
   628         }
       
   629       });
       
   630     }
       
   631   }, arguments[1] || { }));
       
   632 };
       
   633 
       
   634 Effect.DropOut = function(element) {
       
   635   element = $(element);
       
   636   var oldStyle = {
       
   637     top: element.getStyle('top'),
       
   638     left: element.getStyle('left'),
       
   639     opacity: element.getInlineOpacity() };
       
   640   return new Effect.Parallel(
       
   641     [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
       
   642       new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
       
   643     Object.extend(
       
   644       { duration: 0.5,
       
   645         beforeSetup: function(effect) {
       
   646           effect.effects[0].element.makePositioned();
       
   647         },
       
   648         afterFinishInternal: function(effect) {
       
   649           effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
       
   650         }
       
   651       }, arguments[1] || { }));
       
   652 };
       
   653 
       
   654 Effect.Shake = function(element) {
       
   655   element = $(element);
       
   656   var options = Object.extend({
       
   657     distance: 20,
       
   658     duration: 0.5
       
   659   }, arguments[1] || {});
       
   660   var distance = parseFloat(options.distance);
       
   661   var split = parseFloat(options.duration) / 10.0;
       
   662   var oldStyle = {
       
   663     top: element.getStyle('top'),
       
   664     left: element.getStyle('left') };
       
   665     return new Effect.Move(element,
       
   666       { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
       
   667     new Effect.Move(effect.element,
       
   668       { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
       
   669     new Effect.Move(effect.element,
       
   670       { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
       
   671     new Effect.Move(effect.element,
       
   672       { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
       
   673     new Effect.Move(effect.element,
       
   674       { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
       
   675     new Effect.Move(effect.element,
       
   676       { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
       
   677         effect.element.undoPositioned().setStyle(oldStyle);
       
   678   }}); }}); }}); }}); }}); }});
       
   679 };
       
   680 
       
   681 Effect.SlideDown = function(element) {
       
   682   element = $(element).cleanWhitespace();
       
   683   // SlideDown need to have the content of the element wrapped in a container element with fixed height!
       
   684   var oldInnerBottom = element.down().getStyle('bottom');
       
   685   var elementDimensions = element.getDimensions();
       
   686   return new Effect.Scale(element, 100, Object.extend({
       
   687     scaleContent: false,
       
   688     scaleX: false,
       
   689     scaleFrom: window.opera ? 0 : 1,
       
   690     scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
       
   691     restoreAfterFinish: true,
       
   692     afterSetup: function(effect) {
       
   693       effect.element.makePositioned();
       
   694       effect.element.down().makePositioned();
       
   695       if (window.opera) effect.element.setStyle({top: ''});
       
   696       effect.element.makeClipping().setStyle({height: '0px'}).show();
       
   697     },
       
   698     afterUpdateInternal: function(effect) {
       
   699       effect.element.down().setStyle({bottom:
       
   700         (effect.dims[0] - effect.element.clientHeight) + 'px' });
       
   701     },
       
   702     afterFinishInternal: function(effect) {
       
   703       effect.element.undoClipping().undoPositioned();
       
   704       effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
       
   705     }, arguments[1] || { })
       
   706   );
       
   707 };
       
   708 
       
   709 Effect.SlideUp = function(element) {
       
   710   element = $(element).cleanWhitespace();
       
   711   var oldInnerBottom = element.down().getStyle('bottom');
       
   712   var elementDimensions = element.getDimensions();
       
   713   return new Effect.Scale(element, window.opera ? 0 : 1,
       
   714    Object.extend({ scaleContent: false,
       
   715     scaleX: false,
       
   716     scaleMode: 'box',
       
   717     scaleFrom: 100,
       
   718     scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
       
   719     restoreAfterFinish: true,
       
   720     afterSetup: function(effect) {
       
   721       effect.element.makePositioned();
       
   722       effect.element.down().makePositioned();
       
   723       if (window.opera) effect.element.setStyle({top: ''});
       
   724       effect.element.makeClipping().show();
       
   725     },
       
   726     afterUpdateInternal: function(effect) {
       
   727       effect.element.down().setStyle({bottom:
       
   728         (effect.dims[0] - effect.element.clientHeight) + 'px' });
       
   729     },
       
   730     afterFinishInternal: function(effect) {
       
   731       effect.element.hide().undoClipping().undoPositioned();
       
   732       effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
       
   733     }
       
   734    }, arguments[1] || { })
       
   735   );
       
   736 };
       
   737 
       
   738 // Bug in opera makes the TD containing this element expand for a instance after finish
       
   739 Effect.Squish = function(element) {
       
   740   return new Effect.Scale(element, window.opera ? 1 : 0, {
       
   741     restoreAfterFinish: true,
       
   742     beforeSetup: function(effect) {
       
   743       effect.element.makeClipping();
       
   744     },
       
   745     afterFinishInternal: function(effect) {
       
   746       effect.element.hide().undoClipping();
       
   747     }
       
   748   });
       
   749 };
       
   750 
       
   751 Effect.Grow = function(element) {
       
   752   element = $(element);
       
   753   var options = Object.extend({
       
   754     direction: 'center',
       
   755     moveTransition: Effect.Transitions.sinoidal,
       
   756     scaleTransition: Effect.Transitions.sinoidal,
       
   757     opacityTransition: Effect.Transitions.full
       
   758   }, arguments[1] || { });
       
   759   var oldStyle = {
       
   760     top: element.style.top,
       
   761     left: element.style.left,
       
   762     height: element.style.height,
       
   763     width: element.style.width,
       
   764     opacity: element.getInlineOpacity() };
       
   765 
       
   766   var dims = element.getDimensions();
       
   767   var initialMoveX, initialMoveY;
       
   768   var moveX, moveY;
       
   769 
       
   770   switch (options.direction) {
       
   771     case 'top-left':
       
   772       initialMoveX = initialMoveY = moveX = moveY = 0;
       
   773       break;
       
   774     case 'top-right':
       
   775       initialMoveX = dims.width;
       
   776       initialMoveY = moveY = 0;
       
   777       moveX = -dims.width;
       
   778       break;
       
   779     case 'bottom-left':
       
   780       initialMoveX = moveX = 0;
       
   781       initialMoveY = dims.height;
       
   782       moveY = -dims.height;
       
   783       break;
       
   784     case 'bottom-right':
       
   785       initialMoveX = dims.width;
       
   786       initialMoveY = dims.height;
       
   787       moveX = -dims.width;
       
   788       moveY = -dims.height;
       
   789       break;
       
   790     case 'center':
       
   791       initialMoveX = dims.width / 2;
       
   792       initialMoveY = dims.height / 2;
       
   793       moveX = -dims.width / 2;
       
   794       moveY = -dims.height / 2;
       
   795       break;
       
   796   }
       
   797 
       
   798   return new Effect.Move(element, {
       
   799     x: initialMoveX,
       
   800     y: initialMoveY,
       
   801     duration: 0.01,
       
   802     beforeSetup: function(effect) {
       
   803       effect.element.hide().makeClipping().makePositioned();
       
   804     },
       
   805     afterFinishInternal: function(effect) {
       
   806       new Effect.Parallel(
       
   807         [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
       
   808           new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
       
   809           new Effect.Scale(effect.element, 100, {
       
   810             scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
       
   811             sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
       
   812         ], Object.extend({
       
   813              beforeSetup: function(effect) {
       
   814                effect.effects[0].element.setStyle({height: '0px'}).show();
       
   815              },
       
   816              afterFinishInternal: function(effect) {
       
   817                effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
       
   818              }
       
   819            }, options)
       
   820       );
       
   821     }
       
   822   });
       
   823 };
       
   824 
       
   825 Effect.Shrink = function(element) {
       
   826   element = $(element);
       
   827   var options = Object.extend({
       
   828     direction: 'center',
       
   829     moveTransition: Effect.Transitions.sinoidal,
       
   830     scaleTransition: Effect.Transitions.sinoidal,
       
   831     opacityTransition: Effect.Transitions.none
       
   832   }, arguments[1] || { });
       
   833   var oldStyle = {
       
   834     top: element.style.top,
       
   835     left: element.style.left,
       
   836     height: element.style.height,
       
   837     width: element.style.width,
       
   838     opacity: element.getInlineOpacity() };
       
   839 
       
   840   var dims = element.getDimensions();
       
   841   var moveX, moveY;
       
   842 
       
   843   switch (options.direction) {
       
   844     case 'top-left':
       
   845       moveX = moveY = 0;
       
   846       break;
       
   847     case 'top-right':
       
   848       moveX = dims.width;
       
   849       moveY = 0;
       
   850       break;
       
   851     case 'bottom-left':
       
   852       moveX = 0;
       
   853       moveY = dims.height;
       
   854       break;
       
   855     case 'bottom-right':
       
   856       moveX = dims.width;
       
   857       moveY = dims.height;
       
   858       break;
       
   859     case 'center':
       
   860       moveX = dims.width / 2;
       
   861       moveY = dims.height / 2;
       
   862       break;
       
   863   }
       
   864 
       
   865   return new Effect.Parallel(
       
   866     [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
       
   867       new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
       
   868       new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
       
   869     ], Object.extend({
       
   870          beforeStartInternal: function(effect) {
       
   871            effect.effects[0].element.makePositioned().makeClipping();
       
   872          },
       
   873          afterFinishInternal: function(effect) {
       
   874            effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       
   875        }, options)
       
   876   );
       
   877 };
       
   878 
       
   879 Effect.Pulsate = function(element) {
       
   880   element = $(element);
       
   881   var options    = arguments[1] || { },
       
   882     oldOpacity = element.getInlineOpacity(),
       
   883     transition = options.transition || Effect.Transitions.linear,
       
   884     reverser   = function(pos){
       
   885       return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
       
   886     };
       
   887 
       
   888   return new Effect.Opacity(element,
       
   889     Object.extend(Object.extend({  duration: 2.0, from: 0,
       
   890       afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
       
   891     }, options), {transition: reverser}));
       
   892 };
       
   893 
       
   894 Effect.Fold = function(element) {
       
   895   element = $(element);
       
   896   var oldStyle = {
       
   897     top: element.style.top,
       
   898     left: element.style.left,
       
   899     width: element.style.width,
       
   900     height: element.style.height };
       
   901   element.makeClipping();
       
   902   return new Effect.Scale(element, 5, Object.extend({
       
   903     scaleContent: false,
       
   904     scaleX: false,
       
   905     afterFinishInternal: function(effect) {
       
   906     new Effect.Scale(element, 1, {
       
   907       scaleContent: false,
       
   908       scaleY: false,
       
   909       afterFinishInternal: function(effect) {
       
   910         effect.element.hide().undoClipping().setStyle(oldStyle);
       
   911       } });
       
   912   }}, arguments[1] || { }));
       
   913 };
       
   914 
       
   915 Effect.Morph = Class.create(Effect.Base, {
       
   916   initialize: function(element) {
       
   917     this.element = $(element);
       
   918     if (!this.element) throw(Effect._elementDoesNotExistError);
       
   919     var options = Object.extend({
       
   920       style: { }
       
   921     }, arguments[1] || { });
       
   922 
       
   923     if (!Object.isString(options.style)) this.style = $H(options.style);
       
   924     else {
       
   925       if (options.style.include(':'))
       
   926         this.style = options.style.parseStyle();
       
   927       else {
       
   928         this.element.addClassName(options.style);
       
   929         this.style = $H(this.element.getStyles());
       
   930         this.element.removeClassName(options.style);
       
   931         var css = this.element.getStyles();
       
   932         this.style = this.style.reject(function(style) {
       
   933           return style.value == css[style.key];
       
   934         });
       
   935         options.afterFinishInternal = function(effect) {
       
   936           effect.element.addClassName(effect.options.style);
       
   937           effect.transforms.each(function(transform) {
       
   938             effect.element.style[transform.style] = '';
       
   939           });
       
   940         };
       
   941       }
       
   942     }
       
   943     this.start(options);
       
   944   },
       
   945 
       
   946   setup: function(){
       
   947     function parseColor(color){
       
   948       if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
       
   949       color = color.parseColor();
       
   950       return $R(0,2).map(function(i){
       
   951         return parseInt( color.slice(i*2+1,i*2+3), 16 );
       
   952       });
       
   953     }
       
   954     this.transforms = this.style.map(function(pair){
       
   955       var property = pair[0], value = pair[1], unit = null;
       
   956 
       
   957       if (value.parseColor('#zzzzzz') != '#zzzzzz') {
       
   958         value = value.parseColor();
       
   959         unit  = 'color';
       
   960       } else if (property == 'opacity') {
       
   961         value = parseFloat(value);
       
   962         if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
       
   963           this.element.setStyle({zoom: 1});
       
   964       } else if (Element.CSS_LENGTH.test(value)) {
       
   965           var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
       
   966           value = parseFloat(components[1]);
       
   967           unit = (components.length == 3) ? components[2] : null;
       
   968       }
       
   969 
       
   970       var originalValue = this.element.getStyle(property);
       
   971       return {
       
   972         style: property.camelize(),
       
   973         originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
       
   974         targetValue: unit=='color' ? parseColor(value) : value,
       
   975         unit: unit
       
   976       };
       
   977     }.bind(this)).reject(function(transform){
       
   978       return (
       
   979         (transform.originalValue == transform.targetValue) ||
       
   980         (
       
   981           transform.unit != 'color' &&
       
   982           (isNaN(transform.originalValue) || isNaN(transform.targetValue))
       
   983         )
       
   984       );
       
   985     });
       
   986   },
       
   987   update: function(position) {
       
   988     var style = { }, transform, i = this.transforms.length;
       
   989     while(i--)
       
   990       style[(transform = this.transforms[i]).style] =
       
   991         transform.unit=='color' ? '#'+
       
   992           (Math.round(transform.originalValue[0]+
       
   993             (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
       
   994           (Math.round(transform.originalValue[1]+
       
   995             (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
       
   996           (Math.round(transform.originalValue[2]+
       
   997             (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
       
   998         (transform.originalValue +
       
   999           (transform.targetValue - transform.originalValue) * position).toFixed(3) +
       
  1000             (transform.unit === null ? '' : transform.unit);
       
  1001     this.element.setStyle(style, true);
       
  1002   }
       
  1003 });
       
  1004 
       
  1005 Effect.Transform = Class.create({
       
  1006   initialize: function(tracks){
       
  1007     this.tracks  = [];
       
  1008     this.options = arguments[1] || { };
       
  1009     this.addTracks(tracks);
       
  1010   },
       
  1011   addTracks: function(tracks){
       
  1012     tracks.each(function(track){
       
  1013       track = $H(track);
       
  1014       var data = track.values().first();
       
  1015       this.tracks.push($H({
       
  1016         ids:     track.keys().first(),
       
  1017         effect:  Effect.Morph,
       
  1018         options: { style: data }
       
  1019       }));
       
  1020     }.bind(this));
       
  1021     return this;
       
  1022   },
       
  1023   play: function(){
       
  1024     return new Effect.Parallel(
       
  1025       this.tracks.map(function(track){
       
  1026         var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
       
  1027         var elements = [$(ids) || $$(ids)].flatten();
       
  1028         return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
       
  1029       }).flatten(),
       
  1030       this.options
       
  1031     );
       
  1032   }
       
  1033 });
       
  1034 
       
  1035 Element.CSS_PROPERTIES = $w(
       
  1036   'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
       
  1037   'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
       
  1038   'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
       
  1039   'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
       
  1040   'fontSize fontWeight height left letterSpacing lineHeight ' +
       
  1041   'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
       
  1042   'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
       
  1043   'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
       
  1044   'right textIndent top width wordSpacing zIndex');
       
  1045 
       
  1046 Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
       
  1047 
       
  1048 String.__parseStyleElement = document.createElement('div');
       
  1049 String.prototype.parseStyle = function(){
       
  1050   var style, styleRules = $H();
       
  1051   if (Prototype.Browser.WebKit)
       
  1052     style = new Element('div',{style:this}).style;
       
  1053   else {
       
  1054     String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
       
  1055     style = String.__parseStyleElement.childNodes[0].style;
       
  1056   }
       
  1057 
       
  1058   Element.CSS_PROPERTIES.each(function(property){
       
  1059     if (style[property]) styleRules.set(property, style[property]);
       
  1060   });
       
  1061 
       
  1062   if (Prototype.Browser.IE && this.include('opacity'))
       
  1063     styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
       
  1064 
       
  1065   return styleRules;
       
  1066 };
       
  1067 
       
  1068 if (document.defaultView && document.defaultView.getComputedStyle) {
       
  1069   Element.getStyles = function(element) {
       
  1070     var css = document.defaultView.getComputedStyle($(element), null);
       
  1071     return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
       
  1072       styles[property] = css[property];
       
  1073       return styles;
       
  1074     });
       
  1075   };
       
  1076 } else {
       
  1077   Element.getStyles = function(element) {
       
  1078     element = $(element);
       
  1079     var css = element.currentStyle, styles;
       
  1080     styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
       
  1081       results[property] = css[property];
       
  1082       return results;
       
  1083     });
       
  1084     if (!styles.opacity) styles.opacity = element.getOpacity();
       
  1085     return styles;
       
  1086   };
       
  1087 }
       
  1088 
       
  1089 Effect.Methods = {
       
  1090   morph: function(element, style) {
       
  1091     element = $(element);
       
  1092     new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
       
  1093     return element;
       
  1094   },
       
  1095   visualEffect: function(element, effect, options) {
       
  1096     element = $(element);
       
  1097     var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
       
  1098     new Effect[klass](element, options);
       
  1099     return element;
       
  1100   },
       
  1101   highlight: function(element, options) {
       
  1102     element = $(element);
       
  1103     new Effect.Highlight(element, options);
       
  1104     return element;
       
  1105   }
       
  1106 };
       
  1107 
       
  1108 $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
       
  1109   'pulsate shake puff squish switchOff dropOut').each(
       
  1110   function(effect) {
       
  1111     Effect.Methods[effect] = function(element, options){
       
  1112       element = $(element);
       
  1113       Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
       
  1114       return element;
       
  1115     };
       
  1116   }
       
  1117 );
       
  1118 
       
  1119 $w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
       
  1120   function(f) { Effect.Methods[f] = Element[f]; }
       
  1121 );
       
  1122 
       
  1123 Element.addMethods(Effect.Methods);