Last active
March 26, 2017 23:34
-
-
Save zeh/7295acd2cdb4d7f8252b9302672db51f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Converts ANSi Art source text to console.log()-compatible color codes that work on Chrome and FireFox. | |
Usage: | |
console.log.apply(null, ANSIColorUtils.convertToConsole(ansiSource:string, numColumns:number)); | |
Example: | |
console.log.apply(null, ANSIColorUtils.convertToConsole(` | |
[0;40;37m | |
[32m [37m [32m[17C[1;37m° | |
[0;32m [37m [32m [1m°[0;32m[12C[1;42;37mܲ[32mÜÜÜ[0;32mÛ | |
[37m [32mÜÜÜ[1;37m²[42;32m²[0;32mÝ ÜÜÜ[1;42m²ÜÜÛÛÛÛܲ[0;32mÝ | |
[37m [32m [1;42m°²[40;37mÛÛ[0;32mÝ Ü[1;42mÜ[37mÜ[32mÛÛÛÛÛÛ[40;33mÛÛ[42;32mÛÛ[0;32mÛ | |
[37m [32m Þ[1;42mÛÛÛÛÛÛÛÛÛÛÛÛÛ²ÛßÛ[0;32mÛ | |
[37m [32m Û[1;33mÛ[42;32mÛÛÛÛÛßßßß[0;32mÛ[1;42mÞ°Û[0;32mÛÛ | |
[37m [32m Û[1;42mÛ²[0;32mÛÛßßß Û[1;42mÛ[0;32mÛ[1;42mÛ[0;32mÛ | |
[37m [1;30m [0;32m Û[1;42m°[0;32mÝ [1;30m [0;32m ÜÜÛ[1;42m°ß[0;32mÛÛßß | |
[1;30m-Ä-[0m-[1;30m- -[0;32m ß [1;30m-[0;32m ÞÛÛÛÛ [1;30mþ[0;32m [1;30m-[0;32m [1;30mÜ[0m [1;30m--Ä-[0m-[1;30m- -- | |
[0;32m [1;30m°°±[0;32m [1;30m°[0;32m [1;30m°[0;32m [1;30m°²[0;32m ÛÛ[1;42;30mÞÝ[0;32mÝ Ü[1;30mÜÜß [0;32m [1;30m°[0;32m [1;30m°±[0m [1;30m°°° | |
-[0m-[1;30m-Ä- -[0m-[1;30mÄ--[0;32m ²ÛÛßÛ[1;42;30m°²[40mßß[0;32m [1;30m- --Ä-[0m-[1;30m Ä-Ä-- | |
[0m app started [32mßÛÛÛßß[0m version x.y.z | |
[1;30m built @ 2017-01-02 - dev mode is ON | |
[0m` | |
, 37)); | |
*/ | |
export default class ANSIColorUtils { | |
// TODO: | |
// * make this less verbose/repetitive | |
// * better char conversion | |
// * better seekahead | |
private static readonly COLORS = [ | |
"rgb(0, 0, 0)", | |
"rgb(170, 0, 0)", | |
"rgb(0, 170, 0)", | |
"rgb(170, 85, 0)", | |
"rgb(0, 0, 170)", | |
"rgb(170, 0, 170)", | |
"rgb(0, 170, 170)", | |
"rgb(170, 170, 170)", | |
"rgb(85, 85, 85)", | |
"rgb(255, 85, 85)", | |
"rgb(85, 255, 85)", | |
"rgb(255, 255, 85)", | |
"rgb(85, 85, 255)", | |
"rgb(255, 85, 255)", | |
"rgb(85, 255, 255)", | |
"rgb(255, 255, 255)", | |
]; | |
private static readonly CHARS = [ | |
[ 223, "¯"], | |
[ 254, "¦"], | |
[ 220, "_"], | |
[ 221, "¦"], | |
[ 222, "¦"], | |
[ 176, "¦"], | |
[ 177, "¦"], | |
[ 178, "¦"], | |
[ 219, "¦"], | |
[ 196, "-"], | |
]; | |
private static readonly ESC_START = "\x1B["; | |
private static readonly ESC_END_COLOR = "m"; | |
private static readonly ESC_END_SPACES = "C"; | |
private static readonly LINE_FEED = "\n"; | |
public static convertToConsole(ansi:string, columns:number = 0):string[] { | |
let output:string = ""; | |
let colors:string[] = []; | |
let fg = 7; | |
let bg = 0; | |
let flagHigh = false; | |
let flagBlink = false; | |
let flagUnderline = false; | |
let l = ansi.length; | |
let currLineColumns = 0; | |
let pos:number = 0; | |
do { | |
if (ansi.substr(pos, ANSIColorUtils.ESC_START.length) === ANSIColorUtils.ESC_START) { | |
// Color code | |
pos += ANSIColorUtils.ESC_START.length; | |
// Starting code | |
const endColorPos = ansi.indexOf(ANSIColorUtils.ESC_END_COLOR, pos); | |
const endSpacesPos = ansi.indexOf(ANSIColorUtils.ESC_END_SPACES, pos); | |
if (endColorPos > -1 && (endColorPos < endSpacesPos || endSpacesPos < 0)) { | |
// Color command | |
const colorCodes = ansi.substr(pos, endColorPos - pos).split(";"); | |
pos = endColorPos + ANSIColorUtils.ESC_END_COLOR.length; | |
// Adds codes from colors | |
output += "%c"; | |
for (let colorCode of colorCodes) { | |
switch (colorCode) { | |
case "0": | |
fg = 7; | |
bg = 0; | |
flagHigh = false; | |
flagBlink = false; | |
flagUnderline = false; | |
break; | |
case "1": | |
flagHigh = true; | |
break; | |
case "4": | |
flagUnderline = true; | |
break; | |
case "5": | |
flagBlink = true; | |
break; | |
case "7": | |
fg = 7; | |
bg = 0; | |
flagHigh = false; | |
break; | |
case "8": | |
fg = bg; | |
break; | |
case "30": | |
fg = 0; | |
break; | |
case "31": | |
fg = 1; | |
break; | |
case "32": | |
fg = 2; | |
break; | |
case "33": | |
fg = 3; | |
break; | |
case "34": | |
fg = 4; | |
break; | |
case "35": | |
fg = 5; | |
break; | |
case "36": | |
fg = 6; | |
break; | |
case "37": | |
fg = 7; | |
break; | |
case "40": | |
bg = 0; | |
break; | |
case "41": | |
bg = 1; | |
break; | |
case "42": | |
bg = 2; | |
break; | |
case "43": | |
bg = 3; | |
break; | |
case "44": | |
bg = 4; | |
break; | |
case "45": | |
bg = 5; | |
break; | |
case "46": | |
bg = 6; | |
break; | |
case "47": | |
bg = 7; | |
break; | |
default: | |
console.warn(`Error processing color code [${colorCode}].`); | |
break; | |
} | |
} | |
colors.push(`color: ${ANSIColorUtils.COLORS[fg + (flagHigh ? 8 : 0)]}; background-color: ${ANSIColorUtils.COLORS[bg]}`); | |
} else if (endSpacesPos > -1 && (endSpacesPos < endColorPos || endColorPos < 0)) { | |
// Spaces command | |
let spaces = parseInt(ansi.substr(pos, endSpacesPos - pos), 10); | |
output += (" ").repeat(spaces); | |
currLineColumns += spaces; | |
pos = endSpacesPos + ANSIColorUtils.ESC_END_SPACES.length; | |
} | |
} else if (ansi.substr(pos, ANSIColorUtils.LINE_FEED.length) === ANSIColorUtils.LINE_FEED) { | |
// New line | |
if (currLineColumns < columns) output += (" ").repeat(columns - currLineColumns); | |
output += "\n"; | |
currLineColumns = 0; | |
pos += ANSIColorUtils.LINE_FEED.length; | |
} else { | |
// Normal character | |
let nextPosColors = ansi.indexOf(ANSIColorUtils.ESC_START, pos); | |
let nextPosLineFeed = ansi.indexOf(ANSIColorUtils.LINE_FEED, pos); | |
let nextPos; | |
if (nextPosColors < 0 && nextPosLineFeed < 0) { | |
// No more color codes or line feeds | |
nextPos = ansi.length; | |
} else if (nextPosColors < 0) { | |
nextPos = nextPosLineFeed; | |
} else if (nextPosLineFeed < 0) { | |
nextPos = nextPosColors; | |
} else { | |
nextPos = Math.min(nextPosColors, nextPosLineFeed); | |
} | |
output += ansi.substr(pos, nextPos - pos); | |
currLineColumns += nextPos - pos; | |
pos = nextPos; | |
} | |
} while (pos < l); | |
// Add spaces again | |
if (currLineColumns < columns) output += (" ").repeat(columns - currLineColumns); | |
for (let char of ANSIColorUtils.CHARS) { | |
output = output.replace(new RegExp(String.fromCharCode(char[0] as number), "g"), char[1] as string); | |
} | |
return [output].concat(colors); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment