* Code cleanup. WIP theme stuff. Better CPR handling, etc.
This commit is contained in:
parent
7cfe72d53b
commit
1ef9a4a1ce
|
@ -116,7 +116,7 @@ function ANSIEscapeParser(options) {
|
|||
break;
|
||||
}
|
||||
|
||||
if(self.row === 26) {
|
||||
if(self.row === 26) { // :TODO: should be termHeight + 1 ?
|
||||
self.scrollBack++;
|
||||
self.row--;
|
||||
self.rowUpdated();
|
||||
|
@ -126,14 +126,10 @@ function ANSIEscapeParser(options) {
|
|||
self.emit('chunk', text);
|
||||
}
|
||||
|
||||
function mci(mciCode, args) {
|
||||
console.log(mciCode, args);
|
||||
}
|
||||
|
||||
function getProcessedMCI(mci) {
|
||||
if(self.mciReplaceChar.length > 0) {
|
||||
var eraseColor = ansi.sgr(self.lastFlags, self.lastFgColor, self.lastBgColor);
|
||||
return eraseColor + new Array(mci.length + 1).join(self.mciReplaceChar);
|
||||
return eraseColor + new Array(mci.length + 1).join(self.mciReplaceChar);
|
||||
} else {
|
||||
return mci;
|
||||
}
|
||||
|
@ -167,8 +163,20 @@ function ANSIEscapeParser(options) {
|
|||
|
||||
|
||||
self.emit('mci', mciCode, id, args);
|
||||
console.log(self.row + ', ' + self.column);
|
||||
console.log(match[0]);
|
||||
|
||||
self.emit('chunk', getProcessedMCI(match[0]));
|
||||
if(self.mciReplaceChar.length > 0) {
|
||||
escape('m', [self.lastFlags, self.lastFgColor, self.lastBgColor]);
|
||||
self.emit('chunk', ansi.sgr(self.lastFlags, self.lastFgColor, self.lastBgColor));
|
||||
literal(new Array(match[0].length + 1).join(self.mciReplaceChar));
|
||||
} else {
|
||||
literal(match[0]);
|
||||
}
|
||||
|
||||
//literal(getProcessedMCI(match[0]));
|
||||
|
||||
//self.emit('chunk', getProcessedMCI(match[0]));
|
||||
}
|
||||
|
||||
} while(0 !== mciRe.lastIndex);
|
||||
|
|
|
@ -22,7 +22,6 @@ exports.goHome = goHome;
|
|||
exports.disableVT100LineWrapping = disableVT100LineWrapping;
|
||||
exports.setSyncTermFont = setSyncTermFont;
|
||||
exports.fromPipeCode = fromPipeCode;
|
||||
exports.forEachControlCode = forEachControlCode;
|
||||
|
||||
|
||||
//
|
||||
|
@ -48,6 +47,7 @@ var CONTROL = {
|
|||
savePos : 's',
|
||||
restorePos : 'u',
|
||||
queryPos : '6n',
|
||||
queryScreenSize : '255n', // See bansi.txt
|
||||
goto : 'H', // row Pr, column Pc -- same as f
|
||||
gotoAlt : 'f', // same as H
|
||||
|
||||
|
@ -155,61 +155,6 @@ Object.keys(CONTROL).forEach(function onControlName(name) {
|
|||
};
|
||||
});
|
||||
|
||||
// Create a reverse map of CONTROL values to their key/names
|
||||
|
||||
/*
|
||||
var CONTROL_REVERSE_MAP = {};
|
||||
Object.keys(CONTROL).forEach(function onControlName(name) {
|
||||
var code = CONTROL[name];
|
||||
|
||||
CONTROL_REVERSE_MAP[code] = name;
|
||||
});
|
||||
*/
|
||||
|
||||
var CONTROL_RESPONSE = {
|
||||
'R' : 'position',
|
||||
};
|
||||
|
||||
// :TODO: move this to misc utils or such -- use here & parser
|
||||
function getIntArgArray(array) {
|
||||
var i = array.length;
|
||||
while(i--) {
|
||||
array[i] = parseInt(array[i], 10);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
// :TODO: rename this
|
||||
function forEachControlCode(data, cb) {
|
||||
//var re = /\u001b\[([0-9\;])*[R]/g;
|
||||
|
||||
var len = data.length;
|
||||
var pos = 0;
|
||||
|
||||
while(pos < len) {
|
||||
if(0x1b !== data[pos++] || 0x5b !== data[pos++]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var params = '';
|
||||
|
||||
while(pos < len) {
|
||||
var c = data[pos++];
|
||||
|
||||
if(((c > 64) && (c < 91)) || ((c > 96) && (c < 123))) {
|
||||
c = String.fromCharCode(c);
|
||||
var name = CONTROL_RESPONSE[c];
|
||||
if(name) {
|
||||
params = getIntArgArray(params.split(';'));
|
||||
cb(name, params);
|
||||
}
|
||||
}
|
||||
|
||||
params += String.fromCharCode(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create various color methods such as white(), yellowBG(), reset(), ...
|
||||
Object.keys(SGR).forEach(function onSgrName(name) {
|
||||
var code = SGR[name];
|
||||
|
|
55
core/art.js
55
core/art.js
|
@ -17,8 +17,6 @@ exports.getArt = getArt;
|
|||
exports.getArtFromPath = getArtFromPath;
|
||||
exports.display = display;
|
||||
exports.defaultEncodingFromExtension = defaultEncodingFromExtension;
|
||||
exports.ArtDisplayer = ArtDisplayer;
|
||||
|
||||
|
||||
var SAUCE_SIZE = 128;
|
||||
var SAUCE_ID = new Buffer([0x53, 0x41, 0x55, 0x43, 0x45]); // 'SAUCE'
|
||||
|
@ -366,21 +364,10 @@ function defaultEofFromExtension(ext) {
|
|||
return SUPPORTED_ART_TYPES[ext.toLowerCase()].eof;
|
||||
}
|
||||
|
||||
function ArtDisplayer(client) {
|
||||
if(!(this instanceof ArtDisplayer)) {
|
||||
return new ArtDisplayer(client);
|
||||
}
|
||||
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
util.inherits(ArtDisplayer, events.EventEmitter);
|
||||
|
||||
// :TODO: change to display(art, options, cb)
|
||||
// cb(err, mci)
|
||||
|
||||
// :TODO: display({ art : art, client : client, ...}, cb)
|
||||
function display(art, options, cb) {
|
||||
if(!art || 0 === art.length) {
|
||||
cb(new Error('Missing or empty art'));
|
||||
|
@ -413,11 +400,29 @@ function display(art, options, cb) {
|
|||
|
||||
var mci = {};
|
||||
var mciPosQueue = [];
|
||||
var emitter = null;
|
||||
var parseComplete = false;
|
||||
|
||||
var generatedId = 100;
|
||||
|
||||
var onCPR = function(pos) {
|
||||
if(mciPosQueue.length > 0) {
|
||||
var forMapItem = mciPosQueue.shift();
|
||||
mci[forMapItem].position = pos;
|
||||
|
||||
if(parseComplete && 0 === mciPosQueue.length) {
|
||||
completed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function completed() {
|
||||
options.client.removeListener('cursor position report', onCPR);
|
||||
parser.removeAllListeners(); // :TODO: Necessary???
|
||||
cb(null, mci);
|
||||
}
|
||||
|
||||
options.client.on('cursor position report', onCPR);
|
||||
|
||||
parser.on('mci', function onMCI(mciCode, id, args) {
|
||||
id = id || generatedId++;
|
||||
var mapItem = mciCode + id;
|
||||
|
@ -441,33 +446,19 @@ function display(art, options, cb) {
|
|||
|
||||
mciPosQueue.push(mapItem);
|
||||
|
||||
// :TODO: Move this out of the loop
|
||||
if(!emitter) {
|
||||
emitter = options.client.on('onPosition', function onPosition(pos) {
|
||||
if(mciPosQueue.length > 0) {
|
||||
var forMapItem = mciPosQueue.shift();
|
||||
mci[forMapItem].position = pos;
|
||||
|
||||
if(parseComplete && 0 === mciPosQueue.length) {
|
||||
cb(null, mci);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
options.client.term.write(ansi.queryPos());
|
||||
options.client.term.write(ansi.queryPos(), false); // :TODO: don't convert LF's
|
||||
}
|
||||
});
|
||||
|
||||
parser.on('chunk', function onChunk(chunk) {
|
||||
options.client.term.write(chunk);
|
||||
options.client.term.write(chunk, false);// :TODO: don't convert LF's
|
||||
});
|
||||
|
||||
parser.on('complete', function onComplete() {
|
||||
parseComplete = true;
|
||||
|
||||
if(0 === mciPosQueue.length) {
|
||||
cb(null, mci);
|
||||
completed();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
22
core/bbs.js
22
core/bbs.js
|
@ -119,7 +119,9 @@ function startListening() {
|
|||
|
||||
client.on('ready', function onClientReady() {
|
||||
// Go to module -- use default error handler
|
||||
modules.goto(conf.config.entryMod, client);
|
||||
prepareClient(client, function onPrepared() {
|
||||
modules.goto(conf.config.entryMod, client);
|
||||
});
|
||||
});
|
||||
|
||||
client.on('end', function onClientEnd() {
|
||||
|
@ -156,4 +158,22 @@ function removeClient(client) {
|
|||
clientConnections.splice(i, 1);
|
||||
logger.log.debug('Connection count is now %d', clientConnections.length);
|
||||
}
|
||||
}
|
||||
|
||||
function prepareClient(client, cb) {
|
||||
if('*' === conf.config.preLoginTheme) {
|
||||
var theme = require('./theme.js');
|
||||
theme.getRandomTheme(function onRandTheme(err, themeId) {
|
||||
if(err) {
|
||||
// :TODO: how to propertly set default/fallback?
|
||||
client.user.properties.art_theme_name = '';
|
||||
} else {
|
||||
client.user.properties.art_theme_name = themeId;
|
||||
}
|
||||
cb();
|
||||
});
|
||||
} else {
|
||||
client.user.properties.art_theme_name = conf.config.preLoginTheme;
|
||||
cb();
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ var assert = require('assert');
|
|||
var miscUtil = require('./misc_util.js');
|
||||
var ansi = require('./ansi_term.js');
|
||||
var logger = require('./logger.js');
|
||||
var user = require('./user.js');
|
||||
|
||||
exports.Client = Client;
|
||||
|
||||
|
@ -78,28 +79,13 @@ function Client(input, output) {
|
|||
this.input = input;
|
||||
this.output = output;
|
||||
this.term = new term.ClientTerminal(this.output);
|
||||
|
||||
self.on('data', function onData1(data) {
|
||||
//console.log(data);
|
||||
|
||||
onData(data);
|
||||
//handleANSIControlResponse(data);
|
||||
});
|
||||
|
||||
function handleANSIControlResponse(data) {
|
||||
//console.log(data);
|
||||
ansi.forEachControlCode(data, function onControlResponse(name, params) {
|
||||
var eventName = 'on' + name[0].toUpperCase() + name.substr(1);
|
||||
console.log(eventName + ': ' + params);
|
||||
self.emit(eventName, params);
|
||||
});
|
||||
}
|
||||
this.user = new user.User();
|
||||
|
||||
//
|
||||
// Peek at |data| and emit for any specialized handling
|
||||
// such as ANSI control codes or user/keyboard input
|
||||
//
|
||||
function onData(data) {
|
||||
self.on('data', function onData(data) {
|
||||
var len = data.length;
|
||||
var c;
|
||||
var name;
|
||||
|
@ -163,16 +149,14 @@ function Client(input, output) {
|
|||
case 'R' :
|
||||
args = getIntArgArray(match[1].split(';'));
|
||||
if(2 === args.length) {
|
||||
// :TODO: rename to 'cpr' or 'cursor position report'
|
||||
self.emit('onPosition', args);
|
||||
self.emit('cursor position report', args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(0 !== dsrResponseRe.lastIndex);
|
||||
// :TODO: Look for various DSR responses such as cursor position
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
require('util').inherits(Client, stream);
|
||||
|
|
|
@ -92,10 +92,10 @@ ClientTerminal.prototype.isANSI = function() {
|
|||
return 'ansi' === this.termType;
|
||||
};
|
||||
|
||||
ClientTerminal.prototype.write = function(s) {
|
||||
if(this.convertLF && typeof s === 'string') {
|
||||
ClientTerminal.prototype.write = function(s, convertLineFeeds) {
|
||||
convertLineFeeds = typeof convertLineFeeds === 'undefined' ? this.convertLF : convertLineFeeds;
|
||||
if(convertLineFeeds && typeof s === 'string') {
|
||||
s = s.replace(/\n/g, '\r\n');
|
||||
}
|
||||
|
||||
this.output.write(iconv.encode(s, this.outputEncoding));
|
||||
};
|
||||
};
|
|
@ -25,7 +25,10 @@ module.exports = {
|
|||
this.config = {
|
||||
bbsName : 'Another Fine ENiGMA½ BBS',
|
||||
|
||||
// :TODO: probably replace this with 'firstMenu' or somthing once that's available
|
||||
entryMod : 'connect',
|
||||
|
||||
preLoginTheme : '*',
|
||||
|
||||
paths : {
|
||||
mods : paths.join(__dirname, './../mods/'),
|
||||
|
|
|
@ -10,6 +10,7 @@ var async = require('async');
|
|||
|
||||
exports.getThemeInfo = getThemeInfo;
|
||||
exports.getThemeArt = getThemeArt;
|
||||
exports.getRandomTheme = getRandomTheme;
|
||||
|
||||
|
||||
// getThemeInfo(themeName)
|
||||
|
@ -29,7 +30,7 @@ function getThemeInfo(themeID, cb) {
|
|||
} else {
|
||||
try {
|
||||
var info = JSON.parse(data);
|
||||
return info;
|
||||
cb(null, info);
|
||||
} catch(e) {
|
||||
cb(err);
|
||||
}
|
||||
|
@ -37,6 +38,70 @@ function getThemeInfo(themeID, cb) {
|
|||
});
|
||||
}
|
||||
|
||||
var availableThemes;
|
||||
|
||||
function loadAvailableThemes(cb) {
|
||||
// lazy init
|
||||
async.waterfall(
|
||||
[
|
||||
function getDir(callback) {
|
||||
fs.readdir(Config.paths.art, function onReadDir(err, files) {
|
||||
callback(err, files);
|
||||
});
|
||||
},
|
||||
function filterFiles(files, callback) {
|
||||
var filtered = files.filter(function onFilter(file) {
|
||||
return fs.statSync(paths.join(Config.paths.art, file)).isDirectory();
|
||||
});
|
||||
callback(null, filtered);
|
||||
},
|
||||
function populateAvailable(filtered, callback) {
|
||||
filtered.forEach(function onTheme(themeId) {
|
||||
getThemeInfo(themeId, function onThemeInfo(err, info) {
|
||||
if(!err) {
|
||||
if(!availableThemes) {
|
||||
availableThemes = {};
|
||||
}
|
||||
availableThemes[themeId] = info;
|
||||
}
|
||||
callback(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
],
|
||||
function onComplete(err) {
|
||||
if(err) {
|
||||
cb(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!availableThemes) {
|
||||
cb(new Error('No themes found'));
|
||||
return;
|
||||
}
|
||||
|
||||
cb(null);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getRandomTheme(cb) {
|
||||
var themeIds;
|
||||
if(availableThemes) {
|
||||
themeIds = Object.keys(availableThemes);
|
||||
cb(null, themeIds[Math.floor(Math.random() * themeIds.length)]);
|
||||
} else {
|
||||
loadAvailableThemes(function onThemes(err) {
|
||||
if(err) {
|
||||
cb(err);
|
||||
} else {
|
||||
themeIds = Object.keys(availableThemes);
|
||||
cb(null, themeIds[Math.floor(Math.random() * themeIds.length)]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getThemeArt(name, themeID, options, cb) {
|
||||
// allow options to be optional
|
||||
if(typeof cb === 'undefined') {
|
||||
|
|
|
@ -17,6 +17,7 @@ function User() {
|
|||
|
||||
this.id = 0;
|
||||
this.userName = '';
|
||||
this.properties = {};
|
||||
|
||||
this.isValid = function() {
|
||||
if(self.id <= 0 || self.userName.length < 2) {
|
||||
|
|
|
@ -94,8 +94,13 @@ View.prototype.setPosition = function(pos) {
|
|||
assert(!(isNaN(this.position.x)));
|
||||
assert(!(isNaN(this.position.y)));
|
||||
|
||||
assert(this.position.x > 0 && this.position.x < this.client.term.termHeight);
|
||||
assert(this.position.y > 0 && this.position.y < this.client.term.termWidth);
|
||||
assert(
|
||||
this.position.x > 0 && this.position.x <= this.client.term.termHeight,
|
||||
'X position ' + this.position.x + ' out of terminal range ' + this.client.term.termHeight);
|
||||
|
||||
assert(
|
||||
this.position.y > 0 && this.position.y <= this.client.term.termWidth,
|
||||
'Y position ' + this.position.y + ' out of terminal range ' + this.client.term.termWidth);
|
||||
};
|
||||
|
||||
View.prototype.getColor = function() {
|
||||
|
|
|
@ -29,7 +29,7 @@ function entryPoint(client) {
|
|||
// :TODO: types, random, and others? could come from conf.mods.matrix or such
|
||||
|
||||
//art.getArt('SO-CC1.ANS'/* 'MATRIX'*/, { types: ['.ans'], random: true}, function onArt(err, theArt) {
|
||||
theme.getThemeArt('MATRIX_1', 'NU-MAYAN', function onArt(err, theArt) {
|
||||
theme.getThemeArt('MATRIX_1', client.user.properties.art_theme_name, function onArt(err, theArt) {
|
||||
|
||||
//art.getArt('MATRIX_1.ANS', {}, function onArt(err, theArt) {
|
||||
if(!err) {
|
||||
|
|
Loading…
Reference in New Issue