Source: app-wrapper/js/lib/appBase.js
/**
* @fileOverview AppBase class file
* @author Dino Ivankov <dinoivankov@gmail.com>
* @version 1.3.1
*/
const _ = require('lodash');
const path = require('path');
const BaseClass = require('./base').BaseClass;
let _appWrapper;
let appState;
/**
* App base class for extending when creating other app classes
*
* @class
* @extends {appWrapper.BaseClass}
* @memberOf appWrapper
* @property {Boolean} forceUserMessages Flag to force user message output
* @property {Boolean} forceDebug Flag to force debug message output
* @property {Object} boundMethods Object to hold bound method references for event listeners
* @property {Object} timeouts Object that holds references to this class instance timeouts
* @property {Object} intervals Object that holds references to this class instance intervals
* @property {Boolean} needsConfig Flag to indicate whether class instance needs config, triggering warnings if config is not available for the class
*/
class AppBaseClass extends BaseClass {
/**
* Creates class instance, setting basic properties, and returning the instance itself
*
* @constructor
* @return {appWrapper.AppBaseClass} Instance of current class
*/
constructor () {
super();
if (window && window.getAppWrapper && _.isFunction(window.getAppWrapper)){
_appWrapper = window.getAppWrapper();
appState = _appWrapper.getAppState();
_.noop(appState);
}
return this;
}
/**
* Initializes current class instance, setting up logging and
* bound methods to be used in event listeners
*
* @async
* @param {BaseInitializationOptions} options Initialization options
* @return {appWrapper.AppBaseClass} Instance of current class
*/
async initialize (options) {
return await super.initialize(options);
}
/**
* Determines whether logging for this class is regulated through
* configuration, setting the logging by it (or warning if there
* are no configuration settings for this class)
*
* @async
* @param {Object} options Options for logging initialization (currently only 'silent' property is used, determining whether warnings should be printed if no config found)
* @return {appWrapper.AppBaseClass} Instance of the current class
*/
async initializeLogging(options) {
let className = this.constructor.name;
if (appState && appState.config){
if (appState.config.debug && appState.config.debug.forceDebug){
if (_.isUndefined(appState.config.debug.forceDebug[className]) && !(options && options.silent)){
console.error('Class "' + className + '" has no forceDebug config set!');
} else {
this.forceDebug = _.get(appState.config.debug.forceDebug, className);
}
}
if (appState.config.userMessages && appState.config.userMessages.forceUserMessages){
if (_.isUndefined(appState.config.userMessages.forceUserMessages[className]) && !(options && options.silent)){
console.error('Class "' + className + '" has no forceUserMessages config set!');
} else {
this.forceUserMessages = _.get(appState.config.userMessages.forceUserMessages, className);
}
}
} else {
if (this.needsConfig && !(options && options.silent)){
console.warn('Could not get config object (class "' + className + '").');
}
}
return this;
}
/**
* Logs debug message if conditions are met
*
* Message is being interpolated by replacing placeholders
* such as '{1}', '{2}' etc. by corresponding values from 'data' argument
*
* @async
* @param {string} message Message to be logged
* @param {string} type Type of log message (debug, info, warning, error, group, groupCollaped, groupend)
* @param {array} data An array of data strings that are to be applied to logging message
* @param {Boolean} force Flag to force logging output even if config does not allow it
* @return {undefined}
*/
async log(message, type, data, force){
if (!data){
data = [];
}
if (!type){
type = 'info';
}
if (type == 'delimiter'){
return;
}
if (_.isUndefined(force)){
force = this.forceDebug;
}
let debugEnabled = this.getConfig('debug.enabled', false);
let debugLevel = this.getConfig('debug.debugLevel', 3);
let debugLevels = this.getConfig('logger.messageLevels');
let typeLevel = debugLevels && debugLevels[type] ? debugLevels[type] : 0;
let doLog = force || false;
if (!doLog && debugEnabled){
if (typeLevel >= debugLevel){
doLog = true;
}
}
let debugMessage = await this.getMessageObject(debugLevel, message, type, data, false, true, force);
if (doLog){
this._doLog(debugMessage);
if (this.getConfig('debug.alwaysTrace')){
console.trace();
}
if (this.getConfig('debug.passToMain')){
this.message({instruction:'log', data: {message: message, type: type, data: data, force: force}});
}
}
if (debugMessage && debugMessage.message && this.getConfig('debug.debugToFile')){
let messageLine = await this.getDebugMessageFileLine(_.cloneDeep(debugMessage));
let debugMessageFilePath = this.getDebugMessageFilePath();
await _appWrapper.fileManager.writeFileSync(path.resolve(debugMessageFilePath), messageLine, {flag: 'a'});
}
if (appState && appState.allDebugMessages && _.isArray(appState.allDebugMessages)){
let maxDebugMessages = this.getConfig('debug.maxDebugMessages', 1000);
if (appState.allDebugMessages.length > maxDebugMessages){
appState.allDebugMessages = [];
}
appState.allDebugMessages.push(debugMessage);
}
}
/**
* Does actual logging to console (and log file is file logging is enabled)
*
* @param {Object} debugMessage Message object to be logged (returned by this.getMessageObject method)
* @return {undefined}
*/
_doLog (debugMessage){
if (debugMessage.type == 'group'){
if (this.getConfig('debug.debugGroupsCollapsed')){
console.groupCollapsed(debugMessage.message);
} else {
console.group(debugMessage.message);
}
} else if (debugMessage.type == 'groupcollapsed'){
console.groupCollapsed(debugMessage.message);
} else if (debugMessage.type == 'groupend'){
console.groupEnd(debugMessage.message);
} else if (debugMessage.type == 'error'){
console.error(debugMessage.message);
} else if (debugMessage.type == 'warning'){
console.warn(debugMessage.message);
} else if (debugMessage.type == 'table'){
if (debugMessage.originalMessage){
if (!_.isArray(debugMessage.originalMessage)){
console.table([debugMessage.originalMessage]);
} else {
console.table(debugMessage.originalMessage);
}
} else {
console.log(debugMessage.message);
}
} else if (debugMessage.type == 'dir'){
if (debugMessage.originalMessage){
console.dir(debugMessage.originalMessage);
} else {
console.log(debugMessage.message);
}
} else {
console.log(debugMessage.message);
}
let maxVisibleDebugMessages = this.getConfig('debug.maxVisibleDebugMessages', 30);
let messageCount = this.getStateVar('debugMessages.length', 0);
if (messageCount > maxVisibleDebugMessages){
let startIndex = messageCount - (maxVisibleDebugMessages + 1);
if (appState && appState.debugMessages && _.isArray(appState.debugMessages)){
appState.debugMessages = appState.debugMessages.slice(startIndex);
}
}
if (appState && appState.debugMessages && _.isArray(appState.debugMessages)){
let lastMessage = _.last(appState.debugMessages);
if (lastMessage && lastMessage.message == debugMessage.message && lastMessage.type == debugMessage.type){
lastMessage.count++;
lastMessage.timestamps.push(debugMessage.timestamp);
} else {
appState.debugMessages.push(debugMessage);
}
}
}
/**
* Gets JSON represenation of message object for saving into log file,
* removing unneccessary properties and adding necessary ones
*
* @async
* @param {obj} message Message object to be logged (returned by this.getMessageObject method)
* @return {string} JSON encoded representation of message object
*/
async getMessageFileLine(message){
let msg = _.cloneDeep(message);
if (!msg.timestamp){
msg.timestamp = new Date().toString();
}
if (msg.count == 1){
delete msg.count;
delete msg.timestamps;
}
if (msg.type == 'table'){
// msg.type = 'debug';
}
delete msg.originalMessage;
delete msg.tableData;
delete msg.iconClass;
delete msg.force;
delete msg.important;
delete msg.active;
delete msg.typeLevel;
delete msg.stack;
delete msg.stackVisible;
return JSON.stringify(msg);
}
/**
* Returns string representing log line for appending
* to user message log file
*
* @async
* @param {obj} message Message object to be logged (returned by this.getMessageObject method)
* @return {string} String representing log line for appending to user message log file
*/
async getUserMessageFileLine (message){
let line = '';
if (appState.userMessagesToFileStarted){
line += ',\n';
} else {
appState.userMessagesToFileStarted = true;
}
line += await this.getMessageFileLine(message);
return line;
}
/**
* Returns string representing log line for appending
* to debug log file
*
* @async
* @param {obj} message Message object to be logged (returned by this.getMessageObject method)
* @return {string} String representing log line for appending to debug log file
*/
async getDebugMessageFileLine (message){
let line = '';
if (appState.debugToFileStarted){
line += ',\n';
} else {
appState.debugToFileStarted = true;
}
let msg = _.cloneDeep(message);
if (!msg.timestamp){
msg.timestamp = new Date().toString();
}
if (msg.count == 1){
delete msg.count;
delete msg.timestamps;
}
line += await this.getMessageFileLine(msg);
return line;
}
/**
* Logs user message if conditions are met
*
* Message is being interpolated by replacing placeholders
* such as '{1}', '{2}' etc. by corresponding values from 'data' argument
*
* @async
* @param {string} message Message to be logged
* @param {string} type Type of log message (debug, info, warning, error)
* @param {array} data An array of data strings that are to be applied to logging message
* @param {Boolean} important Flag to indicate message importance
* @param {Boolean} dontTranslate Flag to prevent automatic message translation
* @param {Boolean} force Flag to force message output even if configuration wouldn't allow it
* @param {Boolean} passToDebug Flag to force passing same message to debug log
* @return {undefined}
*/
async addUserMessage (message, type, data, important, dontTranslate, force, passToDebug){
if (!type){
type = 'info';
}
if (_.isUndefined(force)){
force = this.forceUserMessages;
}
if (_.isUndefined(passToDebug)){
passToDebug = this.forceDebug;
}
let userMessageLevel = this.getConfig('userMessages.userMessageLevel', 0);
let debugLevels = this.getConfig('logger.messageLevels');
let typeLevel = debugLevels && debugLevels[type] ? debugLevels[type] : 0;
let umHelper = _appWrapper.getHelper('userMessage');
if (!force){
force = false;
}
let userMessage = await this.getMessageObject(userMessageLevel, message, type, data, important, dontTranslate, force);
if (message && passToDebug){
this.log(userMessage.message, type, [], true);
}
if (force || typeLevel >= userMessageLevel){
let maxVisibleUserMessages = this.getConfig('userMessages.maxVisibleUserMessages', 30);
let messageCount = this.getStateVar('userMessages.length', 30);
if (messageCount > maxVisibleUserMessages){
let startIndex = messageCount - (maxVisibleUserMessages + 1);
if (appState && appState.userMessages && _.isArray(appState.userMessages)){
appState.userMessages = appState.userMessages.slice(startIndex);
}
}
if (appState && appState.userMessages && _.isArray(appState.userMessages)){
appState.userMessageQueue.push(userMessage);
}
if (appState.config.userMessages.hideUserMessages && type !== 'delimiter' && appState.status.appInitialized && !appState.status.appShuttingDown && !appState.appError.error){
let notificationDuration = this.getConfig('appNotifications.userMessageDuration');
if (type == 'warning' || type == 'error'){
notificationDuration *= 2;
}
await this.addNotification(message, type, data, dontTranslate, {duration: notificationDuration, pinned: false});
}
}
if (userMessage && userMessage.type && userMessage.type != 'delimiter' && userMessage.message && this.getConfig('userMessages.userMessagesToFile')){
let messageLine = await this.getUserMessageFileLine(_.cloneDeep(userMessage));
let userMessageFilePath = this.getUserMessageFilePath();
await window.getAppWrapper().fileManager.writeFileSync(path.resolve(userMessageFilePath), messageLine, {flag: 'a'});
}
if (appState && appState.allUserMessages && _.isArray(appState.allUserMessages)){
let maxUserMessages = this.getConfig('userMessages.maxUserMessages', 1000);
if (appState.allUserMessages.length > maxUserMessages){
appState.allUserMessages = [];
}
appState.allUserMessages.push(userMessage);
}
umHelper.processUserMessageQueue();
}
/**
* Returns prepared message object based on passed arguments.
*
* Message is being interpolated by replacing placeholders
* such as '{1}', '{2}' etc. by corresponding values from 'data' argument
*
* @async
* @param {int} messageLevel Number representing current message level (0=debug, 1=info, 2=warning, 3=error)
* @param {string} message Message to be logged
* @param {string} type Type of log message (debug, info, warning, error)
* @param {array} data An array of data strings that are to be applied to logging message
* @param {Boolean} important Flag to indicate message importance
* @param {Boolean} dontTranslate Flag to prevent automatic message translation
* @param {Boolean} force Flag to force message output even if configuration wouldn't allow it
* @return {Object} Object that represents log message
*/
async getMessageObject (messageLevel, message, type, data, important, dontTranslate, force){
let userMessage = {};
let debugLevels = this.getConfig('logger.messageLevels');
let typeLevel = debugLevels && debugLevels[type] ? debugLevels[type] : 0;
let timestamp = new Date().toString();
let iconClass = 'fa fa-info-circle';
let originalMessage = _.cloneDeep(message);
let tableData;
if (type == 'warning'){
iconClass = 'fa fa-exclamation-circle';
} else if (type == 'error'){
iconClass = 'fa fa-exclamation-triangle';
}
if (type == 'table' && (_.isObject(message) || _.isArray(message))) {
tableData = await this.getTableMessageData(message);
}
if (message && !dontTranslate && window && window.getAppWrapper() && window.getAppWrapper().appTranslations && window.getAppWrapper().appTranslations.translate){
message = window.getAppWrapper().appTranslations.translate(message);
}
if (message && message.match && message.match(/{(\d+)}/) && _.isArray(data) && data.length) {
message = message.replace(/{(\d+)}/g, (match, number) => {
let index = number - 1;
return !_.isUndefined(data[index]) ? data[index] : match;
});
}
let stack = this._getStack();
userMessage = {
count: 1,
timestamps: [timestamp],
timestamp: timestamp,
message: message,
originalMessage: originalMessage,
tableData: tableData,
iconClass: iconClass,
type: type,
important: important,
force: force,
active: messageLevel >= typeLevel,
typeLevel: typeLevel,
stack: stack,
stackVisible: false
};
if (!message){
userMessage.message = ' ';
userMessage.originalMessage = ' ';
userMessage.type = 'delimiter';
userMessage.timestamp = '';
userMessage.iconClass = '';
}
return userMessage;
}
/**
* Prepares table data for tabular message logging
*
* @async
* @param {Object} message Tabular data
* @return {Object} Table data with tableColumns and tableRows properties
*/
async getTableMessageData(message){
let localMessage = _.cloneDeep(message);
if (!_.isArray(localMessage)){
localMessage = [localMessage];
}
let messageRows = [];
for (let name in localMessage){
messageRows.push(await this.getMessageObjectRow(name, localMessage[name]));
}
let tableData = {
tableRows: messageRows
};
// console.log(JSON.stringify(tableData, ' ', 4));
return tableData;
}
/**
* Gets single row for tabular message logging
*
* @async
* @param {mixed} index Key or array index
* @param {Object} messageRowData Message row data for logging
* @return {Object} Object with row data
*/
async getMessageObjectRow(index, messageRowData) {
let rowContents = {
__columns: [],
__type: 'table',
__data: {}
};
if (_.isObject(messageRowData) || _.isArray(messageRowData)){
for (let name in messageRowData){
let namedRowData = messageRowData[name];
if (_.isObject(namedRowData)){
let newRows = await this.getMessageObjectRow(name, namedRowData);
rowContents.__type = 'table';
rowContents.__columns.push(name);
rowContents.__data[name] = newRows;
} else if (_.isArray(namedRowData)){
let newRows = [];
for (let i=0; i<namedRowData.length; i++){
let newRow = await this.getMessageObjectRow(i, namedRowData[i]);
newRows.push(newRow);
}
rowContents.__type = 'row';
rowContents.__columns.push(name);
rowContents.__data[name] = newRows;
} else {
let newData = {
__type: 'cell',
};
newData.__data = namedRowData;
rowContents.__columns.push(name);
rowContents.__data[name] = newData;
}
}
} else {
rowContents.__type = 'cell';
rowContents.__data[index] = messageRowData[index];
rowContents.__columns.push(index);
}
return rowContents;
}
/**
* Returns path to user message log file
*
* @return {string} Path to user message log file
*/
getUserMessageFilePath () {
let userMessageFilePath = path.join(_appWrapper.getExecPath(), this.getConfig('appConfig.logDir'), this.getConfig('userMessages.userMessagesFilename'));
let rotateLogs = this.getConfig('userMessages.rotateLogs');
if (rotateLogs){
userMessageFilePath += '-' + new Date(sessionStorage.getItem('appStartTime')).toISOString().replace(/T.*$/, '');
}
userMessageFilePath += '.json';
return userMessageFilePath;
}
/**
* Returns path to debug message log file
*
* @return {string} Path to debug message log file
*/
getDebugMessageFilePath () {
let debugMessageFilePath = path.join(_appWrapper.getExecPath(), this.getConfig('appConfig.logDir'), this.getConfig('debug.debugMessagesFilename'));
let rotateLogs = this.getConfig('debug.rotateLogs');
if (rotateLogs){
debugMessageFilePath += '-' + new Date(sessionStorage.getItem('appStartTime')).toISOString().replace(/T.*$/, '');
}
debugMessageFilePath += '.json';
return debugMessageFilePath;
}
/**
* Adds modal message to currently open modal dialog
*
* Message is being interpolated by replacing placeholders
* such as '{1}', '{2}' etc. by corresponding values from 'data' argument
*
* @async
* @param {string} message Message to be logged
* @param {string} type Type of log message (debug, info, warning, error)
* @param {array} data An array of data strings that are to be applied to logging message
* @param {Boolean} important Flag to indicate message importance
* @param {Boolean} dontTranslate Flag to prevent automatic message translation
* @param {Boolean} force Flag to force message output even if configuration wouldn't allow it
* @param {Boolean} passToDebug Flag to force passing same message to debug log
* @return {undefined}
*/
async addModalMessage (message, type, data, important, dontTranslate, force, passToDebug){
if (!type){
type = 'info';
}
if (_.isUndefined(force)){
force = this.forceUserMessages;
}
if (_.isUndefined(passToDebug)){
passToDebug = this.forceDebug;
}
let messageLevel = 0;
if (!force){
force = false;
}
let userMessage = await this.getMessageObject(messageLevel, message, type, data, important, dontTranslate, force);
_appWrapper.getHelper('modal').addModalMessage(userMessage);
}
/**
* Displays app notification
*
* Notification message is being interpolated by replacing placeholders
* such as '{1}', '{2}' etc. by corresponding values from 'data' argument
*
* @async
* @param {string} message Notification message
* @param {string} type Notification message type
* @param {array} data An array of data strings that are to be applied to notification
* @param {Boolean} dontTranslate Flag to prevent automatic notification translation
* @param {Object} options Additional notification options
* @return {undefined}
*/
async addNotification (message, type, data, dontTranslate, options){
let notification = await this.getMessageObject(0, message, type, data, false, dontTranslate);
let duration = _appWrapper.getConfig('appNotifications.duration');
if (options && !_.isUndefined(options.duration)){
duration = options.duration;
}
notification.duration = duration;
notification.pinned = false;
if (options && options.pinned){
notification.pinned = true;
} else {
notification.pinned = false;
}
if (options && options.immediate){
notification.immediate = true;
} else {
notification.immediate = false;
}
if (!duration){
notification.pinned = true;
notification.duration = _appWrapper.getConfig('appNotifications.duration');
}
await _appWrapper.getHelper('appNotifications').addNotification(notification);
await _appWrapper.wait(this.getConfig('minPauseDuration'));
}
/**
* Displays desktop notification
*
* Notification message is being interpolated by replacing placeholders
* such as '{1}', '{2}' etc. by corresponding values from 'data' argument
*
* @async
* @param {string} message Notification message
* @param {array} data An array of data strings that are to be applied to notification
* @param {Boolean} dontTranslate Flag to prevent automatic notification translation
* @param {Object} options Desktop notification options (passed to HTML5 Notification object constructor)
* @param {Object} callbacks Object with onshow, onClicked, onClosed and onerror notification handlers
* @return {undefined}
*/
async addDesktopNotification (message, data, dontTranslate, options, callbacks){
let notification = await this.getMessageObject(0, message, 'info', data, false, dontTranslate);
return await _appWrapper.getHelper('appNotifications').addDesktopNotification(notification, options, callbacks);
}
/**
* Returns configuration var value
*
* @param {string} name String representing path to requested var (i.e. 'appConfig.appInfo.name')
* @param {mixed} defaultValue Default value to be returned if configuration var is not found
* @return {mixed} configuration var value
*/
getConfig (name, defaultValue){
let path = name;
let value;
if (!path.match(/^config\./)){
path = 'config.' + name;
}
value = _.get(appState, path);
if (_.isUndefined(value)){
path = name;
if (!path.match(/^appWrapperConfig\./)){
path = 'appWrapperConfig.' + name;
}
value = _.get(appState.u, path);
}
if (_.isUndefined(value)){
path = name;
if (!path.match(/^userConfig\./)){
path = 'userConfig.' + name;
}
value = _.get(appState.u, path);
}
if (!value && !_.isUndefined(defaultValue)){
value = defaultValue;
}
return value;
}
/**
* Returns translated value for passed arguments
*
* Translation is being interpolated by replacing placeholders
* such as '{1}', '{2}' etc. by corresponding values from 'data' argument
*
* @param {string} text Text to be translated
* @param {string} currentLanguage Curent language code
* @param {array} data An array of data strings that are to be applied to translated message
* @return {string} Translated message with interpolated data
*/
translate (text, currentLanguage, data) {
let translation = text;
if (appState.status.languageInitialized && _appWrapper && _appWrapper.appTranslations && _appWrapper.appTranslations.translate && _.isFunction(_appWrapper.appTranslations.translate)){
translation = _appWrapper.appTranslations.translate(text, currentLanguage, data);
}
return translation;
}
/**
* Emits event to globalEmitter (listened by both main script and app code)
*
* @param {String} eventName Name of the event
* @param {Object} data Event data object
* @return {undefined}
*/
globalEmit (eventName, data){
_appWrapper.windowManager.win.globalEmitter.emit(eventName, data);
}
/**
* Emits 'message' global event, listened by main script
*
* @async
* @param {Object} data Event data object
* @return {undefined}
*/
async message (data){
let returnPromise;
let resolveReference;
returnPromise = new Promise((resolve) => {
resolveReference = resolve;
});
if (data){
if (!data.uuid){
data.uuid = this.getHelper('util').uuid();
}
} else {
data = {
uuid: this.getHelper('util').uuid()
};
}
data._async_ = false;
if (!_appWrapper.windowManager.win.globalEmitter){
this.log('Can not send message - globalEmitter not available', 'error', []);
return false;
} else {
let listener = (messageData) => {
if (messageData && messageData.uuid && messageData.uuid == data.uuid){
_appWrapper.windowManager.win.globalEmitter.removeListener('messageResponse', listener);
resolveReference(messageData);
}
};
_appWrapper.windowManager.win.globalEmitter.on('messageResponse', listener);
_appWrapper.windowManager.win.globalEmitter.emit('message', data);
return returnPromise;
}
}
/**
* Emits 'asyncMessage' global event, listened by main script
*
* @async
* @param {Object} data Message data object
* @return {mixed} Returns data returned by main script async message handler for given message instruction
*/
async asyncMessage (data){
let returnPromise;
let resolveReference;
returnPromise = new Promise((resolve) => {
resolveReference = resolve;
});
if (data){
if (!data.uuid){
data.uuid = this.getHelper('util').uuid();
}
} else {
data = {
uuid: this.getHelper('util').uuid()
};
}
data._async_ = true;
let listener;
listener = (returnData) => {
if (data.uuid == returnData.uuid){
_appWrapper.windowManager.win.globalEmitter.removeListener('asyncMessageResponse', listener);
resolveReference(returnData);
}
};
_appWrapper.windowManager.win.globalEmitter.on('asyncMessageResponse', listener);
_appWrapper.windowManager.win.globalEmitter.emit('asyncMessage', data);
return returnPromise;
}
/**
* Returns info on messages that can be passed to mainScript
*
* @async
* @param {Object} data Message data.data object
* @param {Boolean} verboseOutput Toggles verbose output
* @return {Object} Object with handlerMethods property containing all handler method names
*/
async messageInfo (data, verboseOutput) {
let returnPromise;
let resolveReference;
returnPromise = new Promise((resolve) => {
resolveReference = resolve;
});
let uuid = this.getHelper('util').uuid();
let listener = (messageData) => {
if (messageData && messageData.uuid && messageData.uuid == uuid){
_appWrapper.windowManager.win.globalEmitter.removeListener('messageResponse', listener);
this.messageInfoOutput(messageData, verboseOutput);
resolveReference(messageData);
}
};
_appWrapper.windowManager.win.globalEmitter.on('messageResponse', listener);
this.message({instruction: 'info', uuid: uuid, data: data});
return returnPromise;
}
/**
* Returns info on async messages that can be passed to mainScript
*
* @async
* @param {Object} data Message data.data object
* @param {Boolean} verboseOutput Toggles verbose output
* @return {Object} Object with handlerMethods property containing all handler method names
*/
async asyncMessageInfo (data, verboseOutput) {
let responseData = await this.asyncMessage({instruction: 'info', data: data});
this.messageInfoOutput(responseData, verboseOutput);
return responseData;
}
/**
* Logs messages info to console
* @param {Object} messageData Message response data
* @param {Boolean} verboseOutput Toggles verbose output
* @return {undefined} [description]
*/
messageInfoOutput (messageData, verboseOutput) {
if (messageData && messageData.data && messageData.data.handlerMethods){
let handlerMethodNames = Object.keys(messageData.data.handlerMethods);
let handlerMethodsData = [];
this.log('Handler methods: "{1}"', 'info', [handlerMethodNames.join('", "')], true);
if (verboseOutput){
for (let name in messageData.data.handlerMethods){
let requiredParams = messageData.data.handlerMethods[name].join('\n');
let exampleCallData = [];
let exampleCallProps = [];
let exampleCall = [];
let exampleCallString = '\nappWrapper.message({';
if (messageData._async_){
exampleCallString = '\nappWrapper.asyncMessage({';
}
exampleCallProps.push('instruction: \'' + name + '\'');
if (requiredParams){
messageData.data.handlerMethods[name].forEach((paramName) => {
if (paramName.match(/^data\./)){
exampleCallData.push(paramName.replace(/^data\./, '') + ': \'_value_\'');
} else {
exampleCallProps.push(paramName + ': \'_value_\'');
}
});
}
if (exampleCallProps && exampleCallProps.length){
exampleCall.push('\n ' + exampleCallProps.join(',\n '));
}
if (exampleCallData && exampleCallData.length){
exampleCall.push('\n data: {');
exampleCall.push('\n ' + exampleCallData.join(',\n '));
exampleCall.push('\n }');
}
exampleCallString += exampleCall.join('');
exampleCallString += '\n});';
handlerMethodsData.push({
'Message instruction': name,
'Example call': exampleCallString,
'Required parameters': requiredParams
});
}
if (handlerMethodsData.length == 1){
this.log(handlerMethodsData[0], 'table', [], true);
} else {
this.log(handlerMethodsData, 'table', [], true);
}
}
}
}
/**
* Sets (turns on) application error, triggering rendering of app-error component
*
* @param {String} title App error title
* @param {String} text App error text
* @param {String} debugText App error debug text (shown only if debug is enabled)
* @param {String[]} data An array with replacement data for error title, text and debugText
* @param {Boolean} doNotTranslate Flag to prevent automatic traslation of title and text
* @param {(String|null)} messageType Type of messages to show in app-error ('user', 'debug' or null)
* @param {Boolean} omitIcon Flag to control app-error icon rendering
* @return {undefined}
*/
setAppError (title, text, debugText, data, doNotTranslate, messageType, omitIcon) {
if (!title){
title = appState.appError.defaultTitle;
}
if (_.isUndefined(text)){
text = appState.appError.defaultText;
}
if (_.isUndefined(debugText)){
debugText = '';
}
if (!doNotTranslate){
if (title){
title = this.translate(title);
}
if (text){
text = this.translate(text);
}
}
if (title && title.match && title.match(/{(\d+)}/) && _.isArray(data) && data.length) {
title = title.replace(/{(\d+)}/g, (match, number) => {
let index = number - 1;
return !_.isUndefined(data[index]) ? data[index] : match;
});
}
if (text && text.match && text.match(/{(\d+)}/) && _.isArray(data) && data.length) {
text = text.replace(/{(\d+)}/g, (match, number) => {
let index = number - 1;
return !_.isUndefined(data[index]) ? data[index] : match;
});
}
if (debugText && debugText.match && debugText.match(/{(\d+)}/) && _.isArray(data) && data.length) {
debugText = debugText.replace(/{(\d+)}/g, (match, number) => {
let index = number - 1;
return !_.isUndefined(data[index]) ? data[index] : match;
});
}
appState.appError.title = title;
appState.appError.text = text;
appState.appError.debugText = debugText;
if (!_.isUndefined(messageType)) {
appState.appError.messages = messageType;
}
if (omitIcon){
appState.appError.icon = false;
}
appState.appError.error = true;
}
/**
* Resets (turns off) application error, removing app-error component
*
* @return {undefined}
*/
resetAppError () {
appState.appError.error = false;
appState.appError.title = '';
appState.appError.text = '';
appState.appError.debugText = '';
appState.appError.icon = true;
appState.appError.messages = 'user';
}
/**
* Checks whether debug mode is on
*
* @return {Boolean} True if debug is enabled, false otherwise
*/
isDebugEnabled () {
return this.getConfig('debug.enabled', false);
}
}
exports.AppBaseClass = AppBaseClass;