function callRemoteProc(proc, params, onresult)
{
  try
  {
    var request = null;
    if (!request && typeof window.XMLHttpRequest != 'undefined')
      request = new XMLHttpRequest();

    if (!request && typeof window.ActiveXObject != 'undefined')
    {
      try
      {
        request = new ActiveXObject('Msxml2.XMLHTTP');
      } catch (dummy) {}
    }

    if (!request && typeof window.ActiveXObject != 'undefined')
    {
      try
      {
        request = new ActiveXObject('Microsoft.XMLHTTP');
      } catch (dummy) {}
    }

    if (!request)
      throw "Could not create an XMLHttpRequest object, maybe not supported by this browser";

    request.open('POST', '/rpc.xml');

    var done = false;
    if (typeof onresult != 'undefined')
    {
      var loadFunc = function()
      {
        if (done)
          return;
        done = true;
  
        var status = 200;
        try
        {
          status = request.status;
        } catch (dummy) {}
  
        var result = null;
        if (status != 200)
          result = {error: {code: 'WrongResponseStatus', message: 'Непонятный ответ сервера: ' + status}};
        else if (request.responseXML == null || request.responseXML.documentElement == null)
          result = {error: {code: 'CouldNotParse', message: 'Сервер прислал ответ в неизвестном формате.'}};
        else
          result = parseRPCResponse(request.responseXML);
  
        onresult(params, result);
      }
      var errorFunc = function()
      {
        if (done)
          return;
        done = true;
  
        onresult(params, {error: {code: 'ConnectionError', message: 'При соединении с сервером возникла ошибка.'}});
      }
  
      request.onreadystatechange = function()
      {
        if (request.readyState == 4)
          loadFunc();
      }
      try
      {
        request.onload = loadFunc;
        request.onerror = errorFunc;
      }
      catch (dummy) {}
    }

    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    request.send(encodeRPCParams(proc, params));
  }
  catch (e)
  {
    alert("Не удалось установить соединение с сервером. Возможно, вы используете слишком старый браузер. Данную операцию поддерживают: Firefox, Mozilla, Safari, Opera 8.02+, Internet Explorer 5+. Если вы используете Internet Explorer, то проверьте, разрешено ли использование безопасных ActiveX-объектов.");
    throw e;
  }
}

function parseRPCResponse(doc)
{
  var entry;
  var result = {};
  for (var child = doc.documentElement.firstChild; child; child = child.nextSibling)
  {
    if (child.nodeType == 1)
    {
      var value = null;
      if (child.getAttribute('type') == null || child.getAttribute('type') == '')
        value = getTextValue(child);
      else if (child.getAttribute('type') == 'hash')
      {
        value = {};
        for (entry = child.firstChild; entry; entry = entry.nextSibling)
          if (entry.nodeType == 1 && entry.tagName == 'entry')
            value[entry.getAttribute('name')] = getTextValue(entry);
      }
      else if (child.getAttribute('type') == 'list')
      {
        value = [];
        for (entry = child.firstChild; entry; entry = entry.nextSibling)
          if (entry.nodeType == 1 && entry.tagName == 'entry')
            value.push(getTextValue(entry));
      }
      else if (child.getAttribute('type') == 'html')
        value = child;

      result[child.tagName] = value;
    }
  }
  return result;
}

// Инициализируем таблицу перевода
var trans = [];
for (var i = 0x410; i <= 0x44F; i++)
  trans[i] = i - 0x350; // А-Яа-я
trans[0x401] = 0xA8;    // Ё
trans[0x451] = 0xB8;    // ё

// Сохраняем стандартную функцию escape()
var escapeOrig = window.escape;

// Переопределяем функцию escape()
window.escape = function(str)
{
  var ret = [];
  str = '' + str;
  // Составляем массив кодов символов, попутно переводим кириллицу
  for (var i = 0; i < str.length; i++)
  {
    var n = str.charCodeAt(i);
    if (typeof trans[n] != 'undefined')
      n = trans[n];
    if (n <= 0xFF)
      ret.push(n);
  }
  return escapeOrig(String.fromCharCode.apply(null, ret));
}

function encodeRPCParams(proc, params)
{
  var result = 'proc=' + escape(proc);
  for (var key in params)
    result += '&' + escape(key) + '=' + escape(params[key]);
  return result;
}

function getTextValue(node)
{
  var result = '';
  for (var child = node.firstChild; child; child = child.nextSibling)
    if (child.nodeType == 3)
      result += child.nodeValue;

  return result;
}
