var Ajax = new Class(
{
   Implements: [Events, Options],

   options:
   {
      data: '',
      url: window.location.href,
      cdurl: window.location.href,
	  	headers: {'X-Requested-With': 'XMLHttpRequest', 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'},
	  	async: true,
	  	method: 'post',
	  	urlEncoded: true,
	  	charset: 'utf-8',
      isShowLoader: true,
      onShowLoader: null,
      onHideLoader: null,
      onHistory: null
   },

   initialize: function(options)
   {
      this.setOptions(options);
      this.currentHash = null;
      this.historyInterval = null;
      this.headers = new Hash(this.options.headers);
      this.running = false;
      this.process = 0;
   },
   
   send: function(options)
   {
      this.running = true;
      this.process++;
      this.showLoader();
		  var type = $type(options);
		  if (type == 'string' || type == 'element') options = {data: options};
		  var old = this.options;
		  options = $extend({data: old.data, url: old.url, method: old.method}, options);
		  var data = options.data, url = options.url, method = options.method;
	  	if (this.options.urlEncoded && method == 'post')
      {
			   var charset = (this.options.charset) ? '; charset=' + this.options.charset : '';
			   this.headers.set('Content-type', 'application/x-www-form-urlencoded' + charset);
		  }
		  if (data && method == 'get')
      {
			   url = url + (url.contains('?') ? '&' : '?') + data;
			   data = null;
		  }
      var xhr = new Browser.Request();
		  xhr.open(method.toUpperCase(), url, this.options.async);
      xhr.onreadystatechange = function()
      {
		     if (xhr.readyState != 4) return;
		     if (xhr.status >= 200 && xhr.status < 300) {this.process--; $exec(xhr.responseText); this.fireEvent('complete', xhr);}
         else this.fireEvent('failure', xhr);
	  	   xhr.onreadystatechange = $empty;
	  	   if (this.process < 1) {this.running = false; this.process = 0; this.hideLoader()}
      }.bind(this);
		  this.headers.each(function(value, key)
      {
			   if (!$try(function(){xhr.setRequestHeader(key, value); return true;}.bind(this))) 
         this.fireEvent('exception', [key, value]);
	   	}, this);
		  xhr.send(data);
		  return xhr;
	 },
	 
	 doit: function(func)
	 {
	    var args = new Array();
	    for (var i = 1; i < arguments.length; i++) args[i] = arguments[i]; 
	    return this.call(func, $(document.body).getFormValues(), args);
   },

   call: function(func)
   {
      var params = "", i = 1, args = arguments;
      if (arguments.length == 2 && typeof(arguments[1]) == 'object')
      {
         args = arguments[1];
         i = 0;
      }
      params += "ajaxfunc=" + encodeURIComponent(func);
      for (; i < args.length; i++) params += "&ajaxargs[]=" + encodeURIComponent(this.encodeObj(args[i]));
      return this.send(params);
   },
   
   cdcall: function(func)
   {
   	  var params = '', i = 1, args = arguments;
      if (arguments.length == 2 && typeof(arguments[1]) == 'object')
      {
         args = arguments[1];
         i = 0;
      }
      if (this.options.cdurl.charAt(this.options.cdurl.length - 1) != '?') params = '?';
      params += "ajaxfunc=" + encodeURIComponent(func);
      for (; i < args.length; i++) params += "&ajaxargs[]=" + encodeURIComponent(this.encodeObj(args[i]));
      document.getElementsByTagName('HEAD')[0].appendChild(new Element('script', {'src': this.options.cdurl + params, 'type': 'text/javascript'}));
      return this;
   },

   encodeObj: function(param)
   {
      if (typeof(param) == 'object')
      {
         var obj = "<ajaxarray>";
         for (i in param)
         {
             if (i == 'constructor' || param[i] && ($type(param[i]) == 'function' || $type(param[i]) == 'object')) continue;
             obj += "<k>" + i + "</k><v>" + this.encodeObj(param[i]) + "</v>";
         }
         obj += "</ajaxarray>";
         return obj;
      }
      return param;
   },

   submit: function(func, form, target, url)
   {
      form = $(form);
      var old_target = form.target;
      var old_action = form.action;
      var old_method = form.method;
      var old_enctype = form.encoding;
      url = (url) ? url : this.options.url;
      form.action = url.replace('#', '') + (url.contains('?') ? '&' : '?') + "ajaxfunc=" + encodeURIComponent(func) + "&ajaxsubmit=1&ajaxargs[]=" + encodeURIComponent(this.encodeObj(target.substr(6)));
      form.method = 'post';
      form.target = target;
      form.encoding = 'multipart/form-data';
      form.submit();
      form.target = old_target;
      form.action = old_action;
      form.method = old_method;
      form.encoding = old_enctype;
   },
   
   submitProgress: function(func, form, target, callback, url) 
   {       
      this.isShowLoader = false; 
      this.submit(func, form, target, url);  
      setTimeout(function(){this.progress(target.substr(6), callback);}.bind(this), 500);
   },
   
   progress: function(id, callback)
   {
      this.doit(callback, $('ajax_progress_key_' + id).value, id);
   },

   showLoader: function()
   {
      if (this.options.isShowLoader)
      {
         if (document.body) document.body.style.cursor = 'wait';
         this.fireEvent('loaderShow');
      }
   },

   hideLoader: function()
   {
      if (this.options.isShowLoader)
      {
         if (document.body) document.body.style.cursor = 'default';
         this.fireEvent('loaderHide');
      }
   },

   startHistory: function()
   {
      this.currentHash = window.location.hash;
      if (window.ie)
      {
      	 var el = new Element('iframe', {'styles': {'display': 'none'}, 'id': 'ajax_historyFrame'});
         el.inject(document.body, 'top');
         var iframe = $('ajax_historyFrame').contentWindow.document;
         iframe.open();
         iframe.close();
         iframe.location.hash = this.currentHash;
         if (!this.currentHash) this.currentHash = '#';
      }
   	  this.historyInterval = setInterval(this.history, 100);
   },

   addHistory: function(hash)
   {
   	  if (window.ie)
   	  {
   	     var iframe = $('ajax_historyFrame').contentWindow.document;
         iframe.open();
         iframe.close();
         iframe.location.hash = hash;
   	  }
   	  window.location.hash = hash;
   },

   stopHistory: function()
   {
   	  clearInterval(this.historyInterval);
   	  this.currentHash = null;
   	  this.historyInterval = null;
   },

   history: function()
   {
      var hash;
      if (window.ie) hash = $('ajax_historyFrame').contentWindow.document.location.hash;
      else hash = window.location.hash;
      if (this.currentHash != hash)
      {
         this.currentHash = hash;
         if (window.ie) window.location.hash = hash;
         this.fireEvent('history', this.currentHash.substr(1));
      }
   }

});