Merge branch 'master' of ssh://numinibsd/git/base/enigma-bbs
This commit is contained in:
commit
e213fdd696
|
@ -12,9 +12,13 @@
|
||||||
// * http://www.inwap.com/pdp10/ansicode.txt
|
// * http://www.inwap.com/pdp10/ansicode.txt
|
||||||
//
|
//
|
||||||
|
|
||||||
const assert = require('assert');
|
// ENiGMA½
|
||||||
const miscUtil = require('./misc_util.js');
|
const miscUtil = require('./misc_util.js');
|
||||||
|
|
||||||
|
// deps
|
||||||
|
const assert = require('assert');
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
exports.getFGColorValue = getFGColorValue;
|
exports.getFGColorValue = getFGColorValue;
|
||||||
exports.getBGColorValue = getBGColorValue;
|
exports.getBGColorValue = getBGColorValue;
|
||||||
exports.sgr = sgr;
|
exports.sgr = sgr;
|
||||||
|
@ -23,7 +27,6 @@ exports.clearScreen = clearScreen;
|
||||||
exports.resetScreen = resetScreen;
|
exports.resetScreen = resetScreen;
|
||||||
exports.normal = normal;
|
exports.normal = normal;
|
||||||
exports.goHome = goHome;
|
exports.goHome = goHome;
|
||||||
//exports.deleteLine = deleteLine;
|
|
||||||
exports.disableVT100LineWrapping = disableVT100LineWrapping;
|
exports.disableVT100LineWrapping = disableVT100LineWrapping;
|
||||||
exports.setSyncTERMFont = setSyncTERMFont;
|
exports.setSyncTERMFont = setSyncTERMFont;
|
||||||
exports.getSyncTERMFontFromAlias = getSyncTERMFontFromAlias;
|
exports.getSyncTERMFontFromAlias = getSyncTERMFontFromAlias;
|
||||||
|
@ -35,9 +38,9 @@ exports.setEmulatedBaudRate = setEmulatedBaudRate;
|
||||||
// See also
|
// See also
|
||||||
// https://github.com/TooTallNate/ansi.js/blob/master/lib/ansi.js
|
// https://github.com/TooTallNate/ansi.js/blob/master/lib/ansi.js
|
||||||
|
|
||||||
var ESC_CSI = '\u001b[';
|
const ESC_CSI = '\u001b[';
|
||||||
|
|
||||||
var CONTROL = {
|
const CONTROL = {
|
||||||
up : 'A',
|
up : 'A',
|
||||||
down : 'B',
|
down : 'B',
|
||||||
|
|
||||||
|
@ -124,7 +127,7 @@ var CONTROL = {
|
||||||
// Select Graphics Rendition
|
// Select Graphics Rendition
|
||||||
// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt
|
// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt
|
||||||
//
|
//
|
||||||
var SGRValues = {
|
const SGRValues = {
|
||||||
reset : 0,
|
reset : 0,
|
||||||
bold : 1,
|
bold : 1,
|
||||||
dim : 2,
|
dim : 2,
|
||||||
|
@ -180,7 +183,7 @@ function getBGColorValue(name) {
|
||||||
//
|
//
|
||||||
// See https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt
|
// See https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt
|
||||||
//
|
//
|
||||||
var SYNCTERM_FONT_AND_ENCODING_TABLE = [
|
const SYNCTERM_FONT_AND_ENCODING_TABLE = [
|
||||||
'cp437',
|
'cp437',
|
||||||
'cp1251',
|
'cp1251',
|
||||||
'koi8_r',
|
'koi8_r',
|
||||||
|
@ -233,7 +236,7 @@ var SYNCTERM_FONT_AND_ENCODING_TABLE = [
|
||||||
// This table contains lowercased entries with any spaces
|
// This table contains lowercased entries with any spaces
|
||||||
// replaced with '_' for lookup purposes.
|
// replaced with '_' for lookup purposes.
|
||||||
//
|
//
|
||||||
var FONT_ALIAS_TO_SYNCTERM_MAP = {
|
const FONT_ALIAS_TO_SYNCTERM_MAP = {
|
||||||
'cp437' : 'cp437',
|
'cp437' : 'cp437',
|
||||||
'ibm_vga' : 'cp437',
|
'ibm_vga' : 'cp437',
|
||||||
'ibmpc' : 'cp437',
|
'ibmpc' : 'cp437',
|
||||||
|
@ -267,8 +270,8 @@ var FONT_ALIAS_TO_SYNCTERM_MAP = {
|
||||||
'amiga_p0t-noodle' : 'pot_noodle',
|
'amiga_p0t-noodle' : 'pot_noodle',
|
||||||
|
|
||||||
'mo_soul' : 'mo_soul',
|
'mo_soul' : 'mo_soul',
|
||||||
'mosoul' : 'mo_soul',
|
'mosoul' : 'mo_soul',
|
||||||
'mO\'sOul' : 'mo_soul',
|
'mO\'sOul' : 'mo_soul',
|
||||||
|
|
||||||
'amiga_microknight' : 'microknight',
|
'amiga_microknight' : 'microknight',
|
||||||
'amiga_microknight+' : 'microknight_plus',
|
'amiga_microknight+' : 'microknight_plus',
|
||||||
|
@ -280,13 +283,13 @@ var FONT_ALIAS_TO_SYNCTERM_MAP = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function setSyncTERMFont(name, fontPage) {
|
function setSyncTERMFont(name, fontPage) {
|
||||||
var p1 = miscUtil.valueWithDefault(fontPage, 0);
|
const p1 = miscUtil.valueWithDefault(fontPage, 0);
|
||||||
|
|
||||||
assert(p1 >= 0 && p1 <= 3);
|
assert(p1 >= 0 && p1 <= 3);
|
||||||
|
|
||||||
var p2 = SYNCTERM_FONT_AND_ENCODING_TABLE.indexOf(name);
|
const p2 = SYNCTERM_FONT_AND_ENCODING_TABLE.indexOf(name);
|
||||||
if(p2 > -1) {
|
if(p2 > -1) {
|
||||||
return ESC_CSI + p1 + ';' + p2 + ' D';
|
return `${ESC_CSI}${p1};${p2} D`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
@ -296,20 +299,20 @@ function getSyncTERMFontFromAlias(alias) {
|
||||||
return FONT_ALIAS_TO_SYNCTERM_MAP[alias.toLowerCase().replace(/ /g, '_')];
|
return FONT_ALIAS_TO_SYNCTERM_MAP[alias.toLowerCase().replace(/ /g, '_')];
|
||||||
}
|
}
|
||||||
|
|
||||||
var DEC_CURSOR_STYLE = {
|
const DEC_CURSOR_STYLE = {
|
||||||
'blinking block' : 0,
|
'blinking block' : 0,
|
||||||
'default' : 1,
|
'default' : 1,
|
||||||
'steady block' : 2,
|
'steady block' : 2,
|
||||||
'blinking underline' : 3,
|
'blinking underline' : 3,
|
||||||
'steady underline' : 4,
|
'steady underline' : 4,
|
||||||
'blinking bar' : 5,
|
'blinking bar' : 5,
|
||||||
'steady bar' : 6,
|
'steady bar' : 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
function setCursorStyle(cursorStyle) {
|
function setCursorStyle(cursorStyle) {
|
||||||
var ps = DEC_CURSOR_STYLE[cursorStyle];
|
const ps = DEC_CURSOR_STYLE[cursorStyle];
|
||||||
if(ps) {
|
if(ps) {
|
||||||
return ESC_CSI + ps + ' q';
|
return `${ESC_CSI}${ps} q`;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
|
@ -317,24 +320,24 @@ function setCursorStyle(cursorStyle) {
|
||||||
|
|
||||||
// Create methods such as up(), nextLine(),...
|
// Create methods such as up(), nextLine(),...
|
||||||
Object.keys(CONTROL).forEach(function onControlName(name) {
|
Object.keys(CONTROL).forEach(function onControlName(name) {
|
||||||
var code = CONTROL[name];
|
const code = CONTROL[name];
|
||||||
|
|
||||||
exports[name] = function() {
|
exports[name] = function() {
|
||||||
var c = code;
|
let c = code;
|
||||||
if(arguments.length > 0) {
|
if(arguments.length > 0) {
|
||||||
// arguments are array like -- we want an array
|
// arguments are array like -- we want an array
|
||||||
c = Array.prototype.slice.call(arguments).map(Math.round).join(';') + code;
|
c = Array.prototype.slice.call(arguments).map(Math.round).join(';') + code;
|
||||||
}
|
}
|
||||||
return ESC_CSI + c;
|
return `${ESC_CSI}${c}`;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create various color methods such as white(), yellowBG(), reset(), ...
|
// Create various color methods such as white(), yellowBG(), reset(), ...
|
||||||
Object.keys(SGRValues).forEach(function onSgrName(name) {
|
Object.keys(SGRValues).forEach( name => {
|
||||||
var code = SGRValues[name];
|
const code = SGRValues[name];
|
||||||
|
|
||||||
exports[name] = function() {
|
exports[name] = function() {
|
||||||
return ESC_CSI + code + 'm';
|
return `${ESC_CSI}${code}m`;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -347,28 +350,20 @@ function sgr() {
|
||||||
if(arguments.length <= 0) {
|
if(arguments.length <= 0) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = '';
|
|
||||||
|
|
||||||
// :TODO: this method needs a lot of cleanup!
|
let result = [];
|
||||||
|
const args = Array.isArray(arguments[0]) ? arguments[0] : arguments;
|
||||||
|
|
||||||
var args = Array.isArray(arguments[0]) ? arguments[0] : arguments;
|
for(let i = 0; i < args.length; ++i) {
|
||||||
for(var i = 0; i < args.length; i++) {
|
const arg = args[i];
|
||||||
if(typeof args[i] === 'string') {
|
if(_.isString(arg) && arg in SGRValues) {
|
||||||
if(args[i] in SGRValues) {
|
result.push(SGRValues[arg]);
|
||||||
if(result.length > 0) {
|
} else if(_.isNumber(arg)) {
|
||||||
result += ';';
|
result.push(arg);
|
||||||
}
|
|
||||||
result += SGRValues[args[i]];
|
|
||||||
}
|
|
||||||
} else if(typeof args[i] === 'number') {
|
|
||||||
if(result.length > 0) {
|
|
||||||
result += ';';
|
|
||||||
}
|
|
||||||
result += args[i];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ESC_CSI + result + 'm';
|
|
||||||
|
return `${ESC_CSI}${result.join(';')}m`;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -376,10 +371,10 @@ function sgr() {
|
||||||
// to a ANSI SGR sequence.
|
// to a ANSI SGR sequence.
|
||||||
//
|
//
|
||||||
function getSGRFromGraphicRendition(graphicRendition, initialReset) {
|
function getSGRFromGraphicRendition(graphicRendition, initialReset) {
|
||||||
var sgrSeq = [];
|
let sgrSeq = [];
|
||||||
|
let styleCount = 0;
|
||||||
|
|
||||||
var styleCount = 0;
|
[ 'intensity', 'underline', 'blink', 'negative', 'invisible' ].forEach( s => {
|
||||||
[ 'intensity', 'underline', 'blink', 'negative', 'invisible' ].forEach(function style(s) {
|
|
||||||
if(graphicRendition[s]) {
|
if(graphicRendition[s]) {
|
||||||
sgrSeq.push(graphicRendition[s]);
|
sgrSeq.push(graphicRendition[s]);
|
||||||
++styleCount;
|
++styleCount;
|
||||||
|
@ -414,11 +409,11 @@ function clearScreen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetScreen() {
|
function resetScreen() {
|
||||||
return exports.reset() + exports.eraseData(2) + exports.goHome();
|
return `${exports.reset()}${exports.eraseData(2)}${exports.goHome()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normal() {
|
function normal() {
|
||||||
return sgr(['normal', 'reset']);
|
return sgr( [ 'normal', 'reset' ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
function goHome() {
|
function goHome() {
|
||||||
|
@ -426,40 +421,23 @@ function goHome() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Delete line(s)
|
// Disable auto line wraping @ termWidth
|
||||||
// This method acts like ESC[ p1 M but should work
|
|
||||||
// for all terminals via using eraseLine and movement
|
|
||||||
//
|
//
|
||||||
/*
|
// See:
|
||||||
function deleteLine(count) {
|
// http://stjarnhimlen.se/snippets/vt100.txt
|
||||||
count = count || 1;
|
// https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt
|
||||||
|
|
||||||
console.log(exports.eraseLine)
|
|
||||||
var seq = exports.eraseLine(2); // 2 = entire line
|
|
||||||
var i;
|
|
||||||
for(i = 1; i < count; ++i) {
|
|
||||||
seq +=
|
|
||||||
'\n' + // down a line
|
|
||||||
exports.eraseLine(2); // erase it
|
|
||||||
}
|
|
||||||
|
|
||||||
// now, move back up any we lines we went down
|
|
||||||
if(count > 1) {
|
|
||||||
seq += exports.up(count - 1);
|
|
||||||
}
|
|
||||||
return seq;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// See http://www.termsys.demon.co.uk/vtANSI_BBS.htm
|
// WARNING:
|
||||||
|
// * Not honored by all clients
|
||||||
|
// * If it is honored, ANSI's that rely on this (e.g. do not have \r\n endings
|
||||||
|
// and use term width -- generally 80 columns -- will display garbled!
|
||||||
//
|
//
|
||||||
function disableVT100LineWrapping() {
|
function disableVT100LineWrapping() {
|
||||||
return ESC_CSI + '7l';
|
return `${ESC_CSI}?7l`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setEmulatedBaudRate(rate) {
|
function setEmulatedBaudRate(rate) {
|
||||||
var speed = {
|
const speed = {
|
||||||
unlimited : 0,
|
unlimited : 0,
|
||||||
off : 0,
|
off : 0,
|
||||||
0 : 0,
|
0 : 0,
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Config = require('./config.js').config;
|
// ENiGMA½
|
||||||
|
const Config = require('./config.js').config;
|
||||||
|
|
||||||
var _ = require('lodash');
|
// deps
|
||||||
var assert = require('assert');
|
const _ = require('lodash');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
exports.parseAsset = parseAsset;
|
exports.parseAsset = parseAsset;
|
||||||
exports.getAssetWithShorthand = getAssetWithShorthand;
|
exports.getAssetWithShorthand = getAssetWithShorthand;
|
||||||
|
@ -17,19 +19,20 @@ const ALL_ASSETS = [
|
||||||
'art',
|
'art',
|
||||||
'menu',
|
'menu',
|
||||||
'method',
|
'method',
|
||||||
|
'module',
|
||||||
'systemMethod',
|
'systemMethod',
|
||||||
'systemModule',
|
'systemModule',
|
||||||
'prompt',
|
'prompt',
|
||||||
'config',
|
'config',
|
||||||
];
|
];
|
||||||
|
|
||||||
var ASSET_RE = new RegExp('\\@(' + ALL_ASSETS.join('|') + ')\\:([\\w\\d\\.]*)(?:\\/([\\w\\d\\_]+))*');
|
const ASSET_RE = new RegExp('\\@(' + ALL_ASSETS.join('|') + ')\\:([\\w\\d\\.]*)(?:\\/([\\w\\d\\_]+))*');
|
||||||
|
|
||||||
function parseAsset(s) {
|
function parseAsset(s) {
|
||||||
var m = ASSET_RE.exec(s);
|
const m = ASSET_RE.exec(s);
|
||||||
|
|
||||||
if(m) {
|
if(m) {
|
||||||
var result = { type : m[1] };
|
let result = { type : m[1] };
|
||||||
|
|
||||||
if(m[3]) {
|
if(m[3]) {
|
||||||
result.location = m[2];
|
result.location = m[2];
|
||||||
|
@ -48,7 +51,7 @@ function getAssetWithShorthand(spec, defaultType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if('@' === spec[0]) {
|
if('@' === spec[0]) {
|
||||||
var asset = parseAsset(spec);
|
const asset = parseAsset(spec);
|
||||||
assert(_.isString(asset.type));
|
assert(_.isString(asset.type));
|
||||||
|
|
||||||
return asset;
|
return asset;
|
||||||
|
@ -56,63 +59,48 @@ function getAssetWithShorthand(spec, defaultType) {
|
||||||
return {
|
return {
|
||||||
type : defaultType,
|
type : defaultType,
|
||||||
asset : spec,
|
asset : spec,
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// :TODO: Convert these to getAssetWithShorthand()
|
|
||||||
function getArtAsset(art) {
|
|
||||||
if(!_.isString(art)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if('@' === art[0]) {
|
|
||||||
var artAsset = parseAsset(art);
|
|
||||||
assert('art' === artAsset.type || 'method' === artAsset.type);
|
|
||||||
|
|
||||||
return artAsset;
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
type : 'art',
|
|
||||||
asset : art,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getModuleAsset(module) {
|
function getArtAsset(spec) {
|
||||||
if(!_.isString(module)) {
|
const asset = getAssetWithShorthand(spec, 'art');
|
||||||
|
|
||||||
|
if(!asset) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if('@' === module[0]) {
|
assert( ['art', 'method' ].indexOf(asset.type) > -1);
|
||||||
var modAsset = parseAsset(module);
|
return asset;
|
||||||
assert('module' === modAsset.type || 'systemModule' === modAsset.type);
|
|
||||||
|
|
||||||
return modAsset;
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
type : 'module',
|
|
||||||
asset : module,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveConfigAsset(from) {
|
function getModuleAsset(spec) {
|
||||||
var asset = parseAsset(from);
|
const asset = getAssetWithShorthand(spec, 'module');
|
||||||
|
|
||||||
|
if(!asset) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( ['module', 'systemModule' ].indexOf(asset.type) > -1);
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveConfigAsset(spec) {
|
||||||
|
const asset = parseAsset(spec);
|
||||||
if(asset) {
|
if(asset) {
|
||||||
assert('config' === asset.type);
|
assert('config' === asset.type);
|
||||||
|
|
||||||
var path = asset.asset.split('.');
|
const path = asset.asset.split('.');
|
||||||
var conf = Config;
|
let conf = Config;
|
||||||
for(var i = 0; i < path.length; ++i) {
|
for(let i = 0; i < path.length; ++i) {
|
||||||
if(_.isUndefined(conf[path[i]])) {
|
if(_.isUndefined(conf[path[i]])) {
|
||||||
return from;
|
return spec;
|
||||||
}
|
}
|
||||||
conf = conf[path[i]];
|
conf = conf[path[i]];
|
||||||
}
|
}
|
||||||
return conf;
|
return conf;
|
||||||
} else {
|
} else {
|
||||||
return from;
|
return spec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,4 +110,4 @@ function getViewPropertyAsset(src) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseAsset(src);
|
return parseAsset(src);
|
||||||
};
|
}
|
||||||
|
|
|
@ -180,6 +180,7 @@ function getDefaultConfig() {
|
||||||
themes : paths.join(__dirname, './../mods/themes/'),
|
themes : paths.join(__dirname, './../mods/themes/'),
|
||||||
logs : paths.join(__dirname, './../logs/'), // :TODO: set up based on system, e.g. /var/logs/enigmabbs or such
|
logs : paths.join(__dirname, './../logs/'), // :TODO: set up based on system, e.g. /var/logs/enigmabbs or such
|
||||||
db : paths.join(__dirname, './../db/'),
|
db : paths.join(__dirname, './../db/'),
|
||||||
|
modsDb : paths.join(__dirname, './../db/mods/'),
|
||||||
dropFiles : paths.join(__dirname, './../dropfiles/'), // + "/node<x>/
|
dropFiles : paths.join(__dirname, './../dropfiles/'), // + "/node<x>/
|
||||||
misc : paths.join(__dirname, './../misc/'),
|
misc : paths.join(__dirname, './../misc/'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -72,7 +72,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) {
|
||||||
|
|
||||||
function prepareTerminal(term) {
|
function prepareTerminal(term) {
|
||||||
term.rawWrite(ansi.normal());
|
term.rawWrite(ansi.normal());
|
||||||
term.rawWrite(ansi.disableVT100LineWrapping());
|
//term.rawWrite(ansi.disableVT100LineWrapping());
|
||||||
// :TODO: set xterm stuff -- see x84/others
|
// :TODO: set xterm stuff -- see x84/others
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,42 @@ var sqlite3 = require('sqlite3');
|
||||||
var paths = require('path');
|
var paths = require('path');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
|
const _ = require('lodash');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
// database handles
|
// database handles
|
||||||
var dbs = {};
|
var dbs = {};
|
||||||
|
|
||||||
|
exports.getModDatabasePath = getModDatabasePath;
|
||||||
exports.initializeDatabases = initializeDatabases;
|
exports.initializeDatabases = initializeDatabases;
|
||||||
|
|
||||||
exports.dbs = dbs;
|
exports.dbs = dbs;
|
||||||
|
|
||||||
function getDatabasePath(name) {
|
function getDatabasePath(name) {
|
||||||
return paths.join(conf.config.paths.db, name + '.sqlite3');
|
return paths.join(conf.config.paths.db, `${name}.sqlite3`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModDatabasePath(moduleInfo, suffix) {
|
||||||
|
//
|
||||||
|
// Mods that use a database are stored in Config.paths.modsDb (e.g. enigma-bbs/db/mods)
|
||||||
|
// We expect that moduleInfo defines packageName which will be the base of the modules
|
||||||
|
// filename. An optional suffix may be supplied as well.
|
||||||
|
//
|
||||||
|
const HOST_RE = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
|
||||||
|
|
||||||
|
assert(_.isObject(moduleInfo));
|
||||||
|
assert(_.isString(moduleInfo.packageName), 'moduleInfo must define "packageName"!');
|
||||||
|
|
||||||
|
let full = moduleInfo.packageName;
|
||||||
|
if(suffix) {
|
||||||
|
full += `.${suffix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(
|
||||||
|
(full.split('.').length > 1 && HOST_RE.test(full)),
|
||||||
|
'packageName must follow Reverse Domain Name Notation - https://en.wikipedia.org/wiki/Reverse_domain_name_notation');
|
||||||
|
|
||||||
|
return paths.join(conf.config.paths.modsDb, `${full}.sqlite3`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeDatabases(cb) {
|
function initializeDatabases(cb) {
|
||||||
|
|
|
@ -317,7 +317,7 @@ function FullScreenEditorModule(options) {
|
||||||
// in NetRunner:
|
// in NetRunner:
|
||||||
self.client.term.rawWrite(ansi.reset() + ansi.deleteLine(3));
|
self.client.term.rawWrite(ansi.reset() + ansi.deleteLine(3));
|
||||||
|
|
||||||
self.client.term.rawWrite(ansi.reset() + ansi.eraseLine(2))
|
self.client.term.rawWrite(ansi.reset() + ansi.eraseLine(2));
|
||||||
}
|
}
|
||||||
callback(null);
|
callback(null);
|
||||||
},
|
},
|
||||||
|
@ -424,8 +424,7 @@ function FullScreenEditorModule(options) {
|
||||||
async.series(
|
async.series(
|
||||||
[
|
[
|
||||||
function beforeDisplayArt(callback) {
|
function beforeDisplayArt(callback) {
|
||||||
self.beforeArt();
|
self.beforeArt(callback);
|
||||||
callback(null);
|
|
||||||
},
|
},
|
||||||
function displayHeaderAndBodyArt(callback) {
|
function displayHeaderAndBodyArt(callback) {
|
||||||
assert(_.isString(art.header));
|
assert(_.isString(art.header));
|
||||||
|
|
|
@ -46,8 +46,7 @@ function MenuModule(options) {
|
||||||
async.series(
|
async.series(
|
||||||
[
|
[
|
||||||
function beforeDisplayArt(callback) {
|
function beforeDisplayArt(callback) {
|
||||||
self.beforeArt();
|
self.beforeArt(callback);
|
||||||
callback(null);
|
|
||||||
},
|
},
|
||||||
function displayMenuArt(callback) {
|
function displayMenuArt(callback) {
|
||||||
if(_.isString(self.menuConfig.art)) {
|
if(_.isString(self.menuConfig.art)) {
|
||||||
|
@ -247,7 +246,7 @@ MenuModule.prototype.leave = function() {
|
||||||
this.detachViewControllers();
|
this.detachViewControllers();
|
||||||
};
|
};
|
||||||
|
|
||||||
MenuModule.prototype.beforeArt = function() {
|
MenuModule.prototype.beforeArt = function(cb) {
|
||||||
if(this.cls) {
|
if(this.cls) {
|
||||||
this.client.term.write(ansi.resetScreen());
|
this.client.term.write(ansi.resetScreen());
|
||||||
}
|
}
|
||||||
|
@ -255,6 +254,8 @@ MenuModule.prototype.beforeArt = function() {
|
||||||
if(_.isNumber(this.menuConfig.options.baudRate)) {
|
if(_.isNumber(this.menuConfig.options.baudRate)) {
|
||||||
this.client.term.write(ansi.setEmulatedBaudRate(this.menuConfig.options.baudRate));
|
this.client.term.write(ansi.setEmulatedBaudRate(this.menuConfig.options.baudRate));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return cb(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
MenuModule.prototype.mciReady = function(mciData, cb) {
|
MenuModule.prototype.mciReady = function(mciData, cb) {
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var View = require('./view.js').View;
|
const View = require('./view.js').View;
|
||||||
var miscUtil = require('./misc_util.js');
|
const strUtil = require('./string_util.js');
|
||||||
var strUtil = require('./string_util.js');
|
const ansi = require('./ansi_term.js');
|
||||||
var ansi = require('./ansi_term.js');
|
const colorCodes = require('./color_codes.js');
|
||||||
var colorCodes = require('./color_codes.js');
|
const wordWrapText = require('./word_wrap.js').wordWrapText;
|
||||||
var wordWrapText = require('./word_wrap.js').wordWrapText;
|
|
||||||
|
|
||||||
var assert = require('assert');
|
const assert = require('assert');
|
||||||
var _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
// :TODO: Determine CTRL-* keys for various things
|
// :TODO: Determine CTRL-* keys for various things
|
||||||
// See http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt
|
// See http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt
|
||||||
|
@ -34,7 +33,7 @@ var _ = require('lodash');
|
||||||
//
|
//
|
||||||
// Editors - BBS
|
// Editors - BBS
|
||||||
// * https://github.com/M-griffin/Enthral/blob/master/src/msg_fse.cpp
|
// * https://github.com/M-griffin/Enthral/blob/master/src/msg_fse.cpp
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Editors - Other
|
// Editors - Other
|
||||||
// * http://joe-editor.sourceforge.net/
|
// * http://joe-editor.sourceforge.net/
|
||||||
|
@ -55,12 +54,12 @@ var _ = require('lodash');
|
||||||
|
|
||||||
//
|
//
|
||||||
// To-Do
|
// To-Do
|
||||||
//
|
//
|
||||||
// * Index pos % for emit scroll events
|
// * Index pos % for emit scroll events
|
||||||
// * Some of this shoudl be async'd where there is lots of processing (e.g. word wrap)
|
// * Some of this shoudl be async'd where there is lots of processing (e.g. word wrap)
|
||||||
// * Fix backspace when col=0 (e.g. bs to prev line)
|
// * Fix backspace when col=0 (e.g. bs to prev line)
|
||||||
// * Add back word delete
|
// * Add back word delete
|
||||||
// *
|
// *
|
||||||
|
|
||||||
|
|
||||||
var SPECIAL_KEY_MAP_DEFAULT = {
|
var SPECIAL_KEY_MAP_DEFAULT = {
|
||||||
|
@ -114,6 +113,11 @@ function MultiLineEditTextView(options) {
|
||||||
this.topVisibleIndex = 0;
|
this.topVisibleIndex = 0;
|
||||||
this.mode = options.mode || 'edit'; // edit | preview | read-only
|
this.mode = options.mode || 'edit'; // edit | preview | read-only
|
||||||
|
|
||||||
|
if ('preview' === this.mode) {
|
||||||
|
this.autoScroll = options.autoScroll || true;
|
||||||
|
} else {
|
||||||
|
this.autoScroll = options.autoScroll || false;
|
||||||
|
}
|
||||||
//
|
//
|
||||||
// cursorPos represents zero-based row, col positions
|
// cursorPos represents zero-based row, col positions
|
||||||
// within the editor itself
|
// within the editor itself
|
||||||
|
@ -179,7 +183,7 @@ function MultiLineEditTextView(options) {
|
||||||
|
|
||||||
this.eraseRows = function(startRow, endRow) {
|
this.eraseRows = function(startRow, endRow) {
|
||||||
self.client.term.rawWrite(self.getSGRFor('text') + ansi.hideCursor());
|
self.client.term.rawWrite(self.getSGRFor('text') + ansi.hideCursor());
|
||||||
|
|
||||||
var absPos = self.getAbsolutePosition(startRow, 0);
|
var absPos = self.getAbsolutePosition(startRow, 0);
|
||||||
var absPosEnd = self.getAbsolutePosition(endRow, 0);
|
var absPosEnd = self.getAbsolutePosition(endRow, 0);
|
||||||
var eraseFiller = new Array(self.dimens.width).join(' ');
|
var eraseFiller = new Array(self.dimens.width).join(' ');
|
||||||
|
@ -216,7 +220,7 @@ function MultiLineEditTextView(options) {
|
||||||
if(!_.isNumber(index)) {
|
if(!_.isNumber(index)) {
|
||||||
index = self.getTextLinesIndex();
|
index = self.getTextLinesIndex();
|
||||||
}
|
}
|
||||||
return self.textLines[index].text.replace(/\t/g, ' ');
|
return self.textLines[index].text.replace(/\t/g, ' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getText = function(index) {
|
this.getText = function(index) {
|
||||||
|
@ -266,19 +270,19 @@ function MultiLineEditTextView(options) {
|
||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getOutputText = function(startIndex, endIndex, eolMarker) {
|
this.getOutputText = function(startIndex, endIndex, eolMarker) {
|
||||||
let lines = self.getTextLines(startIndex, endIndex);
|
let lines = self.getTextLines(startIndex, endIndex);
|
||||||
let text = '';
|
let text = '';
|
||||||
var re = new RegExp('\\t{1,' + (self.tabWidth) + '}', 'g');
|
var re = new RegExp('\\t{1,' + (self.tabWidth) + '}', 'g');
|
||||||
|
|
||||||
lines.forEach(line => {
|
lines.forEach(line => {
|
||||||
text += line.text.replace(re, '\t');
|
text += line.text.replace(re, '\t');
|
||||||
if(eolMarker && line.eol) {
|
if(eolMarker && line.eol) {
|
||||||
text += eolMarker;
|
text += eolMarker;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +306,7 @@ function MultiLineEditTextView(options) {
|
||||||
/*
|
/*
|
||||||
this.editTextAtPosition = function(editAction, text, index, col) {
|
this.editTextAtPosition = function(editAction, text, index, col) {
|
||||||
switch(editAction) {
|
switch(editAction) {
|
||||||
case 'insert' :
|
case 'insert' :
|
||||||
self.insertCharactersInText(text, index, col);
|
self.insertCharactersInText(text, index, col);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -329,7 +333,7 @@ function MultiLineEditTextView(options) {
|
||||||
newLines[newLines.length - 1].eol = true;
|
newLines[newLines.length - 1].eol = true;
|
||||||
|
|
||||||
Array.prototype.splice.apply(
|
Array.prototype.splice.apply(
|
||||||
self.textLines,
|
self.textLines,
|
||||||
[ index, (nextEolIndex - index) + 1 ].concat(newLines));
|
[ index, (nextEolIndex - index) + 1 ].concat(newLines));
|
||||||
|
|
||||||
return wrapped.firstWrapRange;
|
return wrapped.firstWrapRange;
|
||||||
|
@ -337,7 +341,7 @@ function MultiLineEditTextView(options) {
|
||||||
|
|
||||||
this.removeCharactersFromText = function(index, col, operation, count) {
|
this.removeCharactersFromText = function(index, col, operation, count) {
|
||||||
if('right' === operation) {
|
if('right' === operation) {
|
||||||
self.textLines[index].text =
|
self.textLines[index].text =
|
||||||
self.textLines[index].text.slice(col, count) +
|
self.textLines[index].text.slice(col, count) +
|
||||||
self.textLines[index].text.slice(col + count);
|
self.textLines[index].text.slice(col + count);
|
||||||
|
|
||||||
|
@ -354,11 +358,11 @@ function MultiLineEditTextView(options) {
|
||||||
} else if ('backspace' === operation) {
|
} else if ('backspace' === operation) {
|
||||||
// :TODO: method for splicing text
|
// :TODO: method for splicing text
|
||||||
self.textLines[index].text =
|
self.textLines[index].text =
|
||||||
self.textLines[index].text.slice(0, col - (count - 1)) +
|
self.textLines[index].text.slice(0, col - (count - 1)) +
|
||||||
self.textLines[index].text.slice(col + 1);
|
self.textLines[index].text.slice(col + 1);
|
||||||
|
|
||||||
self.cursorPos.col -= (count - 1);
|
self.cursorPos.col -= (count - 1);
|
||||||
|
|
||||||
self.updateTextWordWrap(index);
|
self.updateTextWordWrap(index);
|
||||||
self.redrawRows(self.cursorPos.row, self.dimens.height);
|
self.redrawRows(self.cursorPos.row, self.dimens.height);
|
||||||
|
|
||||||
|
@ -405,9 +409,9 @@ function MultiLineEditTextView(options) {
|
||||||
|
|
||||||
this.insertCharactersInText = function(c, index, col) {
|
this.insertCharactersInText = function(c, index, col) {
|
||||||
self.textLines[index].text = [
|
self.textLines[index].text = [
|
||||||
self.textLines[index].text.slice(0, col),
|
self.textLines[index].text.slice(0, col),
|
||||||
c,
|
c,
|
||||||
self.textLines[index].text.slice(col)
|
self.textLines[index].text.slice(col)
|
||||||
].join('');
|
].join('');
|
||||||
|
|
||||||
//self.cursorPos.col++;
|
//self.cursorPos.col++;
|
||||||
|
@ -443,13 +447,13 @@ function MultiLineEditTextView(options) {
|
||||||
//
|
//
|
||||||
absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col);
|
absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col);
|
||||||
self.client.term.write(
|
self.client.term.write(
|
||||||
ansi.hideCursor() +
|
ansi.hideCursor() +
|
||||||
self.getSGRFor('text') +
|
self.getSGRFor('text') +
|
||||||
self.getRenderText(index).slice(self.cursorPos.col - c.length) +
|
self.getRenderText(index).slice(self.cursorPos.col - c.length) +
|
||||||
ansi.goto(absPos.row, absPos.col) +
|
ansi.goto(absPos.row, absPos.col) +
|
||||||
ansi.showCursor(), false
|
ansi.showCursor(), false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getRemainingTabWidth = function(col) {
|
this.getRemainingTabWidth = function(col) {
|
||||||
|
@ -541,7 +545,7 @@ function MultiLineEditTextView(options) {
|
||||||
.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g);
|
.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g);
|
||||||
|
|
||||||
var wrapped;
|
var wrapped;
|
||||||
|
|
||||||
for(var i = 0; i < text.length; ++i) {
|
for(var i = 0; i < text.length; ++i) {
|
||||||
wrapped = self.wordWrapSingleLine(
|
wrapped = self.wordWrapSingleLine(
|
||||||
text[i], // input
|
text[i], // input
|
||||||
|
@ -556,7 +560,7 @@ function MultiLineEditTextView(options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getAbsolutePosition = function(row, col) {
|
this.getAbsolutePosition = function(row, col) {
|
||||||
return {
|
return {
|
||||||
row : self.position.row + row,
|
row : self.position.row + row,
|
||||||
col : self.position.col + col,
|
col : self.position.col + col,
|
||||||
};
|
};
|
||||||
|
@ -610,7 +614,7 @@ function MultiLineEditTextView(options) {
|
||||||
|
|
||||||
this.keyPressDown = function() {
|
this.keyPressDown = function() {
|
||||||
var lastVisibleRow = Math.min(
|
var lastVisibleRow = Math.min(
|
||||||
self.dimens.height,
|
self.dimens.height,
|
||||||
(self.textLines.length - self.topVisibleIndex)) - 1;
|
(self.textLines.length - self.topVisibleIndex)) - 1;
|
||||||
|
|
||||||
if(self.cursorPos.row < lastVisibleRow) {
|
if(self.cursorPos.row < lastVisibleRow) {
|
||||||
|
@ -714,7 +718,7 @@ function MultiLineEditTextView(options) {
|
||||||
var nextEolIndex = self.getNextEndOfLineIndex(index);
|
var nextEolIndex = self.getNextEndOfLineIndex(index);
|
||||||
var text = self.getContiguousText(index, nextEolIndex);
|
var text = self.getContiguousText(index, nextEolIndex);
|
||||||
var newLines = self.wordWrapSingleLine(text.slice(self.cursorPos.col), 'tabsIntact').wrapped;
|
var newLines = self.wordWrapSingleLine(text.slice(self.cursorPos.col), 'tabsIntact').wrapped;
|
||||||
|
|
||||||
newLines.unshift( { text : text.slice(0, self.cursorPos.col), eol : true } );
|
newLines.unshift( { text : text.slice(0, self.cursorPos.col), eol : true } );
|
||||||
for(var i = 1; i < newLines.length; ++i) {
|
for(var i = 1; i < newLines.length; ++i) {
|
||||||
newLines[i] = { text : newLines[i] };
|
newLines[i] = { text : newLines[i] };
|
||||||
|
@ -722,7 +726,7 @@ function MultiLineEditTextView(options) {
|
||||||
newLines[newLines.length - 1].eol = true;
|
newLines[newLines.length - 1].eol = true;
|
||||||
|
|
||||||
Array.prototype.splice.apply(
|
Array.prototype.splice.apply(
|
||||||
self.textLines,
|
self.textLines,
|
||||||
[ index, (nextEolIndex - index) + 1 ].concat(newLines));
|
[ index, (nextEolIndex - index) + 1 ].concat(newLines));
|
||||||
|
|
||||||
// redraw from current row to end of visible area
|
// redraw from current row to end of visible area
|
||||||
|
@ -844,9 +848,9 @@ function MultiLineEditTextView(options) {
|
||||||
self.client.term.rawWrite(ansi.left(move));
|
self.client.term.rawWrite(ansi.left(move));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'up' :
|
case 'up' :
|
||||||
case 'down' :
|
case 'down' :
|
||||||
//
|
//
|
||||||
// Jump to the tabstop nearest the cursor
|
// Jump to the tabstop nearest the cursor
|
||||||
//
|
//
|
||||||
var newCol = self.tabStops.reduce(function r(prev, curr) {
|
var newCol = self.tabStops.reduce(function r(prev, curr) {
|
||||||
|
@ -890,7 +894,7 @@ function MultiLineEditTextView(options) {
|
||||||
this.cursorBeginOfNextLine = function() {
|
this.cursorBeginOfNextLine = function() {
|
||||||
// e.g. when scrolling right past eol
|
// e.g. when scrolling right past eol
|
||||||
var linesBelow = self.getRemainingLinesBelowRow();
|
var linesBelow = self.getRemainingLinesBelowRow();
|
||||||
|
|
||||||
if(linesBelow > 0) {
|
if(linesBelow > 0) {
|
||||||
var lastVisibleRow = Math.min(self.dimens.height, self.textLines.length) - 1;
|
var lastVisibleRow = Math.min(self.dimens.height, self.textLines.length) - 1;
|
||||||
if(self.cursorPos.row < lastVisibleRow) {
|
if(self.cursorPos.row < lastVisibleRow) {
|
||||||
|
@ -1007,9 +1011,9 @@ MultiLineEditTextView.prototype.setText = function(text) {
|
||||||
MultiLineEditTextView.prototype.addText = function(text) {
|
MultiLineEditTextView.prototype.addText = function(text) {
|
||||||
this.insertRawText(text);
|
this.insertRawText(text);
|
||||||
|
|
||||||
if(this.isEditMode()) {
|
if(this.isEditMode() || this.autoScroll) {
|
||||||
this.cursorEndOfDocument();
|
this.cursorEndOfDocument();
|
||||||
} else if(this.isPreviewMode()) {
|
} else {
|
||||||
this.cursorStartOfDocument();
|
this.cursorStartOfDocument();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1020,14 +1024,15 @@ MultiLineEditTextView.prototype.getData = function() {
|
||||||
|
|
||||||
MultiLineEditTextView.prototype.setPropertyValue = function(propName, value) {
|
MultiLineEditTextView.prototype.setPropertyValue = function(propName, value) {
|
||||||
switch(propName) {
|
switch(propName) {
|
||||||
case 'mode' : this.mode = value; break;
|
case 'mode' : this.mode = value; break;
|
||||||
|
case 'autoScroll' : this.autoScroll = value; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiLineEditTextView.super_.prototype.setPropertyValue.call(this, propName, value);
|
MultiLineEditTextView.super_.prototype.setPropertyValue.call(this, propName, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
var HANDLED_SPECIAL_KEYS = [
|
var HANDLED_SPECIAL_KEYS = [
|
||||||
'up', 'down', 'left', 'right',
|
'up', 'down', 'left', 'right',
|
||||||
'home', 'end',
|
'home', 'end',
|
||||||
'page up', 'page down',
|
'page up', 'page down',
|
||||||
'line feed',
|
'line feed',
|
||||||
|
@ -1045,7 +1050,7 @@ MultiLineEditTextView.prototype.onKeyPress = function(ch, key) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var handled;
|
var handled;
|
||||||
|
|
||||||
if(key) {
|
if(key) {
|
||||||
HANDLED_SPECIAL_KEYS.forEach(function aKey(specialKey) {
|
HANDLED_SPECIAL_KEYS.forEach(function aKey(specialKey) {
|
||||||
if(self.isKeyMapped(specialKey, key.name)) {
|
if(self.isKeyMapped(specialKey, key.name)) {
|
||||||
|
|
||||||
|
@ -1068,6 +1073,22 @@ MultiLineEditTextView.prototype.onKeyPress = function(ch, key) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MultiLineEditTextView.prototype.scrollUp = function() {
|
||||||
|
this.scrollDocumentUp();
|
||||||
|
};
|
||||||
|
|
||||||
|
MultiLineEditTextView.prototype.scrollDown = function() {
|
||||||
|
this.scrollDocumentDown();
|
||||||
|
};
|
||||||
|
|
||||||
|
MultiLineEditTextView.prototype.deleteLine = function(line) {
|
||||||
|
this.textLines.splice(line, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
MultiLineEditTextView.prototype.getLineCount = function() {
|
||||||
|
return this.textLines.length;
|
||||||
|
};
|
||||||
|
|
||||||
MultiLineEditTextView.prototype.getTextEditMode = function() {
|
MultiLineEditTextView.prototype.getTextEditMode = function() {
|
||||||
return this.overtypeMode ? 'overtype' : 'insert';
|
return this.overtypeMode ? 'overtype' : 'insert';
|
||||||
};
|
};
|
||||||
|
@ -1075,11 +1096,10 @@ MultiLineEditTextView.prototype.getTextEditMode = function() {
|
||||||
MultiLineEditTextView.prototype.getEditPosition = function() {
|
MultiLineEditTextView.prototype.getEditPosition = function() {
|
||||||
var currentIndex = this.getTextLinesIndex() + 1;
|
var currentIndex = this.getTextLinesIndex() + 1;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
row : this.getTextLinesIndex(this.cursorPos.row),
|
row : this.getTextLinesIndex(this.cursorPos.row),
|
||||||
col : this.cursorPos.col,
|
col : this.cursorPos.col,
|
||||||
percent : Math.floor(((currentIndex / this.textLines.length) * 100)),
|
percent : Math.floor(((currentIndex / this.textLines.length) * 100)),
|
||||||
below : this.getRemainingLinesBelowRow(),
|
below : this.getRemainingLinesBelowRow(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ StandardMenuModule.prototype.enter = function() {
|
||||||
StandardMenuModule.super_.prototype.enter.call(this);
|
StandardMenuModule.super_.prototype.enter.call(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
StandardMenuModule.prototype.beforeArt = function() {
|
StandardMenuModule.prototype.beforeArt = function(cb) {
|
||||||
StandardMenuModule.super_.prototype.beforeArt.call(this);
|
StandardMenuModule.super_.prototype.beforeArt.call(this, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
StandardMenuModule.prototype.mciReady = function(mciData, cb) {
|
StandardMenuModule.prototype.mciReady = function(mciData, cb) {
|
||||||
|
|
|
@ -1,25 +1,23 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var theme = require('./theme.js');
|
// ENiGMA½
|
||||||
var removeClient = require('./client_connections.js').removeClient;
|
const removeClient = require('./client_connections.js').removeClient;
|
||||||
var ansi = require('./ansi_term.js');
|
const ansiNormal = require('./ansi_term.js').normal;
|
||||||
var userDb = require('./database.js').dbs.user;
|
const userLogin = require('./user_login.js').userLogin;
|
||||||
var sysProp = require('./system_property.js');
|
|
||||||
var userLogin = require('./user_login.js').userLogin;
|
|
||||||
|
|
||||||
var async = require('async');
|
// deps
|
||||||
var _ = require('lodash');
|
const _ = require('lodash');
|
||||||
var iconv = require('iconv-lite');
|
const iconv = require('iconv-lite');
|
||||||
|
|
||||||
exports.login = login;
|
exports.login = login;
|
||||||
exports.logoff = logoff;
|
exports.logoff = logoff;
|
||||||
exports.prevMenu = prevMenu;
|
exports.prevMenu = prevMenu;
|
||||||
|
exports.nextMenu = nextMenu;
|
||||||
|
|
||||||
function login(callingMenu, formData, extraArgs) {
|
function login(callingMenu, formData) {
|
||||||
var client = callingMenu.client;
|
|
||||||
|
|
||||||
userLogin(callingMenu.client, formData.value.username, formData.value.password, function authResult(err) {
|
userLogin(callingMenu.client, formData.value.username, formData.value.password, err => {
|
||||||
if(err) {
|
if(err) {
|
||||||
// login failure
|
// login failure
|
||||||
if(err.existingConn && _.has(callingMenu, 'menuConfig.config.tooNodeMenu')) {
|
if(err.existingConn && _.has(callingMenu, 'menuConfig.config.tooNodeMenu')) {
|
||||||
|
@ -36,32 +34,41 @@ function login(callingMenu, formData, extraArgs) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function logoff(callingMenu, formData, extraArgs) {
|
function logoff(callingMenu) {
|
||||||
//
|
//
|
||||||
// Simple logoff. Note that recording of @ logoff properties/stats
|
// Simple logoff. Note that recording of @ logoff properties/stats
|
||||||
// occurs elsewhere!
|
// occurs elsewhere!
|
||||||
//
|
//
|
||||||
var client = callingMenu.client;
|
const client = callingMenu.client;
|
||||||
|
|
||||||
setTimeout(function timeout() {
|
setTimeout( () => {
|
||||||
//
|
//
|
||||||
// For giggles...
|
// For giggles...
|
||||||
//
|
//
|
||||||
client.term.write(
|
client.term.write(
|
||||||
ansi.normal() + '\n' +
|
ansiNormal() + '\n' +
|
||||||
iconv.decode(require('crypto').randomBytes(Math.floor(Math.random() * 65) + 20), client.term.outputEncoding) +
|
iconv.decode(require('crypto').randomBytes(Math.floor(Math.random() * 65) + 20), client.term.outputEncoding) +
|
||||||
'NO CARRIER', null, function written() {
|
'NO CARRIER', null, () => {
|
||||||
|
|
||||||
// after data is written, disconnect & remove the client
|
// after data is written, disconnect & remove the client
|
||||||
removeClient(client);
|
return removeClient(client);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevMenu(callingMenu, formData, extraArgs) {
|
function prevMenu(callingMenu) {
|
||||||
callingMenu.prevMenu(function result(err) {
|
callingMenu.prevMenu( err => {
|
||||||
if(err) {
|
if(err) {
|
||||||
callingMenu.client.log.error( { error : err.toString() }, 'Error attempting to fallback!');
|
callingMenu.client.log.error( { error : err.toString() }, 'Error attempting to fallback!');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nextMenu(callingMenu) {
|
||||||
|
callingMenu.nextMenu( err => {
|
||||||
|
if(err) {
|
||||||
|
callingMenu.client.log.error( { error : err.toString() }, 'Error attempting to go to next menu!');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,174 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var MenuModule = require('../core/menu_module.js').MenuModule;
|
||||||
|
|
||||||
|
// deps
|
||||||
|
const async = require('async');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const net = require('net');
|
||||||
|
|
||||||
|
/*
|
||||||
|
Expected configuration block example:
|
||||||
|
|
||||||
|
config: {
|
||||||
|
host: 192.168.1.171
|
||||||
|
port: 5001
|
||||||
|
bbsTag: SOME_TAG
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.getModule = ErcClientModule;
|
||||||
|
|
||||||
|
exports.moduleInfo = {
|
||||||
|
name : 'ENiGMA Relay Chat Client',
|
||||||
|
desc : 'Chat with other ENiGMA BBSes',
|
||||||
|
author : 'Andrew Pamment',
|
||||||
|
};
|
||||||
|
|
||||||
|
var MciViewIds = {
|
||||||
|
ChatDisplay : 1,
|
||||||
|
InputArea : 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
function ErcClientModule(options) {
|
||||||
|
MenuModule.call(this, options);
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
this.config = options.menuConfig.config;
|
||||||
|
|
||||||
|
this.chatEntryFormat = this.config.chatEntryFormat || '[{bbsTag}] {userName}: {message}';
|
||||||
|
this.systemEntryFormat = this.config.systemEntryFormat || '[*SYSTEM*] {message}';
|
||||||
|
|
||||||
|
this.finishedLoading = function() {
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function validateConfig(callback) {
|
||||||
|
if(_.isString(self.config.host) &&
|
||||||
|
_.isNumber(self.config.port) &&
|
||||||
|
_.isString(self.config.bbsTag))
|
||||||
|
{
|
||||||
|
return callback(null);
|
||||||
|
} else {
|
||||||
|
return callback(new Error('Configuration is missing required option(s)'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function connectToServer(callback) {
|
||||||
|
const connectOpts = {
|
||||||
|
port : self.config.port,
|
||||||
|
host : self.config.host,
|
||||||
|
};
|
||||||
|
|
||||||
|
const chatMessageView = self.viewControllers.menu.getView(MciViewIds.ChatDisplay);
|
||||||
|
|
||||||
|
chatMessageView.setText('Connecting to server...');
|
||||||
|
chatMessageView.redraw();
|
||||||
|
|
||||||
|
self.viewControllers.menu.switchFocus(MciViewIds.InputArea);
|
||||||
|
|
||||||
|
// :TODO: Track actual client->enig connection for optional prevMenu @ final CB
|
||||||
|
self.chatConnection = net.createConnection(connectOpts.port, connectOpts.host);
|
||||||
|
|
||||||
|
self.chatConnection.on('data', data => {
|
||||||
|
data = data.toString();
|
||||||
|
|
||||||
|
if(data.startsWith('ERCHANDSHAKE')) {
|
||||||
|
self.chatConnection.write(`ERCMAGIC|${self.config.bbsTag}|${self.client.user.username}\r\n`);
|
||||||
|
} else if(data.startsWith('{')) {
|
||||||
|
try {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
} catch(e) {
|
||||||
|
return self.client.log.warn( { error : e.message }, 'ERC: Error parsing ERC data from server');
|
||||||
|
}
|
||||||
|
|
||||||
|
let text;
|
||||||
|
try {
|
||||||
|
if(data.userName) {
|
||||||
|
// user message
|
||||||
|
text = self.chatEntryFormat.format(data);
|
||||||
|
} else {
|
||||||
|
// system message
|
||||||
|
text = self.systemEntryFormat.format(data);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
return self.client.log.warn( { error : e.message }, 'ERC: chatEntryFormat error');
|
||||||
|
}
|
||||||
|
|
||||||
|
chatMessageView.addText(text);
|
||||||
|
|
||||||
|
if(chatMessageView.getLineCount() > 30) { // :TODO: should probably be ChatDisplay.height?
|
||||||
|
chatMessageView.deleteLine(0);
|
||||||
|
chatMessageView.scrollDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
chatMessageView.redraw();
|
||||||
|
self.viewControllers.menu.switchFocus(MciViewIds.InputArea);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.chatConnection.once('end', () => {
|
||||||
|
return callback(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.chatConnection.once('error', err => {
|
||||||
|
self.client.log.info(`ERC connection error: ${err.message}`);
|
||||||
|
return callback(new Error('Failed connecting to ERC server!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if(err) {
|
||||||
|
self.client.log.warn( { error : err.message }, 'ERC error');
|
||||||
|
}
|
||||||
|
|
||||||
|
self.prevMenu();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.scrollHandler = function(keyName) {
|
||||||
|
const inputAreaView = self.viewControllers.menu.getView(MciViewIds.InputArea);
|
||||||
|
const chatDisplayView = self.viewControllers.menu.getView(MciViewIds.ChatDisplay);
|
||||||
|
|
||||||
|
if('up arrow' === keyName) {
|
||||||
|
chatDisplayView.scrollUp();
|
||||||
|
} else {
|
||||||
|
chatDisplayView.scrollDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
chatDisplayView.redraw();
|
||||||
|
inputAreaView.setFocus(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.menuMethods = {
|
||||||
|
inputAreaSubmit : function() {
|
||||||
|
const inputAreaView = self.viewControllers.menu.getView(MciViewIds.InputArea);
|
||||||
|
const inputData = inputAreaView.getData();
|
||||||
|
|
||||||
|
if('/quit' === inputData.toLowerCase()) {
|
||||||
|
self.chatConnection.end();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
self.chatConnection.write(`${inputData}\r\n`);
|
||||||
|
} catch(e) {
|
||||||
|
self.client.log.warn( { error : e.message }, 'ERC error');
|
||||||
|
}
|
||||||
|
inputAreaView.clearText();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollUp : function(formData) {
|
||||||
|
self.scrollHandler(formData.key.name);
|
||||||
|
},
|
||||||
|
scrollDown : function(formData) {
|
||||||
|
self.scrollHandler(formData.key.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
require('util').inherits(ErcClientModule, MenuModule);
|
||||||
|
|
||||||
|
ErcClientModule.prototype.mciReady = function(mciData, cb) {
|
||||||
|
this.standardMCIReadyHandler(mciData, cb);
|
||||||
|
};
|
297
mods/menu.hjson
297
mods/menu.hjson
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
ENiGMA½ Menu Configuration
|
ENiGMA½ Menu Configuration
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
//
|
//
|
||||||
// Another SSH specialization: If the user logs in with a new user
|
// Another SSH specialization: If the user logs in with a new user
|
||||||
// name (e.g. "new", "apply", ...) they will be directed to the
|
// name (e.g. "new", "apply", ...) they will be directed to the
|
||||||
// application process.
|
// application process.
|
||||||
//
|
//
|
||||||
sshConnectedNewUser: {
|
sshConnectedNewUser: {
|
||||||
|
@ -157,7 +157,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logoff: {
|
logoff: {
|
||||||
art: LOGOFF
|
art: LOGOFF
|
||||||
desc: Logging Off
|
desc: Logging Off
|
||||||
next: @systemMethod:logoff
|
next: @systemMethod:logoff
|
||||||
|
@ -264,7 +264,7 @@
|
||||||
action: @systemMethod:prevMenu
|
action: @systemMethod:prevMenu
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,7 +361,7 @@
|
||||||
action: @systemMethod:prevMenu
|
action: @systemMethod:prevMenu
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,10 +375,10 @@
|
||||||
status: Feedback to SysOp
|
status: Feedback to SysOp
|
||||||
module: msg_area_post_fse
|
module: msg_area_post_fse
|
||||||
next: [
|
next: [
|
||||||
{
|
{
|
||||||
acs: AS2
|
acs: AS2
|
||||||
next: fullLoginSequenceLoginArt
|
next: fullLoginSequenceLoginArt
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
next: newUserInactiveDone
|
next: newUserInactiveDone
|
||||||
}
|
}
|
||||||
|
@ -510,16 +510,103 @@
|
||||||
module: last_callers
|
module: last_callers
|
||||||
art: LASTCALL
|
art: LASTCALL
|
||||||
options: { pause: true }
|
options: { pause: true }
|
||||||
next: fullLoginSequenceWhosOnline
|
next: fullLoginSequenceWhosOnline
|
||||||
}
|
}
|
||||||
fullLoginSequenceWhosOnline: {
|
fullLoginSequenceWhosOnline: {
|
||||||
desc: Who's Online
|
desc: Who's Online
|
||||||
module: whos_online
|
module: whos_online
|
||||||
art: WHOSON
|
art: WHOSON
|
||||||
options: { pause: true }
|
options: { pause: true }
|
||||||
next: fullLoginSequenceNewScanConfirm
|
next: fullLoginSequenceOnelinerz
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fullLoginSequenceOnelinerz: {
|
||||||
|
desc: Viewing Onelinerz
|
||||||
|
module: onelinerz
|
||||||
|
next: fullLoginSequenceNewScanConfirm
|
||||||
|
options: {
|
||||||
|
cls: true
|
||||||
|
}
|
||||||
|
config: {
|
||||||
|
art: {
|
||||||
|
entries: ONELINER
|
||||||
|
add: ONEADD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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: oneliner
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fullLoginSequenceNewScanConfirm: {
|
fullLoginSequenceNewScanConfirm: {
|
||||||
desc: Logging In
|
desc: Logging In
|
||||||
prompt: loginGlobalNewScan
|
prompt: loginGlobalNewScan
|
||||||
|
@ -644,6 +731,14 @@
|
||||||
value: { command: "K" }
|
value: { command: "K" }
|
||||||
action: @menu:mainMenuFeedbackToSysOp
|
action: @menu:mainMenuFeedbackToSysOp
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
value: { command: "O" }
|
||||||
|
action: @menu:mainMenuOnelinerz
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value: { command: "CHAT"}
|
||||||
|
action: @menu:ercClient
|
||||||
|
}
|
||||||
{
|
{
|
||||||
value: 1
|
value: 1
|
||||||
action: @menu:mainMenu
|
action: @menu:mainMenu
|
||||||
|
@ -665,7 +760,7 @@
|
||||||
mainMenuUserStats: {
|
mainMenuUserStats: {
|
||||||
desc: User Stats
|
desc: User Stats
|
||||||
art: STATUS
|
art: STATUS
|
||||||
options: { pause: true }
|
options: { pause: true }
|
||||||
}
|
}
|
||||||
mainMenuSystemStats: {
|
mainMenuSystemStats: {
|
||||||
desc: System Stats
|
desc: System Stats
|
||||||
|
@ -907,6 +1002,144 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mainMenuOnelinerz: {
|
||||||
|
desc: Viewing Onelinerz
|
||||||
|
module: onelinerz
|
||||||
|
options: {
|
||||||
|
cls: true
|
||||||
|
}
|
||||||
|
config: {
|
||||||
|
art: {
|
||||||
|
entries: ONELINER
|
||||||
|
add: ONEADD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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: oneliner
|
||||||
|
}
|
||||||
|
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
|
||||||
|
config: {
|
||||||
|
host: localhost
|
||||||
|
port: 5001
|
||||||
|
bbsTag: CHANGEME
|
||||||
|
}
|
||||||
|
|
||||||
|
form: {
|
||||||
|
0: {
|
||||||
|
mci: {
|
||||||
|
MT1: {
|
||||||
|
width: 79
|
||||||
|
height: 21
|
||||||
|
mode: preview
|
||||||
|
autoScroll: true
|
||||||
|
}
|
||||||
|
ET3: {
|
||||||
|
autoScale: false
|
||||||
|
width: 77
|
||||||
|
argName: inputArea
|
||||||
|
focus: true
|
||||||
|
submit: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submit: {
|
||||||
|
*: [
|
||||||
|
{
|
||||||
|
value: { inputArea: null }
|
||||||
|
action: @method:inputAreaSubmit
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
actionKeys: [
|
||||||
|
{
|
||||||
|
keys: [ "tab" ]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
keys: [ "up arrow" ]
|
||||||
|
action: @method:scrollDown
|
||||||
|
}
|
||||||
|
{
|
||||||
|
keys: [ "down arrow" ]
|
||||||
|
action: @method:scrollUp
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
// Doors Menu
|
// Doors Menu
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
@ -943,17 +1176,21 @@
|
||||||
value: { command: "DP" }
|
value: { command: "DP" }
|
||||||
action: @menu:doorParty
|
action: @menu:doorParty
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
value: { command: "HL" }
|
||||||
|
action: @menu:telnetBridgeHappyLand
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
doorPimpWars: {
|
doorPimpWars: {
|
||||||
desc: Playing PimpWars
|
desc: Playing PimpWars
|
||||||
module: abracadabra
|
module: abracadabra
|
||||||
config: {
|
config: {
|
||||||
name: PimpWars
|
name: PimpWars
|
||||||
dropFileType: DORINFO
|
dropFileType: DORINFO
|
||||||
cmd: /home/nuskooler/DOS/scripts/pimpwars.sh
|
cmd: /home/nuskooler/DOS/scripts/pimpwars.sh
|
||||||
args: [
|
args: [
|
||||||
"{node}",
|
"{node}",
|
||||||
"{dropFile}",
|
"{dropFile}",
|
||||||
"{srvPort}",
|
"{srvPort}",
|
||||||
|
@ -966,12 +1203,12 @@
|
||||||
|
|
||||||
doorDarkLands: {
|
doorDarkLands: {
|
||||||
desc: Playing Dark Lands
|
desc: Playing Dark Lands
|
||||||
module: abracadabra
|
module: abracadabra
|
||||||
config: {
|
config: {
|
||||||
name: DARKLANDS
|
name: DARKLANDS
|
||||||
dropFileType: DOOR
|
dropFileType: DOOR
|
||||||
cmd: /home/nuskooler/dev/enigma-bbs/doors/darklands/start.sh
|
cmd: /home/nuskooler/dev/enigma-bbs/doors/darklands/start.sh
|
||||||
args: [
|
args: [
|
||||||
"{node}",
|
"{node}",
|
||||||
"{dropFile}",
|
"{dropFile}",
|
||||||
"{srvPort}",
|
"{srvPort}",
|
||||||
|
@ -981,7 +1218,7 @@
|
||||||
io: socket
|
io: socket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doorLORD: {
|
doorLORD: {
|
||||||
desc: Playing L.O.R.D.
|
desc: Playing L.O.R.D.
|
||||||
module: abracadabra
|
module: abracadabra
|
||||||
|
@ -1020,6 +1257,18 @@
|
||||||
bbsTag: XX
|
bbsTag: XX
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
telnetBridgeHappyLand: {
|
||||||
|
desc: Connected to HappyLand BBS
|
||||||
|
module: telnet_bridge
|
||||||
|
config: {
|
||||||
|
host: andrew.homeunix.org
|
||||||
|
port: 2023
|
||||||
|
//host: agency.bbs.geek.nz
|
||||||
|
//port: 23
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
// Message Area Menu
|
// Message Area Menu
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1056,7 +1305,7 @@
|
||||||
{
|
{
|
||||||
value: 1
|
value: 1
|
||||||
action: @menu:messageArea
|
action: @menu:messageArea
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1244,7 +1493,7 @@
|
||||||
{
|
{
|
||||||
keys: [ "n", "shift + n" ]
|
keys: [ "n", "shift + n" ]
|
||||||
action: @method:nextMessage
|
action: @method:nextMessage
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
keys: [ "r", "shift + r" ]
|
keys: [ "r", "shift + r" ]
|
||||||
action: @method:replyMessage
|
action: @method:replyMessage
|
||||||
|
@ -1259,7 +1508,7 @@
|
||||||
{
|
{
|
||||||
keys: [ "?" ]
|
keys: [ "?" ]
|
||||||
action: @method:viewModeMenuHelp
|
action: @method:viewModeMenuHelp
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
keys: [ "down arrow", "up arrow", "page up", "page down" ]
|
keys: [ "down arrow", "up arrow", "page up", "page down" ]
|
||||||
action: @method:movementKeyPressed
|
action: @method:movementKeyPressed
|
||||||
|
@ -1295,7 +1544,7 @@
|
||||||
validate: @systemMethod:validateNonEmpty
|
validate: @systemMethod:validateNonEmpty
|
||||||
}
|
}
|
||||||
ET3: {
|
ET3: {
|
||||||
argName: subject
|
argName: subject
|
||||||
maxLength: 72
|
maxLength: 72
|
||||||
submit: true
|
submit: true
|
||||||
validate: @systemMethod:validateNonEmpty
|
validate: @systemMethod:validateNonEmpty
|
||||||
|
@ -1395,7 +1644,7 @@
|
||||||
width: 79
|
width: 79
|
||||||
height: 4
|
height: 4
|
||||||
argName: quote
|
argName: quote
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
submit: {
|
submit: {
|
||||||
|
@ -1552,7 +1801,7 @@
|
||||||
"mci" : {
|
"mci" : {
|
||||||
"VM1" : {
|
"VM1" : {
|
||||||
"items" : [
|
"items" : [
|
||||||
"Single Line Text Editing Views",
|
"Single Line Text Editing Views",
|
||||||
"Spinner & Toggle Views",
|
"Spinner & Toggle Views",
|
||||||
"Mask Edit Views",
|
"Mask Edit Views",
|
||||||
"Multi Line Text Editor",
|
"Multi Line Text Editor",
|
||||||
|
@ -1735,7 +1984,7 @@
|
||||||
"form" : {
|
"form" : {
|
||||||
"0" : {
|
"0" : {
|
||||||
"BTMT" : {
|
"BTMT" : {
|
||||||
"mci" : {
|
"mci" : {
|
||||||
"MT1" : {
|
"MT1" : {
|
||||||
"width" : 70,
|
"width" : 70,
|
||||||
"height" : 17,
|
"height" : 17,
|
||||||
|
@ -2019,6 +2268,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,315 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ENiGMA½
|
||||||
|
const MenuModule = require('../core/menu_module.js').MenuModule;
|
||||||
|
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');
|
||||||
|
|
||||||
|
// deps
|
||||||
|
const sqlite3 = require('sqlite3');
|
||||||
|
const async = require('async');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const moment = require('moment');
|
||||||
|
|
||||||
|
exports.moduleInfo = {
|
||||||
|
name : 'Onelinerz',
|
||||||
|
desc : 'Standard local onelinerz',
|
||||||
|
author : 'NuSkooler',
|
||||||
|
packageName : 'codes.l33t.enigma.onelinerz',
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getModule = OnelinerzModule;
|
||||||
|
|
||||||
|
const MciCodeIds = {
|
||||||
|
ViewForm : {
|
||||||
|
Entries : 1,
|
||||||
|
AddPrompt : 2,
|
||||||
|
},
|
||||||
|
AddForm : {
|
||||||
|
NewEntry : 1,
|
||||||
|
EntryPreview : 2,
|
||||||
|
AddPrompt : 3,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const FormIds = {
|
||||||
|
View : 0,
|
||||||
|
Add : 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
function OnelinerzModule(options) {
|
||||||
|
MenuModule.call(this, options);
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
const config = this.menuConfig.config;
|
||||||
|
|
||||||
|
this.initSequence = function() {
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function beforeDisplayArt(callback) {
|
||||||
|
self.beforeArt(callback);
|
||||||
|
},
|
||||||
|
function display(callback) {
|
||||||
|
self.displayViewScreen(false, callback);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if(err) {
|
||||||
|
// :TODO: Handle me -- initSequence() should really take a completion callback
|
||||||
|
}
|
||||||
|
self.finishedLoading();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.displayViewScreen = function(clearScreen, cb) {
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function clearAndDisplayArt(callback) {
|
||||||
|
if(self.viewControllers.add) {
|
||||||
|
self.viewControllers.add.setFocus(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(clearScreen) {
|
||||||
|
self.client.term.rawWrite(ansi.resetScreen());
|
||||||
|
}
|
||||||
|
|
||||||
|
theme.displayThemedAsset(
|
||||||
|
config.art.entries,
|
||||||
|
self.client,
|
||||||
|
{ font : self.menuConfig.font, trailingLF : false },
|
||||||
|
(err, artData) => {
|
||||||
|
return callback(err, artData);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function initOrRedrawViewController(artData, callback) {
|
||||||
|
if(_.isUndefined(self.viewControllers.add)) {
|
||||||
|
const vc = self.addViewController(
|
||||||
|
'view',
|
||||||
|
new ViewController( { client : self.client, formId : FormIds.View } )
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadOpts = {
|
||||||
|
callingMenu : self,
|
||||||
|
mciMap : artData.mciMap,
|
||||||
|
formId : FormIds.View,
|
||||||
|
};
|
||||||
|
|
||||||
|
return vc.loadFromMenuConfig(loadOpts, callback);
|
||||||
|
} else {
|
||||||
|
self.viewControllers.view.setFocus(true);
|
||||||
|
self.viewControllers.view.getView(MciCodeIds.ViewForm.AddPrompt).redraw();
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function fetchEntries(callback) {
|
||||||
|
const entriesView = self.viewControllers.view.getView(MciCodeIds.ViewForm.Entries);
|
||||||
|
const limit = entriesView.dimens.height;
|
||||||
|
let entries = [];
|
||||||
|
|
||||||
|
self.db.each(
|
||||||
|
`SELECT *
|
||||||
|
FROM (
|
||||||
|
SELECT *
|
||||||
|
FROM onelinerz
|
||||||
|
ORDER BY timestamp DESC
|
||||||
|
LIMIT ${limit}
|
||||||
|
)
|
||||||
|
ORDER BY timestamp ASC;`,
|
||||||
|
(err, row) => {
|
||||||
|
if(!err) {
|
||||||
|
row.timestamp = moment(row.timestamp); // convert -> moment
|
||||||
|
entries.push(row);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
return callback(err, entriesView, entries);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function populateEntries(entriesView, entries, callback) {
|
||||||
|
const listFormat = config.listFormat || '{username}@{ts}: {oneliner}';// :TODO: should be userName to be consistent
|
||||||
|
const tsFormat = config.timestampFormat || 'ddd h:mma';
|
||||||
|
|
||||||
|
entriesView.setItems(entries.map( e => {
|
||||||
|
return listFormat.format( {
|
||||||
|
userId : e.user_id,
|
||||||
|
username : e.user_name,
|
||||||
|
oneliner : e.oneliner,
|
||||||
|
ts : e.timestamp.format(tsFormat),
|
||||||
|
} );
|
||||||
|
}));
|
||||||
|
|
||||||
|
entriesView.focusItems = entriesView.items; // :TODO: this is a hack
|
||||||
|
entriesView.redraw();
|
||||||
|
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if(cb) {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.displayAddScreen = function(cb) {
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function clearAndDisplayArt(callback) {
|
||||||
|
self.viewControllers.view.setFocus(false);
|
||||||
|
self.client.term.rawWrite(ansi.resetScreen());
|
||||||
|
|
||||||
|
theme.displayThemedAsset(
|
||||||
|
config.art.add,
|
||||||
|
self.client,
|
||||||
|
{ font : self.menuConfig.font },
|
||||||
|
(err, artData) => {
|
||||||
|
return callback(err, artData);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function initOrRedrawViewController(artData, callback) {
|
||||||
|
if(_.isUndefined(self.viewControllers.add)) {
|
||||||
|
const vc = self.addViewController(
|
||||||
|
'add',
|
||||||
|
new ViewController( { client : self.client, formId : FormIds.Add } )
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadOpts = {
|
||||||
|
callingMenu : self,
|
||||||
|
mciMap : artData.mciMap,
|
||||||
|
formId : FormIds.Add,
|
||||||
|
};
|
||||||
|
|
||||||
|
return vc.loadFromMenuConfig(loadOpts, callback);
|
||||||
|
} else {
|
||||||
|
self.viewControllers.add.setFocus(true);
|
||||||
|
self.viewControllers.add.redrawAll();
|
||||||
|
self.viewControllers.add.switchFocus(MciCodeIds.AddForm.NewEntry);
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if(cb) {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clearAddForm = function() {
|
||||||
|
const newEntryView = self.viewControllers.add.getView(MciCodeIds.AddForm.NewEntry);
|
||||||
|
const previewView = self.viewControllers.add.getView(MciCodeIds.AddForm.EntryPreview);
|
||||||
|
|
||||||
|
newEntryView.setText('');
|
||||||
|
|
||||||
|
// preview is optional
|
||||||
|
if(previewView) {
|
||||||
|
previewView.setText('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.menuMethods = {
|
||||||
|
viewAddScreen : function() {
|
||||||
|
self.displayAddScreen();
|
||||||
|
},
|
||||||
|
|
||||||
|
addEntry : function(formData) {
|
||||||
|
if(_.isString(formData.value.oneliner) && formData.value.oneliner.length > 0) {
|
||||||
|
const oneliner = formData.value.oneliner.trim(); // remove any trailing ws
|
||||||
|
|
||||||
|
self.storeNewOneliner(oneliner, err => {
|
||||||
|
if(err) {
|
||||||
|
self.client.log.warn( { error : err.message }, 'Failed saving oneliner');
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clearAddForm();
|
||||||
|
self.displayViewScreen(true); // true=cls
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// empty message - treat as if cancel was hit
|
||||||
|
self.displayViewScreen(true); // true=cls
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelAdd : function() {
|
||||||
|
self.clearAddForm();
|
||||||
|
self.displayViewScreen(true); // true=cls
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.initDatabase = function(cb) {
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function openDatabase(callback) {
|
||||||
|
self.db = new sqlite3.Database(
|
||||||
|
getModDatabasePath(exports.moduleInfo),
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function createTables(callback) {
|
||||||
|
self.db.serialize( () => {
|
||||||
|
self.db.run(
|
||||||
|
`CREATE TABLE IF NOT EXISTS onelinerz (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
user_id INTEGER_NOT NULL,
|
||||||
|
user_name VARCHAR NOT NULL,
|
||||||
|
oneliner VARCHAR NOT NULL,
|
||||||
|
timestamp DATETIME NOT NULL
|
||||||
|
)`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.storeNewOneliner = function(oneliner, cb) {
|
||||||
|
const ts = moment().format('YYYY-MM-DDTHH:mm:ss.SSSZ');
|
||||||
|
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function addRec(callback) {
|
||||||
|
self.db.run(
|
||||||
|
`INSERT INTO onelinerz (user_id, user_name, oneliner, timestamp)
|
||||||
|
VALUES (?, ?, ?, ?);`,
|
||||||
|
[ self.client.user.userId, self.client.user.username, oneliner, ts ],
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function removeOld(callback) {
|
||||||
|
// keep 25 max most recent items - remove the older ones
|
||||||
|
self.db.run(
|
||||||
|
`DELETE FROM onelinerz
|
||||||
|
WHERE id IN (
|
||||||
|
SELECT id
|
||||||
|
FROM onelinerz
|
||||||
|
ORDER BY id DESC
|
||||||
|
LIMIT -1 OFFSET 25
|
||||||
|
);`,
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
require('util').inherits(OnelinerzModule, MenuModule);
|
||||||
|
|
||||||
|
OnelinerzModule.prototype.beforeArt = function(cb) {
|
||||||
|
OnelinerzModule.super_.prototype.beforeArt.call(this, err => {
|
||||||
|
return err ? cb(err) : this.initDatabase(cb);
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,115 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const MenuModule = require('../core/menu_module.js').MenuModule;
|
||||||
|
const resetScreen = require('../core/ansi_term.js').resetScreen;
|
||||||
|
|
||||||
|
const async = require('async');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const net = require('net');
|
||||||
|
|
||||||
|
/*
|
||||||
|
Expected configuration block:
|
||||||
|
|
||||||
|
{
|
||||||
|
module: telnet_bridge
|
||||||
|
...
|
||||||
|
config: {
|
||||||
|
host: somehost.net
|
||||||
|
port: 23
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// :TODO: BUG: When a client disconnects, it's not handled very well -- the log is spammed with tons of errors
|
||||||
|
// :TODO: ENH: Support nodeMax and tooManyArt
|
||||||
|
|
||||||
|
exports.getModule = TelnetBridgeModule;
|
||||||
|
|
||||||
|
exports.moduleInfo = {
|
||||||
|
name : 'Telnet Bridge',
|
||||||
|
desc : 'Connect to other Telnet Systems',
|
||||||
|
author : 'Andrew Pamment',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function TelnetBridgeModule(options) {
|
||||||
|
MenuModule.call(this, options);
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
this.config = options.menuConfig.config;
|
||||||
|
|
||||||
|
this.initSequence = function() {
|
||||||
|
let clientTerminated;
|
||||||
|
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function validateConfig(callback) {
|
||||||
|
if(_.isString(self.config.host) &&
|
||||||
|
_.isNumber(self.config.port))
|
||||||
|
{
|
||||||
|
callback(null);
|
||||||
|
} else {
|
||||||
|
callback(new Error('Configuration is missing required option(s)'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function createTelnetBridge(callback) {
|
||||||
|
const connectOpts = {
|
||||||
|
port : self.config.port,
|
||||||
|
host : self.config.host,
|
||||||
|
};
|
||||||
|
|
||||||
|
let clientTerminated;
|
||||||
|
|
||||||
|
self.client.term.write(resetScreen());
|
||||||
|
self.client.term.write(` Connecting to ${connectOpts.host}, please wait...\n`);
|
||||||
|
|
||||||
|
let bridgeConnection = net.createConnection(connectOpts, () => {
|
||||||
|
self.client.log.info(connectOpts, 'Telnet bridge connection established');
|
||||||
|
|
||||||
|
self.client.term.output.pipe(bridgeConnection);
|
||||||
|
|
||||||
|
self.client.once('end', () => {
|
||||||
|
self.client.log.info('Connection ended. Terminating connection');
|
||||||
|
clientTerminated = true;
|
||||||
|
return bridgeConnection.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const restorePipe = function() {
|
||||||
|
self.client.term.output.unpipe(bridgeConnection);
|
||||||
|
self.client.term.output.resume();
|
||||||
|
};
|
||||||
|
|
||||||
|
bridgeConnection.on('data', data => {
|
||||||
|
// pass along
|
||||||
|
// :TODO: just pipe this as well
|
||||||
|
return self.client.term.rawWrite(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
bridgeConnection.once('end', () => {
|
||||||
|
restorePipe();
|
||||||
|
return callback(clientTerminated ? new Error('Client connection terminated') : null);
|
||||||
|
});
|
||||||
|
|
||||||
|
bridgeConnection.once('error', err => {
|
||||||
|
self.client.log.info(`Telnet bridge connection error: ${err.message}`);
|
||||||
|
restorePipe();
|
||||||
|
return callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if(err) {
|
||||||
|
self.client.log.warn( { error : err.message }, 'Telnet connection error');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!clientTerminated) {
|
||||||
|
self.prevMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
require('util').inherits(TelnetBridgeModule, MenuModule);
|
Binary file not shown.
Binary file not shown.
|
@ -84,6 +84,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fullLoginSequenceOnelinerz: {
|
||||||
|
config: {
|
||||||
|
listFormat: "|00|11{username:<12}|08: |03{oneliner:<59.58}"
|
||||||
|
}
|
||||||
|
0: {
|
||||||
|
mci: {
|
||||||
|
VM1: { height: 10 }
|
||||||
|
TM2: {
|
||||||
|
focusTextStyle: first lower
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1: {
|
||||||
|
mci: {
|
||||||
|
ET1: { width: 60 }
|
||||||
|
TL2: { width: 60 }
|
||||||
|
TM3: {
|
||||||
|
focusTextStyle: first lower
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mainMenuUserStats: {
|
mainMenuUserStats: {
|
||||||
mci: {
|
mci: {
|
||||||
UN1: { width: 17 }
|
UN1: { width: 17 }
|
||||||
|
@ -157,6 +180,30 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mainMenuOnelinerz: {
|
||||||
|
// :TODO: Need way to just duplicate entry here & in menu.hjson, e.g. use: someName + must supply next/etc. in menu
|
||||||
|
config: {
|
||||||
|
listFormat: "|00|11{username:<12}|08: |03{oneliner:<59.58}"
|
||||||
|
}
|
||||||
|
0: {
|
||||||
|
mci: {
|
||||||
|
VM1: { height: 10 }
|
||||||
|
TM2: {
|
||||||
|
focusTextStyle: first lower
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1: {
|
||||||
|
mci: {
|
||||||
|
ET1: { width: 60 }
|
||||||
|
TL2: { width: 60 }
|
||||||
|
TM3: {
|
||||||
|
focusTextStyle: first lower
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
messageAreaMessageList: {
|
messageAreaMessageList: {
|
||||||
config: {
|
config: {
|
||||||
listFormat: "|00|15{msgNum:>4} |03{subj:<29.29} |11{from:<20.20} |03{ts} |01|31{newIndicator}"
|
listFormat: "|00|15{msgNum:>4} |03{subj:<29.29} |11{from:<20.20} |03{ts} |01|31{newIndicator}"
|
||||||
|
@ -336,6 +383,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ercClient: {
|
||||||
|
config: {
|
||||||
|
//chatEntryFormat: "|00|08[|03{bbsTag}|08] |10{userName}|08: |02{message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue