Merge pull request #407 from cognitivegears/feature/Remove_position_check
#222 - Removing Cursor Position Report
This commit is contained in:
commit
3d1b97cf3b
|
@ -28,5 +28,8 @@
|
||||||
],
|
],
|
||||||
"comma-dangle": 0,
|
"comma-dangle": 0,
|
||||||
"no-trailing-spaces" :"warn"
|
"no-trailing-spaces" :"warn"
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2020
|
||||||
}
|
}
|
||||||
}
|
}
|
27
UPGRADE.md
27
UPGRADE.md
|
@ -28,6 +28,33 @@ npm install # or simply 'yarn'
|
||||||
Report your issue on Xibalba BBS, hop in #enigma-bbs on FreeNode and chat, or
|
Report your issue on Xibalba BBS, hop in #enigma-bbs on FreeNode and chat, or
|
||||||
[file a issue on GitHub](https://github.com/NuSkooler/enigma-bbs/issues).
|
[file a issue on GitHub](https://github.com/NuSkooler/enigma-bbs/issues).
|
||||||
|
|
||||||
|
|
||||||
|
# 0.0.12-beta to 0.0.13-beta
|
||||||
|
|
||||||
|
* All features and changes are backwards compatible. There are a few new configuration options in a new `term` section in the configuration. These are all optional, but include the following options in case you use them:
|
||||||
|
|
||||||
|
```hjson
|
||||||
|
{
|
||||||
|
|
||||||
|
term: {
|
||||||
|
// checkUtf8Encoding requires the use of cursor position reports, which are not supported on all terminals.
|
||||||
|
// Using this with a terminal that does not support cursor position reports results in a 2 second delay
|
||||||
|
// during the connect process, but provides better autoconfiguration of utf-8
|
||||||
|
checkUtf8Encoding: true
|
||||||
|
|
||||||
|
|
||||||
|
// Checking the ANSI home position also requires the use of cursor position reports, which are not
|
||||||
|
// supported on all terminals. Using this with a terminal that does not support cursor position reports
|
||||||
|
// results in a 3 second delay during the connect process, but works around positioning problems with
|
||||||
|
// non-standard terminals.
|
||||||
|
checkAnsiHomePosition: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition to these, there are also new options for `term.cp437TermList` and `term.utf8TermList`. Under most circumstances these should not need to be changed. If you want to customize these lists, more information is available in `config_default.js`
|
||||||
|
|
||||||
# 0.0.11-beta to 0.0.12-beta
|
# 0.0.11-beta to 0.0.12-beta
|
||||||
* Be aware that `master` is now mainline! This means all `git pull`'s will yield the latest version. See [WHATSNEW](WHATSNEW.md) for more information.
|
* Be aware that `master` is now mainline! This means all `git pull`'s will yield the latest version. See [WHATSNEW](WHATSNEW.md) for more information.
|
||||||
* **BREAKING CHANGE** There is no longer a `prompt.hjson` file. Prompts are now simply part of the menu set in the `prompts` section. If you have an existing system you will need to add your `prompt.hjson` to your `menu.hjson`'s `includes` section at a minimum. Example:
|
* **BREAKING CHANGE** There is no longer a `prompt.hjson` file. Prompts are now simply part of the menu set in the `prompts` section. If you have an existing system you will need to add your `prompt.hjson` to your `menu.hjson`'s `includes` section at a minimum. Example:
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
# Whats New
|
# Whats New
|
||||||
This document attempts to track **major** changes and additions in ENiGMA½. For details, see GitHub.
|
This document attempts to track **major** changes and additions in ENiGMA½. For details, see GitHub.
|
||||||
|
|
||||||
|
## 0.0.13-beta
|
||||||
|
* Removed terminal `cursor position reports` from most locations in the code. This should greatly increase the number of terminal programs that work with Enigma 1/2. For more information, see [Issue #222](https://github.com/NuSkooler/enigma-bbs/issues/222). This may also resolve other issues, such as [Issue #365](https://github.com/NuSkooler/enigma-bbs/issues/365), and [Issue #320](https://github.com/NuSkooler/enigma-bbs/issues/320). Anyone that previously had terminal incompatibilities please re-check and let us know!
|
||||||
|
* Bumped up the minimum [Node.js](https://nodejs.org/en/) version to V14. This will allow more expressive Javascript programming syntax with ECMAScript 2020 to improve the development experience.
|
||||||
|
* Added new configuration options for `term.checkUtf8Encoding`, `term.checkAnsiHomePostion`, `term.cp437TermList`, and `term.utf8TermList`. More information on these options is available in `UPGRADE.md`
|
||||||
|
* Many additional backward-compatible bug fixes since the first release of 0.0.12-beta. See the [project repository](https://github.com/NuSkooler/enigma-bbs) for more information.
|
||||||
|
|
||||||
## 0.0.12-beta
|
## 0.0.12-beta
|
||||||
* The `master` branch has become mainline. What this means to users is `git pull` will always give you the latest and greatest. Make sure to read [Updating](./docs/admin/updating.md) and keep an eye on `WHATSNEW.md` (this file) and [UPGRADE](UPGRADE.md)! See also [ticket #276](https://github.com/NuSkooler/enigma-bbs/issues/276).
|
* The `master` branch has become mainline. What this means to users is `git pull` will always give you the latest and greatest. Make sure to read [Updating](./docs/admin/updating.md) and keep an eye on `WHATSNEW.md` (this file) and [UPGRADE](UPGRADE.md)! See also [ticket #276](https://github.com/NuSkooler/enigma-bbs/issues/276).
|
||||||
* Development now occurs against [Node.js 14 LTS](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V14.md).
|
* Development now occurs against [Node.js 14 LTS](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V14.md).
|
||||||
|
|
|
@ -21,8 +21,6 @@ function ANSIEscapeParser(options) {
|
||||||
events.EventEmitter.call(this);
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
this.column = 1;
|
this.column = 1;
|
||||||
this.row = 1;
|
|
||||||
this.scrollBack = 0;
|
|
||||||
this.graphicRendition = {};
|
this.graphicRendition = {};
|
||||||
|
|
||||||
this.parseState = {
|
this.parseState = {
|
||||||
|
@ -36,11 +34,15 @@ function ANSIEscapeParser(options) {
|
||||||
trailingLF : 'default', // default|omit|no|yes, ...
|
trailingLF : 'default', // default|omit|no|yes, ...
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, '');
|
this.mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, '');
|
||||||
this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25);
|
this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25);
|
||||||
this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80);
|
this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80);
|
||||||
this.trailingLF = miscUtil.valueWithDefault(options.trailingLF, 'default');
|
this.trailingLF = miscUtil.valueWithDefault(options.trailingLF, 'default');
|
||||||
|
|
||||||
|
|
||||||
|
this.row = Math.min(options?.startRow ?? 1, this.termHeight);
|
||||||
|
|
||||||
self.moveCursor = function(cols, rows) {
|
self.moveCursor = function(cols, rows) {
|
||||||
self.column += cols;
|
self.column += cols;
|
||||||
self.row += rows;
|
self.row += rows;
|
||||||
|
@ -69,14 +71,11 @@ function ANSIEscapeParser(options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.clearScreen = function() {
|
self.clearScreen = function() {
|
||||||
// :TODO: should be doing something with row/column?
|
self.column = 1;
|
||||||
|
self.row = 1;
|
||||||
self.emit('clear screen');
|
self.emit('clear screen');
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
self.rowUpdated = function() {
|
|
||||||
self.emit('row update', self.row + self.scrollBack);
|
|
||||||
};*/
|
|
||||||
|
|
||||||
self.positionUpdated = function() {
|
self.positionUpdated = function() {
|
||||||
self.emit('position update', self.row, self.column);
|
self.emit('position update', self.row, self.column);
|
||||||
|
@ -190,6 +189,7 @@ function ANSIEscapeParser(options) {
|
||||||
|
|
||||||
|
|
||||||
self.emit('mci', {
|
self.emit('mci', {
|
||||||
|
position : [self.row, self.column],
|
||||||
mci : mciCode,
|
mci : mciCode,
|
||||||
id : id ? parseInt(id, 10) : null,
|
id : id ? parseInt(id, 10) : null,
|
||||||
args : args,
|
args : args,
|
||||||
|
@ -215,6 +215,9 @@ function ANSIEscapeParser(options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.reset = function(input) {
|
self.reset = function(input) {
|
||||||
|
self.column = 1;
|
||||||
|
self.row = Math.min(options?.startRow ?? 1, self.termHeight);
|
||||||
|
|
||||||
self.parseState = {
|
self.parseState = {
|
||||||
// ignore anything past EOF marker, if any
|
// ignore anything past EOF marker, if any
|
||||||
buffer : input.split(String.fromCharCode(0x1a), 1)[0],
|
buffer : input.split(String.fromCharCode(0x1a), 1)[0],
|
||||||
|
|
21
core/art.js
21
core/art.js
|
@ -269,19 +269,16 @@ function display(client, art, options, cb) {
|
||||||
termHeight : client.term.termHeight,
|
termHeight : client.term.termHeight,
|
||||||
termWidth : client.term.termWidth,
|
termWidth : client.term.termWidth,
|
||||||
trailingLF : options.trailingLF,
|
trailingLF : options.trailingLF,
|
||||||
|
startRow : options.startRow,
|
||||||
});
|
});
|
||||||
|
|
||||||
let parseComplete = false;
|
let parseComplete = false;
|
||||||
let cprListener;
|
|
||||||
let mciMap;
|
let mciMap;
|
||||||
const mciCprQueue = [];
|
const mciCprQueue = [];
|
||||||
let artHash;
|
let artHash;
|
||||||
let mciMapFromCache;
|
let mciMapFromCache;
|
||||||
|
|
||||||
function completed() {
|
function completed() {
|
||||||
if(cprListener) {
|
|
||||||
client.removeListener('cursor position report', cprListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!options.disableMciCache && !mciMapFromCache) {
|
if(!options.disableMciCache && !mciMapFromCache) {
|
||||||
// cache our MCI findings...
|
// cache our MCI findings...
|
||||||
|
@ -314,18 +311,6 @@ function display(client, art, options, cb) {
|
||||||
// no cached MCI info
|
// no cached MCI info
|
||||||
mciMap = {};
|
mciMap = {};
|
||||||
|
|
||||||
cprListener = function(pos) {
|
|
||||||
if(mciCprQueue.length > 0) {
|
|
||||||
mciMap[mciCprQueue.shift()].position = pos;
|
|
||||||
|
|
||||||
if(parseComplete && 0 === mciCprQueue.length) {
|
|
||||||
return completed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
client.on('cursor position report', cprListener);
|
|
||||||
|
|
||||||
let generatedId = 100;
|
let generatedId = 100;
|
||||||
|
|
||||||
ansiParser.on('mci', mciInfo => {
|
ansiParser.on('mci', mciInfo => {
|
||||||
|
@ -339,6 +324,7 @@ function display(client, art, options, cb) {
|
||||||
mapEntry.focusArgs = mciInfo.args;
|
mapEntry.focusArgs = mciInfo.args;
|
||||||
} else {
|
} else {
|
||||||
mciMap[mapKey] = {
|
mciMap[mapKey] = {
|
||||||
|
position : mciInfo.position,
|
||||||
args : mciInfo.args,
|
args : mciInfo.args,
|
||||||
SGR : mciInfo.SGR,
|
SGR : mciInfo.SGR,
|
||||||
code : mciInfo.mci,
|
code : mciInfo.mci,
|
||||||
|
@ -348,9 +334,6 @@ function display(client, art, options, cb) {
|
||||||
if(!mciInfo.id) {
|
if(!mciInfo.id) {
|
||||||
++generatedId;
|
++generatedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
mciCprQueue.push(mapKey);
|
|
||||||
client.term.rawWrite(ansi.queryPos());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
// ENiGMA½
|
// ENiGMA½
|
||||||
var Log = require('./logger.js').log;
|
var Log = require('./logger.js').log;
|
||||||
var renegadeToAnsi = require('./color_codes.js').renegadeToAnsi;
|
var renegadeToAnsi = require('./color_codes.js').renegadeToAnsi;
|
||||||
|
const Config = require('./config.js').get;
|
||||||
var iconv = require('iconv-lite');
|
var iconv = require('iconv-lite');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
|
||||||
exports.ClientTerminal = ClientTerminal;
|
exports.ClientTerminal = ClientTerminal;
|
||||||
|
|
||||||
function ClientTerminal(output) {
|
function ClientTerminal(output) {
|
||||||
|
@ -115,7 +116,8 @@ ClientTerminal.prototype.isNixTerm = function() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [ 'xterm', 'linux', 'screen', 'dumb', 'rxvt', 'konsole', 'gnome', 'x11 terminal emulator' ].includes(this.termType);
|
const utf8TermList = Config().term.utf8TermList;
|
||||||
|
return utf8TermList.includes(this.termType);
|
||||||
};
|
};
|
||||||
|
|
||||||
ClientTerminal.prototype.isANSI = function() {
|
ClientTerminal.prototype.isANSI = function() {
|
||||||
|
@ -153,7 +155,8 @@ ClientTerminal.prototype.isANSI = function() {
|
||||||
// linux:
|
// linux:
|
||||||
// * JuiceSSH (note: TERM=linux also)
|
// * JuiceSSH (note: TERM=linux also)
|
||||||
//
|
//
|
||||||
return [ 'ansi', 'pcansi', 'pc-ansi', 'ansi-bbs', 'qansi', 'scoansi', 'syncterm', 'ansi-256color', 'ansi-256color-rgb' ].includes(this.termType);
|
const cp437TermList = Config().term.cp437TermList;
|
||||||
|
return cp437TermList.includes(this.termType);
|
||||||
};
|
};
|
||||||
|
|
||||||
// :TODO: probably need to update these to convert IAC (0xff) -> IACIAC (escape it)
|
// :TODO: probably need to update these to convert IAC (0xff) -> IACIAC (escape it)
|
||||||
|
|
|
@ -17,6 +17,24 @@ module.exports = () => {
|
||||||
achievementFile : 'achievements.hjson',
|
achievementFile : 'achievements.hjson',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
term : {
|
||||||
|
// checkUtf8Encoding requires the use of cursor position reports, which are not supported on all terminals.
|
||||||
|
// Using this with a terminal that does not support cursor position reports results in a 2 second delay
|
||||||
|
// during the connect process, but provides better autoconfiguration of utf-8
|
||||||
|
checkUtf8Encoding : true,
|
||||||
|
|
||||||
|
// Checking the ANSI home position also requires the use of cursor position reports, which are not
|
||||||
|
// supported on all terminals. Using this with a terminal that does not support cursor position reports
|
||||||
|
// results in a 3 second delay during the connect process, but works around positioning problems with
|
||||||
|
// non-standard terminals.
|
||||||
|
checkAnsiHomePosition: true,
|
||||||
|
|
||||||
|
// List of terms that should be assumed to use cp437 encoding
|
||||||
|
cp437TermList : ['ansi', 'pcansi', 'pc-ansi', 'ansi-bbs', 'qansi', 'scoansi', 'syncterm', 'ansi-256color', 'ansi-256color-rgb'],
|
||||||
|
// List of terms that should be assumed to use utf8 encoding
|
||||||
|
utf8TermList : ['xterm', 'linux', 'screen', 'dumb', 'rxvt', 'konsole', 'gnome', 'x11 terminal emulator'],
|
||||||
|
},
|
||||||
|
|
||||||
users : {
|
users : {
|
||||||
usernameMin : 2,
|
usernameMin : 2,
|
||||||
usernameMax : 16, // Note that FidoNet wants 36 max
|
usernameMax : 16, // Note that FidoNet wants 36 max
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
// ENiGMA½
|
// ENiGMA½
|
||||||
const ansi = require('./ansi_term.js');
|
const ansi = require('./ansi_term.js');
|
||||||
const Events = require('./events.js');
|
const Events = require('./events.js');
|
||||||
|
const Config = require('./config.js').get;
|
||||||
const { Errors } = require('./enig_error.js');
|
const { Errors } = require('./enig_error.js');
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
|
@ -18,6 +19,13 @@ function ansiDiscoverHomePosition(client, cb) {
|
||||||
// think of home as 0,0. If this is the case, we need to offset
|
// think of home as 0,0. If this is the case, we need to offset
|
||||||
// our positioning to accommodate for such.
|
// our positioning to accommodate for such.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
if( !Config().term.checkAnsiHomePosition ) {
|
||||||
|
// Skip (and assume 1,1) if the home position check is disabled.
|
||||||
|
return cb(null);
|
||||||
|
}
|
||||||
|
|
||||||
const done = (err) => {
|
const done = (err) => {
|
||||||
client.removeListener('cursor position report', cprListener);
|
client.removeListener('cursor position report', cprListener);
|
||||||
clearTimeout(giveUpTimer);
|
clearTimeout(giveUpTimer);
|
||||||
|
@ -68,8 +76,9 @@ function ansiAttemptDetectUTF8(client, cb) {
|
||||||
//
|
//
|
||||||
// We currently only do this if the term hasn't already been ID'd as a
|
// We currently only do this if the term hasn't already been ID'd as a
|
||||||
// "*nix" terminal -- that is, xterm, etc.
|
// "*nix" terminal -- that is, xterm, etc.
|
||||||
//
|
// Also skip this check if checkUtf8Encoding is disabled in the config
|
||||||
if(!client.term.isNixTerm()) {
|
|
||||||
|
if(!client.term.isNixTerm() || !Config().term.checkUtf8Encoding) {
|
||||||
return cb(null);
|
return cb(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +128,8 @@ function ansiAttemptDetectUTF8(client, cb) {
|
||||||
return giveUp();
|
return giveUp();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
client.once('cursor position report', cprListener);
|
client.once('cursor position report', cprListener);
|
||||||
client.term.rawWrite(ansi.goHome() + ansi.queryPos());
|
client.term.rawWrite(ansi.goHome() + ansi.queryPos());
|
||||||
}
|
}
|
||||||
|
@ -216,7 +227,6 @@ function connectEntry(client, nextMenu) {
|
||||||
},
|
},
|
||||||
function discoverHomePosition(callback) {
|
function discoverHomePosition(callback) {
|
||||||
ansiDiscoverHomePosition(client, () => {
|
ansiDiscoverHomePosition(client, () => {
|
||||||
// :TODO: If CPR for home fully fails, we should bail out on the connection with an error, e.g. ANSI support required
|
|
||||||
return callback(null); // we try to continue anyway
|
return callback(null); // we try to continue anyway
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
64
core/fse.js
64
core/fse.js
|
@ -603,7 +603,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
|
||||||
theme.displayThemedAsset(
|
theme.displayThemedAsset(
|
||||||
footerArt,
|
footerArt,
|
||||||
self.client,
|
self.client,
|
||||||
{ font : self.menuConfig.font },
|
{ font : self.menuConfig.font, startRow: self.header.height + self.body.height },
|
||||||
function displayed(err, artData) {
|
function displayed(err, artData) {
|
||||||
callback(err, artData);
|
callback(err, artData);
|
||||||
}
|
}
|
||||||
|
@ -626,19 +626,34 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
|
||||||
async.series(
|
async.series(
|
||||||
[
|
[
|
||||||
function displayHeaderAndBody(callback) {
|
function displayHeaderAndBody(callback) {
|
||||||
async.eachSeries( comps, function dispArt(n, next) {
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function displayHeader(callback) {
|
||||||
theme.displayThemedAsset(
|
theme.displayThemedAsset(
|
||||||
art[n],
|
art['header'],
|
||||||
self.client,
|
self.client,
|
||||||
{ font : self.menuConfig.font },
|
{ font : self.menuConfig.font },
|
||||||
function displayed(err) {
|
function displayed(err, artInfo) {
|
||||||
next(err);
|
return callback(err, artInfo);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, function complete(err) {
|
},
|
||||||
|
function displayBody(artInfo, callback) {
|
||||||
|
theme.displayThemedAsset(
|
||||||
|
art['header'],
|
||||||
|
self.client,
|
||||||
|
{ font : self.menuConfig.font, startRow: artInfo.height + 1 },
|
||||||
|
function displayed(err, artInfo) {
|
||||||
|
return callback(err, artInfo);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
function complete(err) {
|
||||||
//self.body.height = self.client.term.termHeight - self.header.height - 1;
|
//self.body.height = self.client.term.termHeight - self.header.height - 1;
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
function displayFooter(callback) {
|
function displayFooter(callback) {
|
||||||
// we have to treat the footer special
|
// we have to treat the footer special
|
||||||
|
@ -700,31 +715,39 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
|
||||||
|
|
||||||
assert(_.isObject(art));
|
assert(_.isObject(art));
|
||||||
|
|
||||||
async.series(
|
async.waterfall(
|
||||||
[
|
[
|
||||||
function beforeDisplayArt(callback) {
|
function beforeDisplayArt(callback) {
|
||||||
self.beforeArt(callback);
|
self.beforeArt(callback);
|
||||||
},
|
},
|
||||||
function displayHeaderAndBodyArt(callback) {
|
function displayHeader(callback) {
|
||||||
async.eachSeries( [ 'header', 'body' ], function dispArt(n, next) {
|
|
||||||
theme.displayThemedAsset(
|
theme.displayThemedAsset(
|
||||||
art[n],
|
art.header,
|
||||||
self.client,
|
self.client,
|
||||||
{ font : self.menuConfig.font },
|
{ font : self.menuConfig.font },
|
||||||
function displayed(err, artData) {
|
function displayed(err, artInfo) {
|
||||||
if(artData) {
|
if(artInfo) {
|
||||||
mciData[n] = artData;
|
mciData['header'] = artInfo;
|
||||||
self[n] = { height : artData.height };
|
self.header = {height: artInfo.height};
|
||||||
}
|
}
|
||||||
|
return callback(err, artInfo);
|
||||||
next(err);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, function complete(err) {
|
},
|
||||||
callback(err);
|
function displayBody(artInfo, callback) {
|
||||||
|
theme.displayThemedAsset(
|
||||||
|
art.body,
|
||||||
|
self.client,
|
||||||
|
{ font : self.menuConfig.font, startRow: artInfo.height + 1 },
|
||||||
|
function displayed(err, artInfo) {
|
||||||
|
if(artInfo) {
|
||||||
|
mciData['body'] = artInfo;
|
||||||
|
self.body = {height: artInfo.height - self.header.height};
|
||||||
|
}
|
||||||
|
return callback(err, artInfo);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function displayFooter(callback) {
|
function displayFooter(artInfo, callback) {
|
||||||
self.setInitialFooterMode();
|
self.setInitialFooterMode();
|
||||||
|
|
||||||
var footerName = self.getFooterName();
|
var footerName = self.getFooterName();
|
||||||
|
@ -825,7 +848,6 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul
|
||||||
{
|
{
|
||||||
const fromView = self.viewControllers.header.getView(MciViewIds.header.from);
|
const fromView = self.viewControllers.header.getView(MciViewIds.header.from);
|
||||||
const area = getMessageAreaByTag(self.messageAreaTag);
|
const area = getMessageAreaByTag(self.messageAreaTag);
|
||||||
|
|
||||||
if(fromView !== undefined) {
|
if(fromView !== undefined) {
|
||||||
if(area && area.realNames) {
|
if(area && area.realNames) {
|
||||||
fromView.setText(self.client.user.properties[UserProps.RealName] || self.client.user.username);
|
fromView.setText(self.client.user.properties[UserProps.RealName] || self.client.user.username);
|
||||||
|
|
|
@ -56,14 +56,14 @@ exports.MenuModule = class MenuModule extends PluginModule {
|
||||||
initSequence() {
|
initSequence() {
|
||||||
const self = this;
|
const self = this;
|
||||||
const mciData = {};
|
const mciData = {};
|
||||||
let pausePosition;
|
let pausePosition = {row: 0, column: 0};
|
||||||
|
|
||||||
const hasArt = () => {
|
const hasArt = () => {
|
||||||
return _.isString(self.menuConfig.art) ||
|
return _.isString(self.menuConfig.art) ||
|
||||||
(Array.isArray(self.menuConfig.art) && _.has(self.menuConfig.art[0], 'acs'));
|
(Array.isArray(self.menuConfig.art) && _.has(self.menuConfig.art[0], 'acs'));
|
||||||
};
|
};
|
||||||
|
|
||||||
async.series(
|
async.waterfall(
|
||||||
[
|
[
|
||||||
function beforeArtInterrupt(callback) {
|
function beforeArtInterrupt(callback) {
|
||||||
return self.displayQueuedInterruptions(callback);
|
return self.displayQueuedInterruptions(callback);
|
||||||
|
@ -73,7 +73,7 @@ exports.MenuModule = class MenuModule extends PluginModule {
|
||||||
},
|
},
|
||||||
function displayMenuArt(callback) {
|
function displayMenuArt(callback) {
|
||||||
if(!hasArt()) {
|
if(!hasArt()) {
|
||||||
return callback(null);
|
return callback(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.displayAsset(
|
self.displayAsset(
|
||||||
|
@ -86,18 +86,15 @@ exports.MenuModule = class MenuModule extends PluginModule {
|
||||||
mciData.menu = artData.mciMap;
|
mciData.menu = artData.mciMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null); // any errors are non-fatal
|
if(artData) {
|
||||||
|
pausePosition.row = artData.height + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(null, artData); // any errors are non-fatal
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
function moveToPromptLocation(callback) {
|
function displayPromptArt(artData, callback) {
|
||||||
if(self.menuConfig.prompt) {
|
|
||||||
// :TODO: fetch and move cursor to prompt location, if supplied. See notes/etc. on placements
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null);
|
|
||||||
},
|
|
||||||
function displayPromptArt(callback) {
|
|
||||||
if(!_.isString(self.menuConfig.prompt)) {
|
if(!_.isString(self.menuConfig.prompt)) {
|
||||||
return callback(null);
|
return callback(null);
|
||||||
}
|
}
|
||||||
|
@ -106,41 +103,41 @@ exports.MenuModule = class MenuModule extends PluginModule {
|
||||||
return callback(Errors.MissingConfig('Prompt specified but no "promptConfig" block found'));
|
return callback(Errors.MissingConfig('Prompt specified but no "promptConfig" block found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const options = Object.assign({}, self.menuConfig.config);
|
||||||
|
|
||||||
|
if(_.isNumber(artData?.height)) {
|
||||||
|
options.startRow = artData.height + 1;
|
||||||
|
}
|
||||||
|
|
||||||
self.displayAsset(
|
self.displayAsset(
|
||||||
self.menuConfig.promptConfig.art,
|
self.menuConfig.promptConfig.art,
|
||||||
self.menuConfig.config,
|
options,
|
||||||
(err, artData) => {
|
(err, artData) => {
|
||||||
if(artData) {
|
if(artData) {
|
||||||
mciData.prompt = artData.mciMap;
|
mciData.prompt = artData.mciMap;
|
||||||
|
pausePosition.row = artData.height + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(err); // pass err here; prompts *must* have art
|
return callback(err); // pass err here; prompts *must* have art
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
function recordCursorPosition(callback) {
|
|
||||||
if(!self.shouldPause()) {
|
|
||||||
return callback(null); // cursor position not needed
|
|
||||||
}
|
|
||||||
|
|
||||||
self.client.once('cursor position report', pos => {
|
|
||||||
pausePosition = { row : pos[0], col : 1 };
|
|
||||||
self.client.log.trace('After art position recorded', pausePosition );
|
|
||||||
return callback(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.client.term.rawWrite(ansi.queryPos());
|
|
||||||
},
|
|
||||||
function afterArtDisplayed(callback) {
|
function afterArtDisplayed(callback) {
|
||||||
return self.mciReady(mciData, callback);
|
return self.mciReady(mciData, callback);
|
||||||
},
|
},
|
||||||
function displayPauseIfRequested(callback) {
|
function displayPauseIfRequested(callback) {
|
||||||
if(!self.shouldPause()) {
|
if(!self.shouldPause()) {
|
||||||
return callback(null);
|
return callback(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(self.client.term.termHeight > 0 && pausePosition.row > self.client.termHeight) {
|
||||||
|
// If this scrolled, the prompt will go to the bottom of the screen
|
||||||
|
pausePosition.row = self.client.termHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.pausePrompt(pausePosition, callback);
|
return self.pausePrompt(pausePosition, callback);
|
||||||
},
|
},
|
||||||
function finishAndNext(callback) {
|
function finishAndNext(artInfo, callback) {
|
||||||
self.finishedLoading();
|
self.finishedLoading();
|
||||||
self.realTimeInterrupt = 'allowed';
|
self.realTimeInterrupt = 'allowed';
|
||||||
return self.autoNextMenu(callback);
|
return self.autoNextMenu(callback);
|
||||||
|
@ -512,7 +509,7 @@ exports.MenuModule = class MenuModule extends PluginModule {
|
||||||
|
|
||||||
this.optionalMoveToPosition(position);
|
this.optionalMoveToPosition(position);
|
||||||
|
|
||||||
return theme.displayThemedPause(this.client, cb);
|
return theme.displayThemedPause(this.client, {position}, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
promptForInput( { formName, formId, promptName, prevFormName, position } = {}, options, cb) {
|
promptForInput( { formName, formId, promptName, prevFormName, position } = {}, options, cb) {
|
||||||
|
|
|
@ -171,22 +171,15 @@ exports.getModule = class ShowArtModule extends MenuModule {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
const mciData = { menu : artData.mciMap };
|
const mciData = { menu : artData.mciMap };
|
||||||
return callback(null, mciData);
|
if(self.client.term.termHeight > 0 && artData.height > self.client.term.termHeight) {
|
||||||
|
// We must have scrolled, adjust the positioning for pause
|
||||||
|
artData.height = self.client.term.termHeight;
|
||||||
|
}
|
||||||
|
const pausePosition = { row: artData.height + 1, col: 1};
|
||||||
|
return callback(null, mciData, pausePosition);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
function recordCursorPosition(mciData, callback) {
|
|
||||||
if(!options.pause) {
|
|
||||||
return callback(null, mciData, null); // cursor position not needed
|
|
||||||
}
|
|
||||||
|
|
||||||
self.client.once('cursor position report', pos => {
|
|
||||||
const pausePosition = { row : pos[0], col : 1 };
|
|
||||||
return callback(null, mciData, pausePosition);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.client.term.rawWrite(ANSI.queryPos());
|
|
||||||
},
|
|
||||||
function afterArtDisplayed(mciData, pausePosition, callback) {
|
function afterArtDisplayed(mciData, pausePosition, callback) {
|
||||||
self.mciReady(mciData, err => {
|
self.mciReady(mciData, err => {
|
||||||
return callback(err, pausePosition);
|
return callback(err, pausePosition);
|
||||||
|
|
|
@ -495,6 +495,7 @@ function displayPreparedArt(options, artInfo, cb) {
|
||||||
sauce : artInfo.sauce,
|
sauce : artInfo.sauce,
|
||||||
font : options.font,
|
font : options.font,
|
||||||
trailingLF : options.trailingLF,
|
trailingLF : options.trailingLF,
|
||||||
|
startRow : options.startRow,
|
||||||
};
|
};
|
||||||
art.display(options.client, artInfo.data, displayOpts, (err, mciMap, extraInfo) => {
|
art.display(options.client, artInfo.data, displayOpts, (err, mciMap, extraInfo) => {
|
||||||
return cb(err, { mciMap : mciMap, artInfo : artInfo, extraInfo : extraInfo } );
|
return cb(err, { mciMap : mciMap, artInfo : artInfo, extraInfo : extraInfo } );
|
||||||
|
@ -551,6 +552,7 @@ function displayThemedPrompt(name, client, options, cb) {
|
||||||
|
|
||||||
if(options.clearScreen) {
|
if(options.clearScreen) {
|
||||||
client.term.rawWrite(ansi.resetScreen());
|
client.term.rawWrite(ansi.resetScreen());
|
||||||
|
options.position = {row: 1, column: 1};
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -583,12 +585,15 @@ function displayThemedPrompt(name, client, options, cb) {
|
||||||
return callback(null, promptConfig, artInfo);
|
return callback(null, promptConfig, artInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
client.once('cursor position report', pos => {
|
if(_.isNumber(options?.position?.row)) {
|
||||||
artInfo.startRow = pos[0] - artInfo.height;
|
artInfo.startRow = options.position.row;
|
||||||
return callback(null, promptConfig, artInfo);
|
if(client.term.termHeight > 0 && artInfo.startRow + artInfo.height > client.term.termHeight) {
|
||||||
});
|
// in this case, we will have scrolled
|
||||||
|
artInfo.startRow = client.term.termHeight - artInfo.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client.term.rawWrite(ansi.queryPos());
|
return callback(null, promptConfig, artInfo);
|
||||||
},
|
},
|
||||||
function createMCIViews(promptConfig, artInfo, callback) {
|
function createMCIViews(promptConfig, artInfo, callback) {
|
||||||
const assocViewController = usingTempViewController ? new ViewController( { client : client } ) : options.viewController;
|
const assocViewController = usingTempViewController ? new ViewController( { client : client } ) : options.viewController;
|
||||||
|
@ -614,7 +619,9 @@ function displayThemedPrompt(name, client, options, cb) {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function clearPauseArt(artInfo, assocViewController, callback) {
|
function clearPauseArt(artInfo, assocViewController, callback) {
|
||||||
if(options.clearPrompt) {
|
// Only clear with height if clearPrompt is true and if we were able
|
||||||
|
// to determine the row
|
||||||
|
if(options.clearPrompt && artInfo.startRow) {
|
||||||
if(artInfo.startRow && artInfo.height) {
|
if(artInfo.startRow && artInfo.height) {
|
||||||
client.term.rawWrite(ansi.goto(artInfo.startRow, 1));
|
client.term.rawWrite(ansi.goto(artInfo.startRow, 1));
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ For Linux environments it's recommended you run the [install script](install-scr
|
||||||
do things manually, read on...
|
do things manually, read on...
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
* [Node.js](https://nodejs.org/) version **v12.x LTS or higher** (Other versions may work but are not supported).
|
* [Node.js](https://nodejs.org/) version **v14.x LTS or higher**. Versions under v14 are known not to work due to language level changes.
|
||||||
* :bulb: It is **highly** recommended to use [Node Version Manager (NVM)](https://github.com/creationix/nvm) to manage your Node.js installation if you're on a Linux/Unix environment.
|
* :bulb: It is **highly** recommended to use [Node Version Manager (NVM)](https://github.com/creationix/nvm) to manage your Node.js installation if you're on a Linux/Unix environment.
|
||||||
|
|
||||||
* [Python](https://www.python.org/downloads/) for compiling Node.js packages with native extensions via `node-gyp`.
|
* [Python](https://www.python.org/downloads/) for compiling Node.js packages with native extensions via `node-gyp`.
|
||||||
|
|
|
@ -67,6 +67,21 @@
|
||||||
description : 'Yet another awesome ENiGMA½ BBS'
|
description : 'Yet another awesome ENiGMA½ BBS'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
term: {
|
||||||
|
// checkUtf8Encoding requires the use of cursor position reports, which are not supported on all terminals.
|
||||||
|
// Using this with a terminal that does not support cursor position reports results in a 2 second delay
|
||||||
|
// during the connect process, but provides better autoconfiguration of utf-8
|
||||||
|
checkUtf8Encoding : true
|
||||||
|
|
||||||
|
// Checking the ANSI home position also requires the use of cursor position reports, which are not
|
||||||
|
// supported on all terminals. Using this with a terminal that does not support cursor position reports
|
||||||
|
// results in a 3 second delay during the connect process, but works around positioning problems with
|
||||||
|
// non-standard terminals.
|
||||||
|
checkAnsiHomePosition: true
|
||||||
|
|
||||||
|
// other options here include cp437TermList and utf8TermList, see config_default.js for more information
|
||||||
|
}
|
||||||
|
|
||||||
paths: {
|
paths: {
|
||||||
//
|
//
|
||||||
// Other paths can also be configured as well,
|
// Other paths can also be configured as well,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "enigma-bbs",
|
"name": "enigma-bbs",
|
||||||
"version": "0.0.12-beta",
|
"version": "0.0.13-beta",
|
||||||
"description": "ENiGMA½ Bulletin Board System",
|
"description": "ENiGMA½ Bulletin Board System",
|
||||||
"author": "Bryan Ashby <bryan@l33t.codes>",
|
"author": "Bryan Ashby <bryan@l33t.codes>",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
@ -62,9 +62,9 @@
|
||||||
"yazl": "^2.5.1"
|
"yazl": "^2.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "7.19.0"
|
"eslint": "8.12.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue