2015-06-30 19:04:58 +00:00
|
|
|
/* jslint node: true */
|
|
|
|
'use strict';
|
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
var ansi = require('./ansi_term.js');
|
|
|
|
var getPredefinedMCIValue = require('./predefined_mci.js').getPredefinedMCIValue;
|
2015-06-30 19:04:58 +00:00
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
var assert = require('assert');
|
|
|
|
var _ = require('lodash');
|
2015-06-30 19:04:58 +00:00
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
exports.enigmaToAnsi = enigmaToAnsi;
|
|
|
|
exports.stripPipeCodes = exports.stripEnigmaCodes = stripEnigmaCodes;
|
|
|
|
exports.pipeStrLen = exports.enigmaStrLen = enigmaStrLen;
|
|
|
|
exports.pipeToAnsi = exports.renegadeToAnsi = renegadeToAnsi;
|
|
|
|
exports.controlCodesToAnsi = controlCodesToAnsi;
|
2015-06-30 19:04:58 +00:00
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// :TODO: Not really happy with the module name of "color_codes". Would like something better
|
2015-06-30 19:04:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2017-11-13 01:55:57 +00:00
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// Also add:
|
|
|
|
// * fromCelerity(): |<case sensitive letter>
|
|
|
|
// * fromPCBoard(): (@X<bg><fg>)
|
|
|
|
// * fromWildcat(): (@<bg><fg>@ (same as PCBoard without 'X' prefix and '@' suffix)
|
|
|
|
// * fromWWIV(): <ctrl-c><0-7>
|
|
|
|
// * fromSyncronet(): <ctrl-a><colorCode>
|
|
|
|
// See http://wiki.synchro.net/custom:colors
|
2017-09-01 23:42:47 +00:00
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// :TODO: rid of enigmaToAnsi() -- never really use. Instead, create bbsToAnsi() that supports renegade, PCB, WWIV, etc...
|
2015-09-11 03:01:04 +00:00
|
|
|
function enigmaToAnsi(s, client) {
|
2018-06-22 05:15:04 +00:00
|
|
|
if(-1 == s.indexOf('|')) {
|
2018-06-23 03:26:46 +00:00
|
|
|
return s; // no pipe codes present
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
var result = '';
|
|
|
|
var re = /\|([A-Z\d]{2}|\|)/g;
|
2018-06-22 05:15:04 +00:00
|
|
|
var m;
|
|
|
|
var lastIndex = 0;
|
|
|
|
while((m = re.exec(s))) {
|
|
|
|
var val = m[1];
|
|
|
|
|
|
|
|
if('|' == val) {
|
|
|
|
result += '|';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// convert to number
|
2018-06-22 05:15:04 +00:00
|
|
|
val = parseInt(val, 10);
|
|
|
|
if(isNaN(val)) {
|
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// ENiGMA MCI code? Only available if |client|
|
|
|
|
// is supplied.
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
val = getPredefinedMCIValue(client, m[1]) || ('|' + m[1]); // value itself or literal
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(_.isString(val)) {
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + val;
|
|
|
|
} else {
|
|
|
|
assert(val >= 0 && val <= 47);
|
|
|
|
|
|
|
|
var attr = '';
|
|
|
|
if(7 == val) {
|
|
|
|
attr = ansi.sgr('normal');
|
|
|
|
} else if (val < 7 || val >= 16) {
|
|
|
|
attr = ansi.sgr(['normal', val]);
|
|
|
|
} else if (val <= 15) {
|
|
|
|
attr = ansi.sgr(['normal', val - 8, 'bold']);
|
|
|
|
}
|
|
|
|
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastIndex = re.lastIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = (0 === result.length ? s : result + s.substr(lastIndex));
|
|
|
|
|
|
|
|
return result;
|
2015-06-30 19:04:58 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 05:17:09 +00:00
|
|
|
function stripEnigmaCodes(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
|
|
|
}
|
|
|
|
|
|
|
|
function enigmaStrLen(s) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return stripEnigmaCodes(s).length;
|
2015-08-26 05:17:09 +00:00
|
|
|
}
|
|
|
|
|
2017-11-18 23:15:50 +00:00
|
|
|
function ansiSgrFromRenegadeColorCode(cc) {
|
2018-06-22 05:15:04 +00:00
|
|
|
return ansi.sgr({
|
2018-06-23 03:26:46 +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 : [ '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' ],
|
2018-06-22 05:15:04 +00:00
|
|
|
}[cc] || 'normal');
|
2017-11-18 23:15:50 +00:00
|
|
|
}
|
|
|
|
|
2015-12-22 01:07:03 +00:00
|
|
|
function renegadeToAnsi(s, client) {
|
2018-06-22 05:15:04 +00:00
|
|
|
if(-1 == s.indexOf('|')) {
|
2018-06-23 03:26:46 +00:00
|
|
|
return s; // no pipe codes present
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
var result = '';
|
|
|
|
var re = /\|([A-Z\d]{2}|\|)/g;
|
2018-06-22 05:15:04 +00:00
|
|
|
var m;
|
|
|
|
var lastIndex = 0;
|
|
|
|
while((m = re.exec(s))) {
|
|
|
|
var val = m[1];
|
|
|
|
|
|
|
|
if('|' == val) {
|
|
|
|
result += '|';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// convert to number
|
2018-06-22 05:15:04 +00:00
|
|
|
val = parseInt(val, 10);
|
|
|
|
if(isNaN(val)) {
|
2018-06-23 03:26:46 +00:00
|
|
|
val = getPredefinedMCIValue(client, m[1]) || ('|' + m[1]); // value itself or literal
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(_.isString(val)) {
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + val;
|
|
|
|
} else {
|
|
|
|
const attr = ansiSgrFromRenegadeColorCode(val);
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastIndex = re.lastIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
// to ANSI escape sequences. Additionaly supports ENiGMA style
|
|
|
|
// MCI codes.
|
2017-11-13 01:55:57 +00:00
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// Supported control code formats:
|
|
|
|
// * Renegade : |##
|
|
|
|
// * PCBoard : @X## where the first number/char is FG color, and second is BG
|
|
|
|
// * WildCat! : @##@ the same as PCBoard without the X prefix, but with a @ suffix
|
|
|
|
// * WWIV : ^#
|
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
|
2017-11-13 01:55:57 +00:00
|
|
|
//
|
|
|
|
function controlCodesToAnsi(s, client) {
|
2018-06-23 03:26:46 +00:00
|
|
|
const RE = /(\|([A-Z0-9]{2})|\|)|(@X([0-9A-F]{2}))|(@([0-9A-F]{2})@)|(\x03[0-9]|\x03)/g; // eslint-disable-line no-control-regex
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
let m;
|
2018-06-23 03:26:46 +00:00
|
|
|
let result = '';
|
|
|
|
let lastIndex = 0;
|
2018-06-22 05:15:04 +00:00
|
|
|
let v;
|
|
|
|
let fg;
|
|
|
|
let bg;
|
|
|
|
|
|
|
|
while((m = RE.exec(s))) {
|
|
|
|
switch(m[0].charAt(0)) {
|
|
|
|
case '|' :
|
2018-06-23 03:26:46 +00:00
|
|
|
// Renegade or ENiGMA MCI
|
2018-06-22 05:15:04 +00:00
|
|
|
v = parseInt(m[2], 10);
|
|
|
|
|
|
|
|
if(isNaN(v)) {
|
2018-06-23 03:26:46 +00:00
|
|
|
v = getPredefinedMCIValue(client, m[2]) || m[0]; // value itself or literal
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(_.isString(v)) {
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + v;
|
|
|
|
} else {
|
|
|
|
v = ansiSgrFromRenegadeColorCode(v);
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + v;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '@' :
|
2018-06-23 03:26:46 +00:00
|
|
|
// PCBoard @X## or Wildcat! @##@
|
2018-06-22 05:15:04 +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];
|
|
|
|
}
|
|
|
|
|
|
|
|
fg = {
|
2018-06-23 03:26:46 +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' ],
|
2018-06-22 05:15:04 +00:00
|
|
|
}[v.charAt(0)] || ['normal'];
|
|
|
|
|
|
|
|
bg = {
|
2018-06-23 03:26:46 +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' ],
|
2018-06-22 05:15:04 +00:00
|
|
|
}[v.charAt(1)] || [ 'normal' ];
|
|
|
|
|
|
|
|
v = ansi.sgr(fg.concat(bg));
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + v;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\x03' :
|
|
|
|
v = parseInt(m[8], 10);
|
|
|
|
|
|
|
|
if(isNaN(v)) {
|
|
|
|
v += m[0];
|
|
|
|
} else {
|
|
|
|
v = ansi.sgr({
|
2018-06-23 03:26:46 +00:00
|
|
|
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' ],
|
2018-06-22 05:15:04 +00:00
|
|
|
}[v] || 'normal');
|
|
|
|
}
|
|
|
|
|
|
|
|
result += s.substr(lastIndex, m.index - lastIndex) + v;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastIndex = RE.lastIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0 === result.length ? s : result + s.substr(lastIndex));
|
2017-11-13 01:55:57 +00:00
|
|
|
}
|