mirror of
https://github.com/NuSkooler/enigma-bbs.git
synced 2025-06-07 05:05:26 +02:00
* Code cleanup and eslint since -- remove unused variables, clean up RegExs, so on...
This commit is contained in:
parent
a106050ba3
commit
ac1433e84b
112 changed files with 1375 additions and 1898 deletions
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
const miscUtil = require('./misc_util.js');
|
const miscUtil = require('./misc_util.js');
|
||||||
const ansi = require('./ansi_term.js');
|
const ansi = require('./ansi_term.js');
|
||||||
|
const Log = require('./logger.js').log;
|
||||||
|
|
||||||
|
// deps
|
||||||
const events = require('events');
|
const events = require('events');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
@ -24,7 +26,7 @@ function ANSIEscapeParser(options) {
|
||||||
this.graphicRendition = {};
|
this.graphicRendition = {};
|
||||||
|
|
||||||
this.parseState = {
|
this.parseState = {
|
||||||
re : /(?:\x1b\x5b)([\?=;0-9]*?)([ABCDHJKfhlmnpsu])/g,
|
re : /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsu])/g, // eslint-disable-line no-control-regex
|
||||||
};
|
};
|
||||||
|
|
||||||
options = miscUtil.valueWithDefault(options, {
|
options = miscUtil.valueWithDefault(options, {
|
||||||
|
@ -63,7 +65,7 @@ function ANSIEscapeParser(options) {
|
||||||
delete self.savedPosition;
|
delete self.savedPosition;
|
||||||
|
|
||||||
self.positionUpdated();
|
self.positionUpdated();
|
||||||
// self.rowUpdated();
|
// self.rowUpdated();
|
||||||
};
|
};
|
||||||
|
|
||||||
self.clearScreen = function() {
|
self.clearScreen = function() {
|
||||||
|
@ -71,7 +73,7 @@ function ANSIEscapeParser(options) {
|
||||||
self.emit('clear screen');
|
self.emit('clear screen');
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
self.rowUpdated = function() {
|
self.rowUpdated = function() {
|
||||||
self.emit('row update', self.row + self.scrollBack);
|
self.emit('row update', self.row + self.scrollBack);
|
||||||
};*/
|
};*/
|
||||||
|
@ -142,17 +144,9 @@ function ANSIEscapeParser(options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProcessedMCI(mci) {
|
|
||||||
if(self.mciReplaceChar.length > 0) {
|
|
||||||
return ansi.getSGRFromGraphicRendition(self.graphicRendition, true) + new Array(mci.length + 1).join(self.mciReplaceChar);
|
|
||||||
} else {
|
|
||||||
return mci;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseMCI(buffer) {
|
function parseMCI(buffer) {
|
||||||
// :TODO: move this to "constants" seciton @ top
|
// :TODO: move this to "constants" seciton @ top
|
||||||
var mciRe = /\%([A-Z]{2})([0-9]{1,2})?(?:\(([0-9A-Za-z,]+)\))*/g;
|
var mciRe = /%([A-Z]{2})([0-9]{1,2})?(?:\(([0-9A-Za-z,]+)\))*/g;
|
||||||
var pos = 0;
|
var pos = 0;
|
||||||
var match;
|
var match;
|
||||||
var mciCode;
|
var mciCode;
|
||||||
|
@ -197,16 +191,12 @@ function ANSIEscapeParser(options) {
|
||||||
if(self.mciReplaceChar.length > 0) {
|
if(self.mciReplaceChar.length > 0) {
|
||||||
const sgrCtrl = ansi.getSGRFromGraphicRendition(self.graphicRenditionForErase);
|
const sgrCtrl = ansi.getSGRFromGraphicRendition(self.graphicRenditionForErase);
|
||||||
|
|
||||||
self.emit('control', sgrCtrl, 'm', sgrCtrl.slice(2).split(/[\;m]/).slice(0, 3));
|
self.emit('control', sgrCtrl, 'm', sgrCtrl.slice(2).split(/[;m]/).slice(0, 3));
|
||||||
|
|
||||||
literal(new Array(match[0].length + 1).join(self.mciReplaceChar));
|
literal(new Array(match[0].length + 1).join(self.mciReplaceChar));
|
||||||
} else {
|
} else {
|
||||||
literal(match[0]);
|
literal(match[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//literal(getProcessedMCI(match[0]));
|
|
||||||
|
|
||||||
//self.emit('chunk', getProcessedMCI(match[0]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} while(0 !== mciRe.lastIndex);
|
} while(0 !== mciRe.lastIndex);
|
||||||
|
@ -220,7 +210,7 @@ function ANSIEscapeParser(options) {
|
||||||
self.parseState = {
|
self.parseState = {
|
||||||
// ignore anything past EOF marker, if any
|
// ignore anything past EOF marker, if any
|
||||||
buffer : input.split(String.fromCharCode(0x1a), 1)[0],
|
buffer : input.split(String.fromCharCode(0x1a), 1)[0],
|
||||||
re : /(?:\x1b\x5b)([\?=;0-9]*?)([ABCDHJKfhlmnpsu])/g,
|
re : /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsu])/g, // eslint-disable-line no-control-regex
|
||||||
stop : false,
|
stop : false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -291,13 +281,13 @@ function ANSIEscapeParser(options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMCI(lastBit)
|
parseMCI(lastBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emit('complete');
|
self.emit('complete');
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
self.parse = function(buffer, savedRe) {
|
self.parse = function(buffer, savedRe) {
|
||||||
// :TODO: ensure this conforms to ANSI-BBS / CTerm / bansi.txt for movement/etc.
|
// :TODO: ensure this conforms to ANSI-BBS / CTerm / bansi.txt for movement/etc.
|
||||||
// :TODO: move this to "constants" section @ top
|
// :TODO: move this to "constants" section @ top
|
||||||
|
@ -448,7 +438,7 @@ function ANSIEscapeParser(options) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default :
|
default :
|
||||||
console.log('Unknown attribute: ' + arg); // :TODO: Log properly
|
Log.trace( { attribute : arg }, 'Unknown attribute while parsing ANSI');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,9 +108,11 @@ module.exports = class ArchiveUtil {
|
||||||
return this.getArchiver(archType) ? true : false;
|
return this.getArchiver(archType) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// :TODO: implement me:
|
||||||
|
/*
|
||||||
detectTypeWithBuf(buf, cb) {
|
detectTypeWithBuf(buf, cb) {
|
||||||
// :TODO: implement me!
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
detectType(path, cb) {
|
detectType(path, cb) {
|
||||||
fs.open(path, 'r', (err, fd) => {
|
fs.open(path, 'r', (err, fd) => {
|
||||||
|
|
|
@ -52,8 +52,8 @@ exports.Client = Client;
|
||||||
// Resources & Standards:
|
// Resources & Standards:
|
||||||
// * http://www.ansi-bbs.org/ansi-bbs-core-server.html
|
// * http://www.ansi-bbs.org/ansi-bbs-core-server.html
|
||||||
//
|
//
|
||||||
const RE_DSR_RESPONSE_ANYWHERE = /(?:\u001b\[)([0-9\;]+)(R)/;
|
const RE_DSR_RESPONSE_ANYWHERE = /(?:\u001b\[)([0-9;]+)(R)/;
|
||||||
const RE_DEV_ATTR_RESPONSE_ANYWHERE = /(?:\u001b\[)[\=\?]([0-9a-zA-Z\;]+)(c)/;
|
const RE_DEV_ATTR_RESPONSE_ANYWHERE = /(?:\u001b\[)[=?]([0-9a-zA-Z;]+)(c)/;
|
||||||
const RE_META_KEYCODE_ANYWHERE = /(?:\u001b)([a-zA-Z0-9])/;
|
const RE_META_KEYCODE_ANYWHERE = /(?:\u001b)([a-zA-Z0-9])/;
|
||||||
const RE_META_KEYCODE = new RegExp('^' + RE_META_KEYCODE_ANYWHERE.source + '$');
|
const RE_META_KEYCODE = new RegExp('^' + RE_META_KEYCODE_ANYWHERE.source + '$');
|
||||||
const RE_FUNCTION_KEYCODE_ANYWHERE = new RegExp('(?:\u001b+)(O|N|\\[|\\[\\[)(?:' + [
|
const RE_FUNCTION_KEYCODE_ANYWHERE = new RegExp('(?:\u001b+)(O|N|\\[|\\[\\[)(?:' + [
|
||||||
|
@ -72,7 +72,7 @@ const RE_ESC_CODE_ANYWHERE = new RegExp( [
|
||||||
].join('|'));
|
].join('|'));
|
||||||
|
|
||||||
|
|
||||||
function Client(input, output) {
|
function Client(/*input, output*/) {
|
||||||
stream.call(this);
|
stream.call(this);
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
@ -139,8 +139,8 @@ function Client(input, output) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.isMouseInput = function(data) {
|
this.isMouseInput = function(data) {
|
||||||
return /\x1b\[M/.test(data) ||
|
return /\x1b\[M/.test(data) || // eslint-disable-line no-control-regex
|
||||||
/\u001b\[M([\x00\u0020-\uffff]{3})/.test(data) ||
|
/\u001b\[M([\x00\u0020-\uffff]{3})/.test(data) || // eslint-disable-line no-control-regex
|
||||||
/\u001b\[(\d+;\d+;\d+)M/.test(data) ||
|
/\u001b\[(\d+;\d+;\d+)M/.test(data) ||
|
||||||
/\u001b\[<(\d+;\d+;\d+)([mM])/.test(data) ||
|
/\u001b\[<(\d+;\d+;\d+)([mM])/.test(data) ||
|
||||||
/\u001b\[<(\d+;\d+;\d+;\d+)&w/.test(data) ||
|
/\u001b\[<(\d+;\d+;\d+;\d+)&w/.test(data) ||
|
||||||
|
@ -482,7 +482,7 @@ Client.prototype.isLocal = function() {
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// :TODO: getDefaultHandler(name) -- handlers in default_handlers.js or something
|
// :TODO: getDefaultHandler(name) -- handlers in default_handlers.js or something
|
||||||
Client.prototype.defaultHandlerMissingMod = function(err) {
|
Client.prototype.defaultHandlerMissingMod = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
function handler(err) {
|
function handler(err) {
|
||||||
|
|
|
@ -95,7 +95,7 @@ function removeClient(client) {
|
||||||
clientId : client.session.id
|
clientId : client.session.id
|
||||||
},
|
},
|
||||||
'Client disconnected'
|
'Client disconnected'
|
||||||
);
|
);
|
||||||
|
|
||||||
Events.emit('codes.l33t.enigma.system.disconnected', { client : client, connectionCount : clientConnections.length } );
|
Events.emit('codes.l33t.enigma.system.disconnected', { client : client, connectionCount : clientConnections.length } );
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@ exports.ClientTerminal = ClientTerminal;
|
||||||
function ClientTerminal(output) {
|
function ClientTerminal(output) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var outputEncoding = 'cp437';
|
var outputEncoding = 'cp437';
|
||||||
assert(iconv.encodingExists(outputEncoding));
|
assert(iconv.encodingExists(outputEncoding));
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ function renegadeToAnsi(s, client) {
|
||||||
// * http://wiki.synchro.net/custom:colors
|
// * http://wiki.synchro.net/custom:colors
|
||||||
//
|
//
|
||||||
function controlCodesToAnsi(s, client) {
|
function controlCodesToAnsi(s, client) {
|
||||||
const RE = /(\|([A-Z0-9]{2})|\|)|(\@X([0-9A-F]{2}))|(\@([0-9A-F]{2})\@)|(\x03[0-9]|\x03)/g; // eslint-disable-line no-control-regex
|
const RE = /(\|([A-Z0-9]{2})|\|)|(@X([0-9A-F]{2}))|(@([0-9A-F]{2})@)|(\x03[0-9]|\x03)/g; // eslint-disable-line no-control-regex
|
||||||
|
|
||||||
let m;
|
let m;
|
||||||
let result = '';
|
let result = '';
|
||||||
|
|
|
@ -45,59 +45,59 @@ exports.getModule = class CombatNetModule extends MenuModule {
|
||||||
self.client.term.write('Connecting to CombatNet, please wait...\n');
|
self.client.term.write('Connecting to CombatNet, please wait...\n');
|
||||||
|
|
||||||
const restorePipeToNormal = function() {
|
const restorePipeToNormal = function() {
|
||||||
self.client.term.output.removeListener('data', sendToRloginBuffer);
|
self.client.term.output.removeListener('data', sendToRloginBuffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
const rlogin = new RLogin(
|
const rlogin = new RLogin(
|
||||||
{ 'clientUsername' : self.config.password,
|
{ 'clientUsername' : self.config.password,
|
||||||
'serverUsername' : `${self.config.bbsTag}${self.client.user.username}`,
|
'serverUsername' : `${self.config.bbsTag}${self.client.user.username}`,
|
||||||
'host' : self.config.host,
|
'host' : self.config.host,
|
||||||
'port' : self.config.rloginPort,
|
'port' : self.config.rloginPort,
|
||||||
'terminalType' : self.client.term.termClient,
|
'terminalType' : self.client.term.termClient,
|
||||||
'terminalSpeed' : 57600
|
'terminalSpeed' : 57600
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// If there was an error ...
|
// If there was an error ...
|
||||||
rlogin.on('error', err => {
|
rlogin.on('error', err => {
|
||||||
self.client.log.info(`CombatNet rlogin client error: ${err.message}`);
|
self.client.log.info(`CombatNet rlogin client error: ${err.message}`);
|
||||||
restorePipeToNormal();
|
restorePipeToNormal();
|
||||||
callback(err);
|
return callback(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we've been disconnected ...
|
// If we've been disconnected ...
|
||||||
rlogin.on('disconnect', () => {
|
rlogin.on('disconnect', () => {
|
||||||
self.client.log.info(`Disconnected from CombatNet`);
|
self.client.log.info('Disconnected from CombatNet');
|
||||||
restorePipeToNormal();
|
restorePipeToNormal();
|
||||||
callback(null);
|
return callback(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
function sendToRloginBuffer(buffer) {
|
function sendToRloginBuffer(buffer) {
|
||||||
rlogin.send(buffer);
|
rlogin.send(buffer);
|
||||||
};
|
}
|
||||||
|
|
||||||
rlogin.on("connect",
|
rlogin.on('connect',
|
||||||
/* The 'connect' event handler will be supplied with one argument,
|
/* The 'connect' event handler will be supplied with one argument,
|
||||||
a boolean indicating whether or not the connection was established. */
|
a boolean indicating whether or not the connection was established. */
|
||||||
|
|
||||||
function(state) {
|
function(state) {
|
||||||
if(state) {
|
if(state) {
|
||||||
self.client.log.info('Connected to CombatNet');
|
self.client.log.info('Connected to CombatNet');
|
||||||
self.client.term.output.on('data', sendToRloginBuffer);
|
self.client.term.output.on('data', sendToRloginBuffer);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return callback(new Error('Failed to establish establish CombatNet connection'));
|
return callback(new Error('Failed to establish establish CombatNet connection'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// If data (a Buffer) has been received from the server ...
|
// If data (a Buffer) has been received from the server ...
|
||||||
rlogin.on("data", (data) => {
|
rlogin.on('data', (data) => {
|
||||||
self.client.term.rawWrite(data);
|
self.client.term.rawWrite(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
// connect...
|
// connect...
|
||||||
rlogin.connect();
|
rlogin.connect();
|
||||||
|
|
||||||
// note: no explicit callback() until we're finished!
|
// note: no explicit callback() until we're finished!
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,12 +653,12 @@ function getDefaultConfig() {
|
||||||
// FILE_ID.DIZ - https://en.wikipedia.org/wiki/FILE_ID.DIZ
|
// FILE_ID.DIZ - https://en.wikipedia.org/wiki/FILE_ID.DIZ
|
||||||
// Some groups include a FILE_ID.ANS. We try to use that over FILE_ID.DIZ if available.
|
// Some groups include a FILE_ID.ANS. We try to use that over FILE_ID.DIZ if available.
|
||||||
desc : [
|
desc : [
|
||||||
'^[^/\]*FILE_ID\.ANS$', '^[^/\]*FILE_ID\.DIZ$', '^[^/\]*DESC\.SDI$', '^[^/\]*DESCRIPT\.ION$', '^[^/\]*FILE\.DES$', '^[^/\]*FILE\.SDI$', '^[^/\]*DISK\.ID$'
|
'^[^/\]*FILE_ID\.ANS$', '^[^/\]*FILE_ID\.DIZ$', '^[^/\]*DESC\.SDI$', '^[^/\]*DESCRIPT\.ION$', '^[^/\]*FILE\.DES$', '^[^/\]*FILE\.SDI$', '^[^/\]*DISK\.ID$' // eslint-disable-line no-useless-escape
|
||||||
],
|
],
|
||||||
|
|
||||||
// common README filename - https://en.wikipedia.org/wiki/README
|
// common README filename - https://en.wikipedia.org/wiki/README
|
||||||
descLong : [
|
descLong : [
|
||||||
'^[^/\]*\.NFO$', '^[^/\]*README\.1ST$', '^[^/\]*README\.NOW$', '^[^/\]*README\.TXT$', '^[^/\]*READ\.ME$', '^[^/\]*README$', '^[^/\]*README\.md$'
|
'^[^/\]*\.NFO$', '^[^/\]*README\.1ST$', '^[^/\]*README\.NOW$', '^[^/\]*README\.TXT$', '^[^/\]*READ\.ME$', '^[^/\]*README$', '^[^/\]*README\.md$' // eslint-disable-line no-useless-escape
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ util.inherits(ConfigCache, events.EventEmitter);
|
||||||
ConfigCache.prototype.getConfigWithOptions = function(options, cb) {
|
ConfigCache.prototype.getConfigWithOptions = function(options, cb) {
|
||||||
assert(_.isString(options.filePath));
|
assert(_.isString(options.filePath));
|
||||||
|
|
||||||
// var self = this;
|
// var self = this;
|
||||||
var isCached = (options.filePath in this.cache);
|
var isCached = (options.filePath in this.cache);
|
||||||
|
|
||||||
if(options.forceReCache || !isCached) {
|
if(options.forceReCache || !isCached) {
|
||||||
|
|
|
@ -98,7 +98,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) {
|
||||||
source : 'ANSI CPR'
|
source : 'ANSI CPR'
|
||||||
},
|
},
|
||||||
'Window size updated'
|
'Window size updated'
|
||||||
);
|
);
|
||||||
|
|
||||||
return done(null);
|
return done(null);
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,7 +37,7 @@ function getModDatabasePath(moduleInfo, suffix) {
|
||||||
// We expect that moduleInfo defines packageName which will be the base of the modules
|
// We expect that moduleInfo defines packageName which will be the base of the modules
|
||||||
// filename. An optional suffix may be supplied as well.
|
// 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])$/;
|
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(_.isObject(moduleInfo));
|
||||||
assert(_.isString(moduleInfo.packageName), 'moduleInfo must define "packageName"!');
|
assert(_.isString(moduleInfo.packageName), 'moduleInfo must define "packageName"!');
|
||||||
|
|
|
@ -24,8 +24,8 @@ exports.moduleInfo = {
|
||||||
author : 'NuSkooler',
|
author : 'NuSkooler',
|
||||||
};
|
};
|
||||||
|
|
||||||
const SCHEDULE_REGEXP = /(?:^|or )?(@watch\:)([^\0]+)?$/;
|
const SCHEDULE_REGEXP = /(?:^|or )?(@watch:)([^\0]+)?$/;
|
||||||
const ACTION_REGEXP = /\@(method|execute)\:([^\0]+)?$/;
|
const ACTION_REGEXP = /@(method|execute):([^\0]+)?$/;
|
||||||
|
|
||||||
class ScheduledEvent {
|
class ScheduledEvent {
|
||||||
constructor(events, name) {
|
constructor(events, name) {
|
||||||
|
|
|
@ -586,10 +586,6 @@ function addNewFileEntry(fileEntry, filePath, cb) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFileEntry(fileEntry, filePath, cb) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const HASH_NAMES = [ 'sha1', 'sha256', 'md5', 'crc32' ];
|
const HASH_NAMES = [ 'sha1', 'sha256', 'md5', 'crc32' ];
|
||||||
|
|
||||||
function scanFile(filePath, options, iterator, cb) {
|
function scanFile(filePath, options, iterator, cb) {
|
||||||
|
@ -747,7 +743,9 @@ function scanFile(filePath, options, iterator, cb) {
|
||||||
populateFileEntryWithArchive(fileEntry, filePath, stepInfo, callIter, err => {
|
populateFileEntryWithArchive(fileEntry, filePath, stepInfo, callIter, err => {
|
||||||
if(err) {
|
if(err) {
|
||||||
populateFileEntryNonArchive(fileEntry, filePath, stepInfo, callIter, err => {
|
populateFileEntryNonArchive(fileEntry, filePath, stepInfo, callIter, err => {
|
||||||
// :TODO: log err
|
if(err) {
|
||||||
|
logDebug( { error : err.message }, 'Non-archive file entry population failed');
|
||||||
|
}
|
||||||
return callback(null); // ignore err
|
return callback(null); // ignore err
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -756,7 +754,9 @@ function scanFile(filePath, options, iterator, cb) {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
populateFileEntryNonArchive(fileEntry, filePath, stepInfo, callIter, err => {
|
populateFileEntryNonArchive(fileEntry, filePath, stepInfo, callIter, err => {
|
||||||
// :TODO: log err
|
if(err) {
|
||||||
|
logDebug( { error : err.message }, 'Non-archive file entry population failed');
|
||||||
|
}
|
||||||
return callback(null); // ignore err
|
return callback(null); // ignore err
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -874,7 +874,7 @@ function getDescFromFileName(fileName) {
|
||||||
const ext = paths.extname(fileName);
|
const ext = paths.extname(fileName);
|
||||||
const name = paths.basename(fileName, ext);
|
const name = paths.basename(fileName, ext);
|
||||||
|
|
||||||
return _.upperFirst(name.replace(/[\-_.+]/g, ' ').replace(/\s+/g, ' '));
|
return _.upperFirst(name.replace(/[-_.+]/g, ' ').replace(/\s+/g, ' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -59,8 +59,6 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule {
|
||||||
|
|
||||||
return this.gotoMenu(this.menuConfig.config.fileTransferProtocolSelection || 'fileTransferProtocolSelection', modOpts, cb);
|
return this.gotoMenu(this.menuConfig.config.fileTransferProtocolSelection || 'fileTransferProtocolSelection', modOpts, cb);
|
||||||
},
|
},
|
||||||
viewItemInfo : (formData, extraArgs, cb) => {
|
|
||||||
},
|
|
||||||
removeItem : (formData, extraArgs, cb) => {
|
removeItem : (formData, extraArgs, cb) => {
|
||||||
const selectedItem = this.dlQueue.items[formData.value.queueItem];
|
const selectedItem = this.dlQueue.items[formData.value.queueItem];
|
||||||
if(!selectedItem) {
|
if(!selectedItem) {
|
||||||
|
|
|
@ -94,7 +94,7 @@ module.exports = class FileBaseFilters {
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanTags(tags) {
|
cleanTags(tags) {
|
||||||
return tags.toLowerCase().replace(/,?\s+|\,/g, ' ').trim();
|
return tags.toLowerCase().replace(/,?\s+|,/g, ' ').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
setActive(filterUuid) {
|
setActive(filterUuid) {
|
||||||
|
|
|
@ -284,4 +284,3 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,5 +46,5 @@ module.exports = class FNV1a {
|
||||||
get value() {
|
get value() {
|
||||||
return this.hash & 0xffffffff;
|
return this.hash & 0xffffffff;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
46
core/fse.js
46
core/fse.js
|
@ -179,26 +179,24 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
|
||||||
|
|
||||||
self.switchFooter(function next(err) {
|
self.switchFooter(function next(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
// :TODO:... what now?
|
return cb(err);
|
||||||
console.log(err)
|
}
|
||||||
} else {
|
|
||||||
switch(self.footerMode) {
|
|
||||||
case 'editor' :
|
|
||||||
if(!_.isUndefined(self.viewControllers.footerEditorMenu)) {
|
|
||||||
//self.viewControllers.footerEditorMenu.setFocus(false);
|
|
||||||
self.viewControllers.footerEditorMenu.detachClientEvents();
|
|
||||||
}
|
|
||||||
self.viewControllers.body.switchFocus(1);
|
|
||||||
self.observeEditorEvents();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'editorMenu' :
|
switch(self.footerMode) {
|
||||||
self.viewControllers.body.setFocus(false);
|
case 'editor' :
|
||||||
self.viewControllers.footerEditorMenu.switchFocus(1);
|
if(!_.isUndefined(self.viewControllers.footerEditorMenu)) {
|
||||||
break;
|
self.viewControllers.footerEditorMenu.detachClientEvents();
|
||||||
|
}
|
||||||
|
self.viewControllers.body.switchFocus(1);
|
||||||
|
self.observeEditorEvents();
|
||||||
|
break;
|
||||||
|
|
||||||
default : throw new Error('Unexpected mode');
|
case 'editorMenu' :
|
||||||
}
|
self.viewControllers.body.setFocus(false);
|
||||||
|
self.viewControllers.footerEditorMenu.switchFocus(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default : throw new Error('Unexpected mode');
|
||||||
}
|
}
|
||||||
|
|
||||||
return cb(null);
|
return cb(null);
|
||||||
|
@ -534,7 +532,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
|
||||||
art[n],
|
art[n],
|
||||||
self.client,
|
self.client,
|
||||||
{ font : self.menuConfig.font },
|
{ font : self.menuConfig.font },
|
||||||
function displayed(err, artData) {
|
function displayed(err) {
|
||||||
next(err);
|
next(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -645,8 +643,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
|
||||||
],
|
],
|
||||||
function complete(err) {
|
function complete(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
// :TODO: This needs properly handled!
|
self.client.log.warn( { error : err.message }, 'FSE init error');
|
||||||
console.log(err)
|
|
||||||
} else {
|
} else {
|
||||||
self.isReady = true;
|
self.isReady = true;
|
||||||
self.finishedLoading();
|
self.finishedLoading();
|
||||||
|
@ -763,10 +760,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
function complete(err) {
|
function complete(err) {
|
||||||
if(err) {
|
return cb(err);
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
cb(err);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -954,7 +948,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
|
||||||
],
|
],
|
||||||
function complete(err) {
|
function complete(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
console.log(err) // :TODO: needs real impl.
|
self.client.log.warn( { error : err.message }, 'Error displaying quote builder');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
const FTN_ADDRESS_REGEXP = /^([0-9]+:)?([0-9]+)(\/[0-9]+)?(\.[0-9]+)?(@[a-z0-9\-\.]+)?$/i;
|
const FTN_ADDRESS_REGEXP = /^([0-9]+:)?([0-9]+)(\/[0-9]+)?(\.[0-9]+)?(@[a-z0-9\-.]+)?$/i;
|
||||||
const FTN_PATTERN_REGEXP = /^([0-9\*]+:)?([0-9\*]+)(\/[0-9\*]+)?(\.[0-9\*]+)?(@[a-z0-9\-\.\*]+)?$/i;
|
const FTN_PATTERN_REGEXP = /^([0-9*]+:)?([0-9*]+)(\/[0-9*]+)?(\.[0-9*]+)?(@[a-z0-9\-.*]+)?$/i;
|
||||||
|
|
||||||
module.exports = class Address {
|
module.exports = class Address {
|
||||||
constructor(addr) {
|
constructor(addr) {
|
||||||
|
|
|
@ -426,12 +426,12 @@ function Packet(options) {
|
||||||
if(!err) {
|
if(!err) {
|
||||||
// we read some SAUCE - don't re-process that portion into the body
|
// we read some SAUCE - don't re-process that portion into the body
|
||||||
messageBodyBuffer = messageBodyBuffer.slice(0, sauceHeaderPosition) + messageBodyBuffer.slice(sauceHeaderPosition + sauce.SAUCE_SIZE);
|
messageBodyBuffer = messageBodyBuffer.slice(0, sauceHeaderPosition) + messageBodyBuffer.slice(sauceHeaderPosition + sauce.SAUCE_SIZE);
|
||||||
// messageBodyBuffer = messageBodyBuffer.slice(0, sauceHeaderPosition);
|
// messageBodyBuffer = messageBodyBuffer.slice(0, sauceHeaderPosition);
|
||||||
messageBodyData.sauce = theSauce;
|
messageBodyData.sauce = theSauce;
|
||||||
} else {
|
} else {
|
||||||
console.log(err)
|
Log.warn( { error : err.message }, 'Found what looks like to be a SAUCE record, but failed to read');
|
||||||
}
|
}
|
||||||
callback(null); // failure to read SAUCE is OK
|
return callback(null); // failure to read SAUCE is OK
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
callback(null);
|
callback(null);
|
||||||
|
@ -497,7 +497,7 @@ function Packet(options) {
|
||||||
} else if(line.startsWith('--- ')) {
|
} else if(line.startsWith('--- ')) {
|
||||||
// Tear Lines are tracked allowing for specialized display/etc.
|
// Tear Lines are tracked allowing for specialized display/etc.
|
||||||
messageBodyData.tearLine = line;
|
messageBodyData.tearLine = line;
|
||||||
} else if(/^[ ]{1,2}\* Origin\: /.test(line)) { // To spec is " * Origin: ..."
|
} else if(/^[ ]{1,2}\* Origin: /.test(line)) { // To spec is " * Origin: ..."
|
||||||
messageBodyData.originLine = line;
|
messageBodyData.originLine = line;
|
||||||
endOfMessage = true; // Anything past origin is not part of the message body
|
endOfMessage = true; // Anything past origin is not part of the message body
|
||||||
} else if(line.startsWith('SEEN-BY:')) {
|
} else if(line.startsWith('SEEN-BY:')) {
|
||||||
|
@ -1040,6 +1040,6 @@ Packet.prototype.write = function(path, packetHeader, messages, options) {
|
||||||
this.writeStream(
|
this.writeStream(
|
||||||
fs.createWriteStream(path), // :TODO: specify mode/etc.
|
fs.createWriteStream(path), // :TODO: specify mode/etc.
|
||||||
messages,
|
messages,
|
||||||
{ packetHeader : packetHeader, terminatePacket : true }
|
Object.assign( { packetHeader : packetHeader, terminatePacket : true }, options)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -148,7 +148,7 @@ function getMessageIdentifier(message, address, isNetMail = false) {
|
||||||
return isNetMail ?
|
return isNetMail ?
|
||||||
`${addrStr} ${getMessageSerialNumber(message.messageId)}` :
|
`${addrStr} ${getMessageSerialNumber(message.messageId)}` :
|
||||||
`${message.messageId}.${message.areaTag.toLowerCase()}@${addrStr} ${getMessageSerialNumber(message.messageId)}`
|
`${message.messageId}.${message.areaTag.toLowerCase()}@${addrStr} ${getMessageSerialNumber(message.messageId)}`
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -225,11 +225,7 @@ function getVia(address) {
|
||||||
*/
|
*/
|
||||||
const addrStr = new Address(address).toString('5D');
|
const addrStr = new Address(address).toString('5D');
|
||||||
const dateTime = moment().utc().format('YYYYMMDD.HHmmSS.SSSS.UTC');
|
const dateTime = moment().utc().format('YYYYMMDD.HHmmSS.SSSS.UTC');
|
||||||
|
const version = getCleanEnigmaVersion();
|
||||||
const version = packageJson.version
|
|
||||||
.replace(/\-/g, '.')
|
|
||||||
.replace(/alpha/,'a')
|
|
||||||
.replace(/beta/,'b');
|
|
||||||
|
|
||||||
return `${addrStr} @${dateTime} ENiGMA1/2 ${version}`;
|
return `${addrStr} @${dateTime} ENiGMA1/2 ${version}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ function HorizontalMenuView(options) {
|
||||||
ansi.goto(self.position.row, item.col) +
|
ansi.goto(self.position.row, item.col) +
|
||||||
(index === self.focusedItemIndex ? self.getFocusSGR() : self.getSGR()) +
|
(index === self.focusedItemIndex ? self.getFocusSGR() : self.getSGR()) +
|
||||||
strUtil.pad(text, drawWidth, self.fillChar, 'center')
|
strUtil.pad(text, drawWidth, self.fillChar, 'center')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,4 +33,4 @@ MailPacket.prototype.write = function(options) {
|
||||||
// emits 'packet' event per packet constructed
|
// emits 'packet' event per packet constructed
|
||||||
//
|
//
|
||||||
assert(_.isArray(options.messages));
|
assert(_.isArray(options.messages));
|
||||||
}
|
};
|
|
@ -6,7 +6,7 @@ const Message = require('./message.js');
|
||||||
|
|
||||||
exports.getAddressedToInfo = getAddressedToInfo;
|
exports.getAddressedToInfo = getAddressedToInfo;
|
||||||
|
|
||||||
const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Input Output
|
Input Output
|
||||||
|
|
|
@ -4,13 +4,12 @@
|
||||||
// ENiGMA½
|
// ENiGMA½
|
||||||
const TextView = require('./text_view.js').TextView;
|
const TextView = require('./text_view.js').TextView;
|
||||||
const EditTextView = require('./edit_text_view.js').EditTextView;
|
const EditTextView = require('./edit_text_view.js').EditTextView;
|
||||||
const ButtonView = require('./button_view.js').ButtonView;
|
const ButtonView = require('./button_view.js').ButtonView;
|
||||||
const VerticalMenuView = require('./vertical_menu_view.js').VerticalMenuView;
|
const VerticalMenuView = require('./vertical_menu_view.js').VerticalMenuView;
|
||||||
const HorizontalMenuView = require('./horizontal_menu_view.js').HorizontalMenuView;
|
const HorizontalMenuView = require('./horizontal_menu_view.js').HorizontalMenuView;
|
||||||
const SpinnerMenuView = require('./spinner_menu_view.js').SpinnerMenuView;
|
const SpinnerMenuView = require('./spinner_menu_view.js').SpinnerMenuView;
|
||||||
const ToggleMenuView = require('./toggle_menu_view.js').ToggleMenuView;
|
const ToggleMenuView = require('./toggle_menu_view.js').ToggleMenuView;
|
||||||
const MaskEditTextView = require('./mask_edit_text_view.js').MaskEditTextView;
|
const MaskEditTextView = require('./mask_edit_text_view.js').MaskEditTextView;
|
||||||
//const StatusBarView = require('./status_bar_view.js').StatusBarView;
|
|
||||||
const KeyEntryView = require('./key_entry_view.js');
|
const KeyEntryView = require('./key_entry_view.js');
|
||||||
const MultiLineEditTextView = require('./multi_line_edit_text_view.js').MultiLineEditTextView;
|
const MultiLineEditTextView = require('./multi_line_edit_text_view.js').MultiLineEditTextView;
|
||||||
const getPredefinedMCIValue = require('./predefined_mci.js').getPredefinedMCIValue;
|
const getPredefinedMCIValue = require('./predefined_mci.js').getPredefinedMCIValue;
|
||||||
|
@ -37,7 +36,7 @@ MCIViewFactory.UserViewCodes = [
|
||||||
'XY',
|
'XY',
|
||||||
];
|
];
|
||||||
|
|
||||||
MCIViewFactory.prototype.createFromMCI = function(mci, cb) {
|
MCIViewFactory.prototype.createFromMCI = function(mci) {
|
||||||
assert(mci.code);
|
assert(mci.code);
|
||||||
assert(mci.id > 0);
|
assert(mci.id > 0);
|
||||||
assert(mci.position);
|
assert(mci.position);
|
||||||
|
|
|
@ -548,7 +548,7 @@ Message.prototype.getQuoteLines = function(options, cb) {
|
||||||
quoteLines.push(`${lastSgr}${l}`);
|
quoteLines.push(`${lastSgr}${l}`);
|
||||||
|
|
||||||
focusQuoteLines.push(`${options.ansiFocusPrefixSgr}>${lastSgr}${renderSubstr(l, 1, l.length - 1)}`);
|
focusQuoteLines.push(`${options.ansiFocusPrefixSgr}>${lastSgr}${renderSubstr(l, 1, l.length - 1)}`);
|
||||||
lastSgr = (l.match(/(?:\x1b\x5b)[\?=;0-9]*m(?!.*(?:\x1b\x5b)[\?=;0-9]*m)/) || [])[0] || ''; // eslint-disable-line no-control-regex
|
lastSgr = (l.match(/(?:\x1b\x5b)[?=;0-9]*m(?!.*(?:\x1b\x5b)[?=;0-9]*m)/) || [])[0] || ''; // eslint-disable-line no-control-regex
|
||||||
});
|
});
|
||||||
|
|
||||||
quoteLines[quoteLines.length - 1] += options.ansiResetSgr;
|
quoteLines[quoteLines.length - 1] += options.ansiResetSgr;
|
||||||
|
@ -557,7 +557,7 @@ Message.prototype.getQuoteLines = function(options, cb) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const QUOTE_RE = /^ ((?:[A-Za-z0-9]{2}\> )+(?:[A-Za-z0-9]{2}\>)*) */;
|
const QUOTE_RE = /^ ((?:[A-Za-z0-9]{2}> )+(?:[A-Za-z0-9]{2}>)*) */;
|
||||||
const quoted = [];
|
const quoted = [];
|
||||||
const input = _.trimEnd(this.message).replace(/\b/g, '');
|
const input = _.trimEnd(this.message).replace(/\b/g, '');
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ function getAvailableMessageConferences(client, options) {
|
||||||
|
|
||||||
assert(client || true === options.noClient);
|
assert(client || true === options.noClient);
|
||||||
|
|
||||||
// perform ACS check per conf & omit system_internal if desired
|
// perform ACS check per conf & omit system_internal if desired
|
||||||
return _.omitBy(Config.messageConferences, (conf, confTag) => {
|
return _.omitBy(Config.messageConferences, (conf, confTag) => {
|
||||||
if(!options.includeSystemInternal && 'system_internal' === confTag) {
|
if(!options.includeSystemInternal && 'system_internal' === confTag) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -68,7 +68,7 @@ function getSortedAvailMessageConferences(client, options) {
|
||||||
function getAvailableMessageAreasByConfTag(confTag, options) {
|
function getAvailableMessageAreasByConfTag(confTag, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
// :TODO: confTag === "" then find default
|
// :TODO: confTag === "" then find default
|
||||||
|
|
||||||
if(_.has(Config.messageConferences, [ confTag, 'areas' ])) {
|
if(_.has(Config.messageConferences, [ confTag, 'areas' ])) {
|
||||||
const areas = Config.messageConferences[confTag].areas;
|
const areas = Config.messageConferences[confTag].areas;
|
||||||
|
@ -159,18 +159,6 @@ function getMessageConferenceByTag(confTag) {
|
||||||
return Config.messageConferences[confTag];
|
return Config.messageConferences[confTag];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMessageConfByAreaTag(areaTag) {
|
|
||||||
const confs = Config.messageConferences;
|
|
||||||
let conf;
|
|
||||||
_.forEach(confs, (v) => {
|
|
||||||
if(_.has(v, [ 'areas', areaTag ])) {
|
|
||||||
conf = v;
|
|
||||||
return false; // stop iteration
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMessageConfTagByAreaTag(areaTag) {
|
function getMessageConfTagByAreaTag(areaTag) {
|
||||||
const confs = Config.messageConferences;
|
const confs = Config.messageConferences;
|
||||||
return Object.keys(confs).find( (confTag) => {
|
return Object.keys(confs).find( (confTag) => {
|
||||||
|
@ -261,9 +249,9 @@ function changeMessageAreaWithOptions(client, areaTag, options, cb) {
|
||||||
return callback(area ? null : new Error('Invalid message areaTag'), area);
|
return callback(area ? null : new Error('Invalid message areaTag'), area);
|
||||||
},
|
},
|
||||||
function validateAccess(area, callback) {
|
function validateAccess(area, callback) {
|
||||||
//
|
//
|
||||||
// Need at least *read* to access the area
|
// Need at least *read* to access the area
|
||||||
//
|
//
|
||||||
if(!client.acs.hasMessageAreaRead(area)) {
|
if(!client.acs.hasMessageAreaRead(area)) {
|
||||||
return callback(new Error('Access denied to message area'));
|
return callback(new Error('Access denied to message area'));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -36,10 +36,10 @@ function resolvePath(path) {
|
||||||
|
|
||||||
function getCleanEnigmaVersion() {
|
function getCleanEnigmaVersion() {
|
||||||
return packageJson.version
|
return packageJson.version
|
||||||
.replace(/\-/g, '.')
|
.replace(/-/g, '.')
|
||||||
.replace(/alpha/,'a')
|
.replace(/alpha/,'a')
|
||||||
.replace(/beta/,'b')
|
.replace(/beta/,'b')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See also ftn_util.js getTearLine() & getProductIdentifier()
|
// See also ftn_util.js getTearLine() & getProductIdentifier()
|
||||||
|
|
|
@ -98,9 +98,9 @@ exports.getModule = class MessageAreaListModule extends MenuModule {
|
||||||
}, timeout);
|
}, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// :TODO: these concepts have been replaced with the {someKey} style formatting - update me!
|
||||||
|
/*
|
||||||
updateGeneralAreaInfoViews(areaIndex) {
|
updateGeneralAreaInfoViews(areaIndex) {
|
||||||
// :TODO: these concepts have been replaced with the {someKey} style formatting - update me!
|
|
||||||
/* experimental: not yet avail
|
|
||||||
const areaInfo = self.messageAreas[areaIndex];
|
const areaInfo = self.messageAreas[areaIndex];
|
||||||
|
|
||||||
[ MciViewIds.SelAreaInfo1, MciViewIds.SelAreaInfo2 ].forEach(mciId => {
|
[ MciViewIds.SelAreaInfo1, MciViewIds.SelAreaInfo2 ].forEach(mciId => {
|
||||||
|
@ -109,8 +109,8 @@ exports.getModule = class MessageAreaListModule extends MenuModule {
|
||||||
v.setFormatObject(areaInfo.area);
|
v.setFormatObject(areaInfo.area);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
mciReady(mciData, cb) {
|
mciReady(mciData, cb) {
|
||||||
super.mciReady(mciData, err => {
|
super.mciReady(mciData, err => {
|
||||||
|
|
|
@ -48,7 +48,7 @@ exports.getModule = class AreaPostFSEModule extends FullScreenEditorModule {
|
||||||
self.client.log.info(
|
self.client.log.info(
|
||||||
{ to : msg.toUserName, subject : msg.subject, uuid : msg.uuid },
|
{ to : msg.toUserName, subject : msg.subject, uuid : msg.uuid },
|
||||||
'Message persisted'
|
'Message persisted'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.nextMenu(cb);
|
return self.nextMenu(cb);
|
||||||
|
|
|
@ -13,12 +13,12 @@ function MessageScanTossModule() {
|
||||||
require('util').inherits(MessageScanTossModule, PluginModule);
|
require('util').inherits(MessageScanTossModule, PluginModule);
|
||||||
|
|
||||||
MessageScanTossModule.prototype.startup = function(cb) {
|
MessageScanTossModule.prototype.startup = function(cb) {
|
||||||
cb(null);
|
return cb(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
MessageScanTossModule.prototype.shutdown = function(cb) {
|
MessageScanTossModule.prototype.shutdown = function(cb) {
|
||||||
cb(null);
|
return cb(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
MessageScanTossModule.prototype.record = function(message) {
|
MessageScanTossModule.prototype.record = function(/*message*/) {
|
||||||
};
|
};
|
|
@ -4,7 +4,6 @@
|
||||||
const View = require('./view.js').View;
|
const View = require('./view.js').View;
|
||||||
const strUtil = require('./string_util.js');
|
const strUtil = require('./string_util.js');
|
||||||
const ansi = require('./ansi_term.js');
|
const ansi = require('./ansi_term.js');
|
||||||
const colorCodes = require('./color_codes.js');
|
|
||||||
const wordWrapText = require('./word_wrap.js').wordWrapText;
|
const wordWrapText = require('./word_wrap.js').wordWrapText;
|
||||||
const ansiPrep = require('./ansi_prep.js');
|
const ansiPrep = require('./ansi_prep.js');
|
||||||
|
|
||||||
|
@ -12,11 +11,11 @@ const assert = require('assert');
|
||||||
const _ = 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
|
||||||
// http://wiki.synchro.net/howto:editor:slyedit#edit_mode
|
// http://wiki.synchro.net/howto:editor:slyedit#edit_mode
|
||||||
// http://sublime-text-unofficial-documentation.readthedocs.org/en/latest/reference/keyboard_shortcuts_win.html
|
// http://sublime-text-unofficial-documentation.readthedocs.org/en/latest/reference/keyboard_shortcuts_win.html
|
||||||
|
|
||||||
/* Mystic
|
/* Mystic
|
||||||
[^B] Reformat Paragraph [^O] Show this help file
|
[^B] Reformat Paragraph [^O] Show this help file
|
||||||
[^I] Insert tab space [^Q] Enter quote mode
|
[^I] Insert tab space [^Q] Enter quote mode
|
||||||
[^K] Cut current line of text [^V] Toggle insert/overwrite
|
[^K] Cut current line of text [^V] Toggle insert/overwrite
|
||||||
|
@ -268,7 +267,7 @@ function MultiLineEditTextView(options) {
|
||||||
|
|
||||||
if(remain > 0) {
|
if(remain > 0) {
|
||||||
text += ' '.repeat(remain + 1);
|
text += ' '.repeat(remain + 1);
|
||||||
// text += new Array(remain + 1).join(' ');
|
// text += new Array(remain + 1).join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
|
@ -459,7 +458,7 @@ function MultiLineEditTextView(options) {
|
||||||
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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ exports.getModule = class NewScanModule extends MenuModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
newScanMessageConference(cb) {
|
newScanMessageConference(cb) {
|
||||||
// lazy init
|
// lazy init
|
||||||
if(!this.sortedMessageConfs) {
|
if(!this.sortedMessageConfs) {
|
||||||
const getAvailOpts = { includeSystemInternal : true }; // find new private messages, bulletins, etc.
|
const getAvailOpts = { includeSystemInternal : true }; // find new private messages, bulletins, etc.
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ exports.getModule = class NewScanModule extends MenuModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
newScanMessageArea(conf, cb) {
|
newScanMessageArea(conf, cb) {
|
||||||
// :TODO: it would be nice to cache this - must be done by conf!
|
// :TODO: it would be nice to cache this - must be done by conf!
|
||||||
const sortedAreas = msgArea.getSortedAvailMessageAreasByConfTag(conf.confTag, { client : this.client } );
|
const sortedAreas = msgArea.getSortedAvailMessageAreasByConfTag(conf.confTag, { client : this.client } );
|
||||||
const currentArea = sortedAreas[this.currentScanAux.area];
|
const currentArea = sortedAreas[this.currentScanAux.area];
|
||||||
|
|
||||||
|
|
|
@ -284,10 +284,10 @@ exports.getModule = class OnelinerzModule extends MenuModule {
|
||||||
oneliner VARCHAR NOT NULL,
|
oneliner VARCHAR NOT NULL,
|
||||||
timestamp DATETIME NOT NULL
|
timestamp DATETIME NOT NULL
|
||||||
);`
|
);`
|
||||||
,
|
,
|
||||||
err => {
|
err => {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
err => {
|
err => {
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
|
|
||||||
exports.PluginModule = PluginModule;
|
exports.PluginModule = PluginModule;
|
||||||
|
|
||||||
function PluginModule(options) {
|
function PluginModule(/*options*/) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@ exports.readSAUCE = readSAUCE;
|
||||||
|
|
||||||
const SAUCE_SIZE = 128;
|
const SAUCE_SIZE = 128;
|
||||||
const SAUCE_ID = new Buffer([0x53, 0x41, 0x55, 0x43, 0x45]); // 'SAUCE'
|
const SAUCE_ID = new Buffer([0x53, 0x41, 0x55, 0x43, 0x45]); // 'SAUCE'
|
||||||
const COMNT_ID = new Buffer([0x43, 0x4f, 0x4d, 0x4e, 0x54]); // 'COMNT'
|
|
||||||
|
// :TODO read comments
|
||||||
|
//const COMNT_ID = new Buffer([0x43, 0x4f, 0x4d, 0x4e, 0x54]); // 'COMNT'
|
||||||
|
|
||||||
exports.SAUCE_SIZE = SAUCE_SIZE;
|
exports.SAUCE_SIZE = SAUCE_SIZE;
|
||||||
// :TODO: SAUCE should be a class
|
// :TODO: SAUCE should be a class
|
||||||
|
@ -137,7 +139,7 @@ var SAUCE_FONT_TO_ENCODING_HINT = {
|
||||||
};
|
};
|
||||||
|
|
||||||
['437', '720', '737', '775', '819', '850', '852', '855', '857', '858',
|
['437', '720', '737', '775', '819', '850', '852', '855', '857', '858',
|
||||||
'860', '861', '862', '863', '864', '865', '866', '869', '872'].forEach(function onPage(page) {
|
'860', '861', '862', '863', '864', '865', '866', '869', '872'].forEach(function onPage(page) {
|
||||||
var codec = 'cp' + page;
|
var codec = 'cp' + page;
|
||||||
SAUCE_FONT_TO_ENCODING_HINT['IBM EGA43 ' + page] = codec;
|
SAUCE_FONT_TO_ENCODING_HINT['IBM EGA43 ' + page] = codec;
|
||||||
SAUCE_FONT_TO_ENCODING_HINT['IBM EGA ' + page] = codec;
|
SAUCE_FONT_TO_ENCODING_HINT['IBM EGA ' + page] = codec;
|
||||||
|
|
|
@ -1213,7 +1213,30 @@ function FTNMessageScanTossModule() {
|
||||||
|
|
||||||
User.getUserIdAndNameByLookup(lookupName, (err, localToUserId, localUserName) => {
|
User.getUserIdAndNameByLookup(lookupName, (err, localToUserId, localUserName) => {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(Errors.DoesNotExist(`Could not get local user ID for "${message.toUserName}": ${err.message}`));
|
//
|
||||||
|
// Couldn't find a local username. If the toUserName itself is a FTN address
|
||||||
|
// we can only assume the message is to the +op, else we'll have to fail.
|
||||||
|
//
|
||||||
|
const toUserNameAsAddress = Address.fromString(message.toUserName);
|
||||||
|
if(toUserNameAsAddress.isValid()) {
|
||||||
|
|
||||||
|
Log.info(
|
||||||
|
{ toUserName : message.toUserName, fromUserName : message.fromUserName },
|
||||||
|
'No local "to" username for FTN message. Appears to be a FTN address only; assuming addressed to SysOp'
|
||||||
|
);
|
||||||
|
|
||||||
|
User.getUserName(User.RootUserID, (err, sysOpUserName) => {
|
||||||
|
if(err) {
|
||||||
|
return callback(Errors.UnexpectedState('Failed to get SysOp user information'));
|
||||||
|
}
|
||||||
|
|
||||||
|
message.meta.System[Message.SystemMetaNames.LocalToUserID] = User.RootUserID;
|
||||||
|
message.toUserName = sysOpUserName;
|
||||||
|
return callback(null);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return callback(Errors.DoesNotExist(`Could not get local user ID for "${message.toUserName}": ${err.message}`));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we do this after such that error cases can be preseved above
|
// we do this after such that error cases can be preseved above
|
||||||
|
|
|
@ -232,7 +232,7 @@ exports.getModule = class SetNewScanDate extends MenuModule {
|
||||||
const scanDateView = vc.getView(MciViewIds.main.scanDate);
|
const scanDateView = vc.getView(MciViewIds.main.scanDate);
|
||||||
|
|
||||||
// :TODO: MaskTextEditView needs some love: If setText() with input that matches the mask, we should ignore the non-mask chars! Hack in place for now
|
// :TODO: MaskTextEditView needs some love: If setText() with input that matches the mask, we should ignore the non-mask chars! Hack in place for now
|
||||||
const scanDateFormat = self.scanDateFormat.replace(/[\/\-. ]/g, '');
|
const scanDateFormat = self.scanDateFormat.replace(/[/\-. ]/g, '');
|
||||||
scanDateView.setText(today.format(scanDateFormat));
|
scanDateView.setText(today.format(scanDateFormat));
|
||||||
|
|
||||||
if('message' === self.target) {
|
if('message' === self.target) {
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var MenuView = require('./menu_view.js').MenuView;
|
const MenuView = require('./menu_view.js').MenuView;
|
||||||
var ansi = require('./ansi_term.js');
|
const ansi = require('./ansi_term.js');
|
||||||
var strUtil = require('./string_util.js');
|
const strUtil = require('./string_util.js');
|
||||||
|
|
||||||
var util = require('util');
|
const util = require('util');
|
||||||
var assert = require('assert');
|
const assert = require('assert');
|
||||||
var _ = require('lodash');
|
|
||||||
|
|
||||||
exports.SpinnerMenuView = SpinnerMenuView;
|
exports.SpinnerMenuView = SpinnerMenuView;
|
||||||
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/* jslint node: true */
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var userDb = require('./database.js').dbs.user;
|
|
||||||
|
|
||||||
exports.getSystemLoginHistory = getSystemLoginHistory;
|
|
||||||
|
|
||||||
function getSystemLoginHistory(numRequested, cb) {
|
|
||||||
|
|
||||||
numRequested = Math.max(1, numRequested);
|
|
||||||
|
|
||||||
var loginHistory = [];
|
|
||||||
|
|
||||||
userDb.each(
|
|
||||||
'SELECT user_id, user_name, timestamp ' +
|
|
||||||
'FROM user_login_history ' +
|
|
||||||
'ORDER BY timestamp DESC ' +
|
|
||||||
'LIMIT ' + numRequested + ';',
|
|
||||||
function historyRow(err, histEntry) {
|
|
||||||
loginHistory.push( {
|
|
||||||
userId : histEntry.user_id,
|
|
||||||
userName : histEntry.user_name,
|
|
||||||
timestamp : histEntry.timestamp,
|
|
||||||
} );
|
|
||||||
},
|
|
||||||
function complete(err, recCount) {
|
|
||||||
cb(err, loginHistory);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/* jslint node: true */
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var View = require('./view.js').View;
|
|
||||||
var TextView = require('./text_view.js').TextView;
|
|
||||||
|
|
||||||
var assert = require('assert');
|
|
||||||
var _ = require('lodash');
|
|
||||||
|
|
||||||
function StatusBarView(options) {
|
|
||||||
View.call(this, options);
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
require('util').inherits(StatusBarView, View);
|
|
||||||
|
|
||||||
StatusBarView.prototype.redraw = function() {
|
|
||||||
|
|
||||||
StatusBarView.super_.prototype.redraw.call(this);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
StatusBarView.prototype.setPanels = function(panels) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
"panels" : [
|
|
||||||
{
|
|
||||||
"text" : "things and stuff",
|
|
||||||
"width" 20,
|
|
||||||
...
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"width" : 40 // no text, etc... = spacer
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
|---------------------------------------------|
|
|
||||||
| stuff |
|
|
||||||
*/
|
|
||||||
assert(_.isArray(panels));
|
|
||||||
|
|
||||||
this.panels = [];
|
|
||||||
|
|
||||||
var tvOpts = {
|
|
||||||
cursor : 'hide',
|
|
||||||
position : { row : this.position.row, col : 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
panels.forEach(function panel(p) {
|
|
||||||
assert(_.isObject(p));
|
|
||||||
assert(_.has(p, 'width'));
|
|
||||||
|
|
||||||
if(p.text) {
|
|
||||||
this.panels.push( new TextView( { }))
|
|
||||||
} else {
|
|
||||||
this.panels.push( { width : p.width } );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
|
@ -293,7 +293,7 @@ function transformValue(transformerName, value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// :TODO: Use explicit set of chars for paths & function/transforms such that } is allowed as fill/etc.
|
// :TODO: Use explicit set of chars for paths & function/transforms such that } is allowed as fill/etc.
|
||||||
const REGEXP_BASIC_FORMAT = /{([^.!:}]+(?:\.[^.!:}]+)*)(?:\!([^:}]+))?(?:\:([^}]+))?}/g;
|
const REGEXP_BASIC_FORMAT = /{([^.!:}]+(?:\.[^.!:}]+)*)(?:!([^:}]+))?(?::([^}]+))?}/g;
|
||||||
|
|
||||||
function getValue(obj, path) {
|
function getValue(obj, path) {
|
||||||
const value = _.get(obj, path);
|
const value = _.get(obj, path);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
// ENiGMA½
|
// ENiGMA½
|
||||||
const miscUtil = require('./misc_util.js');
|
const miscUtil = require('./misc_util.js');
|
||||||
const ANSIEscapeParser = require('./ansi_escape_parser.js').ANSIEscapeParser;
|
|
||||||
const ANSI = require('./ansi_term.js');
|
const ANSI = require('./ansi_term.js');
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
|
@ -198,11 +197,6 @@ function isPrintable(s) {
|
||||||
return !RE_NON_PRINTABLE.test(s);
|
return !RE_NON_PRINTABLE.test(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringLength(s) {
|
|
||||||
// :TODO: See https://mathiasbynens.be/notes/javascript-unicode
|
|
||||||
return s.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function stripAllLineFeeds(s) {
|
function stripAllLineFeeds(s) {
|
||||||
return s.replace(/\r?\n|[\r\u2028\u2029]/g, '');
|
return s.replace(/\r?\n|[\r\u2028\u2029]/g, '');
|
||||||
}
|
}
|
||||||
|
@ -359,7 +353,7 @@ function formatCount(count, withAbbr = false, decimals = 2) {
|
||||||
|
|
||||||
// :TODO: See notes in word_wrap.js about need to consolidate the various ANSI related RegExp's
|
// :TODO: See notes in word_wrap.js about need to consolidate the various ANSI related RegExp's
|
||||||
//const REGEXP_ANSI_CONTROL_CODES = /(\x1b\x5b)([\?=;0-9]*?)([0-9A-ORZcf-npsu=><])/g;
|
//const REGEXP_ANSI_CONTROL_CODES = /(\x1b\x5b)([\?=;0-9]*?)([0-9A-ORZcf-npsu=><])/g;
|
||||||
const REGEXP_ANSI_CONTROL_CODES = /(?:\x1b\x5b)([\?=;0-9]*?)([A-ORZcf-npsu=><])/g; // eslint-disable-line no-control-regex
|
const REGEXP_ANSI_CONTROL_CODES = /(?:\x1b\x5b)([?=;0-9]*?)([A-ORZcf-npsu=><])/g; // eslint-disable-line no-control-regex
|
||||||
const ANSI_OPCODES_ALLOWED_CLEAN = [
|
const ANSI_OPCODES_ALLOWED_CLEAN = [
|
||||||
//'A', 'B', // up, down
|
//'A', 'B', // up, down
|
||||||
//'C', 'D', // right, left
|
//'C', 'D', // right, left
|
||||||
|
@ -405,194 +399,6 @@ function cleanControlCodes(input, options) {
|
||||||
return cleaned;
|
return cleaned;
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepAnsi(input, options, cb) {
|
|
||||||
if(!input) {
|
|
||||||
return cb(null, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
options.termWidth = options.termWidth || 80;
|
|
||||||
options.termHeight = options.termHeight || 25;
|
|
||||||
options.cols = options.cols || options.termWidth || 80;
|
|
||||||
options.rows = options.rows || options.termHeight || 'auto';
|
|
||||||
options.startCol = options.startCol || 1;
|
|
||||||
options.exportMode = options.exportMode || false;
|
|
||||||
|
|
||||||
const canvas = Array.from( { length : 'auto' === options.rows ? 25 : options.rows }, () => Array.from( { length : options.cols}, () => new Object() ) );
|
|
||||||
const parser = new ANSIEscapeParser( { termHeight : options.termHeight, termWidth : options.termWidth } );
|
|
||||||
|
|
||||||
const state = {
|
|
||||||
row : 0,
|
|
||||||
col : 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let lastRow = 0;
|
|
||||||
|
|
||||||
function ensureRow(row) {
|
|
||||||
if(Array.isArray(canvas[row])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas[row] = Array.from( { length : options.cols}, () => new Object() );
|
|
||||||
}
|
|
||||||
|
|
||||||
parser.on('position update', (row, col) => {
|
|
||||||
state.row = row - 1;
|
|
||||||
state.col = col - 1;
|
|
||||||
|
|
||||||
lastRow = Math.max(state.row, lastRow);
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.on('literal', literal => {
|
|
||||||
//
|
|
||||||
// CR/LF are handled for 'position update'; we don't need the chars themselves
|
|
||||||
//
|
|
||||||
literal = literal.replace(/\r?\n|[\r\u2028\u2029]/g, '');
|
|
||||||
|
|
||||||
for(let c of literal) {
|
|
||||||
if(state.col < options.cols && ('auto' === options.rows || state.row < options.rows)) {
|
|
||||||
ensureRow(state.row);
|
|
||||||
|
|
||||||
canvas[state.row][state.col].char = c;
|
|
||||||
|
|
||||||
if(state.sgr) {
|
|
||||||
canvas[state.row][state.col].sgr = state.sgr;
|
|
||||||
state.sgr = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.col += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.on('control', (match, opCode) => {
|
|
||||||
//
|
|
||||||
// Movement is handled via 'position update', so we really only care about
|
|
||||||
// display opCodes
|
|
||||||
//
|
|
||||||
switch(opCode) {
|
|
||||||
case 'm' :
|
|
||||||
state.sgr = (state.sgr || '') + match;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default :
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function getLastPopulatedColumn(row) {
|
|
||||||
let col = row.length;
|
|
||||||
while(--col > 0) {
|
|
||||||
if(row[col].char || row[col].sgr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return col;
|
|
||||||
}
|
|
||||||
|
|
||||||
parser.on('complete', () => {
|
|
||||||
let output = '';
|
|
||||||
let lastSgr = '';
|
|
||||||
let line;
|
|
||||||
|
|
||||||
canvas.slice(0, lastRow + 1).forEach(row => {
|
|
||||||
const lastCol = getLastPopulatedColumn(row) + 1;
|
|
||||||
|
|
||||||
let i;
|
|
||||||
line = '';
|
|
||||||
for(i = 0; i < lastCol; ++i) {
|
|
||||||
const col = row[i];
|
|
||||||
if(col.sgr) {
|
|
||||||
lastSgr = col.sgr;
|
|
||||||
}
|
|
||||||
line += `${col.sgr || ''}${col.char || ' '}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
output += line;
|
|
||||||
|
|
||||||
if(i < row.length) {
|
|
||||||
output += `${ANSI.blackBG()}${row.slice(i).map( () => ' ').join('')}${lastSgr}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if(options.startCol + options.cols < options.termWidth || options.forceLineTerm) {
|
|
||||||
if(options.startCol + i < options.termWidth || options.forceLineTerm) {
|
|
||||||
output += '\r\n';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(options.exportMode) {
|
|
||||||
//
|
|
||||||
// If we're in export mode, we do some additional hackery:
|
|
||||||
//
|
|
||||||
// * Hard wrap ALL lines at <= 79 *characters* (not visible columns)
|
|
||||||
// if a line must wrap early, we'll place a ESC[A ESC[<N>C where <N>
|
|
||||||
// represents chars to get back to the position we were previously at
|
|
||||||
//
|
|
||||||
// * Replace contig spaces with ESC[<N>C as well to save... space.
|
|
||||||
//
|
|
||||||
// :TODO: this would be better to do as part of the processing above, but this will do for now
|
|
||||||
const MAX_CHARS = 79 - 8; // 79 max, - 8 for max ESC seq's we may prefix a line with
|
|
||||||
let exportOutput = '';
|
|
||||||
|
|
||||||
let m;
|
|
||||||
let afterSeq;
|
|
||||||
let wantMore;
|
|
||||||
let renderStart;
|
|
||||||
|
|
||||||
splitTextAtTerms(output).forEach(fullLine => {
|
|
||||||
renderStart = 0;
|
|
||||||
|
|
||||||
while(fullLine.length > 0) {
|
|
||||||
let splitAt;
|
|
||||||
const ANSI_REGEXP = ANSI.getFullMatchRegExp();
|
|
||||||
wantMore = true;
|
|
||||||
|
|
||||||
while((m = ANSI_REGEXP.exec(fullLine))) {
|
|
||||||
afterSeq = m.index + m[0].length;
|
|
||||||
|
|
||||||
if(afterSeq < MAX_CHARS) {
|
|
||||||
// after current seq
|
|
||||||
splitAt = afterSeq;
|
|
||||||
} else {
|
|
||||||
if(m.index < MAX_CHARS) {
|
|
||||||
// before last found seq
|
|
||||||
splitAt = m.index;
|
|
||||||
wantMore = false; // can't eat up any more
|
|
||||||
}
|
|
||||||
|
|
||||||
break; // seq's beyond this point are >= MAX_CHARS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(splitAt) {
|
|
||||||
if(wantMore) {
|
|
||||||
splitAt = Math.min(fullLine.length, MAX_CHARS - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
splitAt = Math.min(fullLine.length, MAX_CHARS - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const part = fullLine.slice(0, splitAt);
|
|
||||||
fullLine = fullLine.slice(splitAt);
|
|
||||||
renderStart += renderStringLength(part);
|
|
||||||
exportOutput += `${part}\r\n`;
|
|
||||||
|
|
||||||
if(fullLine.length > 0) { // more to go for this line?
|
|
||||||
exportOutput += `${ANSI.up()}${ANSI.right(renderStart)}`;
|
|
||||||
} else {
|
|
||||||
exportOutput += ANSI.up();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return cb(null, exportOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cb(null, output);
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAnsiLine(line) {
|
function isAnsiLine(line) {
|
||||||
return isAnsi(line);// || renderStringLength(line) < line.length;
|
return isAnsi(line);// || renderStringLength(line) < line.length;
|
||||||
}
|
}
|
||||||
|
@ -622,6 +428,7 @@ function isFormattedLine(line) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// :TODO: rename to containsAnsi()
|
||||||
function isAnsi(input) {
|
function isAnsi(input) {
|
||||||
if(!input || 0 === input.length) {
|
if(!input || 0 === input.length) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -647,7 +454,7 @@ function isAnsi(input) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// :TODO: if a similar method is kept, use exec() until threshold
|
// :TODO: if a similar method is kept, use exec() until threshold
|
||||||
const ANSI_DET_REGEXP = /(?:\x1b\x5b)[\?=;0-9]*?[ABCDEFGHJKLMSTfhlmnprsu]/g; // eslint-disable-line no-control-regex
|
const ANSI_DET_REGEXP = /(?:\x1b\x5b)[?=;0-9]*?[ABCDEFGHJKLMSTfhlmnprsu]/g; // eslint-disable-line no-control-regex
|
||||||
const m = input.match(ANSI_DET_REGEXP) || [];
|
const m = input.match(ANSI_DET_REGEXP) || [];
|
||||||
return m.length >= 4; // :TODO: do this reasonably, e.g. a percent or soemthing
|
return m.length >= 4; // :TODO: do this reasonably, e.g. a percent or soemthing
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ function validateEmailAvail(data, cb) {
|
||||||
//
|
//
|
||||||
// See http://stackoverflow.com/questions/7786058/find-the-regex-used-by-html5-forms-for-validation
|
// See http://stackoverflow.com/questions/7786058/find-the-regex-used-by-html5-forms-for-validation
|
||||||
//
|
//
|
||||||
const emailRegExp = /[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9-]+(.[a-z0-9-]+)*/;
|
const emailRegExp = /[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(.[a-z0-9-]+)*/;
|
||||||
if(!emailRegExp.test(data)) {
|
if(!emailRegExp.test(data)) {
|
||||||
return cb(new Error('Invalid email address'));
|
return cb(new Error('Invalid email address'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ function TextView(options) {
|
||||||
this.textMaskChar = options.textMaskChar;
|
this.textMaskChar = options.textMaskChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
this.drawText = function(s) {
|
this.drawText = function(s) {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -115,13 +115,13 @@ function getMergedTheme(menuConfig, promptConfig, theme) {
|
||||||
assert(_.isObject(menuConfig));
|
assert(_.isObject(menuConfig));
|
||||||
assert(_.isObject(theme));
|
assert(_.isObject(theme));
|
||||||
|
|
||||||
// :TODO: merge in defaults (customization.defaults{} )
|
// :TODO: merge in defaults (customization.defaults{} )
|
||||||
// :TODO: apply generic stuff, e.g. "VM" (vs "VM1")
|
// :TODO: apply generic stuff, e.g. "VM" (vs "VM1")
|
||||||
|
|
||||||
//
|
//
|
||||||
// Create a *clone* of menuConfig (menu.hjson) then bring in
|
// Create a *clone* of menuConfig (menu.hjson) then bring in
|
||||||
// promptConfig (prompt.hjson)
|
// promptConfig (prompt.hjson)
|
||||||
//
|
//
|
||||||
var mergedTheme = _.cloneDeep(menuConfig);
|
var mergedTheme = _.cloneDeep(menuConfig);
|
||||||
|
|
||||||
if(_.isObject(promptConfig.prompts)) {
|
if(_.isObject(promptConfig.prompts)) {
|
||||||
|
@ -163,28 +163,28 @@ function getMergedTheme(menuConfig, promptConfig, theme) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// menu.hjson can have a couple different structures:
|
// menu.hjson can have a couple different structures:
|
||||||
// 1) Explicit declaration of expected MCI code(s) under 'form:<id>' before a 'mci' block
|
// 1) Explicit declaration of expected MCI code(s) under 'form:<id>' before a 'mci' block
|
||||||
// (this allows multiple layout types defined by one menu for example)
|
// (this allows multiple layout types defined by one menu for example)
|
||||||
//
|
//
|
||||||
// 2) Non-explicit declaration: 'mci' directly under 'form:<id>'
|
// 2) Non-explicit declaration: 'mci' directly under 'form:<id>'
|
||||||
//
|
//
|
||||||
// theme.hjson has it's own mix:
|
// theme.hjson has it's own mix:
|
||||||
// 1) Explicit: Form ID before 'mci' (generally used where there are > 1 forms)
|
// 1) Explicit: Form ID before 'mci' (generally used where there are > 1 forms)
|
||||||
//
|
//
|
||||||
// 2) Non-explicit: 'mci' directly under an entry
|
// 2) Non-explicit: 'mci' directly under an entry
|
||||||
//
|
//
|
||||||
// Additionally, #1 or #2 may be under an explicit key of MCI code(s) to match up
|
// Additionally, #1 or #2 may be under an explicit key of MCI code(s) to match up
|
||||||
// with menu.hjson in #1.
|
// with menu.hjson in #1.
|
||||||
//
|
//
|
||||||
// * When theming an explicit menu.hjson entry (1), we will use a matching explicit
|
// * When theming an explicit menu.hjson entry (1), we will use a matching explicit
|
||||||
// entry with a matching MCI code(s) key in theme.hjson (e.g. menu="ETVM"/theme="ETVM"
|
// entry with a matching MCI code(s) key in theme.hjson (e.g. menu="ETVM"/theme="ETVM"
|
||||||
// and fall back to generic if a match is not found.
|
// and fall back to generic if a match is not found.
|
||||||
//
|
//
|
||||||
// * If theme.hjson provides form ID's, use them. Otherwise, we'll apply directly assuming
|
// * If theme.hjson provides form ID's, use them. Otherwise, we'll apply directly assuming
|
||||||
// there is a generic 'mci' block.
|
// there is a generic 'mci' block.
|
||||||
//
|
//
|
||||||
function applyToForm(form, menuTheme, formKey) {
|
function applyToForm(form, menuTheme, formKey) {
|
||||||
if(_.isObject(form.mci)) {
|
if(_.isObject(form.mci)) {
|
||||||
// non-explicit: no MCI code(s) key assumed since we found 'mci' directly under form ID
|
// non-explicit: no MCI code(s) key assumed since we found 'mci' directly under form ID
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
/* jslint node: true */
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var View = require('./view.js').View;
|
|
||||||
var miscUtil = require('./misc_util.js');
|
|
||||||
var strUtil = require('./string_util.js');
|
|
||||||
var ansi = require('./ansi_term.js');
|
|
||||||
var util = require('util');
|
|
||||||
var assert = require('assert');
|
|
||||||
|
|
||||||
exports.TickerTextView = TickerTextView;
|
|
||||||
|
|
||||||
function TickerTextView(options) {
|
|
||||||
View.call(this, options);
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.text = options.text || '';
|
|
||||||
this.tickerStyle = options.tickerStyle || 'rightToLeft';
|
|
||||||
assert(this.tickerStyle in TickerTextView.TickerStyles);
|
|
||||||
|
|
||||||
// :TODO: Ticker |text| should have ANSI stripped before calculating any lengths/etc.
|
|
||||||
// strUtil.ansiTextLength(s)
|
|
||||||
// strUtil.pad(..., ignoreAnsi)
|
|
||||||
// strUtil.stylizeString(..., ignoreAnsi)
|
|
||||||
|
|
||||||
this.tickerState = {};
|
|
||||||
switch(this.tickerStyle) {
|
|
||||||
case 'rightToLeft' :
|
|
||||||
this.tickerState.pos = this.position.row + this.dimens.width;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
self.onTickerInterval = function() {
|
|
||||||
switch(self.tickerStyle) {
|
|
||||||
case 'rightToLeft' : self.updateRightToLeftTicker(); break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.updateRightToLeftTicker = function() {
|
|
||||||
// if pos < start
|
|
||||||
// drawRemain()
|
|
||||||
// if pos + remain > end
|
|
||||||
// drawRemain(0, spaceFor)
|
|
||||||
// else
|
|
||||||
// drawString() + remainPading
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
util.inherits(TickerTextView, View);
|
|
||||||
|
|
||||||
TickerTextView.TickerStyles = {
|
|
||||||
leftToRight : 1,
|
|
||||||
rightToLeft : 2,
|
|
||||||
bounce : 3,
|
|
||||||
slamLeft : 4,
|
|
||||||
slamRight : 5,
|
|
||||||
slamBounce : 6,
|
|
||||||
decrypt : 7,
|
|
||||||
typewriter : 8,
|
|
||||||
};
|
|
||||||
Object.freeze(TickerTextView.TickerStyles);
|
|
||||||
|
|
||||||
/*
|
|
||||||
TickerTextView.TICKER_STYLES = [
|
|
||||||
'leftToRight',
|
|
||||||
'rightToLeft',
|
|
||||||
'bounce',
|
|
||||||
'slamLeft',
|
|
||||||
'slamRight',
|
|
||||||
'slamBounce',
|
|
||||||
'decrypt',
|
|
||||||
'typewriter',
|
|
||||||
];
|
|
||||||
*/
|
|
||||||
|
|
||||||
TickerTextView.prototype.controllerAttached = function() {
|
|
||||||
// :TODO: call super
|
|
||||||
};
|
|
||||||
|
|
||||||
TickerTextView.prototype.controllerDetached = function() {
|
|
||||||
// :TODO: call super
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
TickerTextView.prototype.setText = function(text) {
|
|
||||||
this.text = strUtil.stylizeString(text, this.textStyle);
|
|
||||||
|
|
||||||
if(!this.dimens || !this.dimens.width) {
|
|
||||||
this.dimens.width = Math.ceil(this.text.length / 2);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,13 +1,11 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var MenuView = require('./menu_view.js').MenuView;
|
const MenuView = require('./menu_view.js').MenuView;
|
||||||
var ansi = require('./ansi_term.js');
|
const strUtil = require('./string_util.js');
|
||||||
var strUtil = require('./string_util.js');
|
|
||||||
|
|
||||||
var util = require('util');
|
const util = require('util');
|
||||||
var assert = require('assert');
|
const assert = require('assert');
|
||||||
var _ = require('lodash');
|
|
||||||
|
|
||||||
exports.ToggleMenuView = ToggleMenuView;
|
exports.ToggleMenuView = ToggleMenuView;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var userDb = require('./database.js').dbs.user;
|
const userDb = require('./database.js').dbs.user;
|
||||||
var Config = require('./config.js').config;
|
|
||||||
|
|
||||||
var async = require('async');
|
const async = require('async');
|
||||||
var _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
exports.getGroupsForUser = getGroupsForUser;
|
exports.getGroupsForUser = getGroupsForUser;
|
||||||
exports.addUserToGroup = addUserToGroup;
|
exports.addUserToGroup = addUserToGroup;
|
||||||
|
@ -13,23 +12,22 @@ exports.addUserToGroups = addUserToGroups;
|
||||||
exports.removeUserFromGroup = removeUserFromGroup;
|
exports.removeUserFromGroup = removeUserFromGroup;
|
||||||
|
|
||||||
function getGroupsForUser(userId, cb) {
|
function getGroupsForUser(userId, cb) {
|
||||||
var sql =
|
const sql =
|
||||||
'SELECT group_name ' +
|
`SELECT group_name
|
||||||
'FROM user_group_member ' +
|
FROM user_group_member
|
||||||
'WHERE user_id=?;';
|
WHERE user_id=?;`;
|
||||||
|
|
||||||
var groups = [];
|
const groups = [];
|
||||||
|
|
||||||
userDb.each(sql, [ userId ], function rowData(err, row) {
|
userDb.each(sql, [ userId ], (err, row) => {
|
||||||
if(err) {
|
if(err) {
|
||||||
cb(err);
|
return cb(err);
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
groups.push(row.group_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groups.push(row.group_name);
|
||||||
},
|
},
|
||||||
function complete() {
|
() => {
|
||||||
cb(null, groups);
|
return cb(null, groups);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,31 +38,31 @@ function addUserToGroup(userId, groupName, transOrDb, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
transOrDb.run(
|
transOrDb.run(
|
||||||
'REPLACE INTO user_group_member (group_name, user_id) ' +
|
`REPLACE INTO user_group_member (group_name, user_id)
|
||||||
'VALUES(?, ?);',
|
VALUES(?, ?);`,
|
||||||
[ groupName, userId ],
|
[ groupName, userId ],
|
||||||
function complete(err) {
|
err => {
|
||||||
cb(err);
|
return cb(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addUserToGroups(userId, groups, transOrDb, cb) {
|
function addUserToGroups(userId, groups, transOrDb, cb) {
|
||||||
|
|
||||||
async.each(groups, function item(groupName, next) {
|
async.each(groups, (groupName, nextGroupName) => {
|
||||||
addUserToGroup(userId, groupName, transOrDb, next);
|
return addUserToGroup(userId, groupName, transOrDb, nextGroupName);
|
||||||
}, function complete(err) {
|
}, err => {
|
||||||
cb(err);
|
return cb(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeUserFromGroup(userId, groupName, cb) {
|
function removeUserFromGroup(userId, groupName, cb) {
|
||||||
userDb.run(
|
userDb.run(
|
||||||
'DELETE FROM user_group_member ' +
|
`DELETE FROM user_group_member
|
||||||
'WHERE group_name=? AND user_id=?;',
|
WHERE group_name=? AND user_id=?;`,
|
||||||
[ groupName, userId ],
|
[ groupName, userId ],
|
||||||
function complete(err) {
|
err => {
|
||||||
cb(err);
|
return cb(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,20 +166,6 @@ function ViewController(options) {
|
||||||
var propAsset;
|
var propAsset;
|
||||||
var propValue;
|
var propValue;
|
||||||
|
|
||||||
function callModuleMethod(path) {
|
|
||||||
if('' === paths.extname(path)) {
|
|
||||||
path += '.js';
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var methodMod = require(path);
|
|
||||||
// :TODO: fix formData & extraArgs
|
|
||||||
return methodMod[propAsset.asset](self.client.currentMenuModule, {}, {} );
|
|
||||||
} catch(e) {
|
|
||||||
self.client.log.error( { error : e.toString(), methodName : propAsset.asset }, 'Failed to execute asset method');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(var propName in conf) {
|
for(var propName in conf) {
|
||||||
propAsset = asset.getViewPropertyAsset(conf[propName]);
|
propAsset = asset.getViewPropertyAsset(conf[propName]);
|
||||||
if(propAsset) {
|
if(propAsset) {
|
||||||
|
@ -211,7 +197,7 @@ function ViewController(options) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(_.isString(propAsset.location)) {
|
if(_.isString(propAsset.location)) {
|
||||||
|
// :TODO: clean this code up!
|
||||||
} else {
|
} else {
|
||||||
if('systemMethod' === propAsset.type) {
|
if('systemMethod' === propAsset.type) {
|
||||||
// :TODO:
|
// :TODO:
|
||||||
|
@ -681,7 +667,7 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/*
|
/*
|
||||||
function applyThemeCustomization(callback) {
|
function applyThemeCustomization(callback) {
|
||||||
formConfig = formConfig || {};
|
formConfig = formConfig || {};
|
||||||
formConfig.mci = formConfig.mci || {};
|
formConfig.mci = formConfig.mci || {};
|
||||||
|
|
|
@ -13,7 +13,6 @@ const Log = require('./logger.js').log;
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const _ = require('lodash');
|
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const fs = require('graceful-fs');
|
const fs = require('graceful-fs');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
|
@ -111,7 +110,7 @@ class WebPasswordReset {
|
||||||
.replace(/%USERNAME%/g, user.username)
|
.replace(/%USERNAME%/g, user.username)
|
||||||
.replace(/%TOKEN%/g, user.properties.email_password_reset_token)
|
.replace(/%TOKEN%/g, user.properties.email_password_reset_token)
|
||||||
.replace(/%RESET_URL%/g, resetUrl)
|
.replace(/%RESET_URL%/g, resetUrl)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
textTemplate = replaceTokens(textTemplate);
|
textTemplate = replaceTokens(textTemplate);
|
||||||
|
@ -128,7 +127,11 @@ class WebPasswordReset {
|
||||||
};
|
};
|
||||||
|
|
||||||
sendMail(message, (err, info) => {
|
sendMail(message, (err, info) => {
|
||||||
// :TODO: Log me!
|
if(err) {
|
||||||
|
Log.warn( { error : err.message }, 'Failed sending password reset email' );
|
||||||
|
} else {
|
||||||
|
Log.debug( { info : info }, 'Successfully sent password reset email');
|
||||||
|
}
|
||||||
|
|
||||||
return callback(err);
|
return callback(err);
|
||||||
});
|
});
|
||||||
|
@ -162,7 +165,7 @@ class WebPasswordReset {
|
||||||
path : '^\\/reset_password\\?token\\=[a-f0-9]+$', // Config.contentServers.web.forgotPasswordPageTemplate
|
path : '^\\/reset_password\\?token\\=[a-f0-9]+$', // Config.contentServers.web.forgotPasswordPageTemplate
|
||||||
handler : WebPasswordReset.routeResetPasswordGet,
|
handler : WebPasswordReset.routeResetPasswordGet,
|
||||||
},
|
},
|
||||||
// POST handler for performing the actual reset
|
// POST handler for performing the actual reset
|
||||||
{
|
{
|
||||||
method : 'POST',
|
method : 'POST',
|
||||||
path : '^\\/reset_password$',
|
path : '^\\/reset_password$',
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var assert = require('assert');
|
|
||||||
var _ = require('lodash');
|
|
||||||
const renderStringLength = require('./string_util.js').renderStringLength;
|
const renderStringLength = require('./string_util.js').renderStringLength;
|
||||||
|
|
||||||
exports.wordWrapText = wordWrapText2;
|
// deps
|
||||||
|
const assert = require('assert');
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
exports.wordWrapText = wordWrapText;
|
||||||
|
|
||||||
const SPACE_CHARS = [
|
const SPACE_CHARS = [
|
||||||
' ', '\f', '\n', '\r', '\v',
|
' ', '\f', '\n', '\r', '\v',
|
||||||
|
@ -16,7 +18,7 @@ const SPACE_CHARS = [
|
||||||
|
|
||||||
const REGEXP_WORD_WRAP = new RegExp(`\t|[${SPACE_CHARS.join('')}]`, 'g');
|
const REGEXP_WORD_WRAP = new RegExp(`\t|[${SPACE_CHARS.join('')}]`, 'g');
|
||||||
|
|
||||||
function wordWrapText2(text, options) {
|
function wordWrapText(text, options) {
|
||||||
assert(_.isObject(options));
|
assert(_.isObject(options));
|
||||||
assert(_.isNumber(options.width));
|
assert(_.isNumber(options.width));
|
||||||
|
|
||||||
|
@ -99,119 +101,3 @@ function wordWrapText2(text, options) {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function wordWrapText(text, options) {
|
|
||||||
//
|
|
||||||
// options.*:
|
|
||||||
// width : word wrap width
|
|
||||||
// tabHandling : expand (default=expand)
|
|
||||||
// tabWidth : tab width if tabHandling is 'expand' (default=4)
|
|
||||||
// tabChar : character to use for tab expansion
|
|
||||||
//
|
|
||||||
assert(_.isObject(options), 'Missing options!');
|
|
||||||
assert(_.isNumber(options.width), 'Missing options.width!');
|
|
||||||
|
|
||||||
options.tabHandling = options.tabHandling || 'expand';
|
|
||||||
|
|
||||||
if(!_.isNumber(options.tabWidth)) {
|
|
||||||
options.tabWidth = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
options.tabChar = options.tabChar || ' ';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Notes
|
|
||||||
// * Sublime Text 3 for example considers spaces after a word
|
|
||||||
// part of said word. For example, "word " would be wraped
|
|
||||||
// in it's entirity.
|
|
||||||
//
|
|
||||||
// * Tabs in Sublime Text 3 are also treated as a word, so, e.g.
|
|
||||||
// "\t" may resolve to " " and must fit within the space.
|
|
||||||
//
|
|
||||||
// * If a word is ultimately too long to fit, break it up until it does.
|
|
||||||
//
|
|
||||||
// RegExp below is JavaScript '\s' minus the '\t'
|
|
||||||
//
|
|
||||||
var re = new RegExp(
|
|
||||||
'\t|[ \f\n\r\v\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006' +
|
|
||||||
'\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]', 'g');
|
|
||||||
var m;
|
|
||||||
var wordStart = 0;
|
|
||||||
var results = { wrapped : [ '' ] };
|
|
||||||
var i = 0;
|
|
||||||
var word;
|
|
||||||
var wordLen;
|
|
||||||
|
|
||||||
function expandTab(col) {
|
|
||||||
var remainWidth = options.tabWidth - (col % options.tabWidth);
|
|
||||||
return new Array(remainWidth).join(options.tabChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
// :TODO: support wrapping pipe code text (e.g. ignore color codes, expand MCI codes)
|
|
||||||
|
|
||||||
function addWord() {
|
|
||||||
word.match(new RegExp('.{0,' + options.width + '}', 'g')).forEach(function wrd(w) {
|
|
||||||
//wordLen = self.getStringLength(w);
|
|
||||||
|
|
||||||
if(results.wrapped[i].length + w.length > options.width) {
|
|
||||||
//if(results.wrapped[i].length + wordLen > width) {
|
|
||||||
if(0 === i) {
|
|
||||||
results.firstWrapRange = { start : wordStart, end : wordStart + w.length };
|
|
||||||
//results.firstWrapRange = { start : wordStart, end : wordStart + wordLen };
|
|
||||||
}
|
|
||||||
// :TODO: Must handle len of |w| itself > options.width & split how ever many times required (e.g. handle paste)
|
|
||||||
results.wrapped[++i] = w;
|
|
||||||
} else {
|
|
||||||
results.wrapped[i] += w;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
while((m = re.exec(text)) !== null) {
|
|
||||||
word = text.substring(wordStart, re.lastIndex - 1);
|
|
||||||
|
|
||||||
switch(m[0].charAt(0)) {
|
|
||||||
case ' ' :
|
|
||||||
word += m[0];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\t' :
|
|
||||||
//
|
|
||||||
// Expand tab given position
|
|
||||||
//
|
|
||||||
// Nice info here: http://c-for-dummies.com/blog/?p=424
|
|
||||||
//
|
|
||||||
if('expand' === options.tabHandling) {
|
|
||||||
word += expandTab(results.wrapped[i].length + word.length) + options.tabChar;
|
|
||||||
} else {
|
|
||||||
word += m[0];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
addWord();
|
|
||||||
wordStart = re.lastIndex + m[0].length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Remainder
|
|
||||||
//
|
|
||||||
word = text.substring(wordStart);
|
|
||||||
addWord();
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
//const input = 'Hello, |04World! This |08i|02s a test it is \x1b[20Conly a test of the emergency broadcast system. What you see is not a joke!';
|
|
||||||
//const input = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five enturies, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
|
|
||||||
|
|
||||||
/*
|
|
||||||
const iconv = require('iconv-lite');
|
|
||||||
const input = iconv.decode(require('graceful-fs').readFileSync('/home/nuskooler/Downloads/msg_out.txt'), 'cp437');
|
|
||||||
|
|
||||||
const opts = {
|
|
||||||
width : 80,
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(wordWrapText2(input, opts).wrapped, 'utf8')
|
|
||||||
*/
|
|
Loading…
Add table
Add a link
Reference in a new issue