var Ajax = function()
{
   this.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
   };

   this.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;
   };
   
   this.send = function(options)
   {
      this.running = true;
      this.process++;
      this.showLoader();
		  var type = jQuery.type(options);
		  if (type == 'string' || type == 'element') options = {data: options};
		  var old = this.options;
		  options = jQuery.extend({data: old.data, url: old.url, method: old.method}, options);
		  var data = options.data, url = options.url, method = options.method;
		  
      for (var option in this.options)
      {
         if (typeof(options[option]) == 'undefined') options[option] = this.options[option];
      }
      if (options.urlEncoded && options.method == 'post')
      {
         var charset = (options.charset) ? '; charset=' + options.charset : '';
         options.headers['Content-type'] = 'application/x-www-form-urlencoded' + charset;
      }
      if (options.data && options.method == 'get')
      {
         options.url = options.url + (options.url.contains('?') ? '&' : '?') + options.data;
         options.data = null;
      }
      var xhr = this.getXHR(), bind = this;
      xhr.open(options.method.toUpperCase(), options.url, options.async);
      xhr.onreadystatechange = function()
      {
         if (xhr.readyState != 4) return;
         if (xhr.status >= 200 && xhr.status < 300)
         {
            bind.process--;
            bind.exec(xhr.responseText);
            if (typeof(options.onComplete) == 'function') options.onComplete(xhr);
         }
         else if (typeof(options.onFailure) == 'function') options.onFailure(xhr);
         xhr.onreadystatechange = function(){};
         if (bind.process < 1)
         {
            bind.running = false;
            bind.process = 0;
            bind.hideLoader();
         }
      };
      for (var key in options.headers)
      {
         try {xhr.setRequestHeader(key, options.headers[key]);}
         catch (e)
         {
            if (typeof(options.onException) == 'function') options.onException(key, options.headers[key]);
         }
      }
      xhr.send(options.data);
      this.hideLoader();
      return xhr;
   };

   this.getXHR = function()
   {
      try {return new XMLHttpRequest();}
      catch(e) {return new ActiveXObject('MSXML2.XMLHTTP');}
   };

   this.exec = function(text)
   {
      if (!text) return;
      if (window.execScript) window.execScript(text);
      else eval(text);
   };

   this.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);
   };
   
   this.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;
   };

   this.encodeObj = function(param)
   {
      if (typeof(param) == 'object')
      {
         var obj = "<ajaxarray>";
         for (i in param)
         {
             if (i == 'constructor' || param[i] && (jQuery.type(param[i]) == 'function' || jQuery.type(param[i]) == 'object')) continue;
             obj += "<k>" + i + "</k><v>" + this.encodeObj(param[i]) + "</v>";
         }
         obj += "</ajaxarray>";
         return obj;
      }
      return param;
   };

   this.submit = function(func, form, target, url)
   {
      form = document.getElementById(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.indexOf('?')>0 ? '&' : '?') + "ajaxfunc=" + encodeURIComponent(func) + "&ajaxsubmit=1";
      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;
   };

   this.showLoader = function()
   {
      if (this.options.isShowLoader)
      {
         if (document.body) document.body.style.cursor = 'wait';
         if (typeof(this.options.onShowLoader) == 'function') this.options.onShowLoader();
      }
   };

   this.hideLoader = function()
   {
      if (this.options.isShowLoader)
      {
         if (document.body) document.body.style.cursor = 'default';
         if (typeof(this.options.onHideLoader) == 'function') this.options.onHideLoader();
      }
   };

   this.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);
   };

   this.addHistory = function(hash)
   {
   	  if (window.ie)
   	  {
   	     var iframe = $('#ajax_historyFrame').contentWindow.document;
         iframe.open();
         iframe.close();
         iframe.location.hash = hash;
   	  }
   	  window.location.hash = hash;
   };

   this.stopHistory = function()
   {
   	  clearInterval(this.historyInterval);
   	  this.currentHash = null;
   	  this.historyInterval = null;
   };

   this.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;
         if (typeof(this.onHistory) == 'function') this.onHistory(this.currentHash.substr(1));
      }
   }

   this.getFormValues = function(el, pref)
   {
      var values = new Array();
      var elements = this.getFormElements(el);
      for (var i = 0; i < elements.length; i++)
      {
         var el = elements[i];
         if (el.tagName == 'INPUT' && (el.type == 'submit' || el.type == 'image' || el.type == 'reset' || el.type == 'button')) continue;
         var name = el.name, key, value;
         if (name == '') name = el.id;
         if (name.substr(name.length - 2) == '[]') key = name.substr(0, name.length - 2);
         else key = name;
         if (pref)
         {
            var k = key.indexOf(pref);
            if (k != -1) key = key.substr(0, k);
         }
         switch (el.type)
         {
            default:
              value = el.value;
              break;
            case 'textarea':
              if (typeof(CKEDITOR) != 'undefined' && CKEDITOR.instances[el.id]) value = CKEDITOR.instances[el.id].getData();
              else if (typeof(tinyMCE) != 'undefined' && tinyMCE.get(el.id)) value = tinyMCE.get(el.id).getContent();
              else value = el.value;
              break;
            case 'radio':
            case 'checkbox':
              value = new Array();
              value['state'] = (el.checked) ? 1 : 0;
              value['value'] = el.value;
              break;
            case 'select-multiple':
              value = new Array();
              for (var j = 0; j < el.length; j++)
              {
                  if (el.options[j].selected == true) value[j] = el.options[j].value;
              }
              break;
         }
         if (key != name)
         {
            if (typeof(values[key]) == 'undefined') values[key] = new Array();
            values[key][values[key].length] = value;
         }
         else values[key] = value;
      }
      if (arguments.length > 2 && typeof(arguments[2]) == 'object') for (i in arguments[2]) values[i] = arguments[2][i];
      return values;
   };

   this.cleanFormValues = function(el, group, pref)
   {
      var elements = this.getFormElements(el);
      for (var i = 0; i < elements.length; i++)
      {
         var el = elements[i];
         if (pref && el.name.substr(0, pref.length) != pref) continue;
         switch (el.type)
         {
            case 'text':
            case 'hidden':
            case 'select-one':
            case 'select-multiple':
            case 'textarea':
              if (typeof(CKEDITOR) != 'undefined' && CKEDITOR.instances[el.id]) value = CKEDITOR.instances[el.id].setData('');
              else if (typeof(tinyMCE) != 'undefined' && tinyMCE.get(el.id)) value = tinyMCE.get(el.id).setContent('');
              else el.value = '';
              break;
            case 'checkbox':
            case 'radio':
              el.checked = false;
              break;
         }
      }
      if (typeof(validators) != 'undefined') validators.clean(group);
   };

   this.getFormElements = function(el, tags)
   {
      el = document.getElementById(el) || document;
      if (!tags) tags = 'input,select,textarea,checkbox,radio';
      tags = tags.split(',');
      var elements = [];
      var ddup = (tags.length > 1);
      for (var i = 0; i < tags.length; i++)
      {
         tag = tags[i];
         var partial = el.getElementsByTagName(tag.replace(/^\s+|\s+$/g, ''));
         if (ddup) for (var k = 0, j = partial.length; k < j; k++) elements.push(partial[k]);
         else elements = partial;
      }
      return elements;
   };

};
