function Ajax()
{
   this.synchronous = false;  // Булевая переменная, признак синхронного запроса.
   this.isShowAjaxLoader = true;
   this.historyCallback = null;
   this.currentHash = null;
   this.url = null;           // url запроса.

   // Назначение: получение объекта XMLHttpRequest.
   this.getRequestObject = function()
   {
      var req = null;
      if (typeof XMLHttpRequest != "undefined") req = new XMLHttpRequest();
      if (!req && typeof ActiveXObject != "undefined")
      {
         try {req = new ActiveXObject("Msxml2.XMLHTTP");}
         catch(e)
         {
            try {req = new ActiveXObject("Microsoft.XMLHTTP");}
            catch (e2)
            {
               try {req = new ActiveXObject("Msxml2.XMLHTTP.4.0");}
               catch (e3) {req = null;}
            }
         }
      }
      if(!req && window.createRequest) req = window.createRequest();
      if (!req) alert("Request Object Instantiation failed.");
      return req;
   }

   // Назначение: исполнение ajax - запроса.
   // url - uri документа;
   // params - массив параметров, передаваемых документу;
   // httpMethod - способ передачи данных (POST или GET).
   // Функция возвращает true, в случае успеха и false, в случае неудачи.
   this.sendRequest = function(url, params, httpMethod)
   {
      url = this.normalizeUrl(url);
      if (!httpMethod) httpMethod = "POST";
      var req = this.getRequestObject();
      if (req)
      {
         if (ajax.isShowAjaxLoader) ajax.showAjaxLoader();
         req.open(httpMethod, url, !this.synchronous);
         req.setRequestHeader("Method", httpMethod + " " + url + " HTTP/1.1");
         req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
         req.onreadystatechange = function()
         {
            if (req.readyState != 4) return;
            if (req.status == 200 || req.status == 0)
            {
               eval(req.responseText);
               if (ajax.isShowAjaxLoader) ajax.hideAjaxLoader();
               ajax.synchronous = false;
               delete req;
            }
            else
            {
               if (ajax.isShowAjaxLoader) ajax.hideAjaxLoader();
               alert("Error: the server returned the following HTTP status: " + req.status);
               return false;
            }
         }
         req.send(params);
         this.url = null;
         return true;
      }
      this.url = null;
      return false;
   }

   // Назначение: вызов ajax-функции.
   // func - имя ajax-функции.
   this.call = function(func)
   {
      var url;
      if (this.url) url = this.url;
      else url = new String(window.location);
      var params = "", i = 1;
      var 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.sendRequest(url, params);
   }

   // Назначение: postback во фрэйм
   // func - имя ajax-функции
   // form - форма (объект), данный которой отправляются
   // target - идентификатор фрэйма, в который будет осуществлён postback
   this.submit = function(func, form, target)
   {
      var url;
      if (this.url) url = this.url;
      else url = new String(window.location);
      form = $(form);
      url = this.normalizeUrl(url);
      var old_target = form.target;
      var old_action = form.action;
      var old_method = form.method;
      var old_enctype = form.encoding;
      if (url.indexOf("?") != -1) url += "&"; else url += "?";
      form.action = url+"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.submitProgress = function(func, form, target, callback)
   {
      var el = $('ajax_progress_window');
      if (el)
      {
         var fade = $('ajax_fadeout');
         if (fade)
         {
         	var size = getPageSize(); //(new Element(document.body)).getSize()['size'];
            fade.style.zIndex = '999999999';
            fade.style.position = 'absolute';
            fade.style.top = 0;
            fade.style.left = 0; //window.innerHeight && window.scrollMaxY;
            fade.style.background = 'black';
            fade.style.width = size[0] + 'px';
            fade.style.height = size[1] + 'px';
            //fade.setOpacity(0.5);
            fade.style.display = '';
            fade.className = 'fadeoutclass';
         }
         el.style.position = 'absolute';
         el.style.zIndex = '1000000000';
         el.style.left = window.getScrollLeft() + (window.getWidth() - 400) / 2 + 'px';
         el.style.top = window.getScrollTop() + (window.getHeight() - 200) / 2 + 'px';
      }
      this.isShowAjaxLoader = false;
   	  this.submit(func, form, target);
   	  var id = target.substr(6);
   	  //setTimeout("ajax.call('detSpeed', '" + callback + "', document.getElementById('ajax_progress_key_" + id + "').value, '" + id + "')", 500);
      setTimeout("ajax.call('" + callback + "', document.getElementById('ajax_progress_key_" + id + "').value, '" + id + "')", 2000);
   }

   this.cleanProgress = function()
   {
      scon('ajax_progress_window_filename', '');
      scon('ajax_progress_window_total', '0');
      scon('ajax_progress_window_current', '0');
      scon('ajax_progress_window_rate', '');
      scon('ajax_progress_window_progressbar', '');
      if ($('ajax_progress_window_button')) $('ajax_progress_window_button').disabled = true;
	  var el = $('ajax_progress_window');
	  el.style.top = -1000 + 'px';
      this.isShowAjaxLoader = true;
   }

   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 = '#';
      }
      var fn = function()
      {
         var hash;
         if (window.ie) hash = $('ajax_historyFrame').contentWindow.document.location.hash;
         else hash = window.location.hash;
         if (ajax.currentHash != hash)
         {
         	ajax.currentHash = hash;
         	if (window.ie) window.location.hash = hash;
            if (typeof(ajax.historyCallback) == 'function') ajax.historyCallback(ajax.currentHash.substr(1));
         }
      }
   	  setInterval(fn, 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.currentHash = null;
   	  this.historyCallback = null;
   }

   this.getFormValues = function(form, pref)
   {
      var objForm;
      if (typeof(form) == "string") objForm = $(form);
      else objForm = form;
      if (objForm)
      {
         var values = new Array();
         var elements = objForm.getFormElements();
         for(var i = 0; i < elements.length; i++)
         {
            if (elements[i].type && (elements[i].type == 'radio' || elements[i].type == 'checkbox') && elements[i].checked == false) continue;
            var name = elements[i].name;
            if (pref)
            {
               var k = name.indexOf(pref);
               if (k != -1) name = name.substr(0, k);
            }
            if (name)
            {
               if (elements[i].type == 'select-multiple')
               {
                  for (var j = 0; j < elements[i].length; j++)
                  {
                     if (elements[i].options[j].selected == true)
                       if (name.substr(name.length - 2) == '[]')
                       {
                  	      name = name.substr(0, name.length - 2);
                  	      if (typeof(values[name]) == 'undefined') values[name] = new Array();
                  	      values[name][values[name].length] = elements[i].options[j].value;
                       }
                       else values[name][j] = elements[i].options[j].value;
                  }
               }
               else
               {
                  if (name.substr(name.length - 2) == '[]')
                  {
                  	 name = name.substr(0, name.length - 2);
                  	 if (typeof(values[name]) == 'undefined') values[name] = new Array();
                  	 values[name][values[name].length] = elements[i].value;
                  }
                  else values[name] = elements[i].value;
               }
            }
         }
         return values;
      }
   }

   // Назначение: преобразование js-объекта или js-массива в строку.
   // param - js-объект или js-массив.
   this.encodeObj = function(param)
   {
      if (typeof(param) == 'object')
      {
         var obj = "<ajaxarray>";
         for (i in param)
         {
             if (i == 'constructor' || param[i] && typeof(param[i]) == 'function') continue;
             obj += "<k>"+i+"</k><v>" + ajax.encodeObj(param[i]) + "</v>";
         }
         obj += "</ajaxarray>";
         return obj;
      }
      return param;
   }

   this.normalizeUrl = function(url)
   {
      var k = url.indexOf('#');
      if (k > -1) return url.substr(0, k);
      return url;
   }

   // Назначение: отображение признака выполнение запроса.
   this.showAjaxLoader = function()
   {
      if (document.body) document.body.style.cursor = 'wait';
      var el = $('ajax_loader');
      if (el)
      {
         var fade = $('ajax_fadeout');
         if (fade)
         {
         	var size = getPageSize(); //(new Element(document.body)).getSize()['size'];
            fade.style.zIndex = '999999999';
            fade.style.position = 'absolute';
            fade.style.top = 0;
            fade.style.left = 0; //window.innerHeight && window.scrollMaxY;
            //fade.style.left = 0;
            fade.style.background = 'white';
            fade.style.width = size[0] + 'px';
            fade.style.height = size[1] + 'px';
            //fade.setOpacity(0.01);
            fade.style.display = '';
            fade.className = 'fadeoutwhiteclass';
         }
         el.style.position = 'absolute';
         el.style.zIndex = '1000000000';
         el.style.left = window.getScrollLeft() + (window.getWidth() - 250) / 2 + 'px';
         el.style.top = window.getScrollTop() + (window.getHeight() - 86) / 2 + 'px';
         el.style.display = '';
      }
   }

   // Назначение: скрытие признака выполнения запроса.
   this.hideAjaxLoader = function()
   {
      if (document.body) document.body.style.cursor = 'default';
      if ($('ajax_loader')) $('ajax_loader').style.display = 'none';
      if ($('ajax_fadeout')) $('ajax_fadeout').style.display = 'none';
   }
}

// Создание объекта ajax.
var ajax = new Ajax();
//ajax.synchronous = true;