diff --git a/core/ansi_escape_parser.js b/core/ansi_escape_parser.js index ce07af89..bda63b5d 100644 --- a/core/ansi_escape_parser.js +++ b/core/ansi_escape_parser.js @@ -140,11 +140,12 @@ function ANSIEscapeParser(options) { } 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 match; var mciCode; var args; + var id; do { pos = mciRe.lastIndex; @@ -156,15 +157,16 @@ function ANSIEscapeParser(options) { } mciCode = match[1]; + id = match[2] || null; - if(match[2]) { - args = match[2].split(','); + if(match[3]) { + args = match[3].split(','); } else { args = []; } - self.emit('mci', mciCode, args); + self.emit('mci', mciCode, id, args); self.emit('chunk', getProcessedMCI(match[0])); } diff --git a/core/art.js b/core/art.js index ca489a1c..fe961118 100644 --- a/core/art.js +++ b/core/art.js @@ -416,33 +416,37 @@ function display(art, options, cb) { var emitter = null; var parseComplete = false; - parser.on('mci', function onMCI(mciCode, args) { - if(mci[mciCode]) { - mci[mciCode].focusColor = { + var generatedId = 100; + + parser.on('mci', function onMCI(mciCode, id, args) { + id = id || generatedId++; + var mapItem = mciCode + id; + if(mci[mapItem]) { + mci[mapItem].focusColor = { fg : parser.fgColor, bg : parser.bgColor, flags : parser.flags, }; } else { - mci[mciCode] = { + mci[mapItem] = { args : args, color : { fg : parser.fgColor, bg : parser.bgColor, flags : parser.flags, }, - code : mciCode.substr(0, 2), - id : mciCode.substr(2, 1), // :TODO: This NEEDs to read 01-99 + code : mciCode, + id : parseInt(id, 10), }; - mciPosQueue.push(mciCode); + 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 forMciCode = mciPosQueue.shift(); - mci[forMciCode].position = pos; + var forMapItem = mciPosQueue.shift(); + mci[forMapItem].position = pos; if(parseComplete && 0 === mciPosQueue.length) { 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); -} - -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 - // - } \ No newline at end of file diff --git a/core/mci_view_factory.js b/core/mci_view_factory.js index d4c9bf9d..896c5e8a 100644 --- a/core/mci_view_factory.js +++ b/core/mci_view_factory.js @@ -4,6 +4,8 @@ var TextView = require('./text_view.js').TextView; var EditTextView = require('./edit_text_view.js').EditTextView; var ButtonView = require('./button_view.js').ButtonView; +var Config = require('./config.js').config; +var packageJson = require('../package.json'); var assert = require('assert'); exports.MCIViewFactory = MCIViewFactory; @@ -12,6 +14,16 @@ function MCIViewFactory(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) { assert(mci.code); assert(mci.id > 0); @@ -26,7 +38,24 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { }; 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) { options.maxLength = mci.args[0]; options.dimens = { width : options.maxLength }; @@ -39,6 +68,28 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { view = new EditTextView(this.client, options); 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' : if(mci.args.length > 0) { options.text = mci.args[0]; diff --git a/core/text_view.js b/core/text_view.js index fc19499c..edd0029c 100644 --- a/core/text_view.js +++ b/core/text_view.js @@ -19,8 +19,9 @@ function TextView(client, options) { this.maxLength = this.options.maxLength; } - this.textStyle = this.options.textStyle || 'normal'; - this.multiLine = this.options.multiLine || false; + this.textStyle = this.options.textStyle || 'normal'; + this.multiLine = this.options.multiLine || false; + this.fillChar = miscUtil.valueWithDefault(this.options.fillChar, ' ').substr(0, 1); assert(!this.multiLine); // :TODO: not yet supported @@ -43,21 +44,20 @@ TextView.prototype.redraw = function() { TextView.super_.prototype.redraw.call(this); 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)); + // :TODO: If using fillChar, don't just pad. switch to non-focus color & fill with |fillChar| + if(this.isPasswordTextStyle) { this.client.term.write(strUtil.pad(new Array(this.text.length + 1).join(this.textMaskChar), this.dimens.width)); } 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.super_.prototype.setFocus.call(this, focused); - this.client.term.write(ansi.goto(this.position.x, this.position.y)); this.redraw(); this.client.term.write(ansi.goto(this.position.x, this.position.y + this.text.length)); }; diff --git a/core/view_controller.js b/core/view_controller.js index 4318f4aa..c2eb25d5 100644 --- a/core/view_controller.js +++ b/core/view_controller.js @@ -116,10 +116,14 @@ ViewController.prototype.setViewOrder = function(order) { if(0 === viewIdOrder.length) { 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; @@ -130,8 +134,7 @@ ViewController.prototype.setViewOrder = function(order) { this.firstId = viewIdOrder[0]; var lastId = viewIdOrder[viewIdOrder.length - 1]; - this.views[lastId].nextId = this.firstId; - + this.views[lastId].nextId = this.firstId; }; ViewController.prototype.loadFromMCIMap = function(mciMap) { @@ -145,6 +148,7 @@ ViewController.prototype.loadFromMCIMap = function(mciMap) { if(view) { view.on('action', self.onViewAction); self.addView(view); + view.redraw(); } }); }; diff --git a/mods/matrix.js b/mods/matrix.js index 417df3f1..6f5e4491 100644 --- a/mods/matrix.js +++ b/mods/matrix.js @@ -39,6 +39,8 @@ function entryPoint(client) { return; } + console.log(mci); + user.authenticate('NuSkooler', 'password', client, function onAuth(isValid) { console.log(isValid); });