/home/ivoiecob/email.hirewise-va.com/modules/CoreWebclient/js/Ajax.js
'use strict'
var _ = require('underscore'),
$ = require('jquery'),
moment = require('moment'),
ko = require('knockout'),
TextUtils = require('%PathToCoreWebclientModule%/js/utils/Text.js'),
Types = require('%PathToCoreWebclientModule%/js/utils/Types.js'),
Utils = require('%PathToCoreWebclientModule%/js/utils/Common.js'),
Logger = require('%PathToCoreWebclientModule%/js/utils/Logger.js'),
Popups = require('%PathToCoreWebclientModule%/js/Popups.js'),
AlertPopup = require('%PathToCoreWebclientModule%/js/popups/AlertPopup.js'),
App = require('%PathToCoreWebclientModule%/js/App.js'),
Pulse = require('%PathToCoreWebclientModule%/js/Pulse.js'),
Settings = require('%PathToCoreWebclientModule%/js/Settings.js'),
Screens = require('%PathToCoreWebclientModule%/js/Screens.js'),
aFilterDebugInfo = []
function _getRequestDataString(oReqData) {
return (
'start time:' +
oReqData.Time.format('DD.MM, HH:mm:ss') +
'<br />' +
JSON.stringify(oReqData.Request).substr(0, 300) +
'<br />' +
'readyState:' +
oReqData.Xhr.readyState +
(Types.isString(oReqData.Xhr.statusText) ? ':' + oReqData.Xhr.statusText : '') +
'<br />' +
(Types.isString(oReqData.Xhr.responseText) ? ':' + oReqData.Xhr.responseText.substr(0, 300) + '<br />' : '')
)
}
/**
* @constructor
*/
function CAjax() {
this.requests = ko.observableArray([])
this.aOnAllRequestsClosedHandlers = []
this.requests.subscribe(function () {
if (this.requests().length === 0) {
_.each(this.aOnAllRequestsClosedHandlers, function (fHandler) {
if (_.isFunction(fHandler)) {
fHandler()
}
})
}
}, this)
this.aAbortRequestHandlers = {}
this.bAllowRequests = true
this.bInternetConnectionProblem = false
if (Settings.AllowClientDebug) {
App.subscribeEvent(
'%ModuleName%::GetDebugInfo',
_.bind(function (oParams) {
var aInfo = []
if (this.requests().length) {
aInfo.push('<b>Current requests:</b>')
_.each(this.requests(), function (oReqData) {
aInfo.push(_getRequestDataString(oReqData))
})
}
if (aFilterDebugInfo.length > 0) {
aInfo.push('')
aInfo.push('<b>aFilterDebugInfo:</b>')
aInfo = aInfo.concat(aFilterDebugInfo)
}
oParams.Info.push(aInfo.join('<br />'))
}, this)
)
}
}
/**
* @param {string} sModule
* @param {string} sMethod
* @returns {object}
*/
CAjax.prototype.getOpenedRequest = function (sModule, sMethod) {
var oFoundReqData = _.find(this.requests(), function (oReqData) {
return oReqData.Request.Module === sModule && oReqData.Request.Method === sMethod
})
return oFoundReqData ? oFoundReqData.Request : null
}
/**
* @param {string=} sModule = ''
* @param {string=} sMethod = ''
* @returns {boolean}
*/
CAjax.prototype.hasOpenedRequests = function (sModule, sMethod) {
// Do not change requests here. It calls hasOpenedRequests and we get a loop.
sModule = Types.pString(sModule)
sMethod = Types.pString(sMethod)
if (sMethod === '') {
return this.requests().length > 0
} else {
return !!_.find(this.requests(), function (oReqData) {
return oReqData && oReqData.Request.Module === sModule && oReqData.Request.Method === sMethod
})
}
}
/**
* @param {string} sModule
* @param {function} fHandler
*/
CAjax.prototype.registerAbortRequestHandler = function (sModule, fHandler) {
this.aAbortRequestHandlers[sModule] = fHandler
}
/**
* @param {function} fHandler
*/
CAjax.prototype.registerOnAllRequestsClosedHandler = function (fHandler) {
this.aOnAllRequestsClosedHandlers.push(fHandler)
}
/**
* @param {string} sModule
* @param {string} sMethod
* @param {object} oParameters
* @param {function=} fResponseHandler
* @param {object=} oContext
* @param {object=} oMainParams
*/
CAjax.prototype.send = function (sModule, sMethod, oParameters, fResponseHandler, oContext, oMainParams) {
oParameters = oParameters || {}
var oRequest = _.extendOwn(
{
Module: sModule,
Method: sMethod,
Parameters: oParameters,
},
App.getCommonRequestParameters()
)
if (oMainParams) {
oRequest = _.extendOwn(oRequest, oMainParams)
}
if (this.bAllowRequests && !this.bInternetConnectionProblem) {
var oEventParams = {
Module: sModule,
Method: sMethod,
Parameters: oParameters, // can be changed by reference
ResponseHandler: fResponseHandler,
Context: oContext,
Continue: true,
}
App.broadcastEvent('SendAjaxRequest::before', oEventParams)
if (oEventParams.Continue) {
this.abortSameRequests(oRequest)
this.doSend(oRequest, fResponseHandler, oContext)
}
} else {
var oResponse = { Result: false, ErrorCode: Enums.Errors.NotDisplayedError }
this.executeResponseHandler(fResponseHandler, oContext, oResponse, oRequest, 'error')
}
}
/*************************private*************************************/
/**
* @param {Object} oRequest
* @param {Function=} fResponseHandler
* @param {Object=} oContext
*/
CAjax.prototype.doSend = function (oRequest, fResponseHandler, oContext) {
var doneFunc = _.bind(this.done, this, oRequest, fResponseHandler, oContext),
failFunc = _.bind(this.fail, this, oRequest, fResponseHandler, oContext),
alwaysFunc = _.bind(this.always, this, oRequest),
oXhr = null,
oCloneRequest = _.clone(oRequest),
oHeader = { 'X-Client': 'WebClient' }
oHeader['X-DeviceId'] = App.getCurrentDeviceId()
oCloneRequest.Parameters = JSON.stringify(oCloneRequest.Parameters)
let sHost = '?/Api/'
let bWithCredentials = false
try {
if (process.env.NODE_ENV === 'development') {
sHost = process.env.VUE_APP_API_HOST + sHost
bWithCredentials = true
}
} catch (e) {}
oXhr = $.ajax({
url: sHost,
type: 'POST',
async: true,
dataType: 'json',
headers: oHeader,
data: oCloneRequest,
success: doneFunc,
error: failFunc,
complete: alwaysFunc,
xhrFields: {
withCredentials: bWithCredentials
}
})
this.requests().push({ Request: oRequest, Xhr: oXhr, Time: moment() })
}
/**
* @param {Object} oRequest
*/
CAjax.prototype.abortSameRequests = function (oRequest) {
var fHandler = this.aAbortRequestHandlers[oRequest.Module]
if (_.isFunction(fHandler) && this.requests().length > 0) {
_.each(
this.requests(),
_.bind(function (oReqData) {
if (oReqData) {
var oOpenedRequest = oReqData.Request
if (oRequest.Module === oOpenedRequest.Module) {
if (fHandler(oRequest, oOpenedRequest)) {
oReqData.Xhr.abort()
}
}
}
}, this)
)
}
}
/**
* @param {object} oExcept
*/
CAjax.prototype.abortAllRequests = function (oExcept) {
if (typeof oExcept !== 'object') {
oExcept = {
Module: '',
Method: '',
}
}
_.each(
this.requests(),
function (oReqData) {
if (oReqData && (oReqData.Request.Module !== oExcept.Module || oReqData.Request.Method !== oExcept.Method)) {
oReqData.Xhr.abort()
}
},
this
)
}
/**
* @param {object} oExcept
*/
CAjax.prototype.abortAndStopSendRequests = function (oExcept) {
this.bAllowRequests = false
this.abortAllRequests(oExcept)
}
CAjax.prototype.startSendRequests = function () {
this.bAllowRequests = true
}
/**
* @param {Object} oRequest
* @param {Function} fResponseHandler
* @param {Object} oContext
* @param {{Result:boolean}} oResponse
* @param {string} sType
* @param {Object} oXhr
*/
CAjax.prototype.done = function (oRequest, fResponseHandler, oContext, oResponse, sType, oXhr) {
if (
App.getUserRole() !== Enums.UserRole.Anonymous &&
oResponse &&
Types.isNumber(oResponse.AuthenticatedUserId) &&
oResponse.AuthenticatedUserId !== 0 &&
oResponse.AuthenticatedUserId !== App.getUserId()
) {
Popups.showPopup(AlertPopup, [
TextUtils.i18n('%MODULENAME%/ERROR_AUTHENTICATED_USER_CONFLICT'),
function () {
App.logoutAndGotoLogin()
},
'',
TextUtils.i18n('%MODULENAME%/ACTION_LOGOUT'),
])
}
// if oResponse.Result === 0 or oResponse.Result === '' this is not an error
if (oResponse && (oResponse.Result === false || oResponse.Result === null || oResponse.Result === undefined)) {
switch (oResponse.ErrorCode) {
case Enums.Errors.InvalidToken:
this.abortAndStopSendRequests()
App.tokenProblem()
break
case Enums.Errors.AuthError:
if (
App.getUserRole() !== Enums.UserRole.Anonymous &&
!(oRequest.Module === 'Core' && oRequest.Method === 'Logout')
) {
App.logoutAndGotoLogin()
}
break
}
oResponse.Result = false
}
this.executeResponseHandler(fResponseHandler, oContext, oResponse, oRequest, sType, oXhr.status)
}
/**
* @param {Object} oRequest
* @param {Function} fResponseHandler
* @param {Object} oContext
* @param {Object} oXhr
* @param {string} sType
* @param {string} sErrorText
*/
CAjax.prototype.fail = function (oRequest, fResponseHandler, oContext, oXhr, sType, sErrorText) {
var oResponse = { Result: false, ErrorCode: 0 }
switch (sType) {
case 'abort':
oResponse = { Result: false, ErrorCode: Enums.Errors.NotDisplayedError }
break
default:
case 'error':
case 'parseerror':
if (sErrorText === '') {
oResponse = { Result: false, ErrorCode: Enums.Errors.NotDisplayedError, ResponseText: oXhr.responseText }
} else {
var oReqData = _.find(this.requests(), function (oTmpReqData, iIndex) {
return oTmpReqData && _.isEqual(oTmpReqData.Request, oRequest)
})
if (oReqData) {
Logger.log('DataTransferFailed', _getRequestDataString(oReqData))
} else {
var sResponseText = Types.pString(oXhr && oXhr.responseText)
Logger.log('DataTransferFailed', sErrorText, '<br />' + sResponseText.substr(0, 300))
}
oResponse = { Result: false, ErrorCode: Enums.Errors.DataTransferFailed, ResponseText: oXhr.responseText }
}
break
}
this.executeResponseHandler(fResponseHandler, oContext, oResponse, oRequest, sType, oXhr.status)
}
/**
* @param {Function} fResponseHandler
* @param {Object} oContext
* @param {Object} oResponse
* @param {Object} oRequest
* @param {string} sType
* @param {int} status
*/
CAjax.prototype.executeResponseHandler = function (
fResponseHandler,
oContext,
oResponse,
oRequest,
sType,
status = 200
) {
if (!oResponse) {
oResponse = { Result: false, ErrorCode: 0 }
}
App.broadcastEvent('ReceiveAjaxResponse::before', { Request: oRequest, Response: oResponse })
// Check the Internet connection before passing control to the modules.
// It forbids or allows further AJAX requests.
this.checkConnection(oRequest.Module, oRequest.Method, sType)
if (_.isFunction(fResponseHandler) && !oResponse.StopExecuteResponse) {
fResponseHandler.apply(oContext, [oResponse, oRequest, status])
}
App.broadcastEvent('ReceiveAjaxResponse::after', { Request: oRequest, Response: oResponse })
}
/**
* @param {object} oXhr
* @param {string} sType
* @param {object} oRequest
*/
CAjax.prototype.always = function (oRequest, oXhr, sType) {
this.filterRequests(oRequest)
}
CAjax.prototype.filterRequests = function (oRequest, sCallerName) {
this.requests(
_.filter(
this.requests(),
function (oReqData, iIndex) {
if (oReqData) {
if (_.isEqual(oReqData.Request, oRequest)) {
return false
}
var bComplete = oReqData.Xhr.readyState === 4,
bFail =
oReqData.Xhr.readyState === 0 &&
(oReqData.Xhr.statusText === 'abort' || oReqData.Xhr.statusText === 'error'),
bTooLong = moment().diff(oReqData.Time) > 1000 * 60 * 5 // 5 minutes
if (Settings.AllowClientDebug && (bComplete || bFail || bTooLong)) {
if (bTooLong) {
Logger.log(sCallerName, 'remove more than 5 minutes request', _getRequestDataString(oReqData))
} else if (bFail) {
Logger.log(sCallerName, 'remove fail request', _getRequestDataString(oReqData))
} else if (bComplete) {
Logger.log(sCallerName, 'remove complete request', _getRequestDataString(oReqData))
}
}
return !bComplete && !bFail && !bTooLong
}
},
this
)
)
}
CAjax.prototype.checkConnection = (function () {
var iTimer = -1
return function (sModule, sMethod, sStatus) {
clearTimeout(iTimer)
if (sStatus !== 'error') {
Ajax.bInternetConnectionProblem = false
Screens.hideError(true)
} else if (this.bAllowRequests) {
if (sModule === 'Core' && sMethod === 'Ping') {
Ajax.bInternetConnectionProblem = true
Screens.showError(TextUtils.i18n('%MODULENAME%/ERROR_NO_INTERNET_CONNECTION'), true, true)
iTimer = setTimeout(function () {
Ajax.doSend({ Module: 'Core', Method: 'Ping' })
}, 10000)
} else {
Ajax.doSend({ Module: 'Core', Method: 'Ping' })
}
}
}
})()
var Ajax = new CAjax()
module.exports = {
getOpenedRequest: _.bind(Ajax.getOpenedRequest, Ajax),
hasInternetConnectionProblem: function () {
return Ajax.bInternetConnectionProblem
},
hasOpenedRequests: _.bind(Ajax.hasOpenedRequests, Ajax),
registerAbortRequestHandler: _.bind(Ajax.registerAbortRequestHandler, Ajax),
registerOnAllRequestsClosedHandler: _.bind(Ajax.registerOnAllRequestsClosedHandler, Ajax),
abortAndStopSendRequests: _.bind(Ajax.abortAndStopSendRequests, Ajax),
startSendRequests: _.bind(Ajax.startSendRequests, Ajax),
send: _.bind(Ajax.send, Ajax),
}
Pulse.registerEveryMinuteFunction(function () {
Ajax.filterRequests(null, 'pulse')
})
Pulse.registerWakeupFunction(function () {
Logger.log(
'<u>wakeup</u>, hasOpenedRequests: ' +
Ajax.hasOpenedRequests() +
', bInternetConnectionProblem: ' +
Ajax.bInternetConnectionProblem
)
if (Ajax.hasOpenedRequests()) {
_.each(Ajax.requests(), function (oReqData) {
Logger.log('<i>wakeup</i>: ' + _getRequestDataString(oReqData))
})
Ajax.filterRequests(null, 'wakeup')
}
if (Ajax.bInternetConnectionProblem) {
Ajax.checkConnection()
}
})