PageRenderTime 30ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/js/rest/client.js

https://gitlab.com/alexprowars/bitrix
JavaScript | 754 lines | 650 code | 88 blank | 16 comment | 138 complexity | ab362cc46fb028d7adaa3b6d9ed49217 MD5 | raw file
  1. 'use strict';
  2. ;(function(){
  3. /****************** ATTENTION *******************************
  4. * Please do not use Bitrix CoreJS in this class.
  5. * This class can be called on page without Bitrix Framework
  6. *************************************************************/
  7. if (!window.BX)
  8. {
  9. window.BX = {};
  10. }
  11. else if (window.BX.RestClient)
  12. {
  13. return;
  14. }
  15. var BX = window.BX;
  16. BX.RestClient = function (options)
  17. {
  18. options = options || {};
  19. this.endpoint = options.endpoint || '/rest';
  20. this.queryParams = options.queryParams || '';
  21. };
  22. BX.RestClient.prototype.callMethod = function(method, params, callback, sendCallback, logTag)
  23. {
  24. return ajax({
  25. method: method,
  26. data: params,
  27. callback: callback,
  28. sendCallback: sendCallback,
  29. logTag: logTag,
  30. endpoint: this.endpoint,
  31. queryParams: this.queryParams
  32. });
  33. };
  34. /*
  35. calls = [[method,params],[method,params]];
  36. calls = [{method:method,params:params},[method,params]];
  37. calls = {call_id:[method,params],...};
  38. */
  39. BX.RestClient.prototype.callBatch = function(calls, callback, bHaltOnError, sendCallback, logTag)
  40. {
  41. var cmd = Utils.isArray(calls) ? [] : {};
  42. var cnt = 0;
  43. var cb = function(cmd) {
  44. ajax.batch(cmd, callback, bHaltOnError, sendCallback, this.endpoint, this.queryParams, logTag);
  45. }.bind(this);
  46. for(var i in calls)
  47. {
  48. var method = null, params = null;
  49. if(!!calls[i] && calls.hasOwnProperty(i))
  50. {
  51. if(Utils.isArray(calls[i]))
  52. {
  53. method = calls[i][0];
  54. params = calls[i][1];
  55. }
  56. else if(!!calls[i].method)
  57. {
  58. method = calls[i].method;
  59. params = calls[i].params;
  60. }
  61. if(!!method)
  62. {
  63. cnt++;
  64. cmd[i] = [method, params];
  65. }
  66. }
  67. }
  68. if(cnt > 0)
  69. {
  70. var e = function(i)
  71. {
  72. return function(str)
  73. {
  74. cmd[i] = cmd[i][0] + '?' + str;
  75. if(--cnt <= 0)
  76. cb(cmd);
  77. }
  78. };
  79. for(var c in cmd)
  80. {
  81. if(cmd.hasOwnProperty(c))
  82. {
  83. ajax.prepareData(cmd[c][1], '', e(c));
  84. }
  85. }
  86. }
  87. };
  88. BX.RestClient.prototype.setEndpoint = function(url)
  89. {
  90. this.endpoint = url;
  91. };
  92. BX.RestClient.prototype.setQueryParams = function(params)
  93. {
  94. this.queryParams = params;
  95. };
  96. /* self init for bitrix env */
  97. if (typeof BX.namespace !== 'undefined')
  98. {
  99. var BXRest = new BX.RestClient();
  100. if (typeof BX.rest == 'undefined')
  101. {
  102. BX.rest = {};
  103. }
  104. BX.rest.callMethod = function (method, params, callback, sendCallback, logTag)
  105. {
  106. return BXRest.callMethod(method, params, callback, sendCallback, logTag);
  107. };
  108. /*
  109. calls = [[method,params],[method,params]];
  110. calls = [{method:method,params:params},[method,params]];
  111. calls = {call_id:[method,params],...};
  112. */
  113. BX.rest.callBatch = function (calls, callback, bHaltOnError, sendCallback, logTag)
  114. {
  115. return BXRest.callBatch(calls, callback, bHaltOnError, sendCallback, logTag);
  116. };
  117. }
  118. var Utils = {
  119. isArray: function(item) {
  120. return item && Object.prototype.toString.call(item) == "[object Array]";
  121. },
  122. isFunction: function(item) {
  123. return item === null ? false : (typeof (item) == "function" || item instanceof Function);
  124. },
  125. isString: function(item) {
  126. return item === '' ? true : (item ? (typeof (item) == "string" || item instanceof String) : false);
  127. },
  128. isDomNode: function(item) {
  129. return item && typeof (item) == "object" && "nodeType" in item;
  130. },
  131. isDate: function(item) {
  132. return item && Object.prototype.toString.call(item) == "[object Date]";
  133. },
  134. buildQueryString: function(params)
  135. {
  136. var result = '';
  137. for (var key in params)
  138. {
  139. if (!params.hasOwnProperty(key))
  140. {
  141. continue;
  142. }
  143. var value = params[key];
  144. if(this.isArray(value))
  145. {
  146. value.forEach(function(valueElement, index)
  147. {
  148. result += encodeURIComponent(key + "[" + index + "]") + "=" + encodeURIComponent(valueElement) + "&";
  149. });
  150. }
  151. else
  152. {
  153. result += encodeURIComponent(key) + "=" + encodeURIComponent(value) + "&";
  154. }
  155. }
  156. if(result.length > 0)
  157. {
  158. result = result.substr(0, result.length - 1);
  159. }
  160. return result;
  161. },
  162. clone: function(obj, bCopyObj)
  163. {
  164. var _obj, i, l;
  165. if (bCopyObj !== false)
  166. bCopyObj = true;
  167. if (obj === null)
  168. return null;
  169. if (this.isDomNode(obj))
  170. {
  171. _obj = obj.cloneNode(bCopyObj);
  172. }
  173. else if (typeof obj == 'object')
  174. {
  175. if (this.isArray(obj))
  176. {
  177. _obj = [];
  178. for (i=0,l=obj.length;i<l;i++)
  179. {
  180. if (typeof obj[i] == "object" && bCopyObj)
  181. _obj[i] = this.clone(obj[i], bCopyObj);
  182. else
  183. _obj[i] = obj[i];
  184. }
  185. }
  186. else
  187. {
  188. _obj = {};
  189. if (obj.constructor)
  190. {
  191. if (this.isDate(obj))
  192. _obj = new Date(obj);
  193. else
  194. _obj = new obj.constructor();
  195. }
  196. for (i in obj)
  197. {
  198. if (typeof obj[i] == "object" && bCopyObj)
  199. _obj[i] = this.clone(obj[i], bCopyObj);
  200. else
  201. _obj[i] = obj[i];
  202. }
  203. }
  204. }
  205. else
  206. {
  207. _obj = obj;
  208. }
  209. return _obj;
  210. }
  211. };
  212. var ajax = function(config)
  213. {
  214. var hasCallback = !!config.callback && Utils.isFunction(config.callback);
  215. var promise = typeof BX.Promise === 'undefined' || hasCallback? null: new BX.Promise();
  216. var sendCallback = config.sendCallback || function() {};
  217. var withoutRestoringCsrf = config.withoutRestoringCsrf || false;
  218. var xhr = ajax.xhr();
  219. var url = config.endpoint + '/' + ajax.escape(config.method) + '.json'+(config.logTag? '?logTag='+config.logTag: '');
  220. xhr.open('POST', url);
  221. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  222. var bRequestCompleted = false;
  223. // IE fix
  224. xhr.onprogress = function(){};
  225. xhr.ontimeout = function(){};
  226. xhr.timeout = 0;
  227. xhr.onload = function()
  228. {
  229. if(bRequestCompleted)
  230. return;
  231. xhr.onload = function() {};
  232. var bSuccess = ajax.isSuccess(xhr);
  233. var status = xhr.status;
  234. if(bSuccess)
  235. {
  236. var data = xhr.responseText;
  237. if(data.length > 0)
  238. {
  239. try
  240. {
  241. data = JSON.parse(data);
  242. }
  243. catch(e)
  244. {
  245. bSuccess = false;
  246. }
  247. }
  248. if (status == 401)
  249. {
  250. if (data.sessid && !withoutRestoringCsrf)
  251. {
  252. BX.message({'bitrix_sessid': data.sessid});
  253. console.warn('BX.rest: your csrf-token has expired, send query with a new token');
  254. config.withoutRestoringCsrf = true;
  255. if (!hasCallback)
  256. {
  257. config.callback = function(result)
  258. {
  259. if (result.error())
  260. {
  261. promise.reject(result);
  262. }
  263. else
  264. {
  265. promise.fulfill(result);
  266. }
  267. }
  268. }
  269. ajax(config);
  270. return true;
  271. }
  272. }
  273. else if (status == 0)
  274. {
  275. data = {result: {}, error: "ERROR_NETWORK", error_description: "A network error occurred while the request was being executed."};
  276. }
  277. else
  278. {
  279. if (status == 200)
  280. {
  281. if (data.length <= 0)
  282. {
  283. data = {result: {}};
  284. }
  285. }
  286. else if (data.length <= 0)
  287. {
  288. data = {result: {}, error: "BLANK_ANSWER_WITH_ERROR_CODE", error_description: 'Blank answer with error http code: '+status};
  289. }
  290. }
  291. }
  292. xhr = null;
  293. if(bSuccess)
  294. {
  295. var res = new ajaxResult(data, config, status);
  296. if(hasCallback)
  297. {
  298. config.callback.apply(window, [res]);
  299. }
  300. else
  301. {
  302. if (res.error())
  303. {
  304. promise.reject(res);
  305. }
  306. else
  307. {
  308. promise.fulfill(res);
  309. }
  310. }
  311. }
  312. else
  313. {
  314. var res = new ajaxResult({
  315. error: "ERROR_UNEXPECTED_ANSWER",
  316. error_description: "Server returned an unexpected response.",
  317. ex: {}
  318. }, config, 0);
  319. if(hasCallback)
  320. {
  321. config.callback.apply(window, [res]);
  322. }
  323. else
  324. {
  325. promise.reject(res);
  326. }
  327. }
  328. };
  329. xhr.onerror = function(e)
  330. {
  331. var res = new ajaxResult({
  332. error: "ERROR_NETWORK",
  333. error_description: "A network error occurred while the request was being executed.",
  334. ex: e
  335. }, config, 0);
  336. if(hasCallback)
  337. {
  338. config.callback.apply(window, [res]);
  339. }
  340. else
  341. {
  342. promise.reject(res);
  343. }
  344. };
  345. var query_data = '';
  346. if (config.queryParams)
  347. {
  348. query_data = Utils.buildQueryString(config.queryParams);
  349. }
  350. else if (typeof BX.bitrix_sessid !== 'undefined')
  351. {
  352. query_data = 'sessid=' + BX.bitrix_sessid();
  353. }
  354. if(typeof config.start !== 'undefined')
  355. {
  356. query_data += '&start=' + parseInt(config.start);
  357. }
  358. if(!!config.data)
  359. {
  360. ajax.prepareData(config.data, '', function(res)
  361. {
  362. query_data += '&' + res;
  363. xhr.send(query_data);
  364. sendCallback(xhr);
  365. });
  366. }
  367. else
  368. {
  369. xhr.send(query_data);
  370. sendCallback(xhr);
  371. }
  372. return hasCallback || !promise? xhr: promise;
  373. };
  374. ajax.batch = function(calls, callback, bHaltOnError, sendCallback, endpoint, queryParams, logTag)
  375. {
  376. return ajax({
  377. method: 'batch',
  378. data: {halt: !!bHaltOnError ? 1 : 0, cmd: calls},
  379. callback: function(res, config, status)
  380. {
  381. if(!!callback)
  382. {
  383. var error = res.error();
  384. var data = res.data();
  385. var result = Utils.isArray(calls) ? [] : {};
  386. for(var i in calls)
  387. {
  388. if(!!calls[i] && calls.hasOwnProperty(i))
  389. {
  390. if(Utils.isString(calls[i]))
  391. {
  392. var q = calls[i].split('?');
  393. }
  394. else
  395. {
  396. q = [
  397. Utils.isArray(calls[i]) ? calls[i][0] : calls[i].method,
  398. Utils.isArray(calls[i]) ? calls[i][1] : calls[i].data
  399. ];
  400. }
  401. if(data && (typeof data.result[i] !== 'undefined' || typeof data.result_error[i] !== 'undefined'))
  402. {
  403. result[i] = new ajaxResult({
  404. result: typeof data.result[i] !== 'undefined' ? data.result[i] : {},
  405. error: data.result_error[i] || undefined,
  406. total: data.result_total[i],
  407. time: data.result_time[i],
  408. next: data.result_next[i]
  409. }, {
  410. method: q[0],
  411. data: q[1],
  412. callback: callback,
  413. endpoint: endpoint,
  414. queryParams: queryParams
  415. }, res.status);
  416. }
  417. else if (error)
  418. {
  419. result[i] = new ajaxResult({
  420. result: {},
  421. error: error.ex,
  422. total: 0
  423. }, {
  424. method: q[0],
  425. data: q[1],
  426. callback: callback,
  427. endpoint: endpoint,
  428. queryParams: queryParams
  429. }, res.status);
  430. }
  431. }
  432. }
  433. callback.apply(window, [result]);
  434. }
  435. },
  436. sendCallback: sendCallback,
  437. endpoint: endpoint,
  438. queryParams: queryParams,
  439. logTag: logTag
  440. });
  441. };
  442. ajax.xhr = function()
  443. {
  444. return new XMLHttpRequest();
  445. };
  446. ajax.escape = function(str)
  447. {
  448. return encodeURIComponent(str);
  449. };
  450. ajax.prepareData = function(arData, prefix, callback)
  451. {
  452. var data = '', objects = [];
  453. if(Utils.isString(arData) || arData === null)
  454. {
  455. callback.call(document, arData || '');
  456. }
  457. else
  458. {
  459. for(var i in arData)
  460. {
  461. if(!arData.hasOwnProperty(i))
  462. {
  463. continue;
  464. }
  465. var name = ajax.escape(i);
  466. if(prefix)
  467. name = prefix + '[' + name + ']';
  468. if(typeof arData[i] === 'object')
  469. {
  470. objects.push([name, arData[i]]);
  471. }
  472. else
  473. {
  474. if(data.length > 0)
  475. {
  476. data += '&';
  477. }
  478. if(typeof arData[i] === 'boolean')
  479. {
  480. data += name + '=' + (arData[i]? 1: 0);
  481. }
  482. else
  483. {
  484. data += name + '=' + ajax.escape(arData[i])
  485. }
  486. }
  487. }
  488. var cnt = objects.length;
  489. if(cnt > 0)
  490. {
  491. var cb = function(str)
  492. {
  493. data += (!!str ? '&' : '') + str;
  494. if(--cnt <= 0)
  495. {
  496. callback.call(document, data)
  497. }
  498. };
  499. var cnt1 = cnt;
  500. for(var i = 0; i < cnt1; i++)
  501. {
  502. if(Utils.isDomNode(objects[i][1]))
  503. {
  504. if(objects[i][1].tagName.toUpperCase() === 'INPUT' && objects[i][1].type === 'file')
  505. {
  506. if(fileReader.canUse())
  507. {
  508. fileReader(objects[i][1], (function(name)
  509. {
  510. return function(result)
  511. {
  512. if(Utils.isArray(result) && result.length > 0)
  513. {
  514. cb(name + '[0]=' + ajax.escape(result[0]) + '&' + name + '[1]=' + ajax.escape(result[1]));
  515. }
  516. else
  517. {
  518. cb(name + '=');
  519. }
  520. }
  521. })(objects[i][0]));
  522. }
  523. }
  524. else if(typeof objects[i][1].value !== 'undefined')
  525. {
  526. cb(objects[i][0] + '=' + ajax.escape(objects[i][1].value));
  527. }
  528. else
  529. {
  530. cb('');
  531. }
  532. }
  533. else if(Utils.isDate(objects[i][1]))
  534. {
  535. cb(objects[i][0] + '=' + ajax.escape(objects[i][1].toJSON()));
  536. }
  537. else if(Utils.isArray(objects[i][1]) && objects[i][1].length <= 0)
  538. {
  539. cb(objects[i][0] + '=');
  540. }
  541. else
  542. {
  543. ajax.prepareData(objects[i][1], objects[i][0], cb);
  544. }
  545. }
  546. }
  547. else
  548. {
  549. callback.call(document, data)
  550. }
  551. }
  552. };
  553. ajax.isSuccess = function(xhr)
  554. {
  555. return typeof xhr.status === 'undefined' || (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 || xhr.status >= 400 && xhr.status < 500 || xhr.status === 1223 || xhr.status === 0;
  556. };
  557. var ajaxResult = function(answer, query, status)
  558. {
  559. this.answer = answer;
  560. this.query = Utils.clone(query);
  561. this.status = status;
  562. if(typeof this.answer.next !== 'undefined')
  563. {
  564. this.answer.next = parseInt(this.answer.next);
  565. }
  566. if(typeof this.answer.error !== 'undefined')
  567. {
  568. this.answer.ex = new ajaxError(this.status, typeof this.answer.error === 'string' ? this.answer : this.answer.error)
  569. }
  570. };
  571. ajaxResult.prototype.data = function()
  572. {
  573. return this.answer.result;
  574. };
  575. ajaxResult.prototype.time = function()
  576. {
  577. return this.answer.time;
  578. };
  579. ajaxResult.prototype.error = function()
  580. {
  581. return this.answer.ex;
  582. };
  583. ajaxResult.prototype.error_description = function()
  584. {
  585. return this.answer.error_description;
  586. };
  587. ajaxResult.prototype.more = function()
  588. {
  589. return !isNaN(this.answer.next);
  590. };
  591. ajaxResult.prototype.total = function()
  592. {
  593. return parseInt(this.answer.total);
  594. };
  595. ajaxResult.prototype.next = function(cb)
  596. {
  597. if(this.more())
  598. {
  599. this.query.start = this.answer.next;
  600. if(!!cb && Utils.isFunction(cb))
  601. {
  602. this.query.callback = cb;
  603. }
  604. return ajax(this.query);
  605. }
  606. return false;
  607. };
  608. var ajaxError = function(status, ex)
  609. {
  610. this.status = status;
  611. this.ex = ex;
  612. };
  613. ajaxError.prototype.getError = function()
  614. {
  615. return this.ex;
  616. };
  617. ajaxError.prototype.getStatus = function()
  618. {
  619. return this.status;
  620. };
  621. ajaxError.prototype.toString = function()
  622. {
  623. return this.ex.error + (
  624. !!this.ex.error_description
  625. ? ': ' + this.ex.error_description
  626. : ''
  627. ) + ' (' + this.status + ')';
  628. };
  629. var fileReader = function(fileInput, cb)
  630. {
  631. if(fileReader.canUse())
  632. {
  633. var files = fileInput.files,
  634. len = 0,
  635. result = fileInput.multiple ? [] : null;
  636. for(var i = 0, f; f = files[i]; i++)
  637. {
  638. var reader = new window.FileReader();
  639. reader.BXFILENAME = files[i].name;
  640. reader.onload = function(e)
  641. {
  642. e = e || window.event;
  643. var res = [this.BXFILENAME, btoa(e.target.result)];
  644. if(result === null)
  645. result = res;
  646. else
  647. result.push(res);
  648. if(--len <= 0)
  649. {
  650. cb(result);
  651. }
  652. };
  653. reader.readAsBinaryString(f);
  654. }
  655. len = i;
  656. if(len <= 0)
  657. {
  658. cb(result);
  659. }
  660. }
  661. };
  662. fileReader.canUse = function()
  663. {
  664. return !!window.FileReader;
  665. };
  666. })();