* Trailing LF handling is now 'trailingLF' option, with 'default', 'yes', 'no', etc.

* Initial checking of WIP Mystery Skulls theme by Luciano Ayres of blocktronics
* Fix random theme selection
* WIP on theme customization apply: Needs to be much more flexible than current
* MenuModule will use .next > .fallback > default fallback
This commit is contained in:
Bryan Ashby 2015-09-27 15:35:24 -06:00
parent 68b8af7975
commit 05812f57f0
25 changed files with 187 additions and 105 deletions

View File

@ -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)
}

View File

@ -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 = {};

View File

@ -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);
});
}

View File

@ -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);
};

View File

@ -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);
}

View File

@ -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();
}
}
};

View File

@ -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');

View File

@ -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) {

View File

@ -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);
},

Binary file not shown.

BIN
mods/art/MATRIX.ANS Normal file

Binary file not shown.

Binary file not shown.

View File

@ -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
///////////////////////////////////////////////////////////////////////

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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: {
}
}
}
}