xmlrpc.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. // Fix an IE problem (another one)
  2. var HAS_ACTIVEX = false;
  3. try {
  4. new ActiveXObject('MSXML2.DOMDocument');
  5. HAS_ACTIVEX = true;
  6. } catch(e) {}
  7. /**
  8. * XML-RPC communication service.
  9. */
  10. angular.module('xml-rpc', [])
  11. .factory('js2xml', ['helperXmlRpc', function(helperXmlRpc){
  12. /**
  13. * Convert Null to XmlRpc valid value (as xml element)
  14. */
  15. function null2xml_(doc, input) {
  16. return helperXmlRpc.createNode(doc, 'nil');
  17. };
  18. var js2xmlMethod_ = {};
  19. /**
  20. * Convert a string to a valid xmlrpc value (as xml element).
  21. */
  22. function string2xml_(doc, input) {
  23. return helperXmlRpc.createNode(doc, 'string', input);
  24. };
  25. js2xmlMethod_['string'] = string2xml_;
  26. /**
  27. * Convert a number to a valid xmlrpc value (as xml element).
  28. */
  29. function number2xml_(doc, input) {
  30. var type = 'int',
  31. value = parseInt(input),
  32. f = parseFloat(input);
  33. if (value != f) {
  34. type = 'double';
  35. value = f;
  36. }
  37. return helperXmlRpc.createNode(doc, type, value.toString());
  38. };
  39. js2xmlMethod_['number'] = number2xml_;
  40. /**
  41. * Convert a boolean to a valid xmlrpc value (as xml element).
  42. */
  43. function boolean2xml_(doc, input) {
  44. return helperXmlRpc.createNode(doc, 'boolean', (input ? '1' : '0'));
  45. };
  46. js2xmlMethod_['boolean'] = boolean2xml_;
  47. /**
  48. * Convert an Array object to a valid xmlrpc value (as xml element).
  49. */
  50. function array2xml_(doc, input) {
  51. var elements = [];
  52. for (var i=0; i < input.length; i++) {
  53. elements.push(js2xml_(doc, input[i]));
  54. }
  55. return helperXmlRpc.createNode(doc, 'array',
  56. helperXmlRpc.createNode(doc, 'data', elements)
  57. );
  58. };
  59. /**
  60. * Convert an object to a valid xmlrpc value (as xml element).
  61. */
  62. function struct2xml_(doc, input) {
  63. var elements = [];
  64. for (var name in input) {
  65. elements.push(helperXmlRpc.createNode(doc, 'member',
  66. helperXmlRpc.createNode(doc, 'name', name),
  67. js2xml_(doc, input[name])
  68. ));
  69. }
  70. return helperXmlRpc.createNode(doc, 'struct', elements);
  71. };
  72. /**
  73. * Convert a DateTime object to a valid xmlrpc value (as xml element).
  74. */
  75. function date2xml_(doc, input) {
  76. var str = [
  77. input.getFullYear(),
  78. (input.getMonth() + 1 < 10)? '0' + (input.getMonth() + 1):input.getMonth() + 1,
  79. (input.getDate() < 10)? '0' + (input.getDate()):input.getDate(),
  80. 'T',
  81. (input.getHours() < 10)? '0' + (input.getHours()):input.getHours(), ':',
  82. (input.getMinutes() < 10)? '0' + (input.getMinutes()):input.getMinutes(), ':',
  83. (input.getSeconds() < 10)? '0' + (input.getSeconds()):input.getSeconds(),
  84. ];
  85. return helperXmlRpc.createNode(doc, 'dateTime.iso8601', str.join(''));
  86. };
  87. /**
  88. * Convert an object to a valid xmlrpc value (as xml element).
  89. */
  90. function object2xml_(doc, input) {
  91. if (input instanceof Date) {
  92. return date2xml_(doc, input);
  93. }
  94. //else
  95. if (input instanceof Array)
  96. return array2xml_(doc, input);
  97. //else
  98. return struct2xml_(doc, input);
  99. };
  100. js2xmlMethod_['object'] = object2xml_;
  101. /**
  102. * Converts a javascript object to a valid xmlrpc value (as xml element).
  103. */
  104. function js2xml_(doc, input) {
  105. var type = typeof(input);
  106. var method = js2xmlMethod_[type];
  107. if (input === null) {
  108. method = null2xml_;
  109. } else if (method == undefined) {
  110. method = string2xml_;
  111. }
  112. return helperXmlRpc.createNode(doc, 'value', method(doc, input));
  113. };
  114. return {
  115. js2xml:js2xml_
  116. };
  117. }])
  118. .factory('xml2js', ['helperXmlRpc', function(helperXmlRpc){
  119. var isTrue_ = {
  120. '1': true,
  121. 'true': true
  122. };
  123. var xml2jsMethod_ = {};
  124. /**
  125. * Convert an xmlrpc string value (as an xml tree) to a javascript string.
  126. */
  127. function xml2null_(input) {
  128. return null;
  129. };
  130. xml2jsMethod_['nil'] = xml2null_;
  131. /**
  132. * Convert an xmlrpc string value (as an xml tree) to a javascript string.
  133. *
  134. * @param {!Element} input Xmlrpc string to convert.
  135. * @return {string} Javascript conversion of input.
  136. * @protected
  137. */
  138. function xml2string_(input) {
  139. var buf = [];
  140. helperXmlRpc.getTextContent(input, buf, false);
  141. return buf.join('');
  142. };
  143. xml2jsMethod_['string'] = xml2string_;
  144. /**
  145. * Convert an xmlrpc number (int or double) value to a javascript number.
  146. */
  147. function xml2number_(input) {
  148. return parseFloat(helperXmlRpc.getTextContent(input, []));
  149. };
  150. xml2jsMethod_['int'] = xml2number_;
  151. xml2jsMethod_['i4'] = xml2number_;
  152. xml2jsMethod_['double'] = xml2number_;
  153. /**
  154. * Convert an xmlrpc boolean value to a javascript boolean.
  155. */
  156. function xml2boolean_(input) {
  157. var value = helperXmlRpc.getTextContent(input, []).toLowerCase();
  158. return isTrue_[value] || false;
  159. };
  160. xml2jsMethod_['boolean'] = xml2boolean_;
  161. /**
  162. * Convert an xmlrpc struct value to a javascript object.
  163. */
  164. function xml2struct_(input) {
  165. var memberNodes = helperXmlRpc.selectNodes(input, 'member') || [];
  166. var obj = {};
  167. for (var i=0; i < memberNodes.length; i++) {
  168. var node = helperXmlRpc.selectSingleNode(memberNodes[i], 'name');
  169. // If no name found, member is ignored
  170. if (node) {
  171. var label = helperXmlRpc.getTextContent(node, []);
  172. node = helperXmlRpc.selectSingleNode(memberNodes[i], 'value');
  173. obj[label] = xml2js_(node);
  174. }
  175. }
  176. return obj;
  177. };
  178. xml2jsMethod_['struct'] = xml2struct_;
  179. /**
  180. * Convert an xmlrpc array value to a javascript array.
  181. */
  182. function xml2array_(input) {
  183. var valueNodes = helperXmlRpc.selectNodes(input, 'data/value');
  184. if (!valueNodes.length) {
  185. valueNodes = helperXmlRpc.selectNodes(input, './value');
  186. }
  187. if (!valueNodes.length)
  188. return [];
  189. //else
  190. var map_ = (Array.prototype.map) ?
  191. function(arr, f, opt_obj) {
  192. return Array.prototype.map.call(arr, f, opt_obj);
  193. } :
  194. function(arr, f, opt_obj) {
  195. var l = arr.length; // must be fixed during loop... see docs
  196. var res = new Array(l);
  197. var arr2 = (typeof arr == 'string') ? arr.split('') : arr;
  198. for (var i = 0; i < l; i++) {
  199. if (i in arr2) {
  200. res[i] = f.call(opt_obj, arr2[i], i, arr);
  201. }
  202. }
  203. return res;
  204. };
  205. return map_(valueNodes, xml2js_);
  206. };
  207. xml2jsMethod_['array'] = xml2array_;
  208. /**
  209. * Convert an xmlrpc dateTime value to an itrust.date.DateTime.
  210. */
  211. function xml2datetime_(input) {
  212. var value = helperXmlRpc.getTextContent(input, []);
  213. if (!value) {
  214. return new Date();
  215. }
  216. if (value[value.length-1]=='T') {
  217. value = value.substring(0, value.length-1);
  218. }
  219. var parts = value.match(/\d+/g);
  220. if(value.indexOf('-') == -1){
  221. var toSplit = parts[0];
  222. parts[0] = toSplit.substring(0,4);
  223. parts.splice(1, 0, toSplit.substring(4,6));
  224. parts.splice(2, 0, toSplit.substring(6));
  225. }
  226. return new Date(parts[0], parts[1] - 1, parts[2], parts[3], parts[4], parts[5]);
  227. };
  228. xml2jsMethod_['datetime'] = xml2datetime_;
  229. xml2jsMethod_['datetime.iso8601'] = xml2datetime_;
  230. /**
  231. * Convert an xmlrpc value (as an xml tree) to a javascript object.
  232. */
  233. function xml2js_(input) {
  234. var elt = helperXmlRpc.selectSingleNode(input, './*');
  235. if (!elt)
  236. return null;
  237. //else
  238. var method = xml2jsMethod_[elt.nodeName.toLowerCase()];
  239. if (method == undefined) {
  240. method = xml2struct_;
  241. }
  242. return method(elt);
  243. };
  244. return {
  245. xml2js:xml2js_,
  246. };
  247. }])
  248. .factory('xmlrpc', ['$http', 'helperXmlRpc', 'js2xml', 'xml2js', function($http, helperXmlRpc, js2xml, xml2js){
  249. var configuration = {};
  250. /**
  251. * Serialize a XML document to string.
  252. */
  253. function serialize(xml){
  254. var text = xml.xml;
  255. if (text) {
  256. return text;
  257. }
  258. if (typeof XMLSerializer != 'undefined') {
  259. return new XMLSerializer().serializeToString(xml);
  260. }
  261. throw Error('Your browser does not support serializing XML documents');
  262. };
  263. /**
  264. * Creates a xmlrpc call of the given method with given params.
  265. */
  266. function createCall(method, params){
  267. var doc = helperXmlRpc.createDocument('methodCall');
  268. doc.firstChild.appendChild(
  269. helperXmlRpc.createNode(doc, 'methodName', method)
  270. );
  271. if (arguments.length > 2) {
  272. params = helperXmlRpc.cloneArray(arguments);
  273. params.shift();
  274. }
  275. if (params && params.length > 0) {
  276. var paramsNode = helperXmlRpc.createNode(doc, 'params');
  277. for (var i=0; i < params.length; i++) {
  278. paramsNode.appendChild(helperXmlRpc.createNode(doc, 'param',
  279. js2xml.js2xml(doc, params[i])
  280. ));
  281. }
  282. doc.firstChild.appendChild(paramsNode);
  283. }
  284. return (serialize(doc)).replace(/[\s\xa0]+$/, '');
  285. };
  286. // Use the promise system from angular.
  287. // This method return a promise with the response
  288. function callMethod(method, params){
  289. var xmlstr = createCall(method, params);
  290. var targetAddr = configuration.hostName + "" + configuration.pathName;
  291. return $http.post(targetAddr, xmlstr, {headers: {'Content-Type': 'text/xml'}})
  292. .then(function(responseFromServer) {
  293. var responseText = responseFromServer.data;
  294. var response = null;
  295. try {
  296. response = parseResponse(responseText);
  297. } catch (err) {
  298. response = err;
  299. }
  300. return response;
  301. }, function(responseFromServer){
  302. if(responseFromServer.status in configuration){
  303. if(typeof configuration[responseFromServer.status] == "function"){
  304. return configuration[responseFromServer.status].call();
  305. }
  306. }
  307. });
  308. };
  309. /**
  310. * Parse an xmlrpc response and return the js object.
  311. */
  312. function parseResponse(response){
  313. var doc = helperXmlRpc.loadXml(response);
  314. var rootNode = doc.firstChild;
  315. if (!rootNode)
  316. return undefined;
  317. //else
  318. var node = helperXmlRpc.selectSingleNode(rootNode, '//fault');
  319. var isFault = (node != undefined);
  320. node = helperXmlRpc.selectSingleNode(rootNode, '//value');
  321. var value = xml2js.xml2js(node);
  322. if (isFault) {
  323. throw value;
  324. }
  325. //else
  326. return value;
  327. };
  328. /**
  329. * Configure the service (Host name and service path).
  330. * Actually, 401, 404 and 500 server errors are originally defined, but any error code can be added
  331. */
  332. function config(conf) {
  333. angular.extend(configuration, {
  334. hostName:"",
  335. pathName:"/rpc2",
  336. 500:function(){},
  337. 401:function(){},
  338. 404:function(){}
  339. }, conf);
  340. };
  341. config();
  342. return {
  343. callMethod : callMethod,
  344. config : config
  345. };
  346. }])
  347. .factory('helperXmlRpc', function(){
  348. /**
  349. * Clones an array object
  350. */
  351. function cloneArray_(object){
  352. var length = object.length;
  353. if (length > 0) {
  354. var rv = new Array(length);
  355. for (var i = 0; i < length; i++) {
  356. rv[i] = object[i];
  357. }
  358. return rv;
  359. }
  360. return [];
  361. };
  362. /**
  363. * Creates a XML document for IEs browsers
  364. */
  365. function createMsXmlDocument_(){
  366. var doc = new ActiveXObject('MSXML2.DOMDocument');
  367. if (doc) {
  368. doc.resolveExternals = false;
  369. doc.validateOnParse = false;
  370. try {
  371. doc.setProperty('ProhibitDTD', true);
  372. doc.setProperty('MaxXMLSize', 2 * 1024);
  373. doc.setProperty('MaxElementDepth', 256);
  374. } catch (e) {
  375. // No-op.
  376. }
  377. }
  378. return doc;
  379. };
  380. /**
  381. * Creates a XML document
  382. */
  383. function createDocument(opt_rootTagName, opt_namespaceUri){
  384. if (opt_namespaceUri && !opt_rootTagName) {
  385. throw Error("Can't create document with namespace and no root tag");
  386. }
  387. if (HAS_ACTIVEX) {
  388. var doc = createMsXmlDocument_();
  389. if (doc) {
  390. if (opt_rootTagName) {
  391. doc.appendChild(doc.createNode(1,
  392. opt_rootTagName,
  393. opt_namespaceUri || ''));
  394. }
  395. return doc;
  396. }
  397. }
  398. else if (document.implementation && document.implementation.createDocument) {
  399. return document.implementation.createDocument(opt_namespaceUri || '',
  400. opt_rootTagName || '',
  401. null);
  402. }
  403. throw Error('Your browser does not support creating new documents');
  404. };
  405. /**
  406. * Creates a XML node and set the child(ren) node(s)
  407. */
  408. function createNode(doc, nodeName, children){
  409. var elt = doc.createElement(nodeName);
  410. var appendChild = function(child) {
  411. if(typeof child == 'object' && child.nodeType !== 1){
  412. for(var i in child){
  413. elt.appendChild(
  414. (typeof child == 'string') ? doc.createTextNode(child[i]) : child[i]
  415. );
  416. }
  417. } else {
  418. elt.appendChild(
  419. (typeof child == 'string') ? doc.createTextNode(child) : child
  420. );
  421. }
  422. }
  423. if (arguments.length > 3) {
  424. children = cloneArray_(arguments);
  425. children.shift(); //shift doc
  426. children.shift(); //shift nodeName
  427. }
  428. if (typeof children == 'array') {
  429. angular.forEach(children, appendChild);
  430. } else if (children) {
  431. appendChild(children);
  432. }
  433. return elt;
  434. };
  435. /**
  436. * Generate an ID for XMLRPC request
  437. */
  438. function generateId(){
  439. return 'xmlrpc-'+(new Date().getTime())+'-'+Math.floor(Math.random()*1000);
  440. };
  441. /**
  442. * Creates an XML document from a string
  443. */
  444. function loadXml_(xml) {
  445. if (HAS_ACTIVEX) {
  446. var doc = createMsXmlDocument_();
  447. doc.loadXML(xml);
  448. return doc;
  449. }
  450. else if (typeof DOMParser != 'undefined') {
  451. return new DOMParser().parseFromString(xml, 'application/xml');
  452. }
  453. throw Error('Your browser does not support loading xml documents');
  454. };
  455. /**
  456. * Returns the document in which the node is.
  457. */
  458. function getOwnerDocument_(node) {
  459. return (
  460. node.nodeType == 9 ? node :
  461. node.ownerDocument || node.document);
  462. };
  463. /**
  464. * Return a single node with the given name in the given node
  465. */
  466. function selectSingleNode_(node, path) {
  467. if (typeof node.selectSingleNode != 'undefined') {
  468. var doc = getOwnerDocument_(node);
  469. if (typeof doc.setProperty != 'undefined') {
  470. doc.setProperty('SelectionLanguage', 'XPath');
  471. }
  472. return node.selectSingleNode(path);
  473. } else if (document.implementation.hasFeature('XPath', '3.0')) {
  474. var doc = getOwnerDocument_(node);
  475. var resolver = doc.createNSResolver(doc.documentElement);
  476. var result = doc.evaluate(path, node, resolver,
  477. XPathResult.FIRST_ORDERED_NODE_TYPE, null);
  478. return result.singleNodeValue;
  479. }
  480. return null;
  481. };
  482. /**
  483. * Returns the string content of a node
  484. */
  485. function getTextContent_(node, buf, normalizedWhitespace){
  486. var PREDEFINED_TAG_VALUES_ = {'IMG': ' ', 'BR': '\n'};
  487. if (node.nodeName in ['SCRIPT', 'STYLE', 'HEAD', 'IFRAME', 'OBJECT']) {
  488. // ignore certain tags
  489. } else if (node.nodeType == 3) {
  490. if (normalizedWhitespace) {
  491. buf.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));
  492. } else {
  493. buf.push(node.nodeValue);
  494. }
  495. } else if (node.nodeName in PREDEFINED_TAG_VALUES_) {
  496. buf.push(PREDEFINED_TAG_VALUES_[node.nodeName]);
  497. } else {
  498. var child = node.firstChild;
  499. while (child) {
  500. getTextContent_(child, buf, normalizedWhitespace);
  501. child = child.nextSibling;
  502. }
  503. }
  504. return buf.join('');
  505. };
  506. /**
  507. * Returns all the nodes in a array that are inside the given node with the given path
  508. */
  509. function selectNodes_(node, path) {
  510. if (typeof node.selectNodes != 'undefined') {
  511. var doc = getOwnerDocument_(node);
  512. if (typeof doc.setProperty != 'undefined') {
  513. doc.setProperty('SelectionLanguage', 'XPath');
  514. }
  515. return node.selectNodes(path);
  516. } else if (document.implementation.hasFeature('XPath', '3.0')) {
  517. var doc = getOwnerDocument_(node);
  518. var resolver = doc.createNSResolver(doc.documentElement);
  519. var nodes = doc.evaluate(path, node, resolver,
  520. XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  521. var results = [];
  522. var count = nodes.snapshotLength;
  523. for (var i = 0; i < count; i++) {
  524. results.push(nodes.snapshotItem(i));
  525. }
  526. return results;
  527. } else {
  528. return [];
  529. }
  530. };
  531. return {
  532. cloneArray:cloneArray_,
  533. createDocument: createDocument,
  534. createNode: createNode,
  535. generateId: generateId,
  536. loadXml: loadXml_,
  537. getOwnerDocument:getOwnerDocument_,
  538. selectNodes: selectNodes_,
  539. getTextContent : getTextContent_,
  540. selectSingleNode: selectSingleNode_
  541. };
  542. });