* Allow passing of arguments to menu's via menu.json including formatted parameters

* Fix menu.json setting of focus on view
This commit is contained in:
Bryan Ashby 2015-04-04 14:41:04 -06:00
parent ae71aa9feb
commit 113e16df0d
11 changed files with 131 additions and 108 deletions

View File

@ -7,7 +7,6 @@ var assert = require('assert');
var term = require('./client_term.js');
var miscUtil = require('./misc_util.js');
var ansi = require('./ansi_term.js');
var logger = require('./logger.js'); // :TODO: cleanup and just use Log.
var Log = require('./logger.js').log;
var user = require('./user.js');
var moduleUtil = require('./module_util.js');
@ -196,19 +195,27 @@ Client.prototype.address = function() {
return this.input.address();
};
Client.prototype.gotoMenuModule = function(name, cb) {
Client.prototype.gotoMenuModule = function(options, cb) {
var self = this;
assert(options.name);
// Assign a default missing module handler callback if none was provided
cb = miscUtil.valueWithDefault(cb, self.defaultHandlerMissingMod());
self.detachCurrentMenuModule();
menuUtil.loadMenu(name, self, function onMenuModuleLoaded(err, modInst) {
var loadOptions = {
name : options.name,
client : self,
args : options.args
};
menuUtil.loadMenu(loadOptions, function onMenuModuleLoaded(err, modInst) {
if(err) {
cb(err);
} else {
Log.debug({ menuName : name }, 'Goto menu module');
Log.debug({ name : options.name }, 'Goto menu module');
modInst.enter(self);
@ -221,11 +228,12 @@ Client.prototype.gotoMenuModule = function(name, cb) {
// Default error handlers
///////////////////////////////////////////////////////////////////////////////
// :TODO: getDefaultHandler(name) -- handlers in default_handlers.js or something
Client.prototype.defaultHandlerMissingMod = function(err) {
var self = this;
function handler(err) {
logger.log.error(err);
Log.error(err);
self.term.write(ansi.resetScreen());
self.term.write('An unrecoverable error has been encountered!\n');

View File

@ -2,7 +2,7 @@
'use strict';
// ENiGMA½
var logger = require('./logger.js');
var Log = require('./logger.js').log;
var iconv = require('iconv-lite');
var assert = require('assert');
@ -41,7 +41,7 @@ function ClientTerminal(output) {
if(iconv.encodingExists(enc)) {
outputEncoding = enc;
} else {
logger.log.warn({ encoding : enc }, 'Unknown encoding');
Log.warn({ encoding : enc }, 'Unknown encoding');
}
}
});

View File

@ -84,7 +84,8 @@ function connectEntry(client) {
setTimeout(function onTimeout() {
term.write(ansi.clearScreen());
client.gotoMenuModule(Config.entryMod);
client.gotoMenuModule({ name : Config.entryMod } );
//moduleUtil.goto(Config.entryMod, client);
}, timeout);
});

View File

@ -3,17 +3,18 @@
var PluginModule = require('./plugin_module.js').PluginModule;
var theme = require('./theme.js');
var Log = require('./logger.js').log;
var async = require('async');
var assert = require('assert');
exports.MenuModule = MenuModule;
function MenuModule(menuConfig) {
PluginModule.call(this);
function MenuModule(options) {
PluginModule.call(this, options);
var self = this;
this.menuConfig = menuConfig;
this.menuConfig = options.menuConfig;
this.viewControllers = [];
@ -41,7 +42,7 @@ function MenuModule(menuConfig) {
callback(null);
}
],
function onComplete(err) {
function complete(err) {
if(err) {
// :TODO: Log me!!! ... and what else?
console.log(err);

View File

@ -17,7 +17,14 @@ var stripJsonComments = require('strip-json-comments');
exports.loadMenu = loadMenu;
exports.getFormConfig = getFormConfig;
function loadMenu(name, client, cb) {
function loadMenu(options, cb) {
assert(options);
assert(options.name);
assert(options.client);
var name = options.name;
var client = options.client;
/*
TODO:
* check access / ACS
@ -44,21 +51,23 @@ function loadMenu(name, client, cb) {
});
},
function menuConfigLoaded(menuConfig, callback) {
Log.debug( { config : menuConfig }, 'Menu configuration loaded');
var moduleName = menuConfig.module || 'standard_menu';
moduleUtil.loadModule(moduleName, 'mods', function onModule(err, mod) {
callback(err, mod, menuConfig, moduleName);
});
},
}
],
function complete(err, mod, menuConfig, moduleName) {
if(err) {
cb(err);
} else {
Log.debug( { moduleName : moduleName, moduleInfo : mod.moduleInfo }, 'Loading menu module instance');
cb(null, new mod.getModule(menuConfig));
Log.debug(
{ moduleName : moduleName, args : options.args, config : menuConfig, info : mod.moduleInfo },
'Creating menu module instance');
// :TODO: throw from MenuModule() - catch here
cb(null, new mod.getModule({ menuConfig : menuConfig, args : options.args } ));
}
}
);
@ -92,61 +101,3 @@ function getFormConfig(menuConfig, formId, mciMap, cb) {
cb(new Error('No matching form configuration found'));
}
/*
function getFormConfig(menuConfig, mciMap, cb) {
assert(menuConfig);
if(!menuConfig.form) {
cb(new Error('No form section specified for menu'));
return;
}
async.filter(
menuConfig.form,
function check(form, callback) {
if(!form.mciReq || form.mciReq.length <= 0) {
callback(false);
return;
}
var count = form.mciReq.length;
if(Object.keys(mciMap).length === count) {
for(var i = 0; i < count; ++i) {
if(!mciMap[form.mciReq[i]]) {
callback(false);
}
}
callback(true);
}
},
function filtered(form) {
if(form.length > 0) {
assert(1 === form.length);
cb(null, form[0]);
} else {
cb(new Error('No matching form configuration found'));
}
}
);
}
*/
/*
function getFormConfig(menuConfig, mciMap) {
var count = menuConfig.form ? menuConfig.form.length : 0;
var mciReq;
for(var i = 0; i < count; ++i) {
mciReq = menuConfig.form[i].mciReq;
if(mciReq) {
if(mciReq.length === mciMap.length) {
for(var m = 0; m < mciReq.length; ++m) {
if(!mciMap[mciReq[m]]) {
return null;
}
}
}
}
}
}
*/

View File

@ -3,5 +3,5 @@
exports.PluginModule = PluginModule;
function PluginModule() {
function PluginModule(options) {
}

View File

@ -3,7 +3,7 @@
// ENiGMA½
var baseClient = require('../client.js');
var logger = require('../logger.js');
var Log = require('../logger.js').log;
var ServerModule = require('../server_module.js').ServerModule;
var net = require('net');
@ -322,7 +322,7 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) {
event.type = vars.isOrInfo;
if(vars.newEnv === OPTIONS.NEW_ENVIRONMENT_DEP) {
logger.log.warn('Handling deprecated RFC 1408 NEW-ENVIRON');
Log.warn('Handling deprecated RFC 1408 NEW-ENVIRON');
}
});
@ -538,7 +538,7 @@ TelnetClient.prototype.setTermType = function(ttype) {
this.term.env['TERM'] = ttype;
this.term.termType = ttype;
logger.log.debug({ termType : ttype }, 'Set terminal type');
Log.debug({ termType : ttype }, 'Set terminal type');
}
TelnetClient.prototype.handleSbCommand = function(evt) {
@ -571,15 +571,15 @@ TelnetClient.prototype.handleSbCommand = function(evt) {
self.setTermType(evt.envVars[name]);
} else if('COLUMNS' === name && 0 === self.term.termWidth) {
self.term.termWidth = parseInt(evt.envVars[name]);
logger.log.debug({ termWidth : self.term.termWidth, updateSource : 'NEW-ENVIRON'}, 'Window width updated');
Log.debug({ termWidth : self.term.termWidth, updateSource : 'NEW-ENVIRON'}, 'Window width updated');
} else if('ROWS' === name && 0 === self.term.termHeight) {
self.term.termHeight = parseInt(evt.envVars[name]);
logger.log.debug({ termHeight : self.term.termHeight, updateSource : 'NEW-ENVIRON'}, 'Window height updated');
Log.debug({ termHeight : self.term.termHeight, updateSource : 'NEW-ENVIRON'}, 'Window height updated');
} else {
if(name in self.term.env) {
assert(evt.type === SB_COMMANDS.INFO);
logger.log.warn(
Log.warn(
{ varName : name, value : evt.envVars[name], existingValue : self.term.env[name] },
'Environment variable already exists');
} else {
@ -604,7 +604,7 @@ TelnetClient.prototype.handleSbCommand = function(evt) {
self.term.env['ROWS'] = evt.height;
}
logger.log.debug({ termWidth : evt.width , termHeight : evt.height, updateSource : 'NAWS' }, 'Window size updated');
Log.debug({ termWidth : evt.width , termHeight : evt.height, updateSource : 'NAWS' }, 'Window size updated');
} else {
console.log('unhandled SB: ' + JSON.stringify(evt));
}
@ -625,15 +625,15 @@ TelnetClient.prototype.handleMiscCommand = function(evt) {
//
if('ip' === evt.command) {
// Interrupt Process (IP)
logger.log.debug('Interrupt Process (IP) - Ending');
Log.debug('Interrupt Process (IP) - Ending');
this.input.end();
} else if('ayt' === evt.command) {
this.output.write('\b');
logger.log.debug('Are You There (AYT) - Replied "\\b"');
Log.debug('Are You There (AYT) - Replied "\\b"');
} else if(IGNORED_COMMANDS.indexOf(evt.commandCode)) {
logger.log.debug({ evt : evt }, 'Ignoring command');
Log.debug({ evt : evt }, 'Ignoring command');
} else {
logger.log.warn({ evt : evt }, 'Unknown command');
Log.warn({ evt : evt }, 'Unknown command');
}
};

View File

@ -4,7 +4,7 @@
var Config = require('./config.js').config;
var art = require('./art.js');
var miscUtil = require('./misc_util.js');
var logger = require('./logger.js').log;
var Log = require('./logger.js').log;
var fs = require('fs');
var paths = require('path');
var async = require('async');
@ -56,7 +56,7 @@ function initAvailableThemes(cb) {
availableThemes = {};
}
availableThemes[themeId] = info;
logger.debug( { info : info }, 'Theme loaded');
Log.debug( { info : info }, 'Theme loaded');
}
});
});

View File

@ -212,6 +212,9 @@ ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) {
var factory = new MCIViewFactory(this.client);
var self = this;
var formIdKey = options.formId ? options.formId.toString() : '0';
var initialFocusId;
// :TODO: remove all the passing of fromConfig - use local
async.waterfall(
[
@ -258,14 +261,13 @@ ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) {
view.submit = true; // :TODO: should really be actual value
}
if(mciConf.focus) {
self.switchFocus(viewId);
}
if(mciConf.text) {
view.setText(mciConf.text);
}
if(mciConf.focus) {
initialFocusId = viewId;
}
eachCb(null);
},
@ -321,17 +323,32 @@ ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) {
return true;
};
var conf;
for(var c = 0; c < confForFormId.length; ++c) {
if(_.isEqual(formData.value, confForFormId[c].value, formValueCompare)) {
self.client.gotoMenuModule(confForFormId[c].menu);
conf = confForFormId[c];
if(_.isEqual(formData.value, conf.value, formValueCompare)) {
var formattedArgs;
if(conf.args) {
formattedArgs = self.formatMenuArgs(conf.args);
}
self.client.gotoMenuModule( { name : conf.menu, args : formattedArgs } );
break;
}
}
});
}
} else {
callback(null);
}
callback(null);
},
function setInitialFocus(callback) {
if(initialFocusId) {
self.switchFocus(initialFocusId);
}
callback(null);
}
],
function complete(err) {
@ -341,3 +358,28 @@ ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) {
}
);
};
ViewController.prototype.formatMCIString = function(format) {
var self = this;
var view;
return format.replace(/{(\d+)}/g, function replacer(match, number) {
view = self.getView(number);
if(!view) {
return match;
}
return view.getViewData();
});
};
ViewController.prototype.formatMenuArgs = function(args) {
var self = this;
return _.forIn(args, function onArg(value, key) {
if('string' === typeof value) {
args[key] = self.formatMCIString(value);
}
});
};

View File

@ -1,6 +1,14 @@
{
/*
Menu Configuration
Some concept/ideas:
"mci" : {
"BN1" : {
...
"draw" : "@script:blah.js/drawButton"
}
}
*/
"matrix" : {
"art" : "matrix",
@ -20,7 +28,8 @@
"*" : [
{
"value" : { "1" : 0 },
"menu" : "login"
"menu" : "loginForm",
"args" : { "reason" : "show_form" }
},
{
"value" : { "1" : 1 },
@ -36,8 +45,8 @@
}
}
},
"login" : {
"art" : "login",
"loginForm" : {
"art" : "login", // TODO: rename to login_form
"module" : "login",
"form" : {
"0" : {
@ -58,8 +67,15 @@
"submit" : {
"3" : [ // Login
{
"value" : { "3" : null }, // :TODO: allow "value" : null
"menu" : "attempt_login"
"value" : { "3" : null },
"menu" : "attemptLogin",
"args" : { "reason" : "attempt_login", "username" : "{1}", "password" : "{2}" }
}
],
"4" : [ // cancel
{
"value" : { "4" : null },
"menu" : "matrix"
}
]
}
@ -67,6 +83,10 @@
}
}
},
"attemptLogin" : {
"art" : "login",
"module" : "login"
},
"logoff" : {
"art" : "logoff",
"module" : "logoff"