Ajax.Callbacker = Class.create();
Object.extend(Object.extend(Ajax.Callbacker.prototype, Ajax.Request.prototype), {
  initialize: function(callback, url, options) {
    this.callback = callback;

    this.transport = Ajax.getTransport();
    this.setOptions(options);

    var onComplete = this.options.onComplete || Prototype.emptyFunction;
    this.options.onComplete = (function(transport, param) {
      this.invoceCallback();
      onComplete(transport, param);
    }).bind(this);

    this.request(url);
  },

  setOptions: function(options) {
	this.options = {
	  method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   '',
      evalJSON:     true,
      evalJS:       true
    };
    Object.extend(this.options, options || { });

    this.options.method = this.options.method.toLowerCase();

    if (Object.isString(this.options.parameters))
      this.options.parameters = this.options.parameters.toQueryParams();
    else if (Object.isHash(this.options.parameters))
      this.options.parameters = this.options.parameters.toObject();
  },
  
  invoceCallback: function() {
  	var success = this.success();
    var response = this.transport.responseText;
    var args = [success, response];
    this.callback.apply(this,args);

    if (this.success()) {
      if (this.onComplete)
        setTimeout(this.onComplete.bind(this), 10);
    }
  }
});

Ajax.PeriodicalCallbacker = Class.create();
Ajax.PeriodicalCallbacker.prototype = Object.extend(new Ajax.Base(), {
  initialize: function(callback, url, options) {
    this.setOptions(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = {};
    this.callback = callback;
    this.url = url;

    this.start();
  },

  setOptions: function(options) {
	this.options = {
	  method:       'post',
	  asynchronous: true,
	  contentType:  'application/x-www-form-urlencoded',
	  encoding:     'UTF-8',
	  parameters:   '',
      evalJSON:     true,
      evalJS:       true
    };
    Object.extend(this.options, options || { });

    this.options.method = this.options.method.toLowerCase();

    if (Object.isString(this.options.parameters))
      this.options.parameters = this.options.parameters.toQueryParams();
    else if (Object.isHash(this.options.parameters))
      this.options.parameters = this.options.parameters.toObject();
  },
  
  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function() {
    this.updater.options.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(request) {
    if (this.options.decay) {
      this.decay = (request.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = request.responseText;
    }
    this.timer = setTimeout(this.onTimerEvent.bind(this),
      this.decay * this.frequency * 1000);
  },

  onTimerEvent: function() {
    this.updater = new Ajax.Callbacker(this.callback, this.url, this.options);
  }
});
