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

View File

@ -2,7 +2,7 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
var logger = require('./logger.js'); var Log = require('./logger.js').log;
var iconv = require('iconv-lite'); var iconv = require('iconv-lite');
var assert = require('assert'); var assert = require('assert');
@ -41,7 +41,7 @@ function ClientTerminal(output) {
if(iconv.encodingExists(enc)) { if(iconv.encodingExists(enc)) {
outputEncoding = enc; outputEncoding = enc;
} else { } 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() { setTimeout(function onTimeout() {
term.write(ansi.clearScreen()); term.write(ansi.clearScreen());
client.gotoMenuModule(Config.entryMod);
client.gotoMenuModule({ name : Config.entryMod } );
//moduleUtil.goto(Config.entryMod, client); //moduleUtil.goto(Config.entryMod, client);
}, timeout); }, timeout);
}); });

View File

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

View File

@ -17,7 +17,14 @@ var stripJsonComments = require('strip-json-comments');
exports.loadMenu = loadMenu; exports.loadMenu = loadMenu;
exports.getFormConfig = getFormConfig; 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: TODO:
* check access / ACS * check access / ACS
@ -44,21 +51,23 @@ function loadMenu(name, client, cb) {
}); });
}, },
function menuConfigLoaded(menuConfig, callback) { function menuConfigLoaded(menuConfig, callback) {
Log.debug( { config : menuConfig }, 'Menu configuration loaded');
var moduleName = menuConfig.module || 'standard_menu'; var moduleName = menuConfig.module || 'standard_menu';
moduleUtil.loadModule(moduleName, 'mods', function onModule(err, mod) { moduleUtil.loadModule(moduleName, 'mods', function onModule(err, mod) {
callback(err, mod, menuConfig, moduleName); callback(err, mod, menuConfig, moduleName);
}); });
}, }
], ],
function complete(err, mod, menuConfig, moduleName) { function complete(err, mod, menuConfig, moduleName) {
if(err) { if(err) {
cb(err); cb(err);
} else { } else {
Log.debug( { moduleName : moduleName, moduleInfo : mod.moduleInfo }, 'Loading menu module instance'); Log.debug(
cb(null, new mod.getModule(menuConfig)); { 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')); 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; exports.PluginModule = PluginModule;
function PluginModule() { function PluginModule(options) {
} }

View File

@ -3,7 +3,7 @@
// ENiGMA½ // ENiGMA½
var baseClient = require('../client.js'); var baseClient = require('../client.js');
var logger = require('../logger.js'); var Log = require('../logger.js').log;
var ServerModule = require('../server_module.js').ServerModule; var ServerModule = require('../server_module.js').ServerModule;
var net = require('net'); var net = require('net');
@ -322,7 +322,7 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) {
event.type = vars.isOrInfo; event.type = vars.isOrInfo;
if(vars.newEnv === OPTIONS.NEW_ENVIRONMENT_DEP) { 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.env['TERM'] = ttype;
this.term.termType = 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) { TelnetClient.prototype.handleSbCommand = function(evt) {
@ -571,15 +571,15 @@ TelnetClient.prototype.handleSbCommand = function(evt) {
self.setTermType(evt.envVars[name]); self.setTermType(evt.envVars[name]);
} else if('COLUMNS' === name && 0 === self.term.termWidth) { } else if('COLUMNS' === name && 0 === self.term.termWidth) {
self.term.termWidth = parseInt(evt.envVars[name]); 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) { } else if('ROWS' === name && 0 === self.term.termHeight) {
self.term.termHeight = parseInt(evt.envVars[name]); 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 { } else {
if(name in self.term.env) { if(name in self.term.env) {
assert(evt.type === SB_COMMANDS.INFO); assert(evt.type === SB_COMMANDS.INFO);
logger.log.warn( Log.warn(
{ varName : name, value : evt.envVars[name], existingValue : self.term.env[name] }, { varName : name, value : evt.envVars[name], existingValue : self.term.env[name] },
'Environment variable already exists'); 'Environment variable already exists');
} else { } else {
@ -604,7 +604,7 @@ TelnetClient.prototype.handleSbCommand = function(evt) {
self.term.env['ROWS'] = evt.height; 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 { } else {
console.log('unhandled SB: ' + JSON.stringify(evt)); console.log('unhandled SB: ' + JSON.stringify(evt));
} }
@ -625,15 +625,15 @@ TelnetClient.prototype.handleMiscCommand = function(evt) {
// //
if('ip' === evt.command) { if('ip' === evt.command) {
// Interrupt Process (IP) // Interrupt Process (IP)
logger.log.debug('Interrupt Process (IP) - Ending'); Log.debug('Interrupt Process (IP) - Ending');
this.input.end(); this.input.end();
} else if('ayt' === evt.command) { } else if('ayt' === evt.command) {
this.output.write('\b'); 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)) { } else if(IGNORED_COMMANDS.indexOf(evt.commandCode)) {
logger.log.debug({ evt : evt }, 'Ignoring command'); Log.debug({ evt : evt }, 'Ignoring command');
} else { } 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 Config = require('./config.js').config;
var art = require('./art.js'); var art = require('./art.js');
var miscUtil = require('./misc_util.js'); var miscUtil = require('./misc_util.js');
var logger = require('./logger.js').log; var Log = require('./logger.js').log;
var fs = require('fs'); var fs = require('fs');
var paths = require('path'); var paths = require('path');
var async = require('async'); var async = require('async');
@ -56,7 +56,7 @@ function initAvailableThemes(cb) {
availableThemes = {}; availableThemes = {};
} }
availableThemes[themeId] = info; availableThemes[themeId] = info;
logger.debug( { info : info }, 'Theme loaded'); Log.debug( { info : info }, 'Theme loaded');
} }
}); });
}); });

View File

@ -209,9 +209,12 @@ ViewController.prototype.loadFromMCIMap = function(mciMap) {
ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) { ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) {
assert(options.mciMap); assert(options.mciMap);
var factory = new MCIViewFactory(this.client); var factory = new MCIViewFactory(this.client);
var self = this; var self = this;
var formIdKey = options.formId ? options.formId.toString() : '0'; var formIdKey = options.formId ? options.formId.toString() : '0';
var initialFocusId;
// :TODO: remove all the passing of fromConfig - use local
async.waterfall( async.waterfall(
[ [
@ -258,14 +261,13 @@ ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) {
view.submit = true; // :TODO: should really be actual value view.submit = true; // :TODO: should really be actual value
} }
if(mciConf.focus) {
self.switchFocus(viewId);
}
if(mciConf.text) { if(mciConf.text) {
view.setText(mciConf.text); view.setText(mciConf.text);
} }
if(mciConf.focus) {
initialFocusId = viewId;
}
eachCb(null); eachCb(null);
}, },
@ -321,17 +323,32 @@ ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) {
return true; return true;
}; };
var conf;
for(var c = 0; c < confForFormId.length; ++c) { for(var c = 0; c < confForFormId.length; ++c) {
if(_.isEqual(formData.value, confForFormId[c].value, formValueCompare)) { conf = confForFormId[c];
self.client.gotoMenuModule(confForFormId[c].menu); 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; break;
} }
} }
}); });
} }
} else {
callback(null);
} }
callback(null);
},
function setInitialFocus(callback) {
if(initialFocusId) {
self.switchFocus(initialFocusId);
}
callback(null);
} }
], ],
function complete(err) { 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 Menu Configuration
Some concept/ideas:
"mci" : {
"BN1" : {
...
"draw" : "@script:blah.js/drawButton"
}
}
*/ */
"matrix" : { "matrix" : {
"art" : "matrix", "art" : "matrix",
@ -20,7 +28,8 @@
"*" : [ "*" : [
{ {
"value" : { "1" : 0 }, "value" : { "1" : 0 },
"menu" : "login" "menu" : "loginForm",
"args" : { "reason" : "show_form" }
}, },
{ {
"value" : { "1" : 1 }, "value" : { "1" : 1 },
@ -36,8 +45,8 @@
} }
} }
}, },
"login" : { "loginForm" : {
"art" : "login", "art" : "login", // TODO: rename to login_form
"module" : "login", "module" : "login",
"form" : { "form" : {
"0" : { "0" : {
@ -58,8 +67,15 @@
"submit" : { "submit" : {
"3" : [ // Login "3" : [ // Login
{ {
"value" : { "3" : null }, // :TODO: allow "value" : null "value" : { "3" : null },
"menu" : "attempt_login" "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" : { "logoff" : {
"art" : "logoff", "art" : "logoff",
"module" : "logoff" "module" : "logoff"