/* global SystemJS */
const i18n = require('./i18n')
const utils = require('./utils')
const transport = require('./transport')
const conn = require('./AsyncConnection')
const injection = require('./injection')
const ClientRepository = require('./ClientRepository')
const LocalRepository = require('./LocalRepository')
const UBCache = require('./UBCache')
const LocalDataStore = require('@unitybase/cs-shared').LocalDataStore
const iso8601ParseAsDate = LocalDataStore.iso8601ParseAsDate
const truncTimeToUtcNull = LocalDataStore.truncTimeToUtcNull
const formatByPattern = require('@unitybase/cs-shared').formatByPattern
const CryptoJS = require('@unitybase/cryptojs')
// const CryptoJSCore = require('@unitybase/cryptojs/core')
const SHA256 = require('@unitybase/cryptojs/sha256')
const MD5 = require('@unitybase/cryptojs/md5')
// must be imported - it modify a global CryptoJS
require('@unitybase/cryptojs/enc-base64')
const UBNativeMessage = require('./UBNativeMessage')
let _errorReporter = null
let _globalVueInstance = null
/**
* Data layer for accessing UnityBase server from Browser or Node.js
*
* @module @unitybase/ub-pub
*/
const UB = module.exports = {
/**
* Return locale-specific resource from its identifier. `localeString` must be:
*
* - either previously defined dy call to {@link i18nExtend i18nExtend}
* - or be a combination of entity and attribute names so that `UB.i18n('uba_user')`
* or `UB.i18n('uba_role.description')` would be resolved to localized entity caption or entity attribute caption
* - description/documentation/captionSingular of entity/attribute can be localized using hashtag:
* - #description - an entity description
* - #documentation - an entity documentation
* - #captionSingular - if entity.captionSingular is defined - entity.captionSingular, else - entity.caption
* See samples below
* @example
//Localized string can be formatted either by position args:
UB.i18nExtend({
greeting: 'Hello {0}, welcome to {1:i18n}',
Kiev: 'Kyiv city'
})
UB.i18n('greeting', 'Mark', 'Kiev') // Hello Mark, welcome to Kyiv city
// in sample above :i18n modifier is added to the second format args, so `Kiev` is also translated
//Or by named args:
UB.i18nExtend({
namedGreeting: 'Hello {name}, welcome to {place}'
})
UB.i18n('namedGreeting', {name: 'Mark', place: 'Kiev'}) // Hello Mark, welcome to Kiev
//Localization itself can be an object:
UB.i18nExtend({
loginPage: { welcome: 'Welcome to our app', user: 'Dear {user}'}
})
UB.i18n('loginPage.welcome') // Welcome to our app
UB.i18n('loginPage.user', {user: 'Pol}) // Dear Pol
UB.i18n('loginPage') // return object {welcome: "Welcome to our app", user: "Dear {user}"}
UB.i18n('uba_user') // -> "Users" (caption from uba_use.meta)
UB.i18n('uba_user.firstName') // -> "First Name" (caption of uba_user.firstName attribute)
UB.i18n('uba_user.name#description') // "User login in lower case" (description for uba_user.name attribute)
UB.i18n('uba_audit#documentation') // "All changes to UBA..." ( documentation for uba_audit entity )
UB.i18n('uba_audit#captionSingular') // "Security Audit" ( fallback to caption because uba_audit.captionSingular is not defined in meta)
*
* @param {string} localeString
* @param {...*} formatArgs Format args
* @returns {*}
*/
i18n: i18n.i18n.bind(i18n),
/**
* Merge localizationObject to UB.i18n. Usually called form `modelFolder/locale/lang-*.js` scripts
* @param {object} localizationObject
*/
i18nExtend: function (localizationObject) {
return i18n.i18nExtend(localizationObject)
},
/**
* Allows to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
* token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
* @example
var s = UB.format('{1}/ext-lang-{0}.js', 'en', 'locale');
// s now contains the string: ''locale/ext-lang-en.js''
* @param {string} stringToFormat The string to be formatted.
* @param {...*} values The values to replace tokens `{0}`, `{1}`, etc in order.
* @returns {string} The formatted string.
*/
format: function (stringToFormat, ...values) { return utils.format(stringToFormat, ...values) },
/**
* Locale based Date and Number formatters, See details in `@unitybase/cs-shared/formatByPattern`
* @example
const d = new Date(2020, 04, 23, 13, 14)
UB.formatter.formatDate(d, 'date') // without 3rd lang parameter - will be formatted for user default lang (for uk - 23.05.2020)
UB.formatter.formatDate('2020-05-23', 'date', 'uk') // 23.05.2020
UB.formatter.formatDate(d, 'date', 'en') // 05/23/2020
UB.formatter.formatDate(d, 'dateTime', 'uk') // 23.05.2020 13:14
UB.formatter.formatDate(d, 'date', 'en') // 05/23/2020, 1:14 PM
const n = 2305.1
UB.formatter.formatNumber(n, 'sum', 'en') // 2,305.10
UB.formatter.formatNumber('2305.1', 'sum', 'en') // 2,305.10
UB.formatter.formatNumber(n, 'sum') // without 3rd lang parameter - will be formatted for user default lang (for uk "2 305,10")
* @type {module:formatByPattern}
*/
formatter: formatByPattern,
/**
* Copies all the properties of one or several objectsFrom to the specified objectTo.
* Non-simple type copied by reference!
* @param {object} objectTo The receiver of the properties
* @param {...object} objectsFrom The source(s) of the properties
* @returns {object} returns objectTo
*/
apply: function (objectTo, ...objectsFrom) { return utils.apply(objectTo, ...objectsFrom) },
/**
* Creates namespaces to be used for scoping variables and classes so that they are not global.
* @example
UB.ns('DOC.Report')
DOC.Report.myReport = function() { ... }
* @function
* @deprecated Try to avoid namespaces - instead create a module and use require()
* @param {string} namespacePath
* @returns {object} The namespace object.
*/
ns: function (namespacePath) { return utils.ns(namespacePath) },
/**
* Convert UnityBase server Boolean response (0 or 1) to JS Boolean (false or true)
* @param {*} v Value to convert
* @returns {boolean|null}
*/
booleanParse: function (v) { return utils.booleanParse(v) },
/**
* Convert UnityBase server dateTime response (ISO8601 string) to Date object
* @param {Date|string} value String representation of Date in ISO8601 format
* @returns {Date}
*/
iso8601Parse: function (value) { return utils.iso8601Parse(value) },
/**
* Convert UnityBase server date response to Date object.
* `date response` is a day with 00 time (2015-07-17T00:00Z), to get a real date we must add current timezone shift
* @param {string|Date|*} value
* @returns {Date}
*/
iso8601ParseAsDate,
/**
* Convert a local DateTime to Date with zero time in UTC0 timezone as expected by UB server for Date attributes
* @param {Date} v
* @returns {Date}
*/
truncTimeToUtcNull,
/**
* Fast async transformation of data to base64 string
* @param {File|ArrayBuffer|string|Blob|Array} data
* @returns {Promise<string>} resolved to data converted to base64 string
*/
base64FromAny: function (data) { return utils.base64FromAny(data) },
/**
* Fast async transformation of file to Uint8Array
* @example
let f = document.getElementById('inputOfTypeFile').files[0]
ui8Arr = await UB.file2Uint8Array(f)
* @param {File} file
* @returns {Promise<Uint8Array>} resolved to file content as Uint8Array
*/
file2Uint8Array: function (file) { return utils.file2Uint8Array(file) },
/**
* Convert base64 encoded string to decoded array buffer
* @param {string} base64
* @returns {ArrayBuffer}
*/
base64toArrayBuffer: function (base64) { return utils.base64toArrayBuffer(base64) },
/**
* Client-side exception.
* Such exceptions will not be showed as unknown error in {@link UB#showErrorWindow UB.showErrorWindow}
* @type {UBError}
*/
UBError: utils.UBError,
/**
* Quiet exception. Global error handler does not show this exception for user. Use it for silently reject promise.
* @type {UBAbortError}
*/
UBAbortError: utils.UBAbortError,
log: utils.log,
/**
* Log error message to console (if console available)
* @function
* @param {...*} msg
*/
logError: utils.logError,
/**
* Log warning message to console (if console available)
* @method
* @param {...*} msg
*/
logWarn: utils.logWarn,
logDebug: utils.logDebug,
/**
* An asynchronous HTTP request. Returns a Promise, what resolves to the {@link module:transport#XHRResponse XHRResponse} object:
* @example
//Get some data from server:
const resp = await UB.xhr({url: 'getAppInfo'})
console.log('app info: %o', resp.data)
//The same, but in more short form via `UB.get` shorthand:
const resp = await UB.get('getAppInfo')
console.log('app info: %o', resp.data)
//Run POST method:
UB.post('ubql', [
{entity: 'uba_user', method: 'select', fieldList: ['*']}
]).then(function(resp) {
console.log('success!')
}, function(resp) {
console.log('request failed with status' + resp.status);
})
//retrieve binary data as ArrayBuffer
const resp = await UB.get('downloads/cert/ACSK(old).cer', {responseType: 'arraybuffer'})
console.log('Got ArrayBuffer of %d byte length', resp.data.byteLength);
* @param {object} requestConfig Object describing the request to be made and how it should be
* processed. The object has the following properties:
* @param {string} requestConfig.url Absolute or relative URL of the resource that is being requested
* @param {string} [requestConfig.method] HTTP method (e.g. 'GET', 'POST', etc). Default is GET
* @param {Object<string|object>} [requestConfig.params] Map of strings or objects which will be turned
* to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified.
* Keys and values are URL encoded inside a function.
* @param {string|object} [requestConfig.data] Data to be sent as the request message data
* @param {object} [requestConfig.headers] Map of strings or functions which return strings representing
* HTTP headers to send to the server. If the return value of a function is null, the
* header will not be sent. Merged with {@link xhrDefaults UB.xhrDefaults.headers}
* @param {function(data, function)|Array.<function(data, function)>} [requestConfig.transformRequest]
* Transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version.
* @param {function(data, function)|Array.<function(data, function)>} [requestConfig.transformResponse]
* Transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version.
* @param {number|Promise} [requestConfig.timeout] timeout in milliseconds, or {Promise}
* that should abort the request when resolved. Default to {UB.xhrDefaults.timeout}
* @param {boolean} [requestConfig.withCredentials] whether to to set the `withCredentials` flag on the
* XHR object. See <a href="https://developer.mozilla.org/en/http_access_control#section_5">requests with credentials</a>
* for more information.
* @param {string} [requestConfig.responseType] see <a href="https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType">responseType</a>.
* @param {Function} [requestConfig.onProgress] XHR onProgress callback, see <a href="https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent">ProgressEvent</a> for details.
* To be user instead obsolete Q Promise.progress()
* @returns {Promise<XHRResponse>}
*/
xhr: function (requestConfig) { return transport.xhr(requestConfig) },
/**
* Shortcut for {@link module:@unitybase/ub-pub#xhr UB.xhr} to perform a `GET` request
* @example
// GET http://my-api.com?param=val
const resp = await UB.get('http://my-api.com', {params: {param: 'val'}})
console.log(resp.data)
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {object} [config] Optional configuration object as in {@link xhr UB.xhr}
* @returns {Promise<XHRResponse>}
*/
get: function (url, config) { return transport.get(url, config) },
/**
* Shortcut for {@link module:@unitybase/ub-pub#xhr UB.xhr} to perform a `POST` request
* @example
// POST http://my-api.com?param1=12¶m2=someVal with body contains a stringified object
const resp = await UB.post('http://my-api.com', {this: 'is', body: 'of' request}, {params: {param1: 12, param2: 'someVal'}})
console.log(resp.data)
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
* @param {object} [config] Optional configuration object as in {@link module:@unitybase/ub-pub#xhr UB.xhr}
* @returns {Promise<XHRResponse>}
*/
post: function (url, data, config) { return transport.post(url, data, config) },
/**
* Class for communicate with native messages plugin `content script`.
* @type {UBNativeMessage}
*/
UBNativeMessage,
UBConnection: conn,
/**
* Client side cache
* @type {UBCache}
*/
UBCache,
/**
* Create authorized connection to UnityBase server.
*
* For a browser clients in case value of `silenceKerberosLogin` localStorage key is 'true' and 'Negotiate'
* authorization method is enabled for application will try to authenticate user using Kerberos/NTLM method.
*
* Preferred locale tip: to define connection preferredLocale parameter call
* `localStorage.setItem(UB.LDS_KEYS.PREFERRED_LOCALE, 'uk')`
* **before** call to UBConnection.connect
* @example
const UB = require('@unitybase/ub-pub')
let conn = UB.connect({
host: window.location.origin,
path: window.location.pathname,
onCredentialRequired: function(conn, isRepeat){
if (isRepeat){
throw new UB.UBAbortError('invalid credential')
} else {
return Promise.resolve({authSchema: 'UB', login: 'admin', password: 'admin'})
}
},
onAuthorizationFail: function(reason){
alert(reason)
}
})
conn.then(function(conn){
conn.get('stat').then(function(statResp){
document.getElementById('ubstat').innerText = JSON.stringify(statResp.data, null, '\t')
})
conn.Repository('ubm_navshortcut').attrs(['ID', 'code', 'caption']).selectAsArray().then(function(data){
let tmpl = _.template(document.getElementById('repo-template').innerHTML);
let result = tmpl(data.resultData);
// document.getElementById('ubnav').innerText = JSON.stringify(data.resultData);
document.getElementById('ubnav').innerHTML = result;
})
})
* @param {object} cfg
* @param {string} cfg.host Server host
* @param {string} [cfg.path] API path - the same as in Server config `httpServer.path`
* @param {authParamsCallback} cfg.onCredentialRequired Callback for requesting a user credentials.
* See {@link authParamsCallback} description for details
* @param {request2faCallback} [cfg.onRequest2fa] Callback for requesting second factor (if needed).
* See {@link request2faCallback} description for details
* @param {boolean} [cfg.allowSessionPersistent=false] For a non-SPA browser client allow to persist a Session in the local storage between reloading of pages.
* In case user logged out by server side this type persistent not work and UBConnection will call onCredentialRequired handler,
* so user will be prompted for credentials
* @param {Function} [cfg.onAuthorizationFail] Callback for authorization failure. See {@link event:authorizationFail} event. Should handle all errors inside!
* @param {Function} [cfg.onAuthorized] Callback for authorization success.
* See {@link event:authorized} event. On this stage `connection.domain` is still not exists, so do not use Repository inside.
* @param {Function} [cfg.onNeedChangePassword] Callback for a password expiration. See {@link event:passwordExpired} event
* @param {Function} [cfg.onGotApplicationConfig] Called just after application configuration retrieved from server.
* Accept one parameter - connection: UBConnection
* Usually on this stage application inject some scripts required for authentication (locales, cryptography etc).
* Should return a promise then done
* @param {Function} [cfg.onGotApplicationDomain] Called after server returns domainInfo and `connection.domain` is initialized
* @returns {Promise<UBConnection>}
*/
connect: function (cfg) {
return conn.connect(cfg, this).then((conn) => {
if (_globalVueInstance && _globalVueInstance.$i18n) {
_globalVueInstance.$i18n.locale = conn.userLang()
}
this.Repository = function (entityCodeOrUBQL) {
if (typeof entityCodeOrUBQL === 'string') {
return new ClientRepository(conn, entityCodeOrUBQL)
} else {
return new ClientRepository(conn, '').fromUbql(entityCodeOrUBQL)
}
}
this.LocalRepository = function (source, entityName) {
return new LocalRepository(source, entityName, conn)
}
return conn
})
},
/**
* After call to UB.connect this property will point to the active connection
* @type {UBConnection}
*/
connection: null,
/**
* @type {ClientRepository}
*/
ClientRepository,
/**
* Create a new instance of repository for a current connection.
* To be used after connection is created.
*
* @param {string|object} entityCodeOrUBQL The name of the Entity for which the Repository is being created or UBQL
* @returns {ClientRepository}
*/
Repository: function (entityCodeOrUBQL) {
throw new Error('function defined only after connect()')
},
/**
* Create a new instance of a repository based on a local data
* @example
const UB = require('@unitybase/ub-pub')
const localData = {data: [[1, 'Jon'], [2, 'Bob']], fields: ['ID', 'name'], rowCount: 2}
await UB.LocalRepository(localData, 'uba_user').attrs('name').where('ID', '=', 2).selectScalar() // "Bob"
* @example
const UB = require('@unitybase/ub-pub')
const localData = [{ID: 1, name: 'Jon'}, {ID: 2, name: 'Bob'}]
await UB.LocalRepository(localData, 'uba_user').attrs('ID', 'name').selectAsArray()
* @param {Array|TubLocalData} localData
* @param {string} entityName
* @returns {LocalRepository}
*/
LocalRepository: function (localData, entityName) {
return new LocalRepository(localData, entityName, null)
},
/**
* Set an error reporter callback for unhandled errors (including unhandled promise rejections).
* Callback signature `function({errMsg, errCode, entityCode, detail})`
* - `errMsg` is already translated using UB.i18n
*
* This callback also called inside `UBPub.showErrorWindow`
*
* @param {Function} errorReportedFunction
*/
setErrorReporter: function (errorReportedFunction) {
_errorReporter = errorReportedFunction
},
/**
* Default error reported handler. Will translate error message using {@link UB#i18n i18n}.
*
* For a UI other then adminUI developer can call `UB.setErrorReporter` to set his own error reporter
* @example
const UB = require('@unitybase/ub-pub')
const vm = new Vue({
...
methods: {
showError: function(errMsg, errCode, entityCode, detail) {
this.$message({
showClose: true,
message: errMsg,
type: 'error'
})
}
...
})
UB.setErrorReporter(vm.showError.bind(vm))
* @param {string|object|Error|UBError} errMsg message to show
* @param {string} [errCode] error code
* @param {string} [entityCode] entity code
* @param {string} [detail] details
*/
showErrorWindow: function (errMsg, errCode, entityCode, detail) {
let parsed
try {
parsed = utils.parseUBError(errMsg, errCode, entityCode, detail)
if (_errorReporter) {
_errorReporter(parsed)
} else if (this.userAgent && typeof window !== 'undefined') {
window.alert(parsed.errMsg)
} else if (typeof console !== 'undefined') {
console.error(parsed.errMsg, parsed.detail)
}
} catch (e) {
console.error(e)
}
},
/**
* @deprecated Use connection.appConfig instead
*/
appConfig: null,
/**
* Helper class for manipulation with data, stored locally in ({@link TubCachedData} format)
*
* @type {module:LocalDataStore}
*/
LocalDataStore,
userAgent: utils.userAgent,
isChrome: utils.isChrome,
isWebKit: utils.isWebKit,
isGecko: utils.isGecko,
isOpera: utils.isOpera,
isMac: utils.isMac,
isSecureBrowser: utils.isSecureBrowser,
isReactNative: utils.isReactNative,
isNodeJS: utils.isNodeJS,
/**
* Inject external script or css to DOM and return a promise to be resolved when script is loaded.
*
* if script successfully loaded using inject it will not be loaded anymore with repeatable calls to UB.inject.
*
* @example
//Load script.js:
UB.inject('jslibs/script.js')
//Load several script at once and error handling:
Promise.all([UB.inject('jslibs/script.js'), UB.inject('script2.js')])
.catch(function(err){
console.log('Oh! error occurred: ' + err)
})
//Load one script and then load other
UB.inject('jslibs/js_beautify.js').then(function(){
console.log('first script loaded. Continue to load second')
return UB.inject('jslibs/js_beautify1.js')
})
//Load a couple of resources:
Promise.all([UB.inject('css/first.css'), UB.inject('css/second.css')])
* @param {string} url either *js* or *css* resource to load
* @param {string} [charset]
* @returns {Promise}
*/
inject: function (url, charset) { return injection.inject(url, charset) },
addResourceVersion: injection.addResourceVersion,
/**
* CryptoJS instance (included modules are enc-base64, sha256, md5)
*/
CryptoJS,
/**
* Calculate SHA256 checksum
*/
SHA256,
/**
* Calculate MD5 checksum
*/
MD5,
/**
* localDataStorage keys used by @unitybase-ub-pub (in case of browser environment)
*
* @readonly
* @enum
*/
LDS_KEYS: utils.LDS_KEYS,
/**
* Legacy for old adminUI
*
* @private
*/
ux: {},
/**
* Legacy for old adminUI
*
* @private
*/
view: {},
/**
* Legacy for old adminUI
*
* @private
*/
core: {},
/**
* Legacy for old adminUI. UBUtil.js will define this property
*
* @private
*/
Utils: {},
LIMITS: {
/**
* lookups are limited to this value using limit(lookupMaxRows). If result contains a lookupMaxRows rows - error outputted into console.error
*/
lookupMaxRows: 10000,
/**
* If lookup contains more when lookupWarningRows - outputted console.warn
*/
lookupWarningRows: 2500
}
}
/**
* Allow Request reiteration, for example in case of request are repeated after re-auth
*
* @method
*/
UB.xhr.allowRequestReiteration = transport.xhr.allowRequestReiteration
/**
* Direct access to the default HTTP parameters for {xhr}. Can be used, for example, to change http request timeout globally
*
* @example
const UB = require('@unitybase/ub-pub')
UB.xhr.defaults.timeout = 300000 // set all ajax requests timeout to 5 minutes
*/
UB.xhr.defaults = transport.xhrDefaults
/**
* Vue.js integration
* - inject UB localization {@link UB.i18n UB.i18n} to global Vue instance as $ut:
* - inject `@unitybase/ub-pub` to global Vue instance as $UB
*
* @example
const Vue = require('vue')
const UB = require('@unitybase/ub-pub')
Vue.use(UB)
// localization of vue template
<button >{{ $ut('Enter') }}</button>
// in case translation result is HTML + use formatting
<p v-html="$ut('UBAuthHeader', appName)"></p>
// inside binding
<el-tooltip :content="$ut('UBAuthTip')" placement="bottom" effect="light">
// inside vue methods
this.$ut('UBAuthTip')
// using UB inside vue methods
methods: {
hasNegotiate: function () {
return this.$UB.connection.authMethods.indexOf('Negotiate') !== -1
}
}
* @param {Vue} Vue
*/
UB.install = function (Vue) {
_globalVueInstance = Vue
// install method is moved out of module.exports to allow WebStorm code insight inside vue work `this.$UB.?`
Vue.prototype.$UB = UB
/** @inheritDoc */
Vue.prototype.$ut = UB.i18n
Vue.filter('i18n', UB.i18n)
}
let __alreadyAdded = false
let originalOnError = null
/**
* Intercept all unhandled errors including Promise unhandled rejections.
* Errors will be parsed and passed to UB.showErrorWindow {@see setErrorReporter setErrorReporter}
*/
function addBrowserUnhandledRejectionHandler (UBPub) {
if (typeof window === 'undefined' || UBPub.isReactNative || __alreadyAdded) return // non browser environment
if (__alreadyAdded) console.error('module @unitybase/ub-pub imported several times. This is wrong situation and should be fixed by app developer. Try `npm ddp`')
__alreadyAdded = true
if (window.onerror) {
originalOnError = window.onerror
}
// for a unhandled rejection in bluebird-q
if (window.Q && window.Q.getBluebirdPromise) {
window.Q.onerror = function (error) {
window.onerror.apply(UBPub, ['', '', '', '', error])
}
}
// for unhandled rejection in bluebird/native promises (IE 10+)
window.addEventListener('unhandledrejection', function (e) {
// NOTE: e.preventDefault() must be manually called to prevent the default
// action which is currently to log the stack trace to console.warn
e.preventDefault()
// NOTE: parameters are properties of the event detail property
const reason = e.detail ? e.detail.reason : e.reason
const promise = e.detail ? e.detail.promise : e.promise
// See Promise.onPossiblyUnhandledRejection for parameter documentation
if (window.onerror) window.onerror.apply(UBPub, ['', '', '', '', reason])
console.error('UNHANDLED', reason, promise)
})
window.onerror = ubGlobalErrorHandler
}
addBrowserUnhandledRejectionHandler(module.exports)
function ubGlobalErrorHandler (msg, file, line, column, errorObj) {
let message
let detail = ''
if (errorObj && utils.UBAbortError && errorObj instanceof utils.UBAbortError) {
console.log(errorObj)
return
}
let isHandled = errorObj && utils.UBError && errorObj instanceof utils.UBError
if (errorObj && Error && errorObj instanceof Error) {
message = errorObj.message
detail = ''
if (file && (/q\.js/.test(file) === false)) {
detail += 'file: "' + file + '" line: ' + line
}
const strace = errorObj.stack || ''
detail += strace
.replace(/\?ubver=\w*/g, '').replace(/\?ver=\w*/g, '') // remove any versions
.replace(/\\n(?!\d)/g, '\n\t') // beatify stack trace
detail = detail.replace(new RegExp(window.location.origin.replace(/:/g, '\\$&'), 'g'), '') // remove address if same as origin
detail = detail.replace(/\/[\w-]+(\.js)?:/g, '<b>$&</b> line ') // file name is BOLD
detail = detail.replace(/\n/g, '<br> ')
} else if (errorObj && errorObj.data && errorObj.data.errMsg) {
message = errorObj.data.errMsg
} else if (errorObj && errorObj.status === 0) { // long network request
message = 'serverIsBusy'
isHandled = true
} else if (errorObj && errorObj.errMsg) {
message = errorObj.errMsg
detail = errorObj.detail ? errorObj.detail : message
} else {
message = errorObj && (typeof errorObj === 'string') ? errorObj : msg
}
if (errorObj && errorObj.detail) {
detail = errorObj.detail.replace(/\\n(?!\d)/g, '\n\t\t') + // beatify stack trace
(detail ? '<br/>' + detail : '')
// 405 Method Not Allowed
if (errorObj.detail === 'Method Not Allowed') {
message = 'recordNotExistsOrDontHaveRights'
}
}
if (!message) {
message = 'internalServerError'
}
if (!isHandled) {
// see https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
// ResizeObserver loop completed with undelivered notifications
// ResizeObserver loop limit exceeded
if (typeof message === 'string' && message.startsWith('ResizeObserver')) {
console.warn(message)
return
}
if (!detail) {
detail = message
} else {
detail = `<h4>${message}</h4>${detail}`
}
message = 'unknownError'
}
try {
module.exports.showErrorWindow(message, '', '', detail)
} catch (err) {
window.alert(message)
}
if (originalOnError) originalOnError.apply(this, arguments)
}
if (typeof SystemJS !== 'undefined') { // browser
if (!SystemJS.has('@unitybase/cryptojs')) SystemJS.set('@unitybase/cryptojs', SystemJS.newModule(CryptoJS))
if (!SystemJS.has('@unitybase/ub-pub')) SystemJS.set('@unitybase/ub-pub', SystemJS.newModule(module.exports))
}