diff --git a/core/ansi_escape_parser.js b/core/ansi_escape_parser.js index 85684a35..ba08c290 100644 --- a/core/ansi_escape_parser.js +++ b/core/ansi_escape_parser.js @@ -36,15 +36,14 @@ function ANSIEscapeParser(options) { mciReplaceChar : '', termHeight : 25, termWidth : 80, - omitTrailingLF : false, + trailingLF : 'default', // default|omit|no|yes, ... }); this.mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, ''); this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25); this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80); - this.omitTrailingLF = miscUtil.valueWithDefault(options.omitTrailingLF, false); + this.trailingLF = miscUtil.valueWithDefault(options.trailingLF, 'default'); - function getArgArray(array) { var i = array.length; while(i--) { @@ -246,9 +245,28 @@ function ANSIEscapeParser(options) { if(pos < buffer.length) { var lastBit = buffer.slice(pos); - if(self.omitTrailingLF && '\r\n' === lastBit.slice(-2).toString()) { - lastBit = lastBit.slice(pos, -2); // trim off last CRLF + + // :TODO: check for various ending LF's, not just DOS \r\n + if('\r\n' === lastBit.slice(-2).toString()) { + switch(self.trailingLF) { + case 'default' : + // + // Default is to *not* omit the trailing LF + // if we're going to end on termHeight + // + if(this.termHeight === self.row) { + lastBit = lastBit.slice(0, -2); + } + break; + + case 'omit' : + case 'no' : + case false : + lastBit = lastBit.slice(0, -2); + break; + } } + parseMCI(lastBit) } diff --git a/core/art.js b/core/art.js index b69f45c3..eb3067d6 100644 --- a/core/art.js +++ b/core/art.js @@ -417,7 +417,7 @@ function display(options, cb) { mciReplaceChar : mciReplaceChar, termHeight : options.client.term.termHeight, termWidth : options.client.term.termWidth, - omitTrailingLF : options.omitTrailingLF, + trailingLF : options.trailingLF, }); var mciMap = {}; diff --git a/core/bbs.js b/core/bbs.js index 0991d914..d668717b 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -238,18 +238,18 @@ function startListening() { } function prepareClient(client, cb) { + var theme = require('./theme.js'); + // :TODO: it feels like this should go somewhere else... and be a bit more elegant. + if('*' === conf.config.preLoginTheme) { - var theme = require('./theme.js'); - client.user.properties.theme_id = theme.getRandomTheme() || ''; - - theme.loadTheme(client.user.properties.theme_id, function themeLoaded(err, theme) { - client.currentTheme = theme; - cb(null); - }); } else { client.user.properties.theme_id = conf.config.preLoginTheme; - cb(null); } + + theme.loadTheme(client.user.properties.theme_id, function themeLoaded(err, theme) { + client.currentTheme = theme; + cb(null); + }); } \ No newline at end of file diff --git a/core/config_cache.js b/core/config_cache.js index 1c689f54..c8b1e00b 100644 --- a/core/config_cache.js +++ b/core/config_cache.js @@ -11,6 +11,7 @@ var events = require('events'); var util = require('util'); var assert = require('assert'); var hjson = require('hjson'); +var _ = require('lodash'); function ConfigCache() { events.EventEmitter.call(this); @@ -54,21 +55,29 @@ function ConfigCache() { util.inherits(ConfigCache, events.EventEmitter); -ConfigCache.prototype.getConfig = function(filePath, cb) { - var self = this; +ConfigCache.prototype.getConfigWithOptions = function(options, cb) { + assert(_.isString(options.filePath)); - if(filePath in this.cache) { - cb(null, this.cache[filePath], false); - } else { - this.reCacheConfigFromFile(filePath, function fileCached(err, config) { - if(!err) { - self.gaze.add(filePath); + var self = this; + var isCached = (options.filePath in this.cache); + + if(options.forceReCache || !isCached) { + this.reCacheConfigFromFile(options.filePath, function fileCached(err, config) { + if(!err && !isCached) { + self.gaze.add(options.filePath); } cb(err, config, true); }); + } else { + cb(null, this.cache[options.filePath], false); } }; + +ConfigCache.prototype.getConfig = function(filePath, cb) { + this.getConfigWithOptions( { filePath : filePath }, cb); +}; + ConfigCache.prototype.getModConfig = function(fileName, cb) { this.getConfig(paths.join(Config.paths.mods, fileName), cb); }; diff --git a/core/fse.js b/core/fse.js index 7ba5d5f8..bf775609 100644 --- a/core/fse.js +++ b/core/fse.js @@ -378,9 +378,10 @@ function FullScreenEditorModule(options) { self.client, { font : self.menuConfig.font }, function displayed(err, artData) { - mciData[n] = artData; - - self[n] = { height : artData.height }; + if(artData) { + mciData[n] = artData; + self[n] = { height : artData.height }; + } next(err); } diff --git a/core/menu_module.js b/core/menu_module.js index 70d3ab50..c135ed5a 100644 --- a/core/menu_module.js +++ b/core/menu_module.js @@ -48,10 +48,10 @@ function MenuModule(options) { theme.displayThemedAsset( self.menuConfig.art, self.client, - { font : self.menuConfig.font }, + self.menuConfig.options, // can include .font, .trailingLF, etc. function displayed(err, artData) { if(err) { - self.client.log.debug( { art : self.menuConfig.arg, err : err }, 'Could not display art'); + self.client.log.debug( { art : self.menuConfig.art, err : err }, 'Could not display art'); } else { mciData.menu = artData.mciMap; } @@ -83,7 +83,7 @@ function MenuModule(options) { theme.displayThemedAsset( promptConfig.art, self.client, - { font : self.menuConfig.font }, + self.menuConfig.options, // can include .font, .trailingLF, etc. function displayed(err, artData) { if(!err) { mciData.prompt = artData.mciMap; @@ -130,7 +130,6 @@ function MenuModule(options) { self.finishedLoading(); self.nextMenu(); - //self.nextAction(); } ); }; @@ -143,20 +142,22 @@ function MenuModule(options) { return _.isNumber(self.menuConfig.options.nextTimeout); }; - // :TODO: Convert this to process "next" instead of "action" - this.nextAction = function() { - if(!_.isObject(self.menuConfig.form) && !_.isString(self.menuConfig.prompt) && - _.isString(self.menuConfig.action)) - { - menuUtil.handleAction(self.client, null, self.menuConfig); - } - }; - this.nextMenu = function() { - if(!_.isObject(self.menuConfig.form) && !_.isString(self.menuConfig.prompt) && - !_.isUndefined(self.menuConfig.next)) - { + function goNext() { + if(_.isString(self.menuConfig.next)) { + menuUtil.handleNext(self.client, self.menuConfig.next); + } else { + self.client.fallbackMenuModule( { }, function fallback(err) { + // :TODO: this seems sloppy... look into further + }); + } + } + + if(!_.isObject(self.menuConfig.form) && !_.isString(self.menuConfig.prompt)) { /* + If 'next' is supplied, we'll use it. Otherwise, utlize fallback which + may be explicit (supplied) or non-explicit (previous menu) + 'next' may be a simple asset, or a object with next.asset and extrArgs @@ -168,14 +169,13 @@ function MenuModule(options) { asset: assetSpec extraArgs: ... } - */ + */ if(self.hasNextTimeout()) { setTimeout(function nextTimeout() { - menuUtil.handleNext(self.client, self.menuConfig.next); - + goNext(); }, this.menuConfig.options.nextTimeout); } else { - menuUtil.handleNext(self.client, self.menuConfig.next); + goNext(); } } }; diff --git a/core/system_menu_method.js b/core/system_menu_method.js index 9bbaf9e4..6bc3b5fa 100644 --- a/core/system_menu_method.js +++ b/core/system_menu_method.js @@ -22,7 +22,8 @@ function login(callingMenu, formData, extraArgs) { // :TODO: if username exists, record failed login attempt to properties // :TODO: check Config max failed logon attempts/etc. - client.gotoMenuModule( { name : callingMenu.menuConfig.fallback } ); + client.fallbackMenuModule(); + //client.gotoMenuModule( { name : callingMenu.menuConfig.fallback } ); } else { var now = new Date(); var user = callingMenu.client.user; @@ -47,7 +48,7 @@ function login(callingMenu, formData, extraArgs) { 'Already logged in' ); - // :TODO: display custom message if present + // :TODO: display custom message if present (Obv/2: TOONODE.ANS) client.term.write('\nA user by that name is already logged in.\n'); diff --git a/core/theme.js b/core/theme.js index 0f4427b1..8cde3fa0 100644 --- a/core/theme.js +++ b/core/theme.js @@ -79,7 +79,7 @@ function loadTheme(themeID, cb) { var path = paths.join(Config.paths.themes, themeID, 'theme.hjson'); - configCache.getConfig(path, function loaded(err, theme) { + configCache.getConfigWithOptions( { filePath : path, forceReCache : true }, function loaded(err, theme) { if(err) { cb(err); } else { @@ -195,12 +195,13 @@ function displayThemeArt(options, cb) { if(err) { cb(err); } else { + // :TODO: just use simple merge of options -> displayOptions var dispOptions = { art : artInfo.data, sauce : artInfo.sauce, client : options.client, font : options.font, - omitTrailingLF : options.omitTrailingLF, + trailingLF : options.trailingLF, }; art.display(dispOptions, function displayed(err, mciMap, extraInfo) { @@ -250,7 +251,7 @@ function displayThemedPause(options, cb) { displayThemedAsset( promptConfig.art, options.client, - { font : promptConfig.font, omitTrailingLF : true }, + promptConfig.options, function displayed(err, artData) { artInfo = artData; callback(err); @@ -323,11 +324,12 @@ function displayThemedAsset(assetSpec, client, options, cb) { return; } + // :TODO: just use simple merge of options -> displayOptions var dispOpts = { name : artAsset.asset, client : client, font : options.font, - omitTrailingLF : options.omitTrailingLF, + trailingLF : options.trailingLF, }; switch(artAsset.type) { diff --git a/core/view_controller.js b/core/view_controller.js index 6acd42c5..4fb6b851 100644 --- a/core/view_controller.js +++ b/core/view_controller.js @@ -571,15 +571,15 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) { }); }, function applyThemeCustomization(callback) { - if(_.isObject(formConfig)) { + //if(_.isObject(formConfig)) { menuUtil.applyThemeCustomization({ name : self.client.currentMenuModule.menuName, type : 'menus', client : self.client, - configMci : formConfig.mci, + configMci : formConfig ? formConfig.mci : {}, formId : formIdKey, }); - } + //} callback(null); }, diff --git a/mods/art/MAINMENU.ANS b/mods/art/MAINMENU.ANS deleted file mode 100644 index c26cd179..00000000 Binary files a/mods/art/MAINMENU.ANS and /dev/null differ diff --git a/mods/art/MATRIX.ANS b/mods/art/MATRIX.ANS new file mode 100644 index 00000000..3a196643 Binary files /dev/null and b/mods/art/MATRIX.ANS differ diff --git a/mods/art/MATRIX1.ANS b/mods/art/MATRIX1.ANS deleted file mode 100644 index 8deafd58..00000000 Binary files a/mods/art/MATRIX1.ANS and /dev/null differ diff --git a/mods/menu.hjson b/mods/menu.hjson index be583e36..ce01b920 100644 --- a/mods/menu.hjson +++ b/mods/menu.hjson @@ -61,6 +61,9 @@ matrix: { art: matrix + options: { + + } form: { 0: { // :TODO: Make form "0" the default if missing (e.g. optional)... not sure how with current structure though VM: { @@ -69,7 +72,7 @@ submit: true focus: true // :TODO: need a good way to localize these ... Standard Orig->Lookup seems good. - items: [ "Login", "Apply", "Log Off" ] + items: [ "login", "apply", "log off" ] } } @@ -111,45 +114,45 @@ } ]*/ }, - "login2" : { - "art" : "USRCRED", - "fallback" : "matrix", + + login2: { + art: USERLOG next: messageArea //"next" : "fullLoginSequenceLoginArt", - "form" : { - "0" : { - "mci" : { - "ET1" : { - "width" : 20, - "maxLength" : "@config:users.usernameMax", - "argName" : "username", - "focus" : true - }, - "ET2" : { - "width" : 20, - "password" : true, - "maxLength" : "@config:users.passwordMax", - "argName" : "password", - "submit" : true + form: { + 0: { + mci: { + ET1: { + maxLength: @config:users.usernameMax + argName: username + focus: true } - }, - "submit" : { - "*" : [ + ET2: { + password: true + maxLength: @config:users.passwordMax + argName: password + submit: true + } + } + + submit: { + *: [ { - "value" : { "password" : null }, - "action" : "@systemMethod:login" + value: { password: null } + action: @systemMethod:login } ] - }, - "actionKeys" : [ + } + actionKeys: [ { - "keys" : [ "escape" ], - "action" : "@menu:matrix" + keys: [ "escape" ] + action: @menu:matrix } ] } } - }, + } + "logoff" : { "art" : "LOGOFF", "next" : "@systemMethod:logoff", @@ -390,7 +393,7 @@ "next" : "fullLoginSequenceUserStats" }, "fullLoginSequenceUserStats" : { - "art" : "USRSTAT", + art: STATUS "options" : { "pause" : true }, "next" : "mainMenu" }, @@ -404,17 +407,20 @@ "options" : { "pause" : true } //"action" : "@menu:lastCallers" }, - "mainMenu" : { - "art" : "MMENU1", - "desc" : "Main Menu", - "prompt" : "menuCommand", + mainMenu: { + art: MMENU + /*options: { + trailingLF: yes + }*/ + desc: Main Menu + prompt: menuCommand "submit" : [ { "value" : { "command" : "G" }, "action" : "@menu:logoff" }, { - "value" : { "command" : "D" }, + "value" : { "command" : "O" }, "action" : "@menu:doorPimpWars" }, /* @@ -441,20 +447,16 @@ } ] }, - "mainMenuLastCallers" : { - "module" : "last_callers", - "art" : "LASTCALL", - "options" : { "pause" : true }, - "config" : { - "dateTimeFormat" : "ddd MMM Do h:mm a" - }, - "next" : "mainMenu" - }, - "mainMenuUserStats" : { - "art" : "USRSTAT", - "options" : { "pause" : true }, - "next" : "mainMenu" - }, + mainMenuLastCallers: { + module: last_callers + art: LASTCALL + options: { pause: true } + } + mainMenuUserStats: { + art: STATUS + options: { pause: true } + next: mainMenu + } /////////////////////////////////////////////////////////////////////// // Message Area Related /////////////////////////////////////////////////////////////////////// diff --git a/mods/prompt.hjson b/mods/prompt.hjson index 77cdc3af..9661e8cd 100644 --- a/mods/prompt.hjson +++ b/mods/prompt.hjson @@ -59,11 +59,14 @@ /////////////////////////////////////////////////////////////////////// // Standard / Required /////////////////////////////////////////////////////////////////////// - "pause" : { + pause: { // // Any menu 'pause' will use this prompt // - "art" : "pause" + art: pause + options: { + trailingLF: no + } /* "mci" : { // :TODO: Need special pause for a key MCI diff --git a/mods/art/pause.ans b/mods/themes/NU-MAYA/PAUSE.ANS similarity index 100% rename from mods/art/pause.ans rename to mods/themes/NU-MAYA/PAUSE.ANS diff --git a/mods/themes/NU-MAYA/USRSTAT1.ANS b/mods/themes/NU-MAYA/STATUS.ANS similarity index 100% rename from mods/themes/NU-MAYA/USRSTAT1.ANS rename to mods/themes/NU-MAYA/STATUS.ANS diff --git a/mods/themes/NU-MAYA/USRCRED1.ANS b/mods/themes/NU-MAYA/USERLOG.ANS similarity index 100% rename from mods/themes/NU-MAYA/USRCRED1.ANS rename to mods/themes/NU-MAYA/USERLOG.ANS diff --git a/mods/themes/luciano_blocktronics/LASTCALL.ANS b/mods/themes/luciano_blocktronics/LASTCALL.ANS new file mode 100644 index 00000000..a12267de Binary files /dev/null and b/mods/themes/luciano_blocktronics/LASTCALL.ANS differ diff --git a/mods/themes/luciano_blocktronics/MATRIX.ANS b/mods/themes/luciano_blocktronics/MATRIX.ANS new file mode 100644 index 00000000..3a196643 Binary files /dev/null and b/mods/themes/luciano_blocktronics/MATRIX.ANS differ diff --git a/mods/themes/luciano_blocktronics/MMENU.ANS b/mods/themes/luciano_blocktronics/MMENU.ANS new file mode 100644 index 00000000..2cdc20e1 Binary files /dev/null and b/mods/themes/luciano_blocktronics/MMENU.ANS differ diff --git a/mods/themes/luciano_blocktronics/MNUPRMT.ANS b/mods/themes/luciano_blocktronics/MNUPRMT.ANS new file mode 100644 index 00000000..065a0ec9 Binary files /dev/null and b/mods/themes/luciano_blocktronics/MNUPRMT.ANS differ diff --git a/mods/themes/luciano_blocktronics/PAUSE.ANS b/mods/themes/luciano_blocktronics/PAUSE.ANS new file mode 100644 index 00000000..53bae432 Binary files /dev/null and b/mods/themes/luciano_blocktronics/PAUSE.ANS differ diff --git a/mods/themes/luciano_blocktronics/STATUS.ANS b/mods/themes/luciano_blocktronics/STATUS.ANS new file mode 100644 index 00000000..fd945a26 Binary files /dev/null and b/mods/themes/luciano_blocktronics/STATUS.ANS differ diff --git a/mods/themes/luciano_blocktronics/USERLOG.ANS b/mods/themes/luciano_blocktronics/USERLOG.ANS new file mode 100644 index 00000000..6522f679 Binary files /dev/null and b/mods/themes/luciano_blocktronics/USERLOG.ANS differ diff --git a/mods/themes/luciano_blocktronics/theme.hjson b/mods/themes/luciano_blocktronics/theme.hjson new file mode 100644 index 00000000..0d3dcd81 --- /dev/null +++ b/mods/themes/luciano_blocktronics/theme.hjson @@ -0,0 +1,46 @@ +{ + info: { + name: Mystery Skull + author: Luciano Ayres + group: blocktronics + } + + customization: { + + defaults: { + general: { + passwordChar: * + } + + dateTimeFormat: { + short: MMM Do h:mm a + } + } + + menus: { + matrix: { + VM1: { + focusTextStyle: first lower + } + } + + login2: { + ET1: { width: 14 } + ET2: { width: 14 } + } + + mainMenuUserStats: { + UN1: { width: 17 } + UR2: { width: 17 } + LO3: { width: 17 } + UF4: { width: 17 } + UG5: { width: 17 } + UT6: { width: 17 } + UC7: { width: 17 } + } + + mainMenuLastCallers: { + } + } + } +} \ No newline at end of file