* Code cleanup. WIP theme stuff. Better CPR handling, etc.

This commit is contained in:
NuSkooler 2014-10-29 22:23:44 -06:00
parent 7cfe72d53b
commit 1ef9a4a1ce
11 changed files with 147 additions and 125 deletions

View File

@ -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);

View File

@ -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];

View File

@ -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();
}
});

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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));
};
};

View File

@ -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/'),

View File

@ -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') {

View File

@ -17,6 +17,7 @@ function User() {
this.id = 0;
this.userName = '';
this.properties = {};
this.isValid = function() {
if(self.id <= 0 || self.userName.length < 2) {

View File

@ -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() {

View File

@ -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) {