/dev/trunk

To get this branch, use:
bzr branch http://darksoft.org/webbzr/dev/trunk

« back to all changes in this revision

Viewing changes to includes/scriptaculous.1.7.0/effects.js

  • Committer: Suren A. Chilingaryan
  • Date: 2008-04-02 10:23:22 UTC
  • Revision ID: csa@dside.dyndns.org-20080402102322-okib92sicg2dx3o3
Initial import

Show diffs side-by-side

added added

removed removed

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