]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/jsolait/lib/xmlrpc.js
Release 6.2.1
[Github/sugarcrm.git] / jssource / src_files / include / jsolait / lib / xmlrpc.js
1 /*
2
3 Modification information for LGPL compliance
4
5 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
6
7 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
8
9 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
10
11 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3  tags and updated the build system 
12
13 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
14
15 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
16
17 r42807 - 2008-12-29 11:16:59 -0800 (Mon, 29 Dec 2008) - dwong - Branch from trunk/sugarcrm r42806 to branches/tokyo/sugarcrm
18
19 r4085 - 2005-04-13 17:30:42 -0700 (Wed, 13 Apr 2005) - robert - adding meeting scheduler and accept/decline
20
21
22 */
23
24 /*
25   Copyright (c) 2003-2004 Jan-Klaas Kollhof
26   
27   This file is part of the JavaScript o lait library(jsolait).
28   
29   jsolait is free software; you can redistribute it and/or modify
30   it under the terms of the GNU Lesser General Public License as published by
31   the Free Software Foundation; either version 2.1 of the License, or
32   (at your option) any later version.
33  
34   This software is distributed in the hope that it will be useful,
35   but WITHOUT ANY WARRANTY; without even the implied warranty of
36   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   GNU Lesser General Public License for more details.
38  
39   You should have received a copy of the GNU Lesser General Public License
40   along with this software; if not, write to the Free Software
41   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
42 */
43
44         
45 /**
46     Provides an XML-RPC imlementation.
47     It is similar to python's xmlrpclib module.
48 */
49 Module("xmlrpc","1.3.3", function(mod){
50     var xmlext = importModule("xml");
51     var urllib = importModule("urllib");
52     /**
53         Thrown if a  server did not respond with response status 200 (OK).
54     */
55     mod.InvalidServerResponse = Class("InvalidServerResponse", mod.Exception, function(publ, supr){
56         /**
57             Initializes the Exception.
58             @param status       The status returned by the server.
59         */
60         publ.init= function(status){
61             supr(this).init("The server did not respond with a status 200 (OK) but with: " + status);
62             this.status = status;
63         }
64          ///The status returned by the server.
65         publ.status;
66     })
67     
68     /**
69         Thrown if an XML-RPC response is not well formed.
70     */
71     mod.MalformedXmlRpc = Class("MalformedXmlRpc", mod.Exception, function(publ, supr){
72         /**
73             Initializes the Exception.
74             @param msg          The error message of the user.
75             @param xml           The xml document's source.
76             @param trace=null  The error causing this Exception
77         */
78         publ.init= function(msg, xml, trace){
79             supr(this).init(msg,trace);
80             this.xml = xml;
81         }
82          ///The xml source which was mal formed.
83         publ.xml;
84     })
85     /**
86         Thrown if the RPC response is a Fault.        
87     */
88     mod.Fault = Class("Fault", mod.Exception, function(publ, supr){
89         /**
90             Initializes the Exception.
91             @param faultCode       The fault code returned by the rpc call.
92             @param faultString      The fault string returned by the rpc call.
93         */
94         publ.init= function(faultCode, faultString){
95             supr(this).init("XML-RPC Fault: " +  faultCode + "\n\n" + faultString);
96             this.faultCode = faultCode;
97             this.faultString = faultString;
98         }
99         ///The fault code returned from the rpc call.
100         publ.faultCode;
101         ///The fault string returned from the rpc call.
102         publ.faultString;
103     })
104
105     /**
106         Marshalls an object to XML-RPC.(Converts an object into XML-RPC conforming xml.)
107         It just calls the toXmlRpc function of the objcect.
108         So, to customize serialization of objects one just needs to specify/override the toXmlRpc method 
109         which should return an xml string conforming with XML-RPC spec.
110         @param obj    The object to marshall
111         @return         An xml representation of the object.
112     */
113     mod.marshall = function(obj){
114         if(obj.toXmlRpc){
115             return obj.toXmlRpc();
116         }else{
117             var s = "<struct>";
118             for(var attr in obj){
119                 if(typeof obj[attr] != "function"){
120                     s += "<member><name>" + attr + "</name><value>" + mod.marshall(obj[attr]) + "</value></member>";
121                 }
122             }
123             s += "</struct>";
124             return s;
125         }
126     }
127     
128     /**
129         Unmarshalls an XML document to a JavaScript object. (Converts xml to JavaScript object.)
130         It parses the xml source and creates a JavaScript object.
131         @param xml    The xml document source to unmarshall.
132         @return         The JavaScript object created from the XML.
133     */
134     mod.unmarshall = function(xml){
135         try {//try to parse xml ... this will throw an Exception if failed
136             var doc = xmlext.parseXML(xml);
137         }catch(e){
138             throw new mod.MalformedXmlRpc("The server's response could not be parsed.", xml, e);
139         }
140         var rslt = mod.unmarshallDoc(doc, xml);
141         doc=null;
142         return rslt;
143     }
144     
145     /**
146         Unmarshalls an XML document to a JavaScript object like unmarshall but expects a DOM document as parameter.
147         It parses the xml source and creates a JavaScript object.
148         @param doc   The xml document(DOM compatible) to unmarshall.
149         @return         The JavaScript object created from the XML.
150     */
151     mod.unmarshallDoc = function(doc, xml){
152         try{
153             var node = doc.documentElement;
154             if(node==null){//just in case parse xml didn't throw an Exception but returned nothing usefull.
155                 throw new mod.MalformedXmlRpc("No documentElement found.", xml);
156             }
157             switch(node.tagName){
158                 case "methodResponse":
159                     return parseMethodResponse(node);
160                 case "methodCall":
161                     return parseMethodCall(node);
162                 default://nothing usefull returned by parseXML.
163                     throw new mod.MalformedXmlRpc("'methodCall' or 'methodResponse' element expected.\nFound: '" + node.tagName + "'", xml);
164             }
165         }catch(e){
166             if(e instanceof mod.Fault){//just rethrow the fault.
167                 throw e;
168             }else {
169                 throw new mod.MalformedXmlRpc("Unmarshalling of XML failed.", xml, e);    
170             }
171         }
172     }
173     
174     /**
175         Parses a methodeResponse element.
176         @param node  The methodResponse element.
177         @return          The return value of the XML-RPC.
178     */
179     var parseMethodResponse=function(node){
180         try{
181             for(var i=0;i<node.childNodes.length;i++){
182                 var child = node.childNodes.item(i);
183                 if (child.nodeType == 1){
184                     switch (child.tagName){
185                         case "fault": //a fault is thrown as an Exception
186                             throw parseFault(child);
187                         case "params":
188                             var params = parseParams(child);
189                             if(params.length == 1){//params should only have one param
190                                 return params[0];
191                             }else{
192                                 throw new mod.MalformedXmlRpc("'params' element inside 'methodResponse' must have exactly ONE 'param' child element.\nFound: " + params.length);
193                             }
194                         default:
195                             throw new mod.MalformedXmlRpc("'fault' or 'params' element expected.\nFound: '" + child.tagName + "'");                        
196                     }
197                 }
198             }
199             //no child elements found
200             throw new mod.MalformedXmlRpc("No child elements found.");    
201         }catch(e){
202             if(e instanceof mod.Fault){
203                 throw e;
204             }else{
205                 throw new mod.MalformedXmlRpc("'methodResponse' element could not be parsed.",null,e);    
206             }
207         }
208     }
209     /**
210         Parses a methodCall element.
211         @param node  The methodCall element.
212         @return          Array [methodName,params]. 
213     */        
214     var parseMethodCall = function(node){
215         try{
216             var methodName = null;
217             var params = new Array();//default is no parameters
218             for(var i=0;i<node.childNodes.length;i++){
219                 var child = node.childNodes.item(i);
220                 if (child.nodeType == 1){
221                     switch (child.tagName){
222                         case "methodName":
223                             methodName = new String(child.firstChild.nodeValue);
224                             break;
225                         case "params":
226                             params = parseParams(child);
227                             break;
228                         default:
229                             throw new mod.MalformedXmlRpc("'methodName' or 'params' element expected.\nFound: '" + child.tagName + "'");                        
230                     }
231                 }
232             }
233             if(methodName==null){
234                 throw new mod.MalformedXmlRpc("'methodName' element expected.");
235             }else{
236                 return new Array(methodName, params);
237             }
238         }catch(e){
239             throw new mod.MalformedXmlRpc("'methodCall' element could not be parsed.",null,e);    
240         }
241     }
242     /**
243         Parses a params element.
244         @param node  The params element.
245         @return          Array of params values. 
246     */
247     var parseParams = function(node){
248         try{
249             var params=new Array();
250             for(var i=0;i<node.childNodes.length;i++){
251                 var child = node.childNodes.item(i);
252                 if (child.nodeType == 1){
253                     switch (child.tagName){
254                         case "param":
255                             params.push(parseParam(child));
256                             break;
257                         default:
258                             throw new mod.MalformedXmlRpc("'param' element expected.\nFound: '" + child.tagName + "'");                        
259                     }
260                 }
261             }
262             //the specs say a 'params' element can contain any number of 'param' elements. That includes 0 ?!
263             return params;
264         }catch(e){
265             throw new mod.MalformedXmlRpc("'params' element could not be parsed.",null,e);    
266         }
267     }
268     /**
269         Parses a param element.
270         @param node  The param node.
271         @return          The value of the param.
272     */
273     var parseParam = function(node){
274         try{
275             for(var i=0;i<node.childNodes.length;i++){
276                 var child = node.childNodes.item(i);
277                 if (child.nodeType == 1){
278                     switch (child.tagName){
279                         case "value":
280                             return parseValue(child);
281                         default:
282                             throw new mod.MalformedXmlRpc("'value' element expected.\nFound: '" + child.tagName + "'");                        
283                     }
284                 }
285             }
286             //no child elements found, that's an error
287             throw new mod.MalformedXmlRpc("'value' element expected.But none found.");
288         }catch(e){
289             throw new mod.MalformedXmlRpc("'param' element could not be parsed.",null,e);    
290         }
291     }
292     /**
293         Parses a value element.
294         @param node  The value element.
295         @return         The value.
296     */
297     var parseValue = function(node){
298         try{
299             for(var i=0;i<node.childNodes.length;i++){
300                 var child = node.childNodes.item(i);
301                 if (child.nodeType == 1){
302                     switch (child.tagName){
303                         case "string":
304                             var s="" 
305                             //Mozilla has many textnodes with a size of 4096 chars each instead of one large one.
306                             //They all need to be concatenated.
307                             for(var j=0;j<child.childNodes.length;j++){
308                                 s+=new String(child.childNodes.item(j).nodeValue);
309                             }
310                             return s;
311                         case "int":
312                         case "i4":
313                         case "double":
314                             return (child.firstChild) ? new Number(child.firstChild.nodeValue) : 0;
315                         case "boolean":
316                             return Boolean(isNaN(parseInt(child.firstChild.nodeValue)) ? (child.firstChild.nodeValue == "true") : parseInt(child.firstChild.nodeValue));
317                         case "base64":
318                             return parseBase64(child);
319                         case "dateTime.iso8601":
320                             return parseDateTime(child);
321                         case "array":
322                             return parseArray(child);
323                         case "struct":
324                             return parseStruct(child);
325                         case "nil": //for python None todo: ??? is this valid XML-RPC
326                             return null;
327                         default:
328                             throw new mod.MalformedXmlRpc("'string','int','i4','double','boolean','base64','dateTime.iso8601','array' or 'struct' element expected.\nFound: '" + child.tagName + "'");                        
329                     }
330                 }
331             }
332             if(node.firstChild){
333                 var s="" 
334                 //Mozilla has many textnodes with a size of 4096 chars each instead of one large one.
335                 //They all need to be concatenated.
336                 for(var j=0;j<node.childNodes.length;j++){
337                     s+=new String(node.childNodes.item(j).nodeValue);
338                 }
339                 return s;
340             }else{
341                 return "";
342             }
343         }catch(e){
344             throw new mod.MalformedXmlRpc("'value' element could not be parsed.",null,e);    
345         }
346     }
347     /**
348         Parses a base64 element.
349         @param node   The base64 element.
350         @return          A string with the decoded base64.
351     */
352     var parseBase64=function(node){
353         try{
354             var s = node.firstChild.nodeValue;
355             return s.decode("base64");
356         }catch(e){
357             throw new mod.MalformedXmlRpc("'base64' element could not be parsed.",null,e);    
358         }
359     }
360     /**
361         Parses a dateTime.iso8601 element.
362         @param node   The dateTime.iso8601 element.
363         @return           A JavaScript date.
364     */
365     var parseDateTime=function(node){
366         try{
367             if(/^(\d{4})-?(\d{2})-?(\d{2})T(\d{2}):?(\d{2}):?(\d{2})/.test(node.firstChild.nodeValue)){
368                 return new Date(Date.UTC(RegExp.$1, RegExp.$2-1, RegExp.$3, RegExp.$4, RegExp.$5, RegExp.$6));
369             }else{ //todo error message
370                 throw new mod.MalformedXmlRpc("Could not convert the given date.");
371             }
372         }catch(e){
373             throw new mod.MalformedXmlRpc("'dateTime.iso8601' element could not be parsed.",null,e);    
374         }
375     }
376     /**
377         Parses an array element.
378         @param node   The array element.
379         @return           An Array.
380     */
381     var parseArray=function(node){
382         try{
383             for(var i=0;i<node.childNodes.length;i++){
384                 var child = node.childNodes.item(i);
385                 if (child.nodeType == 1){
386                     switch (child.tagName){
387                         case "data":
388                             return parseData(child);
389                         default:
390                             throw new mod.MalformedXmlRpc("'data' element expected.\nFound: '" + child.tagName + "'");                        
391                     }
392                 }
393             }
394             throw new mod.MalformedXmlRpc("'data' element expected. But not found.");   
395         }catch(e){
396             throw new mod.MalformedXmlRpc("'array' element could not be parsed.",null,e);    
397         }
398     }
399     /**
400         Parses a data element.
401         @param node   The data element.
402         @return           The value of a data element.
403     */
404     var parseData=function(node){
405         try{
406             var rslt = new Array();
407             for(var i=0;i<node.childNodes.length;i++){
408                 var child = node.childNodes.item(i);
409                 if (child.nodeType == 1){
410                     switch (child.tagName){
411                         case "value":
412                             rslt.push(parseValue(child));
413                             break;
414                         default:
415                             throw new mod.MalformedXmlRpc("'value' element expected.\nFound: '" + child.tagName + "'");                        
416                     }
417                 }
418             }
419             return rslt;
420         }catch(e){
421             throw new mod.MalformedXmlRpc("'data' element could not be parsed.",null,e);    
422         }
423     }
424     /**
425         Parses a struct element.
426         @param node   The struct element.
427         @return           A JavaScript object. Struct memembers are properties of the object.
428     */
429     var parseStruct=function(node){
430         try{
431             var struct = new Object();
432             for(var i=0;i<node.childNodes.length;i++){
433                 var child = node.childNodes.item(i);
434                 if (child.nodeType == 1){
435                     switch (child.tagName){
436                         case "member":
437                             var member = parseMember(child); //returns [name, value]
438                             if(member[0] != ""){
439                                 struct[member[0]] = member[1];
440                             }
441                             break;
442                         default:
443                             throw new mod.MalformedXmlRpc("'data' element expected.\nFound: '" + child.tagName + "'");                        
444                     }
445                 }
446             }
447             return struct;
448         }catch(e){
449             throw new mod.MalformedXmlRpc("'struct' element could not be parsed.",null,e);    
450         }
451     }
452     /**
453         Parses a member element.
454         @param node  The member element.
455         @return          Array containing [memberName, value].
456     */
457     var parseMember=function(node){
458         try{
459             var name="";
460             var value=null;
461             for(var i=0;i<node.childNodes.length;i++){
462                 var child = node.childNodes.item(i);
463                 if (child.nodeType == 1){
464                     switch (child.tagName){
465                         case "value":
466                             value = parseValue(child); 
467                             break;
468                         case "name":
469                             if(child.hasChildNodes()){
470                                 name = new String(child.firstChild.nodeValue);
471                             }
472                             break;
473                         default:
474                             throw new mod.MalformedXmlRpc("'value' or 'name' element expected.\nFound: '" + child.tagName + "'");                        
475                     }
476                 }
477             }
478             /*if(name == ""){
479                 throw new mod.MalformedXmlRpc("Name for member not found/convertable.");
480             }else{
481                 return new Array(name, value);
482             }*/
483             return [name, value];
484         }catch(e){
485             throw new mod.MalformedXmlRpc("'member' element could not be parsed.",null,e);    
486         }
487     }
488     /**
489         Parses a fault element.
490         @param node  The fault element.
491         @return          A Fault Exception object.
492     */
493     var parseFault = function(node){
494         try{
495             for(var i=0;i<node.childNodes.length;i++){
496                 var child = node.childNodes.item(i);
497                 if (child.nodeType == 1){
498                     switch (child.tagName){
499                         case "value":
500                             var flt = parseValue(child); 
501                             return new mod.Fault(flt.faultCode, flt.faultString);
502                         default:
503                             throw new mod.MalformedXmlRpc("'value' element expected.\nFound: '" + child.tagName + "'");                        
504                     }
505                 }
506             }
507             throw new mod.MalformedXmlRpc("'value' element expected. But not found.");                        
508         }catch(e){
509             throw new mod.MalformedXmlRpc("'fault' element could not be parsed.",null,e);    
510         }
511     }
512
513     /**
514         Class for creating XML-RPC methods.
515         Calling the created method will result in an XML-RPC call to the service.
516         The return value of this call will be the return value of the RPC call.
517         RPC-Faults will be raised as Exceptions.
518         
519         Asynchronous operation:
520         If the last parameter passed to the method is an XMLRPCAsyncCallback object, 
521         then the remote method will be called asynchronously. 
522         The results and errors are passed to the callback.
523     */
524     mod.XMLRPCMethod =Class("XMLRPCMethod", function(publ){
525         
526         var postData = function(url, user, pass, data, callback){
527             if(callback == null){
528                 var rslt = urllib.postURL(url, user, pass, data, [["Content-Type", "text/xml"]]);
529                 return rslt;
530             }else{
531                 urllib.postURL(url, user, pass, data, [["Content-Type", "text/xml"]], callback);
532             }
533         }
534         
535         var handleResponse=function(resp){
536             var status=null;
537             try{//see if the server responded with a response code 200 OK.
538                 status = resp.status;
539             }catch(e){
540             }
541             if(status == 200){
542                 var respDoc=null;
543                 try{
544                     respDoc = resp.responseXML;
545                 }catch(e){
546                 }
547                 var respTxt = ""; 
548                 try{                 
549                     respTxt=resp.responseText;
550                 }catch(e){
551                 }
552                 if(respDoc == null){
553                     if(respTxt == null || respTxt == ""){
554                         throw new mod.MalformedXmlRpc("The server responded with an empty document.", "");
555                     }else{
556                         return mod.unmarshall(respTxt);
557                     }
558                 }else{ //use the respDoc directly so the xml does not have to be parsed.
559                     return mod.unmarshallDoc(respDoc, respTxt);
560                 }
561             }else{
562                 throw new mod.InvalidServerResponse(status);
563             }
564         }
565         
566         var getXML = function(methodName, args){
567             var data='<?xml version="1.0"?><methodCall><methodName>' + methodName + '</methodName>';
568             if (args.length>0){
569                 data += "<params>";
570                 for(var i=0;i<args.length;i++){
571                     data += '<param><value>' + mod.marshall(args[i]) + '</value></param>';
572                 }
573                 data += '</params>';
574             }
575             data += '</methodCall>';
576             return data;
577         }
578         /**
579             Initializes the XML-RPC method.
580             @param url                 The URL of the service providing the method.
581             @param methodName   The name of the method to invoke.
582             @param user=null             The user name to use for HTTP authentication.
583             @param pass=null             The password to use for HTTP authentication.
584         */
585         publ.init = function(url, methodName, user, pass){
586             
587             //this is pretty much a hack.
588             //we create a function which mimics this class and return it instead of really instanciating an object. 
589             var fn=function(){
590                 //sync or async call
591                 if(typeof arguments[arguments.length-1] != "function"){
592                     var data=getXML(fn.methodName,arguments);
593                     var resp = postData(fn.url, fn.user, fn.password, data);
594                     
595                     return handleResponse(resp);
596                 }else{
597                     var args=new Array();
598                     for(var i=0;i<arguments.length;i++){
599                         args.push(arguments[i]);
600                     }
601                     var cb = args.pop();
602                     var data=getXML(fn.methodName, args);
603                     postData(fn.url, fn.user, fn.password, data, function(resp){
604                         var rslt = null;
605                         var exc =null;
606                         try{
607                             rslt = handleResponse(resp);
608                         }catch(e){
609                             exc = e;
610                         }
611                         try{//call the callback for the async call.
612                             cb(rslt,exc);
613                         }catch(e){
614                         }
615                         args = null;
616                         resp = null;
617                     });
618                 }
619             }
620             //make sure the function has the same property as an object created from this class.
621             fn.methodName = methodName;
622             fn.url = url;
623             fn.user = user;
624             fn.password=pass;
625             fn.toMulticall = this.toMulticall;
626             fn.toString = this.toString;
627             fn.setAuthentication=this.setAuthentication;
628             fn.constructor = this.constructor;
629             return fn;
630         }
631                 
632         /**
633             Returns the method representation for system.multicall.
634             @param   All params will be passed to the remote method.
635             @return   An object containing a member methodName and a member params(As required by system.multicall).
636         */
637         publ.toMulticall = function(){
638             var multiCallable = new Object();
639             multiCallable.methodName = this.methodName;
640             var params = [];
641             for(var i=0;i<arguments.length;i++){
642                 params[i] = arguments[i];
643             }
644             multiCallable.params = params;
645             return multiCallable;
646         }
647         /**
648             Sets username and password for HTTP Authentication.
649             @param user    The user name.
650             @param pass    The password.
651         */
652         publ.setAuthentication = function(user, pass){
653             this.user = user;
654             this.password = pass;
655         }
656         ///The name of the remote method.
657         publ.methodName;
658         ///The url of the remote service containing the method.
659         publ.url;
660         ///The user name used for HTTP authorization.
661         publ.user;
662         ///The password used for HTTP authorization.
663         publ.password;
664     })
665     
666     /**
667         Creates proxy objects which resemble the remote service.
668         Method calls of this proxy will result in calls to the service.
669     */
670     mod.ServiceProxy=Class("ServiceProxy", function(publ){
671         /**
672             Initializes a new ServerProxy.
673             The arguments are interpreted as shown in the examples:
674             ServerProxy("url")
675             ServerProxy("url", ["methodName1",...])
676             ServerProxy("url", ["methodName1",...], "user", "pass")
677             ServerProxy("url", "user", "pass")
678             
679             @param url                     The url of the service.
680             @param methodNames=[]  Array of names of methods that can be called on the server.
681                                                 If no methods are given then introspection is used to get the methodnames from the server.
682             @param user=null             The user name to use for HTTP authentication.
683             @param pass=null             The password to use for HTTP authentication.
684         */
685         publ.init = function(url, methodNames, user, pass){
686             if(methodNames instanceof Array){
687                 if(methodNames.length > 0){
688                     var tryIntrospection=false;
689                 }else{
690                     var tryIntrospection=true;
691                 }
692             }else{
693                 pass=user;
694                 user=methodNames;
695                 methodNames=[];
696                 var tryIntrospection=true;
697             }
698             this._url = url;
699             this._user = user;
700             this._password = pass;
701             this._addMethodNames(methodNames);
702             if(tryIntrospection){
703                 try{//it's ok if it fails.
704                     this._introspect();
705                 }catch(e){
706                 }
707             }
708         }
709         
710         /**
711             Adds new XMLRPCMethods to the proxy server which can then be invoked.
712             @param methodNames   Array of names of methods that can be called on the server.
713         */
714         publ._addMethodNames = function(methodNames){
715             for(var i=0;i<methodNames.length;i++){
716                 var obj = this;
717                 //setup obj.childobj...method
718                 var names = methodNames[i].split(".");
719                 for(var n=0;n<names.length-1;n++){
720                     var name = names[n];
721                     if(obj[name]){
722                         obj = obj[name];
723                     }else{
724                         obj[name]  = new Object();
725                         obj = obj[name];
726                     }
727                 }
728                 var name = names[names.length-1];
729                 if(obj[name]){
730                 }else{
731                     var mth = new mod.XMLRPCMethod(this._url, methodNames[i], this._user, this._password);
732                     obj[name] = mth;
733                     this._methods.push(mth);
734                 }
735             }
736         }
737         
738         /**
739             Sets username and password for HTTP Authentication for all methods of this service.
740             @param user    The user name.
741             @param pass    The password.
742         */
743         publ._setAuthentication = function(user, pass){
744             this._user = user;
745             this._password = pass;
746             for(var i=0;i<this._methods.length;i++){
747                 this._methods[i].setAuthentication(user, pass);
748             }
749         }
750         
751         /**
752             Initiate XML-RPC introspection to retrieve methodnames from the server
753             and add them to the server proxy.
754         */
755         publ._introspect = function(){
756             this._addMethodNames(["system.listMethods","system.methodHelp", "system.methodSignature"]);
757             var m = this.system.listMethods();
758             this._addMethodNames(m);
759         }
760         ///The url of the service to resemble.
761         publ._url;
762         ///The user used for HTTP authentication.
763         publ._user;
764         ///The password used for HTTP authentication.
765         publ._password;
766         ///All methods.
767         publ._methods=new Array();
768     })
769     
770     ///@deprecated  Use ServiceProxy instead.
771     mod.ServerProxy= mod.ServiceProxy;
772     
773     /**
774         XML-RPC representation of a string.
775         All '&' and '<' are replaced with the '&amp;'  and  '&lt'.
776         @return  A string containing the String's representation in XML.
777     */
778     String.prototype.toXmlRpc = function(){
779         return "<string>" + this.replace(/&/g, "&amp;").replace(/</g, "&lt;") + "</string>";
780     }
781     /**
782         XML-RPC representation of a number.
783         @return A string containing the Number's representation in XML.
784     */
785     Number.prototype.toXmlRpc = function(){
786         if(this == parseInt(this)){
787             return "<int>" + this + "</int>";
788         }else if(this == parseFloat(this)){
789             return "<double>" + this + "</double>";
790         }else{
791             return false.toXmlRpc();
792         }
793     }
794     /**
795         XML-RPC representation of a boolean.
796         @return A string containing the Boolean's representation in XML.
797     */
798     Boolean.prototype.toXmlRpc = function(){
799         if(this == true) {
800             return "<boolean>1</boolean>";
801         }else{
802             return "<boolean>0</boolean>";
803         }
804     }
805     /**
806         XML-RPC representation of a date(iso 8601).
807         @return A string containing the Date's representation in XML.
808     */
809     Date.prototype.toXmlRpc = function(){
810         var padd=function(s, p){
811             s=p+s
812             return s.substring(s.length - p.length)
813         }
814         var y = padd(this.getUTCFullYear(), "0000");
815         var m = padd(this.getUTCMonth() + 1, "00");
816         var d = padd(this.getUTCDate(), "00");
817         var h = padd(this.getUTCHours(), "00");
818         var min = padd(this.getUTCMinutes(), "00");
819         var s = padd(this.getUTCSeconds(), "00");
820         
821         var isodate = y +  m  + d + "T" + h +  ":" + min + ":" + s
822     
823         return "<dateTime.iso8601>" + isodate + "</dateTime.iso8601>";
824     }
825     /**
826         XML-RPC representation of an array.
827         Each entry in the array is a value in the XML-RPC.
828         @return A string containing the Array's representation in XML.
829     */
830     Array.prototype.toXmlRpc = function(){
831         var retstr = "<array><data>";
832         for(var i=0;i<this.length;i++){
833             retstr += "<value>" + mod.marshall(this[i]) + "</value>";
834         }
835         return retstr + "</data></array>";
836     }
837
838
839     mod.test = function(){
840         print("creating ServiceProxy object using introspection for method construction...\n");
841         var s = new mod.ServiceProxy("http://localhost/testx.py");
842         print("%s created\n".format(s));
843         print("creating and marshalling test data:\n");
844         var o = [1.234, 5, {a:"Hello & < ", b:new Date()}];
845         print(mod.marshall(o));
846         print("\ncalling echo() on remote service...\n");
847         var r = s.echo(o);
848         print("service returned data(marshalled again):\n")
849         print(mod.marshall(r));
850     }
851 })
852
853
854