* Start work on pausable ANSI display/etc.
This commit is contained in:
parent
3f92a7949d
commit
5a00d219f8
|
@ -13,6 +13,11 @@ exports.ANSIEscapeParser = ANSIEscapeParser;
|
||||||
var CR = 0x0d;
|
var CR = 0x0d;
|
||||||
var LF = 0x0a;
|
var LF = 0x0a;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Resources, Specs, etc.
|
||||||
|
//
|
||||||
|
// * https://github.com/M-griffin/EtherTerm/blob/master/ansiParser.cpp
|
||||||
|
|
||||||
function ANSIEscapeParser(options) {
|
function ANSIEscapeParser(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -23,6 +28,10 @@ function ANSIEscapeParser(options) {
|
||||||
this.scrollBack = 0;
|
this.scrollBack = 0;
|
||||||
this.graphicRendition = {};
|
this.graphicRendition = {};
|
||||||
|
|
||||||
|
this.parseState = {
|
||||||
|
re : /(?:\x1b\x5b)([\?=;0-9]*?)([ABCDHJKfhlmnpsu])/g,
|
||||||
|
};
|
||||||
|
|
||||||
options = miscUtil.valueWithDefault(options, {
|
options = miscUtil.valueWithDefault(options, {
|
||||||
mciReplaceChar : '',
|
mciReplaceChar : '',
|
||||||
termHeight : 25,
|
termHeight : 25,
|
||||||
|
@ -187,6 +196,60 @@ function ANSIEscapeParser(options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.reset = function(buffer) {
|
||||||
|
self.parseState = {
|
||||||
|
// ignore anything past EOF marker, if any
|
||||||
|
buffer : buffer.split(String.fromCharCode(0x1a), 1)[0],
|
||||||
|
re : /(?:\x1b\x5b)([\?=;0-9]*?)([ABCDHJKfhlmnpsu])/g,
|
||||||
|
stop : false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
self.stop = function() {
|
||||||
|
self.parseState.stop = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.parse = function() {
|
||||||
|
// :TODO: ensure this conforms to ANSI-BBS / CTerm / bansi.txt for movement/etc.
|
||||||
|
var pos;
|
||||||
|
var match;
|
||||||
|
var opCode;
|
||||||
|
var args;
|
||||||
|
var re = self.parseState.re;
|
||||||
|
var buffer = self.parseState.buffer;
|
||||||
|
|
||||||
|
self.parseState.stop = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(self.parseState.stop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = re.lastIndex;
|
||||||
|
match = re.exec(buffer);
|
||||||
|
|
||||||
|
if(null !== match) {
|
||||||
|
if(match.index > pos) {
|
||||||
|
parseMCI(buffer.slice(pos, match.index));
|
||||||
|
}
|
||||||
|
|
||||||
|
opCode = match[2];
|
||||||
|
args = getArgArray(match[1].split(';'));
|
||||||
|
|
||||||
|
escape(opCode, args);
|
||||||
|
|
||||||
|
self.emit('chunk', match[0]);
|
||||||
|
}
|
||||||
|
} while(0 !== re.lastIndex);
|
||||||
|
|
||||||
|
if(pos < buffer.length) {
|
||||||
|
parseMCI(buffer.slice(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
@ -216,6 +279,8 @@ function ANSIEscapeParser(options) {
|
||||||
self.emit('chunk', match[0]);
|
self.emit('chunk', match[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} while(0 !== re.lastIndex);
|
} while(0 !== re.lastIndex);
|
||||||
|
|
||||||
if(pos < buffer.length) {
|
if(pos < buffer.length) {
|
||||||
|
@ -224,6 +289,7 @@ function ANSIEscapeParser(options) {
|
||||||
|
|
||||||
self.emit('complete');
|
self.emit('complete');
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
function escape(opCode, args) {
|
function escape(opCode, args) {
|
||||||
var arg;
|
var arg;
|
||||||
|
|
36
core/art.js
36
core/art.js
|
@ -382,17 +382,17 @@ function defaultEofFromExtension(ext) {
|
||||||
|
|
||||||
// :TODO: display({ art : art, client : client, ...}, cb)
|
// :TODO: display({ art : art, client : client, ...}, cb)
|
||||||
function display(options, cb) {
|
function display(options, cb) {
|
||||||
assert(
|
assert(_.isObject(options));
|
||||||
'undefined' !== typeof options &&
|
assert(_.isObject(options.client));
|
||||||
'undefined' !== typeof options.client &&
|
assert(!_.isUndefined(options.art));
|
||||||
'undefined' !== typeof options.art,
|
|
||||||
'Missing required options');
|
|
||||||
|
|
||||||
if(0 === options.art.length) {
|
if(0 === options.art.length) {
|
||||||
cb(new Error('Empty art'));
|
cb(new Error('Empty art'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pause = none/off | end | termHeight | [ "key1", "key2", ... ]
|
||||||
|
|
||||||
var cancelKeys = miscUtil.valueWithDefault(options.cancelKeys, []);
|
var cancelKeys = miscUtil.valueWithDefault(options.cancelKeys, []);
|
||||||
var pauseKeys = miscUtil.valueWithDefault(options.pauseKeys, []);
|
var pauseKeys = miscUtil.valueWithDefault(options.pauseKeys, []);
|
||||||
var pauseAtTermHeight = miscUtil.valueWithDefault(options.pauseAtTermHeight, false);
|
var pauseAtTermHeight = miscUtil.valueWithDefault(options.pauseAtTermHeight, false);
|
||||||
|
@ -426,7 +426,7 @@ function display(options, cb) {
|
||||||
|
|
||||||
var generatedId = 100;
|
var generatedId = 100;
|
||||||
|
|
||||||
var onCPR = function(pos) {
|
var cprListener = function(pos) {
|
||||||
if(mciPosQueue.length > 0) {
|
if(mciPosQueue.length > 0) {
|
||||||
var forMapItem = mciPosQueue.shift();
|
var forMapItem = mciPosQueue.shift();
|
||||||
mciMap[forMapItem].position = pos;
|
mciMap[forMapItem].position = pos;
|
||||||
|
@ -438,7 +438,7 @@ function display(options, cb) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function completed() {
|
function completed() {
|
||||||
options.client.removeListener('cursor position report', onCPR);
|
options.client.removeListener('cursor position report', cprListener);
|
||||||
parser.removeAllListeners(); // :TODO: Necessary???
|
parser.removeAllListeners(); // :TODO: Necessary???
|
||||||
|
|
||||||
if(iceColors) {
|
if(iceColors) {
|
||||||
|
@ -448,7 +448,22 @@ function display(options, cb) {
|
||||||
cb(null, mciMap);
|
cb(null, mciMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
options.client.on('cursor position report', onCPR);
|
options.client.on('cursor position report', cprListener);
|
||||||
|
|
||||||
|
//options.pause = 'termHeight'; // :TODO: remove!!
|
||||||
|
var nextTermHeight = options.client.termHeight;
|
||||||
|
|
||||||
|
parser.on('row update', function rowUpdate(row) {
|
||||||
|
if(row >= nextTermHeight) {
|
||||||
|
if('termHeight' === options.pause) {
|
||||||
|
options.client.waitForKeyPress(function kp(k) {
|
||||||
|
parser.parse();
|
||||||
|
});
|
||||||
|
parser.stop();
|
||||||
|
}
|
||||||
|
nextTermHeight *= 2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
parser.on('mci', function mciEncountered(mciInfo) {
|
parser.on('mci', function mciEncountered(mciInfo) {
|
||||||
|
|
||||||
|
@ -463,6 +478,7 @@ function display(options, cb) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// :TODO: ensure generatedId's do not conflict with any |id|
|
// :TODO: ensure generatedId's do not conflict with any |id|
|
||||||
|
// :TODO: Bug here - should only generate & increment ID's for the initial entry, not the "focus" version
|
||||||
var id = !_.isNumber(mciInfo.id) ? generatedId++ : mciInfo.id;
|
var id = !_.isNumber(mciInfo.id) ? generatedId++ : mciInfo.id;
|
||||||
var mapKey = mciInfo.mci + id;
|
var mapKey = mciInfo.mci + id;
|
||||||
var mapEntry = mciMap[mapKey];
|
var mapEntry = mciMap[mapKey];
|
||||||
|
@ -522,5 +538,7 @@ function display(options, cb) {
|
||||||
options.client.term.write(ansi.blinkToBrightIntensity());
|
options.client.term.write(ansi.blinkToBrightIntensity());
|
||||||
}
|
}
|
||||||
|
|
||||||
parser.parse(options.art);
|
parser.reset(options.art);
|
||||||
|
parser.parse();
|
||||||
|
//parser.parse(options.art);
|
||||||
}
|
}
|
|
@ -217,7 +217,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"demoMain" : {
|
"demoMain" : {
|
||||||
"art" : "demo_main",
|
"art" : "demo_selection_vm.ans",
|
||||||
"options" : { "cls" : true },
|
"options" : { "cls" : true },
|
||||||
"form" : {
|
"form" : {
|
||||||
"0" : {
|
"0" : {
|
||||||
|
@ -227,6 +227,7 @@
|
||||||
"items" : [
|
"items" : [
|
||||||
"Single Line Text Editing Views",
|
"Single Line Text Editing Views",
|
||||||
"Spinner & Toggle Views",
|
"Spinner & Toggle Views",
|
||||||
|
"Art Display",
|
||||||
"Other"
|
"Other"
|
||||||
],
|
],
|
||||||
// :TODO: justify not working??
|
// :TODO: justify not working??
|
||||||
|
@ -242,6 +243,10 @@
|
||||||
{
|
{
|
||||||
"value" : { "1" : 1 },
|
"value" : { "1" : 1 },
|
||||||
"action" : "@menu:demoSpinAndToggleView"
|
"action" : "@menu:demoSpinAndToggleView"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value" : { "1" : 2 },
|
||||||
|
"action" : "@menu:demoArtDisplay"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -327,7 +332,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"demoArtDisplay" : {
|
||||||
|
"art" : "demo_selection_vm.ans",
|
||||||
|
"options" : { "cls" : true },
|
||||||
|
"form" : {
|
||||||
|
"0" : {
|
||||||
|
"VM1" : {
|
||||||
|
"mci" : {
|
||||||
|
"VM1" : {
|
||||||
|
"items" : [
|
||||||
|
"Defaults - DOS ANSI",
|
||||||
|
"Defaults - Amiga",
|
||||||
|
"Pause at Term Height"
|
||||||
|
],
|
||||||
|
// :TODO: justify not working??
|
||||||
|
"focusTextStyle" : "small i"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"submit" : {
|
||||||
|
"*" : [
|
||||||
|
{
|
||||||
|
"value" : { "1" : 0 },
|
||||||
|
"action" : "@menu:demoDefaultsDosAnsi"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"demoDefaultsDosAnsi" : {
|
||||||
|
"art" : "bw_bc.ans",
|
||||||
|
"options" : { "cls" : true }
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
:TODO: conceptual simplified menus -- actions/etc. without forms
|
:TODO: conceptual simplified menus -- actions/etc. without forms
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue