2015-06-30 19:04:58 +00:00
|
|
|
/* jslint node: true */
|
|
|
|
'use strict';
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const ANSI = require('./ansi_term.js');
|
2018-09-15 02:34:39 +00:00
|
|
|
const { getPredefinedMCIValue } = require('./predefined_mci.js');
|
2015-06-30 19:04:58 +00:00
|
|
|
|
2018-09-15 02:34:39 +00:00
|
|
|
// deps
|
2022-06-05 20:04:25 +00:00
|
|
|
const _ = require('lodash');
|
2015-06-30 19:04:58 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
exports.stripMciColorCodes = stripMciColorCodes;
|
|
|
|
exports.pipeStringLength = pipeStringLength;
|
|
|
|
exports.pipeToAnsi = exports.renegadeToAnsi = renegadeToAnsi;
|
|
|
|
exports.controlCodesToAnsi = controlCodesToAnsi;
|
2015-06-30 19:04:58 +00:00
|
|
|
|
2018-09-15 19:12:54 +00:00
|
|
|
// :TODO: Not really happy with the module name of "color_codes". Would like something better ... control_code_string?
|
2015-06-30 19:04:58 +00:00
|
|
|
|
2018-09-15 19:12:54 +00:00
|
|
|
function stripMciColorCodes(s) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return s.replace(/\|[A-Z\d]{2}/g, '');
|
2015-08-26 05:17:09 +00:00
|
|
|
}
|
|
|
|
|
2018-09-15 19:12:54 +00:00
|
|
|
function pipeStringLength(s) {
|
|
|
|
return stripMciColorCodes(s).length;
|
2015-08-26 05:17:09 +00:00
|
|
|
}
|
|
|
|
|
2017-11-18 23:15:50 +00:00
|
|
|
function ansiSgrFromRenegadeColorCode(cc) {
|
2022-06-05 20:04:25 +00:00
|
|
|
return ANSI.sgr(
|
|
|
|
{
|
|
|
|
0: ['reset', 'black'],
|
|
|
|
1: ['reset', 'blue'],
|
|
|
|
2: ['reset', 'green'],
|
|
|
|
3: ['reset', 'cyan'],
|
|
|
|
4: ['reset', 'red'],
|
|
|
|
5: ['reset', 'magenta'],
|
|
|
|
6: ['reset', 'yellow'],
|
|
|
|
7: ['reset', 'white'],
|
|
|
|
|
|
|
|
8: ['bold', 'black'],
|
|
|
|
9: ['bold', 'blue'],
|
|
|
|
10: ['bold', 'green'],
|
|
|
|
11: ['bold', 'cyan'],
|
|
|
|
12: ['bold', 'red'],
|
|
|
|
13: ['bold', 'magenta'],
|
|
|
|
14: ['bold', 'yellow'],
|
|
|
|
15: ['bold', 'white'],
|
|
|
|
|
|
|
|
16: ['blackBG'],
|
|
|
|
17: ['blueBG'],
|
|
|
|
18: ['greenBG'],
|
|
|
|
19: ['cyanBG'],
|
|
|
|
20: ['redBG'],
|
|
|
|
21: ['magentaBG'],
|
|
|
|
22: ['yellowBG'],
|
|
|
|
23: ['whiteBG'],
|
|
|
|
|
|
|
|
24: ['blink', 'blackBG'],
|
|
|
|
25: ['blink', 'blueBG'],
|
|
|
|
26: ['blink', 'greenBG'],
|
|
|
|
27: ['blink', 'cyanBG'],
|
|
|
|
28: ['blink', 'redBG'],
|
|
|
|
29: ['blink', 'magentaBG'],
|
|
|
|
30: ['blink', 'yellowBG'],
|
|
|
|
31: ['blink', 'whiteBG'],
|
|
|
|
}[cc] || 'normal'
|
|
|
|
);
|
2017-11-18 23:15:50 +00:00
|
|
|
}
|
|
|
|
|
2018-09-15 19:12:54 +00:00
|
|
|
function ansiSgrFromCnetStyleColorCode(cc) {
|
2022-06-05 20:04:25 +00:00
|
|
|
return ANSI.sgr(
|
|
|
|
{
|
|
|
|
c0: ['reset', 'black'],
|
|
|
|
c1: ['reset', 'red'],
|
|
|
|
c2: ['reset', 'green'],
|
|
|
|
c3: ['reset', 'yellow'],
|
|
|
|
c4: ['reset', 'blue'],
|
|
|
|
c5: ['reset', 'magenta'],
|
|
|
|
c6: ['reset', 'cyan'],
|
|
|
|
c7: ['reset', 'white'],
|
|
|
|
|
|
|
|
c8: ['bold', 'black'],
|
|
|
|
c9: ['bold', 'red'],
|
|
|
|
ca: ['bold', 'green'],
|
|
|
|
cb: ['bold', 'yellow'],
|
|
|
|
cc: ['bold', 'blue'],
|
|
|
|
cd: ['bold', 'magenta'],
|
|
|
|
ce: ['bold', 'cyan'],
|
|
|
|
cf: ['bold', 'white'],
|
|
|
|
|
|
|
|
z0: ['blackBG'],
|
|
|
|
z1: ['redBG'],
|
|
|
|
z2: ['greenBG'],
|
|
|
|
z3: ['yellowBG'],
|
|
|
|
z4: ['blueBG'],
|
|
|
|
z5: ['magentaBG'],
|
|
|
|
z6: ['cyanBG'],
|
|
|
|
z7: ['whiteBG'],
|
|
|
|
}[cc] || 'normal'
|
|
|
|
);
|
2018-09-15 19:12:54 +00:00
|
|
|
}
|
|
|
|
|
2015-12-22 01:07:03 +00:00
|
|
|
function renegadeToAnsi(s, client) {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (-1 == s.indexOf('|')) {
|
|
|
|
return s; // no pipe codes present
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
let result = '';
|
|
|
|
const re = /\|(?:(C[FBUD])([0-9]{1,2})|([0-9]{2})|([A-Z]{2})|(\|))/g;
|
2018-09-15 02:34:39 +00:00
|
|
|
let m;
|
|
|
|
let lastIndex = 0;
|
2022-06-05 20:04:25 +00:00
|
|
|
while ((m = re.exec(s))) {
|
|
|
|
if (m[3]) {
|
2018-09-15 03:11:44 +00:00
|
|
|
// |## color
|
|
|
|
const val = parseInt(m[3], 10);
|
2018-06-22 05:15:04 +00:00
|
|
|
const attr = ansiSgrFromRenegadeColorCode(val);
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + attr;
|
2022-06-05 20:04:25 +00:00
|
|
|
} else if (m[4] || m[1]) {
|
2018-09-15 03:11:44 +00:00
|
|
|
// |AA MCI code or |Cx## movement where ## is in m[1]
|
2018-11-17 20:24:16 +00:00
|
|
|
let val = getPredefinedMCIValue(client, m[4] || m[1], m[2]);
|
2022-06-05 20:04:25 +00:00
|
|
|
val = _.isString(val) ? val : m[0]; // value itself or literal
|
2018-09-15 03:11:44 +00:00
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + val;
|
2022-06-05 20:04:25 +00:00
|
|
|
} else if (m[5]) {
|
2018-09-15 03:11:44 +00:00
|
|
|
// || -- literal '|', that is.
|
|
|
|
result += '|';
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
lastIndex = re.lastIndex;
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
return 0 === result.length ? s : result + s.substr(lastIndex);
|
2015-07-06 01:05:55 +00:00
|
|
|
}
|
2017-11-13 01:55:57 +00:00
|
|
|
|
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// Converts various control codes popular in BBS packages
|
2020-12-31 22:35:44 +00:00
|
|
|
// to ANSI escape sequences. Additionally supports ENiGMA style
|
2018-06-23 03:26:46 +00:00
|
|
|
// MCI codes.
|
2017-11-13 01:55:57 +00:00
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// Supported control code formats:
|
2018-09-15 19:12:54 +00:00
|
|
|
// * Renegade : |##
|
2019-01-19 06:46:15 +00:00
|
|
|
// * PCBoard : @X## where the first number/char is BG color, and second is FG
|
2018-09-15 19:12:54 +00:00
|
|
|
// * WildCat! : @##@ the same as PCBoard without the X prefix, but with a @ suffix
|
|
|
|
// * WWIV : ^#
|
2020-12-31 22:35:44 +00:00
|
|
|
// * CNET Control-Y: AKA Y-Style -- 0x19## where ## is a specific set of codes (older format)
|
|
|
|
// * CNET Control-Q: AKA Q-style -- 0x11##} where ## is a specific set of codes (newer format)
|
2017-11-13 01:55:57 +00:00
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// TODO: Add Synchronet and Celerity format support
|
2017-11-13 01:55:57 +00:00
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// Resources:
|
|
|
|
// * http://wiki.synchro.net/custom:colors
|
2020-12-31 22:35:44 +00:00
|
|
|
// * https://archive.org/stream/C-Net_Pro_3.0_1994_Perspective_Software/C-Net_Pro_3.0_1994_Perspective_Software_djvu.txt
|
2017-11-13 01:55:57 +00:00
|
|
|
//
|
|
|
|
function controlCodesToAnsi(s, client) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const RE =
|
|
|
|
/(\|([A-Z0-9]{2})|\|)|(@X([0-9A-F]{2}))|(@([0-9A-F]{2})@)|(\x03[0-9]|\x03)|(\x19(c[0-9a-f]|z[0-7]|n1|f1|q1)|\x19)|(\x11(c[0-9a-f]|z[0-7]|n1|f1|q1)}|\x11)/g; // eslint-disable-line no-control-regex
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
let m;
|
2022-06-05 20:04:25 +00:00
|
|
|
let result = '';
|
|
|
|
let lastIndex = 0;
|
2018-06-22 05:15:04 +00:00
|
|
|
let v;
|
|
|
|
let fg;
|
|
|
|
let bg;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
while ((m = RE.exec(s))) {
|
|
|
|
switch (m[0].charAt(0)) {
|
|
|
|
case '|':
|
2018-09-15 02:34:39 +00:00
|
|
|
// Renegade |##
|
2018-06-22 05:15:04 +00:00
|
|
|
v = parseInt(m[2], 10);
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (isNaN(v)) {
|
|
|
|
v = getPredefinedMCIValue(client, m[2]) || m[0]; // value itself or literal
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (_.isString(v)) {
|
2018-06-22 05:15:04 +00:00
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + v;
|
|
|
|
} else {
|
|
|
|
v = ansiSgrFromRenegadeColorCode(v);
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + v;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
case '@':
|
2018-06-23 03:26:46 +00:00
|
|
|
// PCBoard @X## or Wildcat! @##@
|
2022-06-05 20:04:25 +00:00
|
|
|
if ('@' === m[0].substr(-1)) {
|
2018-06-23 03:26:46 +00:00
|
|
|
// Wildcat!
|
2018-06-22 05:15:04 +00:00
|
|
|
v = m[6];
|
|
|
|
} else {
|
|
|
|
v = m[4];
|
|
|
|
}
|
|
|
|
|
|
|
|
bg = {
|
2022-06-05 20:04:25 +00:00
|
|
|
0: ['blackBG'],
|
|
|
|
1: ['blueBG'],
|
|
|
|
2: ['greenBG'],
|
|
|
|
3: ['cyanBG'],
|
|
|
|
4: ['redBG'],
|
|
|
|
5: ['magentaBG'],
|
|
|
|
6: ['yellowBG'],
|
|
|
|
7: ['whiteBG'],
|
|
|
|
|
|
|
|
8: ['bold', 'blackBG'],
|
|
|
|
9: ['bold', 'blueBG'],
|
|
|
|
A: ['bold', 'greenBG'],
|
|
|
|
B: ['bold', 'cyanBG'],
|
|
|
|
C: ['bold', 'redBG'],
|
|
|
|
D: ['bold', 'magentaBG'],
|
|
|
|
E: ['bold', 'yellowBG'],
|
|
|
|
F: ['bold', 'whiteBG'],
|
|
|
|
}[v.charAt(0)] || ['normal'];
|
2019-01-19 06:46:15 +00:00
|
|
|
|
|
|
|
fg = {
|
2022-06-05 20:04:25 +00:00
|
|
|
0: ['reset', 'black'],
|
|
|
|
1: ['reset', 'blue'],
|
|
|
|
2: ['reset', 'green'],
|
|
|
|
3: ['reset', 'cyan'],
|
|
|
|
4: ['reset', 'red'],
|
|
|
|
5: ['reset', 'magenta'],
|
|
|
|
6: ['reset', 'yellow'],
|
|
|
|
7: ['reset', 'white'],
|
|
|
|
|
|
|
|
8: ['blink', 'black'],
|
|
|
|
9: ['blink', 'blue'],
|
|
|
|
A: ['blink', 'green'],
|
|
|
|
B: ['blink', 'cyan'],
|
|
|
|
C: ['blink', 'red'],
|
|
|
|
D: ['blink', 'magenta'],
|
|
|
|
E: ['blink', 'yellow'],
|
|
|
|
F: ['blink', 'white'],
|
2019-01-19 06:46:15 +00:00
|
|
|
}[v.charAt(1)] || ['normal'];
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2018-09-15 02:34:39 +00:00
|
|
|
v = ANSI.sgr(fg.concat(bg));
|
2018-06-22 05:15:04 +00:00
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + v;
|
|
|
|
break;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
case '\x03':
|
2018-09-15 02:34:39 +00:00
|
|
|
// WWIV
|
2018-06-22 05:15:04 +00:00
|
|
|
v = parseInt(m[8], 10);
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (isNaN(v)) {
|
2018-06-22 05:15:04 +00:00
|
|
|
v += m[0];
|
|
|
|
} else {
|
2022-06-05 20:04:25 +00:00
|
|
|
v = ANSI.sgr(
|
|
|
|
{
|
|
|
|
0: ['reset', 'black'],
|
|
|
|
1: ['bold', 'cyan'],
|
|
|
|
2: ['bold', 'yellow'],
|
|
|
|
3: ['reset', 'magenta'],
|
|
|
|
4: ['bold', 'white', 'blueBG'],
|
|
|
|
5: ['reset', 'green'],
|
|
|
|
6: ['bold', 'blink', 'red'],
|
|
|
|
7: ['bold', 'blue'],
|
|
|
|
8: ['reset', 'blue'],
|
|
|
|
9: ['reset', 'cyan'],
|
|
|
|
}[v] || 'normal'
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + v;
|
|
|
|
break;
|
2018-09-15 19:12:54 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
case '\x19':
|
|
|
|
case '\0x11':
|
2018-09-15 19:12:54 +00:00
|
|
|
// CNET "Y-Style" & "Q-Style"
|
|
|
|
v = m[9] || m[11];
|
2022-06-05 20:04:25 +00:00
|
|
|
if (v) {
|
|
|
|
if ('n1' === v) {
|
2018-12-18 04:38:09 +00:00
|
|
|
v = '\n';
|
2022-06-05 20:04:25 +00:00
|
|
|
} else if ('f1' === v) {
|
2018-12-18 04:38:09 +00:00
|
|
|
v = ANSI.clearScreen();
|
|
|
|
} else {
|
|
|
|
v = ansiSgrFromCnetStyleColorCode(v);
|
|
|
|
}
|
2018-09-15 19:12:54 +00:00
|
|
|
} else {
|
2018-12-18 04:38:09 +00:00
|
|
|
v = m[0];
|
2018-09-15 19:12:54 +00:00
|
|
|
}
|
2018-12-18 04:38:09 +00:00
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + v;
|
2018-09-15 19:12:54 +00:00
|
|
|
break;
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
lastIndex = RE.lastIndex;
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
return 0 === result.length ? s : result + s.substr(lastIndex);
|
|
|
|
}
|