From b19d06df26490bdce52c1ab512bf70724a349102 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 20:53:27 -0600 Subject: [PATCH 01/11] Default to NOT showing non-authenticated users in who's online / getActiveNodeList() --- core/client_connections.js | 43 +++++++++++++------------- mods/whos_online.js | 62 ++++++++++++++------------------------ 2 files changed, 43 insertions(+), 62 deletions(-) diff --git a/core/client_connections.js b/core/client_connections.js index f74ba49b..f5aabac6 100644 --- a/core/client_connections.js +++ b/core/client_connections.js @@ -1,28 +1,39 @@ /* jslint node: true */ 'use strict'; -var logger = require('./logger.js'); +// ENiGMA½ +const logger = require('./logger.js'); -var _ = require('lodash'); -var moment = require('moment'); +// deps +const _ = require('lodash'); +const moment = require('moment'); exports.getActiveConnections = getActiveConnections; exports.getActiveNodeList = getActiveNodeList; exports.addNewClient = addNewClient; exports.removeClient = removeClient; -var clientConnections = []; +const clientConnections = []; exports.clientConnections = clientConnections; function getActiveConnections() { return clientConnections; } -function getActiveNodeList() { +function getActiveNodeList(authUsersOnly) { + + if(!_.isBoolean(authUsersOnly)) { + authUsersOnly = true; + } + const now = moment(); + + const activeConnections = getActiveConnections().filter(ac => { + return ((authUsersOnly && ac.user.isAuthenticated()) || !authUsersOnly); + }); - return _.map(getActiveConnections(), ac => { - let entry = { + return _.map(activeConnections, ac => { + const entry = { node : ac.node, authenticated : ac.user.isAuthenticated(), userId : ac.user.userId, @@ -46,13 +57,13 @@ function getActiveNodeList() { } function addNewClient(client, clientSock) { - var id = client.session.id = clientConnections.push(client) - 1; + const id = client.session.id = clientConnections.push(client) - 1; // Create a client specific logger // Note that this will be updated @ login with additional information client.log = logger.log.child( { clientId : id } ); - var connInfo = { + const connInfo = { ip : clientSock.remoteAddress, serverName : client.session.serverName, isSecure : client.session.isSecure, @@ -71,7 +82,7 @@ function addNewClient(client, clientSock) { function removeClient(client) { client.end(); - var i = clientConnections.indexOf(client); + const i = clientConnections.indexOf(client); if(i > -1) { clientConnections.splice(i, 1); @@ -84,15 +95,3 @@ function removeClient(client) { ); } } - -/* :TODO: make a public API elsewhere -function getActiveClientInformation() { - var info = {}; - - clientConnections.forEach(function connEntry(cc) { - - }); - - return info; -} -*/ \ No newline at end of file diff --git a/mods/whos_online.js b/mods/whos_online.js index a607ae02..86cde07a 100644 --- a/mods/whos_online.js +++ b/mods/whos_online.js @@ -1,14 +1,14 @@ /* jslint node: true */ 'use strict'; -var MenuModule = require('../core/menu_module.js').MenuModule; -var ViewController = require('../core/view_controller.js').ViewController; -var getActiveNodeList = require('../core/client_connections.js').getActiveNodeList; +// ENiGMA½ +const MenuModule = require('../core/menu_module.js').MenuModule; +const ViewController = require('../core/view_controller.js').ViewController; +const getActiveNodeList = require('../core/client_connections.js').getActiveNodeList; -var moment = require('moment'); -var async = require('async'); -var assert = require('assert'); -var _ = require('lodash'); +// deps +const async = require('async'); +const _ = require('lodash'); exports.moduleInfo = { name : 'Who\'s Online', @@ -17,26 +17,9 @@ exports.moduleInfo = { packageName : 'codes.l33t.enigma.whosonline' }; -/* -node -userName -userId -action -note -affils -timeOnSec -location -realName -serverName (Telnet, SSH, ...) - -default -{node} - {username} - {action} - {timeOnSec} - -*/ - exports.getModule = WhosOnlineModule; -var MciCodeIds = { +const MciCodeIds = { OnlineList : 1, }; @@ -47,30 +30,29 @@ function WhosOnlineModule(options) { require('util').inherits(WhosOnlineModule, MenuModule); WhosOnlineModule.prototype.mciReady = function(mciData, cb) { - var self = this; - var vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); + const self = this; + const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); async.series( [ function callParentMciReady(callback) { - WhosOnlineModule.super_.prototype.mciReady.call(self, mciData, callback); + return WhosOnlineModule.super_.prototype.mciReady.call(self, mciData, callback); }, function loadFromConfig(callback) { - var loadOpts = { + const loadOpts = { callingMenu : self, mciMap : mciData.menu, noInput : true, }; - vc.loadFromMenuConfig(loadOpts, callback); + return vc.loadFromMenuConfig(loadOpts, callback); }, function populateList(callback) { - var onlineListView = vc.getView(MciCodeIds.OnlineList); - - const listFormat = self.menuConfig.config.listFormat || '{node} - {userName} - {action} - {timeOn}'; - const nonAuthUser = self.menuConfig.config.nonAuthUser || 'Logging In'; - const otherUnknown = self.menuConfig.config.otherUnknown || 'N/A'; - const onlineList = getActiveNodeList().slice(0, onlineListView.height); + const onlineListView = vc.getView(MciCodeIds.OnlineList); + const listFormat = self.menuConfig.config.listFormat || '{node} - {userName} - {action} - {timeOn}'; + const nonAuthUser = self.menuConfig.config.nonAuthUser || 'Logging In'; + const otherUnknown = self.menuConfig.config.otherUnknown || 'N/A'; + const onlineList = getActiveNodeList(self.menuConfig.config.authUsersOnly).slice(0, onlineListView.height); onlineListView.setItems(_.map(onlineList, oe => { if(oe.authenticated) { @@ -85,16 +67,16 @@ WhosOnlineModule.prototype.mciReady = function(mciData, cb) { })); onlineListView.focusItems = onlineListView.items; - onlineListView.redraw(); - callback(null); + + return callback(null); } ], function complete(err) { if(err) { - self.client.log.error( { error : err.toString() }, 'Error loading who\'s online'); + self.client.log.error( { error : err.message }, 'Error loading who\'s online'); } - cb(err); + return cb(err); } ); }; From 7ce2a3bbe549d7082734bb4c605d1188769ab962 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 21:26:50 -0600 Subject: [PATCH 02/11] Remove assert --- oputil.js | 1 - 1 file changed, 1 deletion(-) diff --git a/oputil.js b/oputil.js index b7851d15..11f4fd8f 100755 --- a/oputil.js +++ b/oputil.js @@ -12,7 +12,6 @@ const resolvePath = require('./core/misc_util.js').resolvePath; // deps const _ = require('lodash'); const async = require('async'); -const assert = require('assert'); const inq = require('inquirer'); const mkdirsSync = require('fs-extra').mkdirsSync; const fs = require('fs'); From 9bd39f6d80da25bc56febd8677bbbf7810a3f16d Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 21:27:32 -0600 Subject: [PATCH 03/11] format() that works with RA pipe codes & ANSI ESC seqs --- core/string_format.js | 327 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 core/string_format.js diff --git a/core/string_format.js b/core/string_format.js new file mode 100644 index 00000000..70b5b813 --- /dev/null +++ b/core/string_format.js @@ -0,0 +1,327 @@ +/* jslint node: true */ +'use strict'; + +const EnigError = require('./enig_error.js').EnigError; +const pad = require('./string_util.js').pad; +const stylizeString = require('./string_util.js').stylizeString; + +// deps +const _ = require('lodash'); + +/* + String formatting HEAVILY inspired by David Chambers string-format library + and the mini-language branch specifically which was gratiously released + under the DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE. + + We need some extra functionality. Namely, support for RA style pipe codes + and ANSI escape sequences. +*/ + +class ValueError extends EnigError { } +class KeyError extends EnigError { } + +const SpecRegExp = { + FillAlign : /^(.)?([<>=^])/, + Sign : /^[ +-]/, + Width : /^\d*/, + Precision : /^\d+/, +}; + +function tokenizeFormatSpec(spec) { + const tokens = { + fill : '', + align : '', + sign : '', + '#' : false, + '0' : false, + width : '', + ',' : false, + precision : '', + type : '', + }; + + let index = 0; + let match; + + function incIndexByMatch() { + index += match[0].length; + } + + match = SpecRegExp.FillAlign.exec(spec); + if(match) { + if(match[1]) { + tokens.fill = match[1]; + } + tokens.align = match[2]; + incIndexByMatch(); + } + + match = SpecRegExp.Sign.exec(spec.slice(index)); + if(match) { + tokens.sign = match[0]; + incIndexByMatch(); + } + + if('#' === spec.charAt(index)) { + tokens['#'] = true; + ++index; + } + + if('0' === spec.charAt(index)) { + tokens['0'] = true; + ++index; + } + + match = SpecRegExp.Width.exec(spec.slice(index)); + tokens.width = match[0]; + incIndexByMatch(); + + if(',' === spec.charAt(index)) { + tokens[','] = true; + ++index; + } + + if('.' === spec.charAt(index)) { + ++index; + + match = SpecRegExp.Precision.exec(spec.slice(index)); + if(!match) { + throw new ValueError('Format specifier missing precision'); + } + + tokens.precision = match[0]; + incIndexByMatch(); + } + + if(index < spec.length) { + tokens.type = spec.charAt(index); + ++index; + } + + if(index < spec.length) { + throw new ValueError('Invalid conversion specification'); + } + + if(tokens[','] && 's' === tokens.type) { + throw new ValueError(`Cannot specify ',' with 's'`); // eslint-disable-line quotes + } + + return tokens; +} + +function quote(s) { + return `"${s.replace(/"/g, '\\"')}"`; +} + +function getPadAlign(align) { + return { + '<' : 'right', + '>' : 'left', + '^' : 'center', + }[align] || '<'; +} + +function formatString(value, tokens) { + const fill = tokens.fill || (tokens['0'] ? '0' : ' '); + const align = tokens.align || (tokens['0'] ? '=' : '<'); + const precision = Number(tokens.precision || value.length); // :TODO: consider pipe/ANSI length + + if('' !== tokens.type && 's' !== tokens.type) { + throw new ValueError(`Unknown format code "${tokens.type}" for String object`); + } + + if(tokens[',']) { + throw new ValueError(`Cannot specify ',' with 's'`); // eslint-disable-line quotes + } + + if(tokens.sign) { + throw new ValueError('Sign not allowed in string format specifier'); + } + + if(tokens['#']) { + throw new ValueError('Alternate form (#) not allowed in string format specifier'); + } + + if('=' === align) { + throw new ValueError('"=" alignment not allowed in string format specifier'); + } + + return pad(value.slice(0, precision), parseInt(tokens.width), fill, getPadAlign(align)); +} + +const FormatNumRegExp = { + UpperType : /[A-Z]/, + ExponentRep : /e[+-](?=\d$)/, +}; + +function formatNumberHelper(n, precision, type) { + if(FormatNumRegExp.UpperType.test(type)) { + return formatNumberHelper(n, precision, type.toLowerCase()).toUpperCase(); + } + + switch(type) { + case 'c' : return String.fromCharCode(n); + case 'd' : return n.toString(10); + case 'b' : return n.toString(2); + case 'o' : return n.toString(8); + case 'x' : return n.toString(16); + case 'e' : return n.toExponential(precision).replace(FormatNumRegExp.ExponentRep, '$&0'); + case 'f' : return n.toFixed(precision); + case 'g' : return n.toPrecision(precision || 1); + case '%' : return formatNumberHelper(n * 100, precision, 'f') + '%'; + case '' : return formatNumberHelper(n, precision, 'd'); + + default : + throw new ValueError(`Unknown format code "${type}" for object of type 'float'`); + } +} + +function formatNumber(value, tokens) { + const fill = tokens.fill || (tokens['0'] ? '0' : ' '); + const align = tokens.align || (tokens['0'] ? '=' : '>'); + const width = Number(tokens.width); + const type = tokens.type || (tokens.precision ? 'g' : ''); + + if( [ 'c', 'd', 'b', 'o', 'x', 'X' ].indexOf(type) > -1) { + if(0 !== value % 1) { + throw new ValueError(`Cannot format non-integer with format specifier "${type}"`); + } + + if('' !== tokens.sign && 'c' !== type) { + throw new ValueError(`Sign not allowed with integer format specifier 'c'`); // eslint-disable-line quotes + } + + if(tokens[','] && 'd' !== type) { + throw new ValueError(`Cannot specify ',' with '${type}'`); + } + + if('' !== tokens.precision) { + throw new ValueError('Precision not allowed in integer format specifier'); + } + } else if( [ 'e', 'E', 'f', 'F', 'g', 'G', '%' ].indexOf(type) > - 1) { + if(tokens['#']) { + throw new ValueError('Alternate form (#) not allowed in float format specifier'); + } + } + + const s = formatNumberHelper(Math.abs(value), Number(tokens.precision || 6), type); + const sign = value < 0 || 1 / value < 0 ? + '-' : + '-' === tokens.sign ? '' : tokens.sign; + + const prefix = tokens['#'] && ( [ 'b', 'o', 'x', 'X' ].indexOf(type) > -1 ) ? '0' + type : ''; + + if(tokens[',']) { + const match = /^(\d*)(.*)$/.exec(s); + const separated = match[1].replace(/.(?=(...)+$)/g, '$&,') + match[2]; + + if('=' !== align) { + return pad(sign + separated, width, fill, getPadAlign(align)); + } + + if('0' === fill) { + const shortfall = Math.max(0, width - sign.length - separated.length); + const digits = /^\d*/.exec(separated)[0].length; + let padding = ''; + // :TODO: do this differntly... + for(let n = 0; n < shortfall; n++) { + padding = ((digits + n) % 4 === 3 ? ',' : '0') + padding; + } + + return sign + (/^,/.test(padding) ? '0' : '') + padding + separated; + } + + return sign + pad(separated, width - sign.length, fill, getPadAlign('>')); + } + + if(0 === width) { + return sign + prefix + s; + } + + if('=' === align) { + return sign + prefix + pad(s, width - sign.length - prefix.length, fill, getPadAlign('>')); + } + + return pad(sign + prefix + s, width, fill, getPadAlign(align)); +} + +const transformers = { + // String standard + toUpperCase : String.prototype.toUpperCase, + toLowerCase : String.prototype.toLowerCase, + + // some super l33b BBS styles!! + styleUpper : (s) => stylizeString(s, 'upper'), + styleLower : (s) => stylizeString(s, 'lower'), + styleTitle : (s) => stylizeString(s, 'title'), + styleFirstLower : (s) => stylizeString(s, 'first lower'), + styleSmallVowels : (s) => stylizeString(s, 'small vowels'), + styleBigVowels : (s) => stylizeString(s, 'big vowels'), + styleSmallI : (s) => stylizeString(s, 'small i'), + styleMixed : (s) => stylizeString(s, 'mixed'), + styleL33t : (s) => stylizeString(s, 'l33t'), +}; + +function transformValue(transformerName, value) { + if(transformerName in transformers) { + const transformer = transformers[transformerName]; + value = transformer.apply(value, [ value ] ); + } + + return value; +} + +// :TODO: Use explicit set of chars for paths & function/transforms such that } is allowed as fill/etc. +const REGEXP_BASIC_FORMAT = /{([^.!:}]+(?:\.[^.!:}]+)*)(?:\!([^:}]+))?(?:\:([^}]+))?}/g; + +function getValue(obj, path) { + const value = _.get(obj, path); + if(value) { + return _.isFunction(value) ? value() : value; + } + + throw new KeyError(quote(path)); +} + +module.exports = function format(fmt, obj) { + + const re = REGEXP_BASIC_FORMAT; + let match; + let pos; + let out = ''; + do { + pos = re.lastIndex; + match = re.exec(fmt); + + if(match) { + if(match.index > pos) { + out += fmt.slice(pos, match.index); + } + + const objPath = match[1]; + const transformer = match[2]; + const formatSpec = match[3]; + + let value = getValue(obj, objPath); + if(transformer) { + value = transformValue(transformer, value); + } + + const tokens = tokenizeFormatSpec(formatSpec || ''); + + if(!isNaN(parseInt(value))) { + out += formatNumber(value, tokens); + } else { + out += formatString(value, tokens); + } + } + + } while(0 !== re.lastIndex); + + // remainder + if(pos < fmt.length) { + out += fmt.slice(pos); + } + + return out; +}; From ef6c21e48ea00d73b105493c7a20ce31a5a035ae Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 21:28:02 -0600 Subject: [PATCH 04/11] comment --- core/bbs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/core/bbs.js b/core/bbs.js index 0214a9e4..ff5e996f 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -11,6 +11,7 @@ const logger = require('./logger.js'); const database = require('./database.js'); const clientConns = require('./client_connections.js'); +// deps const async = require('async'); const util = require('util'); const _ = require('lodash'); From 3319e8d7f98d9e2cfa3c9b35756a385bbd40c10b Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 21:28:17 -0600 Subject: [PATCH 05/11] Formatting --- core/client_connections.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/client_connections.js b/core/client_connections.js index f5aabac6..e4a5c5ff 100644 --- a/core/client_connections.js +++ b/core/client_connections.js @@ -16,9 +16,7 @@ exports.removeClient = removeClient; const clientConnections = []; exports.clientConnections = clientConnections; -function getActiveConnections() { - return clientConnections; -} +function getActiveConnections() { return clientConnections; } function getActiveNodeList(authUsersOnly) { From 3bae1095376d2bd9bf23dd7d92ebe929e9e048ad Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 21:28:29 -0600 Subject: [PATCH 06/11] Fix formatting on banner --- core/connect.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/connect.js b/core/connect.js index b7547190..bd2e40a2 100644 --- a/core/connect.js +++ b/core/connect.js @@ -122,10 +122,10 @@ function prepareTerminal(term) { function displayBanner(term) { // note: intentional formatting: term.pipeWrite(` - |06Connected to |02EN|10i|02GMA|10½ |06BBS version |12|VN - |06Copyright (c) 2014-2016 Bryan Ashby |14- |12http://l33t.codes/ - |06Updates & source |14- |12https://github.com/NuSkooler/enigma-bbs/ - |00` +|06Connected to |02EN|10i|02GMA|10½ |06BBS version |12|VN +|06Copyright (c) 2014-2016 Bryan Ashby |14- |12http://l33t.codes/ +|06Updates & source |14- |12https://github.com/NuSkooler/enigma-bbs/ +|00` ); } From 6fa19f9ac3f7fae5b1c022b97a69cac019b6bc11 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 21:29:49 -0600 Subject: [PATCH 07/11] assert() that won't expload when not in 'dev' mode --- core/enigma_assert.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/enigma_assert.js diff --git a/core/enigma_assert.js b/core/enigma_assert.js new file mode 100644 index 00000000..2001825d --- /dev/null +++ b/core/enigma_assert.js @@ -0,0 +1,18 @@ +/* jslint node: true */ +'use strict'; + +// ENiGMA½ +const Config = require('./config.js').config; +const Log = require('./logger.js').log; + +// deps +const assert = require('assert'); + +module.exports = function(condition, message) { + if(Config.debug.assertsEnabled) { + assert.apply(this, arguments); + } else if(!(condition)) { + const stack = new Error().stack; + Log.error( { condition : condition, stack : stack }, message || 'Assertion failed' ); + } +}; From 8d484daa3a1b4491c0329735b1a84933e4308519 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 21:29:57 -0600 Subject: [PATCH 08/11] Enigma error objects --- core/enig_error.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 core/enig_error.js diff --git a/core/enig_error.js b/core/enig_error.js new file mode 100644 index 00000000..69722a85 --- /dev/null +++ b/core/enig_error.js @@ -0,0 +1,19 @@ +/* jslint node: true */ +'use strict'; + +class EnigError extends Error { + constructor(message) { + super(message); + + this.name = this.constructor.name; + this.message = message; + + if(typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, this.constructor); + } else { + this.stack = (new Error(message)).stack; + } + } +} + +exports.EnigError = EnigError; \ No newline at end of file From 8002bbe8fee9ff0211749f6141ce8c3e911d4663 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 21:30:25 -0600 Subject: [PATCH 09/11] Start using new enig assert vs standard assert --- core/view.js | 56 +++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/core/view.js b/core/view.js index c8916bf7..fccca541 100644 --- a/core/view.js +++ b/core/view.js @@ -1,18 +1,19 @@ /* jslint node: true */ 'use strict'; -var events = require('events'); -var util = require('util'); -var assert = require('assert'); -var ansi = require('./ansi_term.js'); -var colorCodes = require('./color_codes.js'); +// ENiGMA½ +const events = require('events'); +const util = require('util'); +const ansi = require('./ansi_term.js'); +const colorCodes = require('./color_codes.js'); +const enigAssert = require('./enigma_assert.js'); -var _ = require('lodash'); +// deps +const _ = require('lodash'); exports.View = View; -exports.VIEW_SPECIAL_KEY_MAP_DEFAULT = VIEW_SPECIAL_KEY_MAP_DEFAULT; -var VIEW_SPECIAL_KEY_MAP_DEFAULT = { +const VIEW_SPECIAL_KEY_MAP_DEFAULT = { accept : [ 'return' ], exit : [ 'esc' ], backspace : [ 'backspace', 'del' ], @@ -27,11 +28,13 @@ var VIEW_SPECIAL_KEY_MAP_DEFAULT = { clearLine : [ 'ctrl + y' ], }; +exports.VIEW_SPECIAL_KEY_MAP_DEFAULT = VIEW_SPECIAL_KEY_MAP_DEFAULT; + function View(options) { events.EventEmitter.call(this); - assert(_.isObject(options)); - assert(_.isObject(options.client)); + enigAssert(_.isObject(options)); + enigAssert(_.isObject(options.client)); var self = this; @@ -131,7 +134,7 @@ View.prototype.setPosition = function(pos) { this.position.col = parseInt(arguments[1], 10); } - // santaize + // sanatize this.position.row = Math.max(this.position.row, 1); this.position.col = Math.max(this.position.col, 1); this.position.row = Math.min(this.position.row, this.client.term.termHeight); @@ -139,25 +142,23 @@ View.prototype.setPosition = function(pos) { }; View.prototype.setDimension = function(dimens) { - assert(_.isObject(dimens) && _.isNumber(dimens.height) && _.isNumber(dimens.width)); + enigAssert(_.isObject(dimens) && _.isNumber(dimens.height) && _.isNumber(dimens.width)); this.dimens = dimens; this.autoScale = { height : false, width : false }; }; View.prototype.setHeight = function(height) { - height = parseInt(height, 10); - assert(_.isNumber(height)); - // :TODO: assert height is within this.client.term.termHeight + height = parseInt(height) || 1; + height = Math.min(height, this.client.term.termHeight); this.dimens.height = height; this.autoScale.height = false; }; View.prototype.setWidth = function(width) { - width = parseInt(width); - assert(_.isNumber(width)); - // :TODO: assert width is appropriate for this.client.term.termWidth + width = parseInt(width) || 1; + width = Math.min(width, this.client.term.termWidth); this.dimens.width = width; this.autoScale.width = false; @@ -168,7 +169,7 @@ View.prototype.getSGR = function() { }; View.prototype.getStyleSGR = function(n) { - assert(_.isNumber(n)); + n = parseInt(n) || 0; return this['styleSGR' + n]; }; @@ -241,21 +242,22 @@ View.prototype.redraw = function() { }; View.prototype.setFocus = function(focused) { - assert(this.acceptsFocus, 'View does not accept focus'); + enigAssert(this.acceptsFocus, 'View does not accept focus'); this.hasFocus = focused; this.restoreCursor(); }; -View.prototype.onKeyPress = function(ch, key) { - if(false === this.hasFocus) { - console.log('doh!'); // :TODO: fix me -- assert here? +View.prototype.onKeyPress = function(ch, key) { + enigAssert(this.hasFocus, 'View does not have focus'); + enigAssert(this.acceptsInput, 'View does not accept input'); + + if(!this.hasFocus || !this.acceptsInput) { + return; } - assert(this.hasFocus, 'View does not have focus'); - assert(this.acceptsInput, 'View does not accept input'); if(key) { - assert(this.specialKeyMap, 'No special key map defined'); + enigAssert(this.specialKeyMap, 'No special key map defined'); if(this.isKeyMapped('accept', key.name)) { this.emit('action', 'accept', key); @@ -265,7 +267,7 @@ View.prototype.onKeyPress = function(ch, key) { } if(ch) { - assert(1 === ch.length); + enigAssert(1 === ch.length); } this.emit('key press', ch, key); From bbba6cd2142a7678276331c446ded30c1a68cb1a Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 21:30:51 -0600 Subject: [PATCH 10/11] Use new string_format.js lib for formatting...more to come! --- mods/onelinerz.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mods/onelinerz.js b/mods/onelinerz.js index d3639a47..fd7467e2 100644 --- a/mods/onelinerz.js +++ b/mods/onelinerz.js @@ -7,6 +7,7 @@ const getModDatabasePath = require('../core/database.js').getModDatabasePath; const ViewController = require('../core/view_controller.js').ViewController; const theme = require('../core/theme.js'); const ansi = require('../core/ansi_term.js'); +const stringFormat = require('../core/string_format.js'); // deps const sqlite3 = require('sqlite3'); @@ -145,7 +146,7 @@ function OnelinerzModule(options) { const tsFormat = config.timestampFormat || 'ddd h:mma'; entriesView.setItems(entries.map( e => { - return listFormat.format( { + return stringFormat(listFormat, { userId : e.user_id, username : e.user_name, oneliner : e.oneliner, From b80cd18012bd49a1ab4ac974b94f90f69c5b7da2 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 26 Aug 2016 22:34:37 -0600 Subject: [PATCH 11/11] Add Rumorz --- mods/menu.hjson | 90 ++++++++++++++++++ mods/themes/luciano_blocktronics/MMENU.ANS | Bin 3394 -> 3398 bytes mods/themes/luciano_blocktronics/RUMORADD.ANS | Bin 0 -> 717 bytes mods/themes/luciano_blocktronics/RUMORS.ANS | Bin 0 -> 1573 bytes mods/themes/luciano_blocktronics/theme.hjson | 25 +++++ 5 files changed, 115 insertions(+) create mode 100644 mods/themes/luciano_blocktronics/RUMORADD.ANS create mode 100644 mods/themes/luciano_blocktronics/RUMORS.ANS diff --git a/mods/menu.hjson b/mods/menu.hjson index 92184ba1..2bcf4810 100644 --- a/mods/menu.hjson +++ b/mods/menu.hjson @@ -745,6 +745,10 @@ value: { command: "O" } action: @menu:mainMenuOnelinerz } + { + value: { command: "R" } + action: @menu:mainMenuRumorz + } { value: { command: "CHAT"} action: @menu:ercClient @@ -1108,6 +1112,92 @@ } } + mainMenuRumorz: { + desc: Rumorz + module: rumorz + options: { + cls: true + } + config: { + art: { + entries: RUMORS + add: RUMORADD + } + } + form: { + 0: { + mci: { + VM1: { + focus: false + height: 10 + } + TM2: { + argName: addOrExit + items: [ "yeah!", "nah" ] + "hotKeys" : { "Y" : 0, "N" : 1, "Q" : 1 } + submit: true + focus: true + } + } + submit: { + *: [ + { + value: { addOrExit: 0 } + action: @method:viewAddScreen + } + { + value: { addOrExit: null } + action: @systemMethod:nextMenu + } + ] + } + actionKeys: [ + { + keys: [ "escape" ] + action: @systemMethod:nextMenu + } + ] + }, + 1: { + mci: { + ET1: { + focus: true + maxLength: 70 + argName: rumor + } + TL2: { + width: 60 + } + TM3: { + argName: addOrCancel + items: [ "add", "cancel" ] + "hotKeys" : { "A" : 0, "C" : 1, "Q" : 1 } + submit: true + } + } + + submit: { + *: [ + { + value: { addOrCancel: 0 } + action: @method:addEntry + } + { + value: { addOrCancel: 1 } + action: @method:cancelAdd + } + ] + } + actionKeys: [ + { + keys: [ "escape" ] + action: @method:cancelAdd + } + ] + } + } + } + ercClient: { art: erc module: erc_client diff --git a/mods/themes/luciano_blocktronics/MMENU.ANS b/mods/themes/luciano_blocktronics/MMENU.ANS index 3b02a64316afcb7d14f96541e117748510254eb0..b23050131b90c856e7de67d328d37942e126a103 100644 GIT binary patch delta 26 icmX>kbxdl*dL9l1)u15hXj99{n|U%BZ%>xxRRsWgV+eKt delta 22 ecmX>mbx3N%dL9<(Xfxx<+j%k>uTNIwRRsWEvImF& diff --git a/mods/themes/luciano_blocktronics/RUMORADD.ANS b/mods/themes/luciano_blocktronics/RUMORADD.ANS new file mode 100644 index 0000000000000000000000000000000000000000..f98a0a2ffc83e5ce622f3645a5dd181ec2e92a46 GIT binary patch literal 717 zcmb_ayH3L}6ipesvLLc}$?iB&Qpi-I2q94ksfEQ85)sN^9ub9);*)IQU*R0PjZ~G9 z8!7kUbI!fFPO4>7%BGNJ9xT+>{7hs!M6yJOB;|`JP$U zl=YG5od*{b3rdi@fF!Z>z&V6T;FB*nylO8%;+bmj8O45_Mb!l70_HO=utM? z+^Xr)iN-FJ>DKW+f-(#5+uB@0&=0@g ZtX5Z7-&XHddftBhbaeM$ZcUrte*lPk9ajJV literal 0 HcmV?d00001 diff --git a/mods/themes/luciano_blocktronics/theme.hjson b/mods/themes/luciano_blocktronics/theme.hjson index def17c34..008a629d 100644 --- a/mods/themes/luciano_blocktronics/theme.hjson +++ b/mods/themes/luciano_blocktronics/theme.hjson @@ -271,6 +271,31 @@ } } + mainMenuRumorz: { + config: { + listFormat: "|00|11 {rumor}" + focusListFormat: "|00|15> |14{rumor}" + } + 0: { + mci: { + VM1: { height: 14 } + TM2: { + focusTextStyle: upper + items: [ "yes", "no" ] + } + } + } + 1: { + mci: { + ET1: { width: 60 } + TL2: { width: 60 } + TM3: { + focusTextStyle: upper + } + } + } + } + bbsList: { 0: { mci: {