@slavmak2486/bx24ts
Version:
Library for bitrix24
680 lines (591 loc) • 21.8 kB
text/typescript
import { batchCmdElement } from "../types/batchElement";
import { eventElement } from "../types/eventElement";
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
import { CallResult } from "../callResult";
import {cloneDeep, get as __get} from 'lodash'
import { logger } from "../types/authBaseServe";
import { getAuth } from "../types/getAuth";
// проблемы декларирования методов HTMLElement
declare global{
interface Document{
attachEvent(event: string, listener: EventListener): boolean;
detachEvent(event: string, listener: EventListener): void;
}
interface Window{
attachEvent(event: string, listener: EventListener): boolean;
detachEvent(event: string, listener: EventListener): void;
}
}
interface elementCbArray{
uid:string,
cb:(args:any)=>void
}
export abstract class baseBX24{
cbArray:elementCbArray[]=[];
AUTH_CONNECTOR="";
CLIENT_ID: string | undefined="";
CLIENT_SECRET: string | undefined="";
isInit=false;
DOMAIN: string;
PROTOCOL: number;
APP_SID: string|boolean=false;
PATH="/rest";
LANG="";
AUTH_ID="";
REFRESH_ID="";
MEMBER_ID="";
PLACEMENT="";
IS_ADMIN=false;
AUTH_EXPIRES=0;
USER_OPTIONS: any;
APP_OPTIONS: any;
PLACEMENT_OPTIONS: any;
url='';
timeoutCall=0;
arrEvents:eventElement[]=[];
logger:logger=console;
isReadyVal=false; //check
getHttpString(value:any, prefix=''):string{
if (value instanceof Date){
return prefix+'='+encodeURIComponent(value.toISOString());
}
else if (typeof value=='object'){
const resultObj=[];
for (const field in value){
resultObj.push(this.getHttpString(value[field], prefix+`${prefix!=''?'[':""}${field}${prefix!=''?']':''}`));
}
return resultObj.join('&');
}
else if (prefix!=''){
return encodeURIComponent(prefix)+'='+encodeURIComponent(value);
}
return encodeURIComponent(value);
}
setTimeoutCall(ms:number){
this.timeoutCall=ms;
}
clearTimeoutCall(){
this.timeoutCall=0;
}
isFunction(item:any){
return item === null ? false : (typeof (item) == "function" || item instanceof Function);
}
addEvent(event:string, handler:(params:any)=>void){
this.arrEvents.push({
event:event,
handler:handler
});
}
emitEvent(event:string, params?:any){
const arrHandler=this.arrEvents.filter(el=>{return el.event==event});
for (const idx in arrHandler){
setTimeout(()=>{
arrHandler[idx].handler.call(this, params);
}, 10);
}
}
uniqid(){
const charsList = '0123456789abcdefghijklmnopqrstuvwxyz';
let s = '';
for (let i = 0; i <32; i++)
s += charsList[Math.round(Math.random()*(charsList.length-1))];
return s;
}
setCallback(cb?:(args:any)=>void){
const cbId = this.uniqid();
if (cb){
this.cbArray.push({uid:cbId, cb:cb});
}
return cbId;
}
abstract runCallback(e:MessageEvent):void
doInit(){
this.emitEvent('init', this);
}
abstract sendMessage(cmd:string, params:any, cb?:(params:any)=>void):void
constructor(){
this.DOMAIN="";
this.PROTOCOL=1;
}
userOption={
get:(name:string)=>{
return this.USER_OPTIONS[name];
},
set:(name:string, value:any, cb:(params:any)=>void)=>{
this.USER_OPTIONS[name] = value;
this.sendMessage('setUserOption', {name:name,value:value}, cb);
}
}
appOption={
get: (name:string)=>{
return this.APP_OPTIONS[name];
},
set:(name:string,value:any,cb:(params:any)=>void)=>{
if (this.isAdmin()){
this.APP_OPTIONS[name] = value;
this.sendMessage('setAppOption', {name:name,value:value}, cb);
}
else{
console.error('Access denied!');
}
}
}
utilReady(){
if (document.readyState === "complete")
{
return this.runReady();
}
let __readyHandler:EventListener;
if (document.addEventListener)
{
__readyHandler=()=>{
document.removeEventListener("DOMContentLoaded", __readyHandler, false);
this.runReady();
}
document.addEventListener("DOMContentLoaded", __readyHandler, false);
window.addEventListener("load", ()=>{this.runReady()}, false);
}
else if (document.attachEvent)
{
__readyHandler =()=>{
if (document.readyState === "complete")
{
document.detachEvent("onreadystatechange", __readyHandler);
this.runReady();
}
}
document.attachEvent("onreadystatechange", __readyHandler);
window.attachEvent("onload", ()=>{this.runReady()});
}
this.utilReady = () => null;
return null;
}
runReady():void{
if (!this.isReadyVal)
{
if (!document.body){
setTimeout(this.runReady, 15);
return;
}
this.isReadyVal = true;
this.ready = (handler)=>{
if (typeof handler=='function')
{
setTimeout(handler, 10);
}
};
this.emitEvent('ready');
}
return;
}
abstract refreshAuth(cb?:(params:any)=>void):void;
abstract refreshAuthAsync():Promise<getAuth>;
protected callSuccess(xhr:AxiosResponse):boolean{
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;
}
protected call(url:string, config:{
method:string,
data:any,
callback?:(params: any)=>void
}):Promise<CallResult>{
return new Promise((resolve, reject)=>{
const params=cloneDeep(config.data);
params.auth=this.AUTH_ID;
if (this.AUTH_CONNECTOR&&!params.auth_connector){
params.auth_connector=this.AUTH_CONNECTOR;
}
const options:AxiosRequestConfig = {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: this.getHttpString(params),
url:url
};
if (this.timeoutCall){
options.timeout=this.timeoutCall;
}
axios(options).then(res=>{
const data = res.data;
const result = new CallResult(data, config, this, res.status);
resolve(result);
})
.catch((err:AxiosError)=>{
if (!err?.response?.data){
reject(err);
}
else if (__get(err, ['response','data','error'], undefined)=='expired_token'&&!url.includes('oauth.bitrix.info/oauth/token/')){
try {
this.refreshAuth(()=>{
this.call(url, config).then(resolve).catch(reject);
});
} catch (error) {
reject(error);
}
}
else{
reject(__get(err, ['response','data'], err.message));
}
});
});
}
getAuth(){
return (this.isInit && this.AUTH_EXPIRES > (new Date()).valueOf())
? {access_token: this.AUTH_ID, refresh_token: this.REFRESH_ID, expires_in: this.AUTH_EXPIRES, domain: this.DOMAIN, member_id: this.MEMBER_ID}
: false;
}
// callBatch(cmd:batchCmdElement, haltOnError?:boolean):Promise<{[key:string|number]:CallResult}>;
// callBatch(cmd:batchCmdElement, cb:(params:{[key:string|number]:CallResult})=>void|boolean, haltOnError?:boolean):void;
// callBatch(cmd:batchCmdElement, haltOnError?:boolean):Promise<{[key:string|number]:CallResult}>;
// callBatch(cmd:batchCmdElement, cb:(params:{[key:string|number]:CallResult})=>void|boolean, haltOnError?:boolean):void;
// callBatch(
// cmd:batchCmdElement,
// cb?:((params:{[key:string|number]:CallResult})=>void)|boolean,
// haltOnError=false):void|Promise<{[key:string|number]:CallResult
// }>
callBatch<T extends batchCmdElement>(cmd:T, haltOnError?:boolean):Promise<{[key in keyof T]:CallResult}>
callBatch<T extends batchCmdElement>(cmd:T, cb:(params:{[key in keyof T]:CallResult})=>void):void
callBatch<T extends batchCmdElement>(cmd:T, cb:(params:{[key in keyof T]:CallResult})=>void, haltOnError:boolean):void
callBatch<T extends batchCmdElement>(
cmd:T,
cb?:((params:{[key in keyof T]:CallResult})=>void)|boolean,
haltOnError=false
):void|Promise<{[key in keyof T]:CallResult}>
{
const startCb=cb;
const startHaltOnError=haltOnError;
if (typeof cb == 'boolean'){
haltOnError=cb;
cb=undefined;
}
else{
cb=cb as (params:{[key in keyof T]:CallResult})=>void;
}
const comands:Partial<{
[key in keyof T]:string
}>={};
let cnt=0;
for(const idx in cmd){
const row=cmd[idx];
const method=Array.isArray(row)?row[0]:row.method;
const params=Array.isArray(row)?row[1]:row.params;
if(method)
{
cnt++;
comands[idx] = `${method}?${this.getHttpString(params)}`;
}
}
if (cnt>0){
const url=this.url?`${this.url}batch.json`:`http${this.PROTOCOL?'s':''}://${this.DOMAIN}${this.PATH}/batch.json`;
const params={
cmd:comands as {
[key in keyof T]:string
},
halt:haltOnError?1:0
};
if (!startCb){
if (this.AUTH_EXPIRES<(new Date()).valueOf()){
return new Promise((resolve, reject)=>{
this.refreshAuthAsync().then(()=>{
this.callBatch(cmd, startHaltOnError).then(res=>{
resolve(res);
})
.catch(err=>{
reject(err);
})
})
.catch((error:Error)=>{
reject(error);
});
});
}
else{
return new Promise((resolve, reject)=>{
this.call(url, {
method:'batch',
data: params,
}).then(res=>{
resolve(this.formatResultForBatch(res, cmd));
})
.catch(reject)
});
}
}
else if (typeof startCb=='function'){
if (this.AUTH_EXPIRES<(new Date()).valueOf()){
this.refreshAuthAsync().then(()=>{
this.callBatch(cmd, startCb, startHaltOnError);
})
.catch(error=>{
console.error(error);
});
}
else{
this.call(url, {
method:'batch',
data:params,
callback: startCb
}).then(res=>{
if (typeof cb =='function')
cb(this.formatResultForBatch(res, cmd, cb));
})
.catch(err=>{
if (err instanceof Error){
throw err;
}
const result=this.formatResultForBatch(
new CallResult(err, {
method:'batch',
data:params
}, this, 500),
cmd,
cb as (params:any)=>void
);
if (typeof cb ==='function')
cb(result);
return false;
})
}
}
}
else{
return;
}
}
formatResultForBatch<T extends CallResult, R extends batchCmdElement>(
res:T,
calls:R,
callback?:(params:any)=>void)
:{[key in keyof R]:CallResult}
{
const data = res.data();
const result:Partial<{[key in keyof R]:CallResult}>={};
for(const idx in calls){
const cmd=calls[idx];
if (data?.result?.[idx]!==undefined|| data?.result_error?.[idx]!==undefined){
result[idx]=new CallResult({
result: data.result?.[idx]||{},
error:data.result_error[idx]||undefined,
total:data.result_total[idx],
time:data.result_time[idx],
next: data.result_next[idx]
}, {
method:Array.isArray(cmd)?cmd[0]:cmd?.method,
data: Array.isArray(cmd)?cmd[1]:cmd?.params,
callback:callback
}, this, res.status)
}
else{
result[idx]={
data:()=>({}),
total:()=>0,
error_description:()=>JSON.stringify(res),
answer:data?.result?.[idx],
query: {
method:Array.isArray(cmd)?cmd[0]:cmd?.method,
data: Array.isArray(cmd)?cmd[1]:cmd?.params,
callback:callback
},
next:()=>false,
bx24:this,
time:()=>({}),
status:res.status,
more:()=>false,
error:()=>JSON.stringify(res)
};
}
}
return result as { [key in keyof R]: CallResult; };
}
callMethod(method:string, params:any):Promise<CallResult>;
callMethod(method:string, params:any, cb?:(params:CallResult)=>void):void;
callMethod(method:string, params:any, cb?:(params:CallResult)=>void):void|Promise<CallResult>{
const url=this.url?`${this.url}${method}.json`:`http${this.PROTOCOL?'s':''}://${this.DOMAIN}${this.PATH}/${method}.json`;
if (!cb){
if (this.AUTH_EXPIRES<(new Date()).valueOf()){
return new Promise((resolve, reject)=>{
try {
this.refreshAuth(()=>{
this.callMethod(method, params)?.then(res=>{
resolve(res);
})
.catch(err=>{
reject(err);
})
});
} catch (error) {
reject(error);
}
});
}
else{
return new Promise((resolve)=>{
this.call(url, {
method,
data:params
}).then(res=>{
resolve(res);
})
.catch(err=>{
resolve(new CallResult(err, {
data:params,
method,
callback:cb
}, this, 500));
})
});
}
}
else {
if (this.AUTH_EXPIRES<(new Date()).valueOf()){
this.refreshAuth(()=>{
this.callMethod(method, params, cb);
});
}
else{
this.call(url, {
method,
data:params,
callback:cb
}).then(res=>{
return cb(res);
})
.catch(err=>{
cb(new CallResult({error_description:String(err)}, {
data:params,
method,
callback:cb
},this, 500));
})
}
}
}
//Getters
isAdmin(){
return this.IS_ADMIN;
}
getLang(){
return this.LANG;
}
getDomain(){
return this.DOMAIN;
}
isReady(){
return this.isReadyVal;
}
ready(handler:(params:any)=>void){
this.addEvent('ready', handler);
}
getScrollSize(){
return {
scrollWidth: Math.max(document.documentElement.scrollWidth, document.documentElement.offsetWidth),
scrollHeight: Math.max(document.documentElement.scrollHeight, document.documentElement.offsetHeight)
};
}
///////////////////////////////////////////////////////////////////////////////////////
init(callback?:(params:any)=>void){
if(callback)
{
this.addEvent('init', callback);
}
}
install(callback:(params:any)=>void){
if(callback)
{
this.addEvent('install', callback);
}
}
callBind(event:string, handler:string, auth_type?:number, callback?:(params: CallResult) => void){
if(!this.isInit){
// var _a = arguments;
this.init(()=>{
this.callBind(event, handler, auth_type, callback);
});
}
else if(this.isAdmin())
{
const params = {
event: event||'',
handler: handler||'',
auth_type: (typeof auth_type == 'undefined') ? 0 : auth_type
};
return this.callMethod('event.bind', params, callback);
}
return false;
}
//select dialog
selectAccess = (title:string, value:string[], cb:(params:any)=>void)=>{
if(typeof(value)==='function')
{
cb = value; value = [];
}
this.sendMessage('selectAccess', {value, title}, cb);
};
selectUser = (title:string, cb:(params:any)=>void)=>{
if(typeof(title)==='function'){
cb = title; title = '';
}
this.sendMessage('selectUser', {title: title, mult:false}, cb);
};
selectUsers = (title:string, cb:(params:any)=>void)=>{
if(typeof (title)!=='string'){
cb = title; title = '';
}
this.sendMessage('selectUser', {title: title, mult:true}, cb);
};
selectCRM = (params:{
entityType:string[],
multiple?:boolean,
value?:string[]
}, cb:(params:any)=>void)=>{
this.sendMessage('selectCRM', {
entityType: params.entityType,
multiple: params.multiple,
value: params.value,
}, cb);
};
//Methods sendMessage
installFinish(){
this.sendMessage('setInstallFinish',{});
}
resizeWindow(width:number, height:number, cb:(params:any)=>void){
if(width > 0 && height > 0){
this.sendMessage('resizeWindow', {width:width,height:height}, cb);
}
}
fitWindow(cb?:(params:any)=>void){
this.sendMessage('resizeWindow', {
width:'100%', height:this.getScrollSize().scrollHeight
}, cb);
}
closeApplication(cb:(params:any)=>void){
this.sendMessage('closeApplication', {}, cb);
}
reloadWindow(cb:(params:any)=>void){
this.sendMessage('reloadWindow', {}, cb);
}
setTitle(title:string, cb:(params:any)=>void){
this.sendMessage('setTitle', {title:title}, cb);
}
scrollParentWindow(scroll:number, cb:(params:any)=>void){
if(scroll>0)
{
this.sendMessage('setScroll', {scroll:scroll}, cb);
}
}
//placement
placement={
info:()=>({
placement:this.PLACEMENT,
options:this.PLACEMENT_OPTIONS
}),
getInterface:(cb?:(params: any) => void)=>{
this.sendMessage('getInterface', {}, cb);
},
// call:(method:string, params:any, cb?:(params:CallResult)=>void):void,
call:(cmd:string, params?:any, cb?:(params:any)=>void):void=>{
this.sendMessage(cmd, params, cb);
},
bindEvent:(eventName:string, cb:(params:any)=>void)=>{
this.sendMessage('placementBindEvent', {event:eventName}, cb);
}
}
}