845 lines
17 KiB
JavaScript
845 lines
17 KiB
JavaScript
'use strict';
|
|
|
|
;(function(){
|
|
|
|
/************************ ATTENTION ***************************
|
|
* Please do not use Bitrix CoreJS in this class. *
|
|
* This class can be called on page without Bitrix Framework *
|
|
**************************************************************/
|
|
|
|
if (!window.BX)
|
|
{
|
|
window.BX = {};
|
|
}
|
|
else if (window.BX.RestClient)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var BX = window.BX;
|
|
|
|
BX.RestClient = function (options)
|
|
{
|
|
options = options || {};
|
|
|
|
this.endpoint = options.endpoint || '/rest';
|
|
this.queryParams = options.queryParams || '';
|
|
this.cors = options.cors === true;
|
|
};
|
|
|
|
BX.RestClient.prototype.callMethod = function(method, params, callback, sendCallback, logTag)
|
|
{
|
|
return ajax({
|
|
method: method,
|
|
data: params,
|
|
callback: callback,
|
|
sendCallback: sendCallback,
|
|
logTag: logTag,
|
|
endpoint: this.endpoint,
|
|
queryParams: this.queryParams,
|
|
cors: this.cors
|
|
});
|
|
};
|
|
|
|
/*
|
|
calls = [[method,params],[method,params]];
|
|
calls = [{method:method,params:params},[method,params]];
|
|
calls = {call_id:[method,params],...};
|
|
*/
|
|
BX.RestClient.prototype.callBatch = function(calls, callback, bHaltOnError, sendCallback, logTag)
|
|
{
|
|
var cmd = Utils.isArray(calls) ? [] : {};
|
|
var cnt = 0;
|
|
var cb = function(cmd) {
|
|
ajax.batch(cmd, callback, bHaltOnError, sendCallback, this.endpoint, this.queryParams, this.cors, logTag);
|
|
}.bind(this);
|
|
|
|
for(var i in calls)
|
|
{
|
|
var method = null, params = null;
|
|
|
|
if(!!calls[i] && calls.hasOwnProperty(i))
|
|
{
|
|
if(Utils.isArray(calls[i]))
|
|
{
|
|
method = calls[i][0];
|
|
params = calls[i][1];
|
|
}
|
|
else if(!!calls[i].method)
|
|
{
|
|
method = calls[i].method;
|
|
params = calls[i].params;
|
|
}
|
|
|
|
if(!!method)
|
|
{
|
|
cnt++;
|
|
cmd[i] = [method, params];
|
|
}
|
|
}
|
|
}
|
|
|
|
if(cnt > 0)
|
|
{
|
|
var e = function(i)
|
|
{
|
|
return function(str)
|
|
{
|
|
cmd[i] = cmd[i][0] + '?' + str;
|
|
if(--cnt <= 0)
|
|
cb(cmd);
|
|
}
|
|
};
|
|
|
|
for(var c in cmd)
|
|
{
|
|
if(cmd.hasOwnProperty(c))
|
|
{
|
|
ajax.prepareData(cmd[c][1], '', e(c));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
BX.RestClient.prototype.setEndpoint = function(url)
|
|
{
|
|
this.endpoint = url;
|
|
};
|
|
|
|
BX.RestClient.prototype.enableCorsRequest = function(value)
|
|
{
|
|
this.cors = value === true;
|
|
};
|
|
|
|
BX.RestClient.prototype.setQueryParams = function(params)
|
|
{
|
|
this.queryParams = params;
|
|
};
|
|
|
|
/* self init for bitrix env */
|
|
if (typeof BX.namespace !== 'undefined')
|
|
{
|
|
var BXRest = new BX.RestClient();
|
|
|
|
if (typeof BX.rest == 'undefined')
|
|
{
|
|
BX.rest = {};
|
|
}
|
|
|
|
BX.rest.callMethod = function (method, params, callback, sendCallback, logTag)
|
|
{
|
|
return BXRest.callMethod(method, params, callback, sendCallback, logTag);
|
|
};
|
|
|
|
/*
|
|
calls = [[method,params],[method,params]];
|
|
calls = [{method:method,params:params},[method,params]];
|
|
calls = {call_id:[method,params],...};
|
|
*/
|
|
BX.rest.callBatch = function (calls, callback, bHaltOnError, sendCallback, logTag)
|
|
{
|
|
return BXRest.callBatch(calls, callback, bHaltOnError, sendCallback, logTag);
|
|
};
|
|
}
|
|
|
|
var Utils = {
|
|
isArray: function(item) {
|
|
return item && Object.prototype.toString.call(item) == "[object Array]";
|
|
},
|
|
isFunction: function(item) {
|
|
return item === null ? false : (typeof (item) == "function" || item instanceof Function);
|
|
},
|
|
isString: function(item) {
|
|
return item === '' ? true : (item ? (typeof (item) == "string" || item instanceof String) : false);
|
|
},
|
|
isDomNode: function(item) {
|
|
return item && typeof (item) == "object" && "nodeType" in item;
|
|
},
|
|
isDate: function(item) {
|
|
return item && Object.prototype.toString.call(item) == "[object Date]";
|
|
},
|
|
buildQueryString: function(params)
|
|
{
|
|
var result = '';
|
|
for (var key in params)
|
|
{
|
|
if (!params.hasOwnProperty(key))
|
|
{
|
|
continue;
|
|
}
|
|
var value = params[key];
|
|
if(this.isArray(value))
|
|
{
|
|
value.forEach(function(valueElement, index)
|
|
{
|
|
result += encodeURIComponent(key + "[" + index + "]") + "=" + encodeURIComponent(valueElement) + "&";
|
|
});
|
|
}
|
|
else
|
|
{
|
|
result += encodeURIComponent(key) + "=" + encodeURIComponent(value) + "&";
|
|
}
|
|
}
|
|
|
|
if(result.length > 0)
|
|
{
|
|
result = result.substr(0, result.length - 1);
|
|
}
|
|
return result;
|
|
},
|
|
clone: function(obj, bCopyObj)
|
|
{
|
|
var _obj, i, l;
|
|
if (bCopyObj !== false)
|
|
bCopyObj = true;
|
|
|
|
if (obj === null)
|
|
return null;
|
|
|
|
if (this.isDomNode(obj))
|
|
{
|
|
_obj = obj.cloneNode(bCopyObj);
|
|
}
|
|
else if (typeof obj == 'object')
|
|
{
|
|
if (this.isArray(obj))
|
|
{
|
|
_obj = [];
|
|
for (i=0,l=obj.length;i<l;i++)
|
|
{
|
|
if (typeof obj[i] == "object" && bCopyObj)
|
|
_obj[i] = this.clone(obj[i], bCopyObj);
|
|
else
|
|
_obj[i] = obj[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_obj = {};
|
|
if (obj.constructor)
|
|
{
|
|
if (this.isDate(obj))
|
|
_obj = new Date(obj);
|
|
else
|
|
_obj = new obj.constructor();
|
|
}
|
|
|
|
for (i in obj)
|
|
{
|
|
if (typeof obj[i] == "object" && bCopyObj)
|
|
_obj[i] = this.clone(obj[i], bCopyObj);
|
|
else
|
|
_obj[i] = obj[i];
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
_obj = obj;
|
|
}
|
|
|
|
return _obj;
|
|
}
|
|
};
|
|
|
|
var ajax = function(config)
|
|
{
|
|
var hasCallback = !!config.callback && Utils.isFunction(config.callback);
|
|
var promise = typeof BX.Promise === 'undefined' || hasCallback? null: new BX.Promise();
|
|
var sendCallback = config.sendCallback || function() {};
|
|
var withoutRestoringCsrf = config.withoutRestoringCsrf || false;
|
|
var loginAttempt = config.loginAttempt || 0;
|
|
|
|
var xhr = ajax.xhr();
|
|
|
|
var url = config.endpoint + '/' + ajax.escape(config.method) + '.json'+(config.logTag? '?logTag='+config.logTag: '');
|
|
|
|
xhr.open('POST', url);
|
|
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
|
|
|
if (config.cors)
|
|
{
|
|
xhr.withCredentials = true;
|
|
}
|
|
|
|
var bRequestCompleted = false;
|
|
|
|
// IE fix
|
|
xhr.onprogress = function(){};
|
|
xhr.ontimeout = function(){};
|
|
xhr.timeout = 0;
|
|
|
|
xhr.onload = function()
|
|
{
|
|
if(bRequestCompleted)
|
|
return;
|
|
|
|
xhr.onload = function() {};
|
|
|
|
var bSuccess = ajax.isSuccess(xhr);
|
|
|
|
var status = xhr.status;
|
|
if(bSuccess)
|
|
{
|
|
var data = xhr.responseText;
|
|
|
|
if(data.length > 0)
|
|
{
|
|
try
|
|
{
|
|
data = JSON.parse(data);
|
|
}
|
|
catch(e)
|
|
{
|
|
bSuccess = false;
|
|
}
|
|
}
|
|
|
|
if (status == 401)
|
|
{
|
|
if (data.extended_error === 'user_not_authorized' && loginAttempt === 0)
|
|
{
|
|
if (data.sessid)
|
|
{
|
|
console.warn('BX.rest: csrf-token has expired, replace to a new token');
|
|
BX.message({'bitrix_sessid': data.sessid});
|
|
}
|
|
|
|
if('BXDesktopSystem' in window)
|
|
{
|
|
console.warn('BX.rest: you are not authorized, trying to log in');
|
|
config.loginAttempt = 1;
|
|
BXDesktopSystem.Login({
|
|
// todo: support auth failure callback in future (when it is ready)
|
|
success: function()
|
|
{
|
|
console.warn('BX.rest: successfully logged in, repeating request');
|
|
if (!hasCallback)
|
|
{
|
|
config.callback = function(result)
|
|
{
|
|
if (result.error())
|
|
{
|
|
promise.reject(result);
|
|
}
|
|
else
|
|
{
|
|
promise.fulfill(result);
|
|
}
|
|
}
|
|
}
|
|
ajax(config);
|
|
}
|
|
});
|
|
return true;
|
|
}
|
|
}
|
|
else if (data.sessid && !withoutRestoringCsrf)
|
|
{
|
|
BX.message({'bitrix_sessid': data.sessid});
|
|
console.warn('BX.rest: your csrf-token has expired, send query with a new token');
|
|
|
|
config.withoutRestoringCsrf = true;
|
|
|
|
if (!hasCallback)
|
|
{
|
|
config.callback = function(result)
|
|
{
|
|
if (result.error())
|
|
{
|
|
promise.reject(result);
|
|
}
|
|
else
|
|
{
|
|
promise.fulfill(result);
|
|
}
|
|
}
|
|
}
|
|
ajax(config);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
else if (status == 0)
|
|
{
|
|
data = {result: {}, error: "ERROR_NETWORK", error_description: "A network error occurred while the request was being executed."};
|
|
}
|
|
else
|
|
{
|
|
if (status == 200)
|
|
{
|
|
if (data.length <= 0)
|
|
{
|
|
data = {result: {}, error: "BLANK_ANSWER", error_description: "Empty answer with correct http code, network error possible."};
|
|
}
|
|
}
|
|
else if (data.length <= 0)
|
|
{
|
|
data = {result: {}, error: "BLANK_ANSWER_WITH_ERROR_CODE", error_description: 'Empty answer with error http code: '+status};
|
|
}
|
|
}
|
|
}
|
|
|
|
xhr = null;
|
|
if(bSuccess)
|
|
{
|
|
var res = new ajaxResult(data, config, status);
|
|
if(hasCallback)
|
|
{
|
|
config.callback.apply(window, [res]);
|
|
}
|
|
else
|
|
{
|
|
if (res.error())
|
|
{
|
|
promise.reject(res);
|
|
}
|
|
else
|
|
{
|
|
promise.fulfill(res);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var res = new ajaxResult({
|
|
error: "ERROR_UNEXPECTED_ANSWER",
|
|
error_description: "Server returned an unexpected response.",
|
|
ex: {}
|
|
}, config, 0);
|
|
if(hasCallback)
|
|
{
|
|
config.callback.apply(window, [res]);
|
|
}
|
|
else
|
|
{
|
|
promise.reject(res);
|
|
}
|
|
}
|
|
};
|
|
|
|
xhr.onerror = function(e)
|
|
{
|
|
var res = new ajaxResult({
|
|
error: "ERROR_NETWORK",
|
|
error_description: "A network error occurred while the request was being executed.",
|
|
ex: e
|
|
}, config, 0);
|
|
if(hasCallback)
|
|
{
|
|
config.callback.apply(window, [res]);
|
|
}
|
|
else
|
|
{
|
|
promise.reject(res);
|
|
}
|
|
};
|
|
|
|
var query_data = '';
|
|
if (config.queryParams)
|
|
{
|
|
query_data = Utils.buildQueryString(config.queryParams);
|
|
}
|
|
else if (typeof BX.bitrix_sessid !== 'undefined')
|
|
{
|
|
query_data = 'sessid=' + BX.bitrix_sessid();
|
|
}
|
|
|
|
if(typeof config.start !== 'undefined')
|
|
{
|
|
query_data += '&start=' + parseInt(config.start);
|
|
}
|
|
|
|
if(!!config.data)
|
|
{
|
|
ajax.prepareData(config.data, '', function(res)
|
|
{
|
|
query_data += '&' + res;
|
|
xhr.send(query_data);
|
|
sendCallback(xhr);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
xhr.send(query_data);
|
|
sendCallback(xhr);
|
|
}
|
|
|
|
return hasCallback || !promise? xhr: promise;
|
|
};
|
|
|
|
ajax.batch = function(calls, callback, bHaltOnError, sendCallback, endpoint, queryParams, cors, logTag)
|
|
{
|
|
return ajax({
|
|
method: 'batch',
|
|
data: {halt: !!bHaltOnError ? 1 : 0, cmd: calls},
|
|
callback: function(res, config, status)
|
|
{
|
|
if(!callback)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var error = res.error();
|
|
var data = res.data();
|
|
var result = Utils.isArray(calls) ? [] : {};
|
|
|
|
for(var i in calls)
|
|
{
|
|
if(!!calls[i] && calls.hasOwnProperty(i))
|
|
{
|
|
if(Utils.isString(calls[i]))
|
|
{
|
|
var q = calls[i].split('?');
|
|
}
|
|
else
|
|
{
|
|
q = [
|
|
Utils.isArray(calls[i]) ? calls[i][0] : calls[i].method,
|
|
Utils.isArray(calls[i]) ? calls[i][1] : calls[i].data
|
|
];
|
|
}
|
|
|
|
if (
|
|
data
|
|
&& typeof data.result !== 'undefined'
|
|
&& (
|
|
typeof data.result[i] !== 'undefined'
|
|
|| typeof data.result_error[i] !== 'undefined'
|
|
)
|
|
)
|
|
{
|
|
result[i] = new ajaxResult({
|
|
result: typeof data.result[i] !== 'undefined' ? data.result[i] : {},
|
|
error: data.result_error[i] || undefined,
|
|
total: data.result_total[i],
|
|
time: data.result_time[i],
|
|
next: data.result_next[i]
|
|
}, {
|
|
method: q[0],
|
|
data: q[1],
|
|
callback: callback,
|
|
endpoint: endpoint,
|
|
queryParams: queryParams,
|
|
cors: cors
|
|
}, res.status);
|
|
}
|
|
else if (error)
|
|
{
|
|
result[i] = new ajaxResult({
|
|
result: {},
|
|
error: error.ex,
|
|
total: 0
|
|
}, {
|
|
method: q[0],
|
|
data: q[1],
|
|
callback: callback,
|
|
endpoint: endpoint,
|
|
queryParams: queryParams,
|
|
cors: cors
|
|
}, res.status);
|
|
}
|
|
}
|
|
}
|
|
|
|
callback.apply(window, [result]);
|
|
},
|
|
sendCallback: sendCallback,
|
|
endpoint: endpoint,
|
|
queryParams: queryParams,
|
|
cors: cors,
|
|
logTag: logTag
|
|
});
|
|
};
|
|
|
|
ajax.xhr = function()
|
|
{
|
|
return new XMLHttpRequest();
|
|
};
|
|
|
|
ajax.escape = function(str)
|
|
{
|
|
return encodeURIComponent(str);
|
|
};
|
|
|
|
ajax.prepareData = function(arData, prefix, callback)
|
|
{
|
|
var data = '', objects = [];
|
|
if(Utils.isString(arData) || arData === null)
|
|
{
|
|
callback.call(document, arData || '');
|
|
}
|
|
else
|
|
{
|
|
for(var i in arData)
|
|
{
|
|
if(!arData.hasOwnProperty(i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var name = ajax.escape(i);
|
|
|
|
if(prefix)
|
|
name = prefix + '[' + name + ']';
|
|
|
|
if(typeof arData[i] === 'object')
|
|
{
|
|
if(Utils.isArray(arData[i]) && arData[i].length <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
objects.push([name, arData[i]]);
|
|
}
|
|
else
|
|
{
|
|
if(data.length > 0)
|
|
{
|
|
data += '&';
|
|
}
|
|
|
|
if(typeof arData[i] === 'boolean')
|
|
{
|
|
data += name + '=' + (arData[i]? 1: 0);
|
|
}
|
|
else
|
|
{
|
|
data += name + '=' + ajax.escape(arData[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
var cnt = objects.length;
|
|
if(cnt > 0)
|
|
{
|
|
var cb = function(str)
|
|
{
|
|
data += (!!str ? '&' : '') + str;
|
|
if(--cnt <= 0)
|
|
{
|
|
callback.call(document, data)
|
|
}
|
|
};
|
|
|
|
var cnt1 = cnt;
|
|
for(var i = 0; i < cnt1; i++)
|
|
{
|
|
if(Utils.isDomNode(objects[i][1]))
|
|
{
|
|
if(objects[i][1].tagName.toUpperCase() === 'INPUT' && objects[i][1].type === 'file')
|
|
{
|
|
if(fileReader.canUse())
|
|
{
|
|
fileReader(objects[i][1], (function(name)
|
|
{
|
|
return function(result)
|
|
{
|
|
if(Utils.isArray(result) && result.length > 0)
|
|
{
|
|
cb(name + '[0]=' + ajax.escape(result[0]) + '&' + name + '[1]=' + ajax.escape(result[1]));
|
|
}
|
|
else
|
|
{
|
|
cb(name + '=');
|
|
}
|
|
}
|
|
})(objects[i][0]));
|
|
}
|
|
}
|
|
else if(typeof objects[i][1].value !== 'undefined')
|
|
{
|
|
cb(objects[i][0] + '=' + ajax.escape(objects[i][1].value));
|
|
}
|
|
else
|
|
{
|
|
cb('');
|
|
}
|
|
}
|
|
else if(Utils.isDate(objects[i][1]))
|
|
{
|
|
cb(objects[i][0] + '=' + ajax.escape(objects[i][1].toJSON()));
|
|
}
|
|
else if(Utils.isArray(objects[i][1]) && objects[i][1].length <= 0)
|
|
{
|
|
cb(objects[i][0] + '=');
|
|
}
|
|
else
|
|
{
|
|
ajax.prepareData(objects[i][1], objects[i][0], cb);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
callback.call(document, data)
|
|
}
|
|
}
|
|
};
|
|
|
|
ajax.isSuccess = function(xhr)
|
|
{
|
|
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;
|
|
};
|
|
|
|
var ajaxResult = function(answer, query, status)
|
|
{
|
|
this.answer = answer;
|
|
this.query = Utils.clone(query);
|
|
this.status = status;
|
|
|
|
if(typeof this.answer.next !== 'undefined')
|
|
{
|
|
this.answer.next = parseInt(this.answer.next);
|
|
}
|
|
|
|
if(typeof this.answer.error !== 'undefined')
|
|
{
|
|
this.answer.ex = new ajaxError(this.status, typeof this.answer.error === 'string' ? this.answer : this.answer.error)
|
|
}
|
|
};
|
|
|
|
ajaxResult.prototype.data = function()
|
|
{
|
|
return this.answer.result;
|
|
};
|
|
|
|
ajaxResult.prototype.time = function()
|
|
{
|
|
return this.answer.time;
|
|
};
|
|
|
|
ajaxResult.prototype.error = function()
|
|
{
|
|
return this.answer.ex;
|
|
};
|
|
|
|
ajaxResult.prototype.error_description = function()
|
|
{
|
|
return this.answer.error_description;
|
|
};
|
|
|
|
ajaxResult.prototype.more = function()
|
|
{
|
|
return !isNaN(this.answer.next);
|
|
};
|
|
|
|
ajaxResult.prototype.total = function()
|
|
{
|
|
return parseInt(this.answer.total);
|
|
};
|
|
|
|
ajaxResult.prototype.next = function(cb)
|
|
{
|
|
if(this.more())
|
|
{
|
|
this.query.start = this.answer.next;
|
|
|
|
if(!!cb && Utils.isFunction(cb))
|
|
{
|
|
this.query.callback = cb;
|
|
}
|
|
|
|
return ajax(this.query);
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
var ajaxError = function(status, ex)
|
|
{
|
|
this.status = status;
|
|
this.ex = ex;
|
|
};
|
|
|
|
ajaxError.prototype.getError = function()
|
|
{
|
|
return this.ex;
|
|
};
|
|
|
|
ajaxError.prototype.getStatus = function()
|
|
{
|
|
return this.status;
|
|
};
|
|
|
|
ajaxError.prototype.toString = function()
|
|
{
|
|
return this.ex.error + (
|
|
!!this.ex.error_description
|
|
? ': ' + this.ex.error_description
|
|
: ''
|
|
) + ' (' + this.status + ')';
|
|
};
|
|
|
|
var arrayBufferToData = function(arrayBuffer)
|
|
{
|
|
var uint8 = new Uint8Array(arrayBuffer);
|
|
var data = '';
|
|
|
|
// TypedArray.prototype.forEach is not supported in some browsers as IE.
|
|
if (typeof uint8.forEach === 'function')
|
|
{
|
|
uint8.forEach(function (value)
|
|
{
|
|
data += String.fromCharCode(value);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
var length = uint8.length;
|
|
for (var i = 0; i < length; i += 1)
|
|
{
|
|
data += String.fromCharCode(uint8[i]);
|
|
}
|
|
}
|
|
return btoa(data);
|
|
};
|
|
|
|
var fileReader = function(fileInput, cb)
|
|
{
|
|
if(fileReader.canUse())
|
|
{
|
|
var files = fileInput.files,
|
|
len = 0,
|
|
result = fileInput.multiple ? [] : null;
|
|
|
|
for(var i = 0, f; f = files[i]; i++)
|
|
{
|
|
var reader = new window.FileReader();
|
|
|
|
reader.BXFILENAME = files[i].name;
|
|
|
|
reader.onload = function(e)
|
|
{
|
|
e = e || window.event;
|
|
|
|
var res = [this.BXFILENAME, arrayBufferToData(e.target.result)];
|
|
|
|
if(result === null)
|
|
result = res;
|
|
else
|
|
result.push(res);
|
|
|
|
if(--len <= 0)
|
|
{
|
|
cb(result);
|
|
}
|
|
};
|
|
|
|
reader.readAsArrayBuffer(f);
|
|
}
|
|
len = i;
|
|
if(len <= 0)
|
|
{
|
|
cb(result);
|
|
}
|
|
}
|
|
};
|
|
|
|
fileReader.canUse = function()
|
|
{
|
|
return !!window.FileReader;
|
|
};
|
|
|
|
})(); |