diff --git a/core/client.js b/core/client.js index 26d369ab..f3a57047 100644 --- a/core/client.js +++ b/core/client.js @@ -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'); diff --git a/core/client_term.js b/core/client_term.js index 962e40e0..1da28b5c 100644 --- a/core/client_term.js +++ b/core/client_term.js @@ -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'); } } }); diff --git a/core/connect.js b/core/connect.js index 55b982d0..016c6258 100644 --- a/core/connect.js +++ b/core/connect.js @@ -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); }); diff --git a/core/menu_module.js b/core/menu_module.js index 7fa0502a..de81a2dd 100644 --- a/core/menu_module.js +++ b/core/menu_module.js @@ -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); diff --git a/core/menu_util.js b/core/menu_util.js index c0700354..e4beae57 100644 --- a/core/menu_util.js +++ b/core/menu_util.js @@ -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; - } - } - } - } - } -} -*/ \ No newline at end of file diff --git a/core/plugin_module.js b/core/plugin_module.js index 7ecb4af9..aeac4950 100644 --- a/core/plugin_module.js +++ b/core/plugin_module.js @@ -3,5 +3,5 @@ exports.PluginModule = PluginModule; -function PluginModule() { +function PluginModule(options) { } \ No newline at end of file diff --git a/core/servers/telnet.js b/core/servers/telnet.js index 6ff7de81..2da43c26 100644 --- a/core/servers/telnet.js +++ b/core/servers/telnet.js @@ -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'); } }; diff --git a/core/string_util.js b/core/string_util.js index 8c550f87..f50e967d 100644 --- a/core/string_util.js +++ b/core/string_util.js @@ -144,4 +144,4 @@ function pad(s, len, padChar, dir, stringColor, padColor) { } return stringColor + s; -} \ No newline at end of file +} diff --git a/core/theme.js b/core/theme.js index 5b750649..7ca68a27 100644 --- a/core/theme.js +++ b/core/theme.js @@ -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'); } }); }); diff --git a/core/view_controller.js b/core/view_controller.js index e7b76f79..19175161 100644 --- a/core/view_controller.js +++ b/core/view_controller.js @@ -209,9 +209,12 @@ ViewController.prototype.loadFromMCIMap = function(mciMap) { ViewController.prototype.loadFromMCIMapAndConfig = function(options, cb) { assert(options.mciMap); - var factory = new MCIViewFactory(this.client); - var self = this; - var formIdKey = options.formId ? options.formId.toString() : '0'; + 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); + } + }); +}; \ No newline at end of file diff --git a/mods/menu.json b/mods/menu.json index f4a47f8e..16a9ad6b 100644 --- a/mods/menu.json +++ b/mods/menu.json @@ -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,14 +67,25 @@ "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" } ] } } } - } + } + }, + "attemptLogin" : { + "art" : "login", + "module" : "login" }, "logoff" : { "art" : "logoff",