* Default IDs, support for some basic TextView/labels via MCI
This commit is contained in:
parent
8119c1688a
commit
1dff6a6906
|
@ -140,11 +140,12 @@ function ANSIEscapeParser(options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseMCI(buffer) {
|
function parseMCI(buffer) {
|
||||||
var mciRe = /\%([A-Z]{2}[0-9]{1,2})(?:\(([0-9A-Z,]+)\))*/g;
|
var mciRe = /\%([A-Z]{2})([0-9]{1,2})?(?:\(([0-9A-Z,]+)\))*/g;
|
||||||
var pos = 0;
|
var pos = 0;
|
||||||
var match;
|
var match;
|
||||||
var mciCode;
|
var mciCode;
|
||||||
var args;
|
var args;
|
||||||
|
var id;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
pos = mciRe.lastIndex;
|
pos = mciRe.lastIndex;
|
||||||
|
@ -156,15 +157,16 @@ function ANSIEscapeParser(options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mciCode = match[1];
|
mciCode = match[1];
|
||||||
|
id = match[2] || null;
|
||||||
|
|
||||||
if(match[2]) {
|
if(match[3]) {
|
||||||
args = match[2].split(',');
|
args = match[3].split(',');
|
||||||
} else {
|
} else {
|
||||||
args = [];
|
args = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
self.emit('mci', mciCode, args);
|
self.emit('mci', mciCode, id, args);
|
||||||
|
|
||||||
self.emit('chunk', getProcessedMCI(match[0]));
|
self.emit('chunk', getProcessedMCI(match[0]));
|
||||||
}
|
}
|
||||||
|
|
228
core/art.js
228
core/art.js
|
@ -416,33 +416,37 @@ function display(art, options, cb) {
|
||||||
var emitter = null;
|
var emitter = null;
|
||||||
var parseComplete = false;
|
var parseComplete = false;
|
||||||
|
|
||||||
parser.on('mci', function onMCI(mciCode, args) {
|
var generatedId = 100;
|
||||||
if(mci[mciCode]) {
|
|
||||||
mci[mciCode].focusColor = {
|
parser.on('mci', function onMCI(mciCode, id, args) {
|
||||||
|
id = id || generatedId++;
|
||||||
|
var mapItem = mciCode + id;
|
||||||
|
if(mci[mapItem]) {
|
||||||
|
mci[mapItem].focusColor = {
|
||||||
fg : parser.fgColor,
|
fg : parser.fgColor,
|
||||||
bg : parser.bgColor,
|
bg : parser.bgColor,
|
||||||
flags : parser.flags,
|
flags : parser.flags,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
mci[mciCode] = {
|
mci[mapItem] = {
|
||||||
args : args,
|
args : args,
|
||||||
color : {
|
color : {
|
||||||
fg : parser.fgColor,
|
fg : parser.fgColor,
|
||||||
bg : parser.bgColor,
|
bg : parser.bgColor,
|
||||||
flags : parser.flags,
|
flags : parser.flags,
|
||||||
},
|
},
|
||||||
code : mciCode.substr(0, 2),
|
code : mciCode,
|
||||||
id : mciCode.substr(2, 1), // :TODO: This NEEDs to read 01-99
|
id : parseInt(id, 10),
|
||||||
};
|
};
|
||||||
|
|
||||||
mciPosQueue.push(mciCode);
|
mciPosQueue.push(mapItem);
|
||||||
|
|
||||||
// :TODO: Move this out of the loop
|
// :TODO: Move this out of the loop
|
||||||
if(!emitter) {
|
if(!emitter) {
|
||||||
emitter = options.client.on('onPosition', function onPosition(pos) {
|
emitter = options.client.on('onPosition', function onPosition(pos) {
|
||||||
if(mciPosQueue.length > 0) {
|
if(mciPosQueue.length > 0) {
|
||||||
var forMciCode = mciPosQueue.shift();
|
var forMapItem = mciPosQueue.shift();
|
||||||
mci[forMciCode].position = pos;
|
mci[forMapItem].position = pos;
|
||||||
|
|
||||||
if(parseComplete && 0 === mciPosQueue.length) {
|
if(parseComplete && 0 === mciPosQueue.length) {
|
||||||
cb(null, mci);
|
cb(null, mci);
|
||||||
|
@ -451,7 +455,7 @@ function display(art, options, cb) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
options.client.term.write(ansi.queryPos());
|
options.client.term.write(ansi.queryPos());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -468,208 +472,4 @@ function display(art, options, cb) {
|
||||||
});
|
});
|
||||||
|
|
||||||
parser.parse(art);
|
parser.parse(art);
|
||||||
}
|
|
||||||
|
|
||||||
ArtDisplayer.prototype.display = function(art, options) {
|
|
||||||
var client = this.client;
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var cancelKeys = miscUtil.valueWithDefault(options.cancelKeys, []);
|
|
||||||
var pauseKeys = miscUtil.valueWithDefault(options.pauseKeys, []);
|
|
||||||
var pauseAtTermHeight = miscUtil.valueWithDefault(options.pauseAtTermHeight, false);
|
|
||||||
|
|
||||||
var canceled = false;
|
|
||||||
if(cancelKeys.length > 0 || pauseKeys.length > 0) {
|
|
||||||
var onDataKeyCheck = function(data) {
|
|
||||||
var key = String.fromCharCode(data[0]);
|
|
||||||
if(-1 !== cancelKeys.indexOf(key)) {
|
|
||||||
canceled = true;
|
|
||||||
removeDataListener();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
client.on('data', onDataKeyCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDataListener() {
|
|
||||||
client.removeListener('data', onDataKeyCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Try to split lines supporting various linebreaks we may encounter:
|
|
||||||
// - DOS \r\n
|
|
||||||
// - *nix \n
|
|
||||||
// - Old Apple \r
|
|
||||||
// - Unicode PARAGRAPH SEPARATOR (U+2029) and LINE SEPARATOR (U+2028)
|
|
||||||
//
|
|
||||||
// See also http://stackoverflow.com/questions/5034781/js-regex-to-split-by-line
|
|
||||||
//
|
|
||||||
var lines = art.split(/\r?\n|\r|[\u2028\u2029]/);
|
|
||||||
var i = 0;
|
|
||||||
var count = lines.length;
|
|
||||||
if(0 === count) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var termHeight = client.term.termHeight;
|
|
||||||
|
|
||||||
var aep = require('./ansi_escape_parser.js');
|
|
||||||
var p = new aep.ANSIEscapeParser();
|
|
||||||
|
|
||||||
var currentRow = 0;
|
|
||||||
var lastRow = 0;
|
|
||||||
p.on('row update', function onRowUpdated(row) {
|
|
||||||
currentRow = row;
|
|
||||||
});
|
|
||||||
|
|
||||||
//--------
|
|
||||||
var mci = {};
|
|
||||||
var mciPosQueue = [];
|
|
||||||
var parseComplete = false;
|
|
||||||
|
|
||||||
var emitter = null;
|
|
||||||
|
|
||||||
p.on('mci', function onMCI(mciCode, args) {
|
|
||||||
if(mci[mciCode]) {
|
|
||||||
mci[mciCode].fgColorAlt = p.fgColor;
|
|
||||||
mci[mciCode].bgColorAlt = p.bgColor;
|
|
||||||
mci[mciCode].flagsAlt = p.flags;
|
|
||||||
} else {
|
|
||||||
mci[mciCode] = {
|
|
||||||
args : args,
|
|
||||||
fgColor : p.fgColor,
|
|
||||||
bgColor : p.bgColor,
|
|
||||||
flags : p.flags,
|
|
||||||
};
|
|
||||||
|
|
||||||
mciPosQueue.push(mciCode);
|
|
||||||
|
|
||||||
if(!emitter) {
|
|
||||||
emitter = client.on('onPosition', function onPosition(pos) {
|
|
||||||
if(mciPosQueue.length > 0) {
|
|
||||||
var mc = mciPosQueue.shift();
|
|
||||||
console.log('position @ ' + mc + ': ' + pos);
|
|
||||||
mci[mc].pos = pos;
|
|
||||||
|
|
||||||
if(parseComplete && 0 === mciPosQueue.length) {
|
|
||||||
//console.log(mci);
|
|
||||||
var p1 = mci['LV1'].pos;
|
|
||||||
client.term.write(ansi.sgr(['red']));
|
|
||||||
var g = ansi.goto(p1);
|
|
||||||
console.log(g);
|
|
||||||
client.term.write(ansi.goto(p1[0], p1[1]));
|
|
||||||
client.term.write('Hello, World');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
p.on('chunk', function onChunk(chunk) {
|
|
||||||
client.term.write(chunk);
|
|
||||||
});
|
|
||||||
|
|
||||||
p.on('complete', function onComplete() {
|
|
||||||
//console.log(mci);
|
|
||||||
parseComplete = true;
|
|
||||||
if(0 === mciPosQueue.length) {
|
|
||||||
console.log('mci from complete');
|
|
||||||
console.log(mci);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
p.parse(art);
|
|
||||||
|
|
||||||
|
|
||||||
//-----------
|
|
||||||
/*
|
|
||||||
var line;
|
|
||||||
(function nextLine() {
|
|
||||||
if(i === count) {
|
|
||||||
self.emit('complete');
|
|
||||||
removeDataListener();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(canceled) {
|
|
||||||
self.emit('canceled');
|
|
||||||
removeDataListener();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
line = lines[i];
|
|
||||||
client.term.write(line + '\n');
|
|
||||||
p.parse(line + '\r\n');
|
|
||||||
i++;
|
|
||||||
|
|
||||||
if(pauseAtTermHeight && currentRow !== lastRow && (0 === currentRow % termHeight)) {
|
|
||||||
lastRow = currentRow;
|
|
||||||
client.getch(function onKey(k) {
|
|
||||||
nextLine();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setTimeout(nextLine, 20);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
(function nextLine() {
|
|
||||||
if(i === count) {
|
|
||||||
client.emit('complete', true);
|
|
||||||
removeDataListener();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(canceled) {
|
|
||||||
console.log('canceled');
|
|
||||||
client.emit('canceled');
|
|
||||||
removeDataListener();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.term.write(lines[i] + '\n');
|
|
||||||
|
|
||||||
//
|
|
||||||
// :TODO: support pauseAtTermHeight:
|
|
||||||
//
|
|
||||||
// - All cursor movement should be recorded for pauseAtTermHeight support &
|
|
||||||
// handling > termWidth scenarios
|
|
||||||
// - MCI codes should be processed
|
|
||||||
// - All other ANSI/CSI ignored
|
|
||||||
// - Count normal chars
|
|
||||||
//
|
|
||||||
|
|
||||||
//setTimeout(nextLine, 20);
|
|
||||||
//i++;
|
|
||||||
|
|
||||||
if(pauseAtTermHeight && i > 0 && (0 === i % termHeight)) {
|
|
||||||
console.log('pausing @ ' + i);
|
|
||||||
client.getch(function onKey() {
|
|
||||||
i++;
|
|
||||||
nextLine();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
i++;
|
|
||||||
// :TODO: If local, use setTimeout(nextLine, 20) or so -- allow to pause/cancel
|
|
||||||
//process.nextTick(nextLine);
|
|
||||||
setTimeout(nextLine, 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// ANSI parser for quick scanning & handling
|
|
||||||
// of basic ANSI sequences that can be used for output to clients:
|
|
||||||
//
|
|
||||||
function ANSIOutputParser(ansi) {
|
|
||||||
//
|
|
||||||
// cb's
|
|
||||||
// - onMCI
|
|
||||||
// - onTermHeight
|
|
||||||
// -
|
|
||||||
}
|
}
|
|
@ -4,6 +4,8 @@
|
||||||
var TextView = require('./text_view.js').TextView;
|
var TextView = require('./text_view.js').TextView;
|
||||||
var EditTextView = require('./edit_text_view.js').EditTextView;
|
var EditTextView = require('./edit_text_view.js').EditTextView;
|
||||||
var ButtonView = require('./button_view.js').ButtonView;
|
var ButtonView = require('./button_view.js').ButtonView;
|
||||||
|
var Config = require('./config.js').config;
|
||||||
|
var packageJson = require('../package.json');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
|
||||||
exports.MCIViewFactory = MCIViewFactory;
|
exports.MCIViewFactory = MCIViewFactory;
|
||||||
|
@ -12,6 +14,16 @@ function MCIViewFactory(client) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MCIViewFactory.prototype.getPredefinedViewLabel = function(name) {
|
||||||
|
var label;
|
||||||
|
switch(name) {
|
||||||
|
case 'BN' : label = Config.bbsName; break;
|
||||||
|
case 'VL' : label = 'ENiGMA½ v' + packageJson.version; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return label;
|
||||||
|
};
|
||||||
|
|
||||||
MCIViewFactory.prototype.createFromMCI = function(mci) {
|
MCIViewFactory.prototype.createFromMCI = function(mci) {
|
||||||
assert(mci.code);
|
assert(mci.code);
|
||||||
assert(mci.id > 0);
|
assert(mci.id > 0);
|
||||||
|
@ -26,7 +38,24 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
|
||||||
};
|
};
|
||||||
|
|
||||||
switch(mci.code) {
|
switch(mci.code) {
|
||||||
case 'EV' :
|
case 'TL' :
|
||||||
|
if(mci.args.length > 0) {
|
||||||
|
options.textStyle = mci.args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mci.args.length > 1) {
|
||||||
|
options.justify = mci.args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mci.args.length > 2) {
|
||||||
|
options.maxLength = mci.args[2];
|
||||||
|
options.dimens = { width : options.maxLength };
|
||||||
|
}
|
||||||
|
|
||||||
|
view = new TextView(this.client, options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ET' :
|
||||||
if(mci.args.length > 0) {
|
if(mci.args.length > 0) {
|
||||||
options.maxLength = mci.args[0];
|
options.maxLength = mci.args[0];
|
||||||
options.dimens = { width : options.maxLength };
|
options.dimens = { width : options.maxLength };
|
||||||
|
@ -39,6 +68,28 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
|
||||||
view = new EditTextView(this.client, options);
|
view = new EditTextView(this.client, options);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'PL' :
|
||||||
|
if(mci.args.length > 0) {
|
||||||
|
options.text = this.getPredefinedViewLabel(mci.args[0]);
|
||||||
|
if(options.text) {
|
||||||
|
if(mci.args.length > 1) {
|
||||||
|
options.textStyle = mci.args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mci.args.length > 2) {
|
||||||
|
options.justify = mci.args[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mci.args.length > 3) {
|
||||||
|
options.maxLength = mci.args[3];
|
||||||
|
options.dimens = { width : options.maxLength };
|
||||||
|
}
|
||||||
|
|
||||||
|
view = new TextView(this.client, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'BV' :
|
case 'BV' :
|
||||||
if(mci.args.length > 0) {
|
if(mci.args.length > 0) {
|
||||||
options.text = mci.args[0];
|
options.text = mci.args[0];
|
||||||
|
|
|
@ -19,8 +19,9 @@ function TextView(client, options) {
|
||||||
this.maxLength = this.options.maxLength;
|
this.maxLength = this.options.maxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.textStyle = this.options.textStyle || 'normal';
|
this.textStyle = this.options.textStyle || 'normal';
|
||||||
this.multiLine = this.options.multiLine || false;
|
this.multiLine = this.options.multiLine || false;
|
||||||
|
this.fillChar = miscUtil.valueWithDefault(this.options.fillChar, ' ').substr(0, 1);
|
||||||
|
|
||||||
assert(!this.multiLine); // :TODO: not yet supported
|
assert(!this.multiLine); // :TODO: not yet supported
|
||||||
|
|
||||||
|
@ -43,21 +44,20 @@ TextView.prototype.redraw = function() {
|
||||||
TextView.super_.prototype.redraw.call(this);
|
TextView.super_.prototype.redraw.call(this);
|
||||||
|
|
||||||
var color = this.hasFocus ? this.getFocusColor() : this.getColor();
|
var color = this.hasFocus ? this.getFocusColor() : this.getColor();
|
||||||
|
|
||||||
//this.client.term.write(ansi.sgr(color.flags, color.fg, color.bg));
|
|
||||||
this.client.term.write(this.getANSIColor(color));
|
this.client.term.write(this.getANSIColor(color));
|
||||||
|
|
||||||
|
// :TODO: If using fillChar, don't just pad. switch to non-focus color & fill with |fillChar|
|
||||||
|
|
||||||
if(this.isPasswordTextStyle) {
|
if(this.isPasswordTextStyle) {
|
||||||
this.client.term.write(strUtil.pad(new Array(this.text.length + 1).join(this.textMaskChar), this.dimens.width));
|
this.client.term.write(strUtil.pad(new Array(this.text.length + 1).join(this.textMaskChar), this.dimens.width));
|
||||||
} else {
|
} else {
|
||||||
this.client.term.write(strUtil.pad(this.text, this.dimens.width));
|
this.client.term.write(strUtil.pad(this.text, this.dimens.width));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TextView.prototype.setFocus = function(focused) {
|
TextView.prototype.setFocus = function(focused) {
|
||||||
TextView.super_.prototype.setFocus.call(this, focused);
|
TextView.super_.prototype.setFocus.call(this, focused);
|
||||||
|
|
||||||
this.client.term.write(ansi.goto(this.position.x, this.position.y));
|
|
||||||
this.redraw();
|
this.redraw();
|
||||||
this.client.term.write(ansi.goto(this.position.x, this.position.y + this.text.length));
|
this.client.term.write(ansi.goto(this.position.x, this.position.y + this.text.length));
|
||||||
};
|
};
|
||||||
|
|
|
@ -116,10 +116,14 @@ ViewController.prototype.setViewOrder = function(order) {
|
||||||
|
|
||||||
if(0 === viewIdOrder.length) {
|
if(0 === viewIdOrder.length) {
|
||||||
for(var id in this.views) {
|
for(var id in this.views) {
|
||||||
viewIdOrder.push(id);
|
if(this.views[id].acceptsFocus) {
|
||||||
|
viewIdOrder.push(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewIdOrder.sort();
|
viewIdOrder.sort(function intSort(a, b) {
|
||||||
|
return a - b;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var view;
|
var view;
|
||||||
|
@ -130,8 +134,7 @@ ViewController.prototype.setViewOrder = function(order) {
|
||||||
|
|
||||||
this.firstId = viewIdOrder[0];
|
this.firstId = viewIdOrder[0];
|
||||||
var lastId = viewIdOrder[viewIdOrder.length - 1];
|
var lastId = viewIdOrder[viewIdOrder.length - 1];
|
||||||
this.views[lastId].nextId = this.firstId;
|
this.views[lastId].nextId = this.firstId;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ViewController.prototype.loadFromMCIMap = function(mciMap) {
|
ViewController.prototype.loadFromMCIMap = function(mciMap) {
|
||||||
|
@ -145,6 +148,7 @@ ViewController.prototype.loadFromMCIMap = function(mciMap) {
|
||||||
if(view) {
|
if(view) {
|
||||||
view.on('action', self.onViewAction);
|
view.on('action', self.onViewAction);
|
||||||
self.addView(view);
|
self.addView(view);
|
||||||
|
view.redraw();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,6 +39,8 @@ function entryPoint(client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(mci);
|
||||||
|
|
||||||
user.authenticate('NuSkooler', 'password', client, function onAuth(isValid) {
|
user.authenticate('NuSkooler', 'password', client, function onAuth(isValid) {
|
||||||
console.log(isValid);
|
console.log(isValid);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue