  var rpc;
  RPC.READY_STATE_COMPLETE=4;
  function RPC(fe,skip_alerts) {
    if (window.XMLHttpRequest){
      this.req=new XMLHttpRequest();
    } else if (window.ActiveXObject){
      this.req=new ActiveXObject("Microsoft.XMLHTTP");
    } else {
      alert("RPC not available");
    }
    this.url=fe ? fe : "rpc_save.html";
    var req=this.req;
    if(skip_alerts){
      this.skip_alerts=skip_alerts;
      req.skip_alerts=skip_alerts;
    }
    var rpc=this;
    this.req.onreadystatechange= this.callback = function(){ 
       if (req.readyState==RPC.READY_STATE_COMPLETE){
           var httpStatus=req.status;
           if (httpStatus==200 || httpStatus==0){
                var doc = req.responseXML;
                if(!doc) {
                   if (!this.skip_alerts) alert("Cannot parse server reply :\n" + req.responseText.substr(0,256)); 
                   var cons = document.getElementById('__console'); 
                   if(cons) { cons.innerHTML += req.responseText; } 
                   return false; 
		}
                var response = doc.getElementsByTagName('response').item(0);
                if(!response) {
                   if (!this.skip_alerts) alert("Server reply has no response element " + "\n" + req.responseText);
                   return false;
                }
                var more = response.getAttribute('waitmore');
                var qid  = response.getAttribute('qid');
                var qn   = response.getAttribute('qn');
		var opts = response.getAttribute('opts');
                if(more) {
                   if(rpc.onStartWaiting) { rpc.onStartWaiting(); } 
                   document.body.style.cursor = "wait";
                   setTimeout(function() { var newrpc = new RPC(fe);
			newrpc.onEndWaiting   = rpc.onEndWaiting;
			newrpc.request('__wait',{q:qid,qn:qn,opts:opts},rpc.onOk);
		   }, parseInt(more) );
                } else {
                   var rc=response.getAttribute('rc');
                   if (rc == 'EXCEPTION') {
                     var ex = xml_getObjectAttributes(response); 
                     var func_name = 'exception_' + ex.exceptionClass.replace(/::/g,'_');
                     if(eval("window." + func_name)) { 
                       eval(func_name + "(ex)");
                     } else { 
                       alert(ex.text ? ex.text : 'произошла ошибка на сервере');
                     }
		     if(rpc.onEndWaiting) rpc.onEndWaiting(req);
                     document.body.style.cursor ='default';
                     return false;
                   }
                   if(rc == 'FAILED') { 
                       alert('произошла ошибка на сервере');
    		      if(rpc.onEndWaiting) rpc.onEndWaiting(req);
                       document.body.style.cursor ='default';
                       return false;
                   } 
                   rpc.onOk(response,req);
    		   if(rpc.onEndWaiting) rpc.onEndWaiting(req);
                   document.body.style.cursor ='auto';
                }
           } else {
                rpc.onError(req);
                document.body.style.cursor ='auto';
           }
        }
    };
  }
  RPC.prototype.onError = function(req) { 
      if (!this.skip_alerts) 
           alert("error fetching data!" 
        +"\n\nreadyState:"+req.readyState
        +"\nstatus: "+req.status
        +"\nheaders: "+req.getAllResponseHeaders());
  }
  RPC.prototype.onOk = function() {
      alert("Done");
  }
  RPC.prototype.request = function(proc,args,onOk,sync,method) {
      this.req.open((method ? method : "POST"), this.url, (sync ? false : true));
      this.req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      var qs = '';
      if (proc !== null && proc !== undefined && proc.length > 0) qs += '_proc=' + proc;
      for (var arg in args)  { var val=args[arg];
          if (typeof(val)=='object'){ for(k in val) { 
            if (val[k] && val[k].constructor != Function)
              qs+='&' + escape(arg) + '=' + escape(val[k]);
          }}
          else { qs +='&' + escape(arg) + '=' + escape(val); }
      }
      this.onOk = onOk;
      this.req.send(qs);
      if(sync) this.callback(); 
  }

  function server_call(proc,args,onOk,fe,sync,method,skip_alerts,onStartWaiting,onEndWaiting) {
     rpc = new RPC(fe,skip_alerts);
     rpc.onStartWaiting = onStartWaiting;
     rpc.onEndWaiting   = onEndWaiting;
     rpc.request(proc,args,onOk,sync,method);
  }

  function xml_getObjectAttribute(node,attrName) {
     for(var i=0;i<node.childNodes.length;i++) {
        var nn = node.childNodes[i];
        if(nn.nodeType==1) {
          if (nn.tagName==attrName) {
             return nn.childNodes[0]?nn.childNodes[0].data:null;
          }
        }
     }
     return 'KO';
  }
  function xml_getObjectAttributes(node) {
     var o = {};
     if(!node) { 
        alert("Empty node got");
        return o;
     }
     for(var i=0;i<node.attributes.length;i++) { 
        o [ node.attributes[i].nodeName ] = node.attributes[i].nodeValue;
     }
     for(var i=0;i<node.childNodes.length;i++) {
        var nn = node.childNodes[i];
        if(nn.nodeType==1) {
          if(nn.getAttribute('class')=='subobject') { 
             o[ nn.tagName  ] = null;
             for(var j=0;j<nn.childNodes.length;j++) { 
               var nnn=nn.childNodes[j];
               if(nnn.nodeType==1) { 
                  o[ nn.tagName  ] = xml_getObjectAttributes(nnn);
               }
               break;
             }
          } else 
	  if(nn.getAttribute('class')=='subobjects') {
	     o[ nn.tagName  ] = [];
	     for(var j=0;j<nn.childNodes.length;j++) {
               var nnn=nn.childNodes[j];
               if(nnn.nodeType==1) {    
                  o[ nn.tagName  ][ o[nn.tagName].length] = xml_getObjectAttributes(nnn);
               }
             }
	  } else 
          if(nn.childNodes[0]) { 
             o[ nn.tagName  ] = nn.childNodes[0].data;
          } else {
             o[ nn.tagName  ] = null;
          }
        }
     }
     return o;
  }
  function xml_getObjectList(node) { 
    var l = new Array;
    for(var i=0;i<node.childNodes.length;i++) {
        var nn = node.childNodes[i];
        if(nn.nodeType==1) {
          l[l.length] = xml_getObjectAttributes(nn);
        }
    }
    return l;
  }

