diff --git a/core/ansi_escape_parser.js b/core/ansi_escape_parser.js index 107a5ff1..d62eae22 100644 --- a/core/ansi_escape_parser.js +++ b/core/ansi_escape_parser.js @@ -21,7 +21,7 @@ function ANSIEscapeParser(options) { this.column = 1; this.row = 1; this.scrollBack = 0; - this.graphicRendition = { styles : [] }; + this.graphicRendition = {}; options = miscUtil.valueWithDefault(options, { mciReplaceChar : '', @@ -118,7 +118,7 @@ function ANSIEscapeParser(options) { function getProcessedMCI(mci) { if(self.mciReplaceChar.length > 0) { - return ansi.getSGRFromGraphicRendition(self.graphicRendition) + new Array(mci.length + 1).join(self.mciReplaceChar); + return ansi.getSGRFromGraphicRendition(self.graphicRendition, true) + new Array(mci.length + 1).join(self.mciReplaceChar); } else { return mci; } @@ -161,10 +161,14 @@ function ANSIEscapeParser(options) { } - self.emit('mci', mciCode, id, args); + self.emit('mci', { + mci : mciCode, + id : id ? parseInt(id, 10) : null, + args : args, + SGR : ansi.getSGRFromGraphicRendition(self.graphicRendition, true) + }); if(self.mciReplaceChar.length > 0) { - //self.emit('chunk', ansi.sgr(self.eraseColor.style, self.eraseColor.fgColor, self.eraseColor.bgColor)); self.emit('chunk', ansi.getSGRFromGraphicRendition(self.graphicRenditionForErase)); literal(new Array(match[0].length + 1).join(self.mciReplaceChar)); } else { @@ -270,10 +274,6 @@ function ANSIEscapeParser(options) { // set graphic rendition case 'm' : - - self.graphicRendition = { styles : [ 0 ] }; // reset - //self.graphicRendition.styles = [ 0 ]; - for(i = 0, len = args.length; i < len; ++i) { arg = args[i]; @@ -281,18 +281,54 @@ function ANSIEscapeParser(options) { self.graphicRendition.fg = arg; } else if(ANSIEscapeParser.backgroundColors[arg]) { self.graphicRendition.bg = arg; - } else if(39 === arg) { - delete self.graphicRendition.fg; - } else if(49 === arg) { - delete self.graphicRendition.bg; } else if(ANSIEscapeParser.styles[arg]) { - self.graphicRendition.styles.push(arg); - } else if(22 === arg) { - // :TODO: remove bold. + switch(arg) { + case 0 : + // clear out everything + delete self.graphicRendition.intensity; + delete self.graphicRendition.underline; + delete self.graphicRendition.blink; + delete self.graphicRendition.negative; + delete self.graphicRendition.invisible; + + self.graphicRendition.fg = 39; + self.graphicRendition.bg = 49; + break; + + case 1 : + case 2 : + case 22 : + self.graphicRendition.intensity = arg; + break; + + case 4 : + case 24 : + self.graphicRendition.underline = arg; + break; + + case 5 : + case 6 : + case 25 : + self.graphicRendition.blink = arg; + break; + + case 7 : + case 27 : + self.graphicRendition.negative = arg; + break; + + case 8 : + case 28 : + self.graphicRendition.invisible = arg; + break; + + default : + console.log('Unknown attribute: ' + arg); // :TODO: Log properly + break; + } } } - console.log(self.graphicRendition) break; // erase display/screen @@ -317,6 +353,8 @@ ANSIEscapeParser.foregroundColors = { 35 : 'magenta', 36 : 'cyan', 37 : 'white', + 39 : 'default', // same as white for most implementations + 90 : 'grey' }; Object.freeze(ANSIEscapeParser.foregroundColors); @@ -329,20 +367,39 @@ ANSIEscapeParser.backgroundColors = { 44 : 'blue', 45 : 'magenta', 46 : 'cyan', - 47 : 'white' + 47 : 'white', + 49 : 'default', // same as black for most implementations }; Object.freeze(ANSIEscapeParser.backgroundColors); -// :TODO: ensure these all align with that of ansi_term.js +// :TODO: ensure these names all align with that of ansi_term.js +// +// See the following specs: +// * http://www.ansi-bbs.org/ansi-bbs-core-server.html +// * http://www.vt100.net/docs/vt510-rm/SGR +// * https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt +// +// Note that these are intentionally not in order such that they +// can be grouped by concept here in code. +// ANSIEscapeParser.styles = { - 0 : 'default', - 1 : 'bright', - 2 : 'dim', - 5 : 'slowBlink', - 6 : 'fastBlink', - 7 : 'negative', - 8 : 'concealed', - 22 : 'normal', - 27 : 'positive', + 0 : 'default', // Everything disabled + + 1 : 'intensityBright', // aka bold + 2 : 'intensityDim', + 22 : 'intensityNormal', + + 4 : 'underlineOn', // Not supported by most BBS-like terminals + 24 : 'underlineOff', // Not supported by most BBS-like terminals + + 5 : 'blinkSlow', // blinkSlow & blinkFast are generally treated the same + 6 : 'blinkFast', // blinkSlow & blinkFast are generally treated the same + 25 : 'blinkOff', + + 7 : 'negativeImageOn', // Generally not supported or treated as "reverse FG & BG" + 27 : 'negativeImageOff', // Generally not supported or treated as "reverse FG & BG" + + 8 : 'invisibleOn', // FG set to BG + 28 : 'invisibleOff', // Not supported by most BBS-like terminals }; Object.freeze(ANSIEscapeParser.styles); \ No newline at end of file diff --git a/core/ansi_term.js b/core/ansi_term.js index fc7b00f1..52269e2e 100644 --- a/core/ansi_term.js +++ b/core/ansi_term.js @@ -326,16 +326,35 @@ function sgr() { // // Converts a Graphic Rendition object used elsewhere -// to a ANSI SGR sequence +// to a ANSI SGR sequence. // -function getSGRFromGraphicRendition(graphicRendition) { - var sgrSeq = graphicRendition.styles.slice(0); // start out with styles +function getSGRFromGraphicRendition(graphicRendition, initialReset) { + var sgrSeq = []; + + var styleCount = 0; + [ 'intensity', 'underline', 'blink', 'negative', 'invisible' ].forEach(function style(s) { + if(graphicRendition[s]) { + sgrSeq.push(graphicRendition[s]); + ++styleCount; + } + }); + + if(!styleCount) { + sgrSeq.push(0); + } + if(graphicRendition.fg) { sgrSeq.push(graphicRendition.fg); } + if(graphicRendition.bg) { sgrSeq.push(graphicRendition.bg); } + + if(initialReset) { + sgrSeq.unshift(0); + } + return sgr(sgrSeq); } diff --git a/core/art.js b/core/art.js index 1df6c06d..6f2766a5 100644 --- a/core/art.js +++ b/core/art.js @@ -420,7 +420,7 @@ function display(options, cb) { termWidth : options.client.term.termWidth, }); - var mci = {}; + var mciMap = {}; var mciPosQueue = []; var parseComplete = false; @@ -429,7 +429,7 @@ function display(options, cb) { var onCPR = function(pos) { if(mciPosQueue.length > 0) { var forMapItem = mciPosQueue.shift(); - mci[forMapItem].position = pos; + mciMap[forMapItem].position = pos; if(parseComplete && 0 === mciPosQueue.length) { completed(); @@ -445,50 +445,36 @@ function display(options, cb) { // options.client.term.write(ansi.blinkNormal()); } - cb(null, mci); + cb(null, mciMap); } options.client.on('cursor position report', onCPR); - parser.on('mci', function onMCI(mciCode, id, args) { + parser.on('mci', function mciEncountered(mciInfo) { + // :TODO: ensure generatedId's do not conflict with any |id| - id = id || generatedId++; - var mapItem = mciCode + id; - // :TODO: Avoid mutiple [] lookups here - if(mci[mapItem]) { - mci[mapItem].focusGraphicRendition = parser.graphicRendition; - - /* - mci[mapItem].focusColor = { - fg : parser.fgColor, - bg : parser.bgColor, - flags : parser.style, - }; - */ - mci[mapItem].focusArgs = args; + var id = _.isUndefined(mciInfo.id) ? generatedId++ : mciInfo.id; + var mapKey = mciInfo.mci + id; + var mapEntry = mciMap[mapKey]; + if(mapEntry) { + mapEntry.focusSGR = mciInfo.SGR; + mapEntry.focusArgs = mciInfo.args; } else { - mci[mapItem] = { - args : args, - /* - color : { - fg : parser.fgColor, - bg : parser.bgColor, - flags : parser.style, - }, - */ - graphicRendition : parser.graphicRendition, - code : mciCode, - id : parseInt(id, 10), + mciMap[mapKey] = { + args : mciInfo.args, + SGR : mciInfo.SGR, + code : mciInfo.mci, + id : id, }; - mciPosQueue.push(mapItem); + mciPosQueue.push(mapKey); options.client.term.write(ansi.queryPos(), false); // :TODO: don't convert LF's } }); parser.on('chunk', function onChunk(chunk) { - options.client.term.write(chunk, false);// :TODO: don't convert LF's + options.client.term.write(chunk, false); }); parser.on('complete', function onComplete() { diff --git a/core/mci_view_factory.js b/core/mci_view_factory.js index e34b58cc..d88e107d 100644 --- a/core/mci_view_factory.js +++ b/core/mci_view_factory.js @@ -56,15 +56,11 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { var view; var options = { - client : this.client, - id : mci.id, - color : mci.color, - focusColor : mci.focusColor, - - graphicRendition : mci.graphicRendition, - focusGraphicRendition : mci.focusGraphicRendition, - - position : { x : mci.position[0], y : mci.position[1] }, + client : this.client, + id : mci.id, + ansiSGR : mci.SGR, + ansiFocusSGR : mci.focusSGR, + position : { x : mci.position[0], y : mci.position[1] }, }; function setOption(pos, name) { @@ -183,6 +179,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { break; case 'TM' : + // :TODO: convert to new Graphics Rendition system here: if(mci.args.length > 0) { var color = { fg : parseInt(mci.args[0], 10), flags : 0 }; if(mci.args.length > 1) { diff --git a/core/string_util.js b/core/string_util.js index eed9ca31..e2ca4f3f 100644 --- a/core/string_util.js +++ b/core/string_util.js @@ -112,19 +112,19 @@ function stylizeString(s, style) { } // Based on http://www.webtoolkit.info/ -function pad(s, len, padChar, dir, stringColor, padColor) { +function pad(s, len, padChar, dir, stringSGR, padSGR) { len = miscUtil.valueWithDefault(len, 0); padChar = miscUtil.valueWithDefault(padChar, ' '); dir = miscUtil.valueWithDefault(dir, 'right'); - stringColor = miscUtil.valueWithDefault(stringColor, ''); - padColor = miscUtil.valueWithDefault(padColor, ''); + stringSGR = miscUtil.valueWithDefault(stringSGR, ''); + padSGR = miscUtil.valueWithDefault(padSGR, ''); var padlen = len - s.length; switch(dir) { case 'L' : case 'left' : - s = padColor + new Array(padlen).join(padChar) + stringColor + s; + s = padSGR + new Array(padlen).join(padChar) + stringSGR + s; break; case 'C' : @@ -132,16 +132,16 @@ function pad(s, len, padChar, dir, stringColor, padColor) { case 'both' : var right = Math.ceil(padlen / 2); var left = padlen - right; - s = padColor + new Array(left + 1).join(padChar) + stringColor + s + padColor + new Array(right + 1).join(padChar); + s = padSGR + new Array(left + 1).join(padChar) + stringSGR + s + padSGR + new Array(right + 1).join(padChar); break; case 'R' : case 'right' : - s = stringColor + s + padColor + new Array(padlen).join(padChar); + s = stringSGR + s + padSGR + new Array(padlen).join(padChar); break; default : break; } - return stringColor + s; + return stringSGR + s; } diff --git a/core/text_view.js b/core/text_view.js index c08e939e..a3909588 100644 --- a/core/text_view.js +++ b/core/text_view.js @@ -81,11 +81,6 @@ TextView.prototype.setFocus = function(focused) { this.redraw(); - console.log('---') - console.log(this.graphicRendition) - console.log(this.focusGraphicRendition) - console.log('---') - // position & SGR for cursor this.client.term.write(ansi.goto(this.position.x, this.position.y + this.text.length)); this.client.term.write(this.getFocusSGR()); diff --git a/core/view.js b/core/view.js index 09b641a5..61d8037b 100644 --- a/core/view.js +++ b/core/view.js @@ -64,8 +64,8 @@ function View(options) { this.dimens.width = options.dimens.width; } - this.graphicRendition = options.graphicRendition || { fg : 7, bg : 0, styles : [ 0 ] }; - this.focusGraphicRendition = options.focusGraphicRendition || this.graphicRendition; + this.ansiSGR = options.ansiSGR || ansi.getSGRFromGraphicRendition( { fg : 39, bg : 49 }, true); + this.ansiFocusSGR = options.ansiFocusSGR || this.ansiSGR; if(options.styleColor1) { this.styleColor1 = options.styleColor1; @@ -167,20 +167,12 @@ View.prototype.setColor = function(color, bgColor, flags) { }; */ -View.prototype.getGraphicRendition = function() { - return this.graphicRendition; -} - -View.prototype.getFocusGraphicRendition = function() { - return this.focusGraphicRendition; -} - View.prototype.getSGR = function() { - return ansi.getSGRFromGraphicRendition(this.getGraphicRendition()); + return this.ansiSGR; } View.prototype.getFocusSGR = function() { - return ansi.getSGRFromGraphicRendition(this.getFocusGraphicRendition()); + return this.ansiFocusSGR; } View.prototype.redraw = function() { diff --git a/core/view_controller.js b/core/view_controller.js index 202daae2..808bdb32 100644 --- a/core/view_controller.js +++ b/core/view_controller.js @@ -184,6 +184,7 @@ function ViewController(options) { setViewProp('maxLength'); setViewProp('width', function(v) { view.dimens.width = parseInt(v, 10); }); + // :TODO: This needs converted to new GraphicRendition object and possibly allow escaped ANSI SGR here if string setViewProp('styleColor1', function(v) { if(!_.has(v, 'fg')) { return;