Fx.Push = new Class({

  Extends: Fx,

  options: {
    style: true,
    modifiers: { x: 'left', y: 'top' },
    friction: 0,
    limit: false,
    bounce: 0,
    gravity: false
  },

  initialize: function(element, options) {
    this.element = this.subject = $(element);
    this.parent(options);    
  },

  start: function(sx, sy) {
    var m = this.options.modifiers;
    this.position = { x: (this.options.style ? this.element.getStyle(m.x) : this.element[m.x]).toInt(),
      y: (this.options.style ? this.element.getStyle(m.y) : this.element[m.y]).toInt()
    };
    this.speed = { x: sx, y: sy };
    this.time = 0;
    this.startTimer();
    this.lastStepTime = this.time;
    this.onStart();
    return this;
  },

  step: function() {
    var time = $time();

    var a = $merge(this.options.gravity);

    if (this.options.friction) {
      a.x -= this.speed.x * this.options.friction;
      a.y -= this.speed.y * this.options.friction;
    }

    this.speed.x += a.x;
    this.speed.y += a.y;

    var oldPosition = $merge(this.position);

    this.position.x += this.speed.x;
    this.position.y += this.speed.y;

    if (this.options.limit) {
      var l = this.options.limit;
      if (this.position.x < l.x[0]) { this.position.x = l.x[0]; this.speed.x = this.options.bounce * -this.speed.x; }
      else if (this.position.x > l.x[1]) { this.position.x = l.x[1]; this.speed.x = this.options.bounce * -this.speed.x; }
      if (this.position.y < l.y[0]) { this.position.y = l.y[0]; this.speed.y = this.options.bounce * -this.speed.y; }
      else if (this.position.y > l.y[1]) { this.position.y = l.y[1]; this.speed.y = this.options.bounce * -this.speed.y; }
    }

    var dx = this.position.x - oldPosition.x;
    var dy = this.position.y - oldPosition.y;

    if (this.options.friction) {
      if ((Math.abs(dx) + Math.abs(dy)) < 0.01) {
        this.complete();
        return;
      }
    } else if (time > this.time + this.options.duration) {
      this.complete();
      return;
    }

    for (var z in this.options.modifiers) {
      var modifier = this.options.modifiers[z];
      if (!modifier) continue;
      this.element[this.options.style ? 'setStyle' : 'set'](modifier, this.position[z].toInt());
      var value = this.position[z].toInt();
      if (this.options.style) this.element.setStyle(modifier, value);
      else this.element[modifier] = value;
    }
    this.lastStepTime = time;

  }
});
