Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
0b8916194e
|
@ -11,6 +11,7 @@ const logger = require('./logger.js');
|
||||||
const database = require('./database.js');
|
const database = require('./database.js');
|
||||||
const clientConns = require('./client_connections.js');
|
const clientConns = require('./client_connections.js');
|
||||||
|
|
||||||
|
// deps
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
|
@ -1,28 +1,37 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var logger = require('./logger.js');
|
// ENiGMA½
|
||||||
|
const logger = require('./logger.js');
|
||||||
|
|
||||||
var _ = require('lodash');
|
// deps
|
||||||
var moment = require('moment');
|
const _ = require('lodash');
|
||||||
|
const moment = require('moment');
|
||||||
|
|
||||||
exports.getActiveConnections = getActiveConnections;
|
exports.getActiveConnections = getActiveConnections;
|
||||||
exports.getActiveNodeList = getActiveNodeList;
|
exports.getActiveNodeList = getActiveNodeList;
|
||||||
exports.addNewClient = addNewClient;
|
exports.addNewClient = addNewClient;
|
||||||
exports.removeClient = removeClient;
|
exports.removeClient = removeClient;
|
||||||
|
|
||||||
var clientConnections = [];
|
const clientConnections = [];
|
||||||
exports.clientConnections = clientConnections;
|
exports.clientConnections = clientConnections;
|
||||||
|
|
||||||
function getActiveConnections() {
|
function getActiveConnections() { return clientConnections; }
|
||||||
return clientConnections;
|
|
||||||
}
|
function getActiveNodeList(authUsersOnly) {
|
||||||
|
|
||||||
|
if(!_.isBoolean(authUsersOnly)) {
|
||||||
|
authUsersOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
function getActiveNodeList() {
|
|
||||||
const now = moment();
|
const now = moment();
|
||||||
|
|
||||||
|
const activeConnections = getActiveConnections().filter(ac => {
|
||||||
|
return ((authUsersOnly && ac.user.isAuthenticated()) || !authUsersOnly);
|
||||||
|
});
|
||||||
|
|
||||||
return _.map(getActiveConnections(), ac => {
|
return _.map(activeConnections, ac => {
|
||||||
let entry = {
|
const entry = {
|
||||||
node : ac.node,
|
node : ac.node,
|
||||||
authenticated : ac.user.isAuthenticated(),
|
authenticated : ac.user.isAuthenticated(),
|
||||||
userId : ac.user.userId,
|
userId : ac.user.userId,
|
||||||
|
@ -46,13 +55,13 @@ function getActiveNodeList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNewClient(client, clientSock) {
|
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
|
// Create a client specific logger
|
||||||
// Note that this will be updated @ login with additional information
|
// Note that this will be updated @ login with additional information
|
||||||
client.log = logger.log.child( { clientId : id } );
|
client.log = logger.log.child( { clientId : id } );
|
||||||
|
|
||||||
var connInfo = {
|
const connInfo = {
|
||||||
ip : clientSock.remoteAddress,
|
ip : clientSock.remoteAddress,
|
||||||
serverName : client.session.serverName,
|
serverName : client.session.serverName,
|
||||||
isSecure : client.session.isSecure,
|
isSecure : client.session.isSecure,
|
||||||
|
@ -71,7 +80,7 @@ function addNewClient(client, clientSock) {
|
||||||
function removeClient(client) {
|
function removeClient(client) {
|
||||||
client.end();
|
client.end();
|
||||||
|
|
||||||
var i = clientConnections.indexOf(client);
|
const i = clientConnections.indexOf(client);
|
||||||
if(i > -1) {
|
if(i > -1) {
|
||||||
clientConnections.splice(i, 1);
|
clientConnections.splice(i, 1);
|
||||||
|
|
||||||
|
@ -84,15 +93,3 @@ function removeClient(client) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* :TODO: make a public API elsewhere
|
|
||||||
function getActiveClientInformation() {
|
|
||||||
var info = {};
|
|
||||||
|
|
||||||
clientConnections.forEach(function connEntry(cc) {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -122,10 +122,10 @@ function prepareTerminal(term) {
|
||||||
function displayBanner(term) {
|
function displayBanner(term) {
|
||||||
// note: intentional formatting:
|
// note: intentional formatting:
|
||||||
term.pipeWrite(`
|
term.pipeWrite(`
|
||||||
|06Connected to |02EN|10i|02GMA|10½ |06BBS version |12|VN
|
|06Connected to |02EN|10i|02GMA|10½ |06BBS version |12|VN
|
||||||
|06Copyright (c) 2014-2016 Bryan Ashby |14- |12http://l33t.codes/
|
|06Copyright (c) 2014-2016 Bryan Ashby |14- |12http://l33t.codes/
|
||||||
|06Updates & source |14- |12https://github.com/NuSkooler/enigma-bbs/
|
|06Updates & source |14- |12https://github.com/NuSkooler/enigma-bbs/
|
||||||
|00`
|
|00`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
@ -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' );
|
||||||
|
}
|
||||||
|
};
|
|
@ -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;
|
||||||
|
};
|
56
core/view.js
56
core/view.js
|
@ -1,18 +1,19 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var events = require('events');
|
// ENiGMA½
|
||||||
var util = require('util');
|
const events = require('events');
|
||||||
var assert = require('assert');
|
const util = require('util');
|
||||||
var ansi = require('./ansi_term.js');
|
const ansi = require('./ansi_term.js');
|
||||||
var colorCodes = require('./color_codes.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 = 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' ],
|
accept : [ 'return' ],
|
||||||
exit : [ 'esc' ],
|
exit : [ 'esc' ],
|
||||||
backspace : [ 'backspace', 'del' ],
|
backspace : [ 'backspace', 'del' ],
|
||||||
|
@ -27,11 +28,13 @@ var VIEW_SPECIAL_KEY_MAP_DEFAULT = {
|
||||||
clearLine : [ 'ctrl + y' ],
|
clearLine : [ 'ctrl + y' ],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.VIEW_SPECIAL_KEY_MAP_DEFAULT = VIEW_SPECIAL_KEY_MAP_DEFAULT;
|
||||||
|
|
||||||
function View(options) {
|
function View(options) {
|
||||||
events.EventEmitter.call(this);
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
assert(_.isObject(options));
|
enigAssert(_.isObject(options));
|
||||||
assert(_.isObject(options.client));
|
enigAssert(_.isObject(options.client));
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -131,7 +134,7 @@ View.prototype.setPosition = function(pos) {
|
||||||
this.position.col = parseInt(arguments[1], 10);
|
this.position.col = parseInt(arguments[1], 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
// santaize
|
// sanatize
|
||||||
this.position.row = Math.max(this.position.row, 1);
|
this.position.row = Math.max(this.position.row, 1);
|
||||||
this.position.col = Math.max(this.position.col, 1);
|
this.position.col = Math.max(this.position.col, 1);
|
||||||
this.position.row = Math.min(this.position.row, this.client.term.termHeight);
|
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) {
|
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.dimens = dimens;
|
||||||
this.autoScale = { height : false, width : false };
|
this.autoScale = { height : false, width : false };
|
||||||
};
|
};
|
||||||
|
|
||||||
View.prototype.setHeight = function(height) {
|
View.prototype.setHeight = function(height) {
|
||||||
height = parseInt(height, 10);
|
height = parseInt(height) || 1;
|
||||||
assert(_.isNumber(height));
|
height = Math.min(height, this.client.term.termHeight);
|
||||||
// :TODO: assert height is within this.client.term.termHeight
|
|
||||||
|
|
||||||
this.dimens.height = height;
|
this.dimens.height = height;
|
||||||
this.autoScale.height = false;
|
this.autoScale.height = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
View.prototype.setWidth = function(width) {
|
View.prototype.setWidth = function(width) {
|
||||||
width = parseInt(width);
|
width = parseInt(width) || 1;
|
||||||
assert(_.isNumber(width));
|
width = Math.min(width, this.client.term.termWidth);
|
||||||
// :TODO: assert width is appropriate for this.client.term.termWidth
|
|
||||||
|
|
||||||
this.dimens.width = width;
|
this.dimens.width = width;
|
||||||
this.autoScale.width = false;
|
this.autoScale.width = false;
|
||||||
|
@ -168,7 +169,7 @@ View.prototype.getSGR = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
View.prototype.getStyleSGR = function(n) {
|
View.prototype.getStyleSGR = function(n) {
|
||||||
assert(_.isNumber(n));
|
n = parseInt(n) || 0;
|
||||||
return this['styleSGR' + n];
|
return this['styleSGR' + n];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -241,21 +242,22 @@ View.prototype.redraw = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
View.prototype.setFocus = function(focused) {
|
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.hasFocus = focused;
|
||||||
this.restoreCursor();
|
this.restoreCursor();
|
||||||
};
|
};
|
||||||
|
|
||||||
View.prototype.onKeyPress = function(ch, key) {
|
View.prototype.onKeyPress = function(ch, key) {
|
||||||
if(false === this.hasFocus) {
|
enigAssert(this.hasFocus, 'View does not have focus');
|
||||||
console.log('doh!'); // :TODO: fix me -- assert here?
|
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) {
|
if(key) {
|
||||||
assert(this.specialKeyMap, 'No special key map defined');
|
enigAssert(this.specialKeyMap, 'No special key map defined');
|
||||||
|
|
||||||
if(this.isKeyMapped('accept', key.name)) {
|
if(this.isKeyMapped('accept', key.name)) {
|
||||||
this.emit('action', 'accept', key);
|
this.emit('action', 'accept', key);
|
||||||
|
@ -265,7 +267,7 @@ View.prototype.onKeyPress = function(ch, key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ch) {
|
if(ch) {
|
||||||
assert(1 === ch.length);
|
enigAssert(1 === ch.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('key press', ch, key);
|
this.emit('key press', ch, key);
|
||||||
|
|
|
@ -745,6 +745,10 @@
|
||||||
value: { command: "O" }
|
value: { command: "O" }
|
||||||
action: @menu:mainMenuOnelinerz
|
action: @menu:mainMenuOnelinerz
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
value: { command: "R" }
|
||||||
|
action: @menu:mainMenuRumorz
|
||||||
|
}
|
||||||
{
|
{
|
||||||
value: { command: "CHAT"}
|
value: { command: "CHAT"}
|
||||||
action: @menu:ercClient
|
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: {
|
ercClient: {
|
||||||
art: erc
|
art: erc
|
||||||
module: erc_client
|
module: erc_client
|
||||||
|
|
|
@ -7,6 +7,7 @@ const getModDatabasePath = require('../core/database.js').getModDatabasePath;
|
||||||
const ViewController = require('../core/view_controller.js').ViewController;
|
const ViewController = require('../core/view_controller.js').ViewController;
|
||||||
const theme = require('../core/theme.js');
|
const theme = require('../core/theme.js');
|
||||||
const ansi = require('../core/ansi_term.js');
|
const ansi = require('../core/ansi_term.js');
|
||||||
|
const stringFormat = require('../core/string_format.js');
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
const sqlite3 = require('sqlite3');
|
const sqlite3 = require('sqlite3');
|
||||||
|
@ -145,7 +146,7 @@ function OnelinerzModule(options) {
|
||||||
const tsFormat = config.timestampFormat || 'ddd h:mma';
|
const tsFormat = config.timestampFormat || 'ddd h:mma';
|
||||||
|
|
||||||
entriesView.setItems(entries.map( e => {
|
entriesView.setItems(entries.map( e => {
|
||||||
return listFormat.format( {
|
return stringFormat(listFormat, {
|
||||||
userId : e.user_id,
|
userId : e.user_id,
|
||||||
username : e.user_name,
|
username : e.user_name,
|
||||||
oneliner : e.oneliner,
|
oneliner : e.oneliner,
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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: {
|
bbsList: {
|
||||||
0: {
|
0: {
|
||||||
mci: {
|
mci: {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var MenuModule = require('../core/menu_module.js').MenuModule;
|
// ENiGMA½
|
||||||
var ViewController = require('../core/view_controller.js').ViewController;
|
const MenuModule = require('../core/menu_module.js').MenuModule;
|
||||||
var getActiveNodeList = require('../core/client_connections.js').getActiveNodeList;
|
const ViewController = require('../core/view_controller.js').ViewController;
|
||||||
|
const getActiveNodeList = require('../core/client_connections.js').getActiveNodeList;
|
||||||
|
|
||||||
var moment = require('moment');
|
// deps
|
||||||
var async = require('async');
|
const async = require('async');
|
||||||
var assert = require('assert');
|
const _ = require('lodash');
|
||||||
var _ = require('lodash');
|
|
||||||
|
|
||||||
exports.moduleInfo = {
|
exports.moduleInfo = {
|
||||||
name : 'Who\'s Online',
|
name : 'Who\'s Online',
|
||||||
|
@ -17,26 +17,9 @@ exports.moduleInfo = {
|
||||||
packageName : 'codes.l33t.enigma.whosonline'
|
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;
|
exports.getModule = WhosOnlineModule;
|
||||||
|
|
||||||
var MciCodeIds = {
|
const MciCodeIds = {
|
||||||
OnlineList : 1,
|
OnlineList : 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,30 +30,29 @@ function WhosOnlineModule(options) {
|
||||||
require('util').inherits(WhosOnlineModule, MenuModule);
|
require('util').inherits(WhosOnlineModule, MenuModule);
|
||||||
|
|
||||||
WhosOnlineModule.prototype.mciReady = function(mciData, cb) {
|
WhosOnlineModule.prototype.mciReady = function(mciData, cb) {
|
||||||
var self = this;
|
const self = this;
|
||||||
var vc = self.viewControllers.allViews = new ViewController( { client : self.client } );
|
const vc = self.viewControllers.allViews = new ViewController( { client : self.client } );
|
||||||
|
|
||||||
async.series(
|
async.series(
|
||||||
[
|
[
|
||||||
function callParentMciReady(callback) {
|
function callParentMciReady(callback) {
|
||||||
WhosOnlineModule.super_.prototype.mciReady.call(self, mciData, callback);
|
return WhosOnlineModule.super_.prototype.mciReady.call(self, mciData, callback);
|
||||||
},
|
},
|
||||||
function loadFromConfig(callback) {
|
function loadFromConfig(callback) {
|
||||||
var loadOpts = {
|
const loadOpts = {
|
||||||
callingMenu : self,
|
callingMenu : self,
|
||||||
mciMap : mciData.menu,
|
mciMap : mciData.menu,
|
||||||
noInput : true,
|
noInput : true,
|
||||||
};
|
};
|
||||||
|
|
||||||
vc.loadFromMenuConfig(loadOpts, callback);
|
return vc.loadFromMenuConfig(loadOpts, callback);
|
||||||
},
|
},
|
||||||
function populateList(callback) {
|
function populateList(callback) {
|
||||||
var onlineListView = vc.getView(MciCodeIds.OnlineList);
|
const onlineListView = vc.getView(MciCodeIds.OnlineList);
|
||||||
|
const listFormat = self.menuConfig.config.listFormat || '{node} - {userName} - {action} - {timeOn}';
|
||||||
const listFormat = self.menuConfig.config.listFormat || '{node} - {userName} - {action} - {timeOn}';
|
const nonAuthUser = self.menuConfig.config.nonAuthUser || 'Logging In';
|
||||||
const nonAuthUser = self.menuConfig.config.nonAuthUser || 'Logging In';
|
const otherUnknown = self.menuConfig.config.otherUnknown || 'N/A';
|
||||||
const otherUnknown = self.menuConfig.config.otherUnknown || 'N/A';
|
const onlineList = getActiveNodeList(self.menuConfig.config.authUsersOnly).slice(0, onlineListView.height);
|
||||||
const onlineList = getActiveNodeList().slice(0, onlineListView.height);
|
|
||||||
|
|
||||||
onlineListView.setItems(_.map(onlineList, oe => {
|
onlineListView.setItems(_.map(onlineList, oe => {
|
||||||
if(oe.authenticated) {
|
if(oe.authenticated) {
|
||||||
|
@ -85,16 +67,16 @@ WhosOnlineModule.prototype.mciReady = function(mciData, cb) {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
onlineListView.focusItems = onlineListView.items;
|
onlineListView.focusItems = onlineListView.items;
|
||||||
|
|
||||||
onlineListView.redraw();
|
onlineListView.redraw();
|
||||||
callback(null);
|
|
||||||
|
return callback(null);
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
function complete(err) {
|
function complete(err) {
|
||||||
if(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);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,6 @@ const resolvePath = require('./core/misc_util.js').resolvePath;
|
||||||
// deps
|
// deps
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const assert = require('assert');
|
|
||||||
const inq = require('inquirer');
|
const inq = require('inquirer');
|
||||||
const mkdirsSync = require('fs-extra').mkdirsSync;
|
const mkdirsSync = require('fs-extra').mkdirsSync;
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
Loading…
Reference in New Issue