Skip to content

Commit

Permalink
add js deansi
Browse files Browse the repository at this point in the history
  • Loading branch information
Sven Fuchs committed Jul 4, 2011
1 parent 1c4871a commit a88e446
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 0 deletions.
50 changes: 50 additions & 0 deletions public/javascripts/lib/deansi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
var Deansi = {
styles: {
'0': null,
'1': 'bold',
'4': 'underscore',
'30': 'black',
'31': 'red',
'32': 'green',
'33': 'yellow',
'34': 'blue',
'35': 'magenta',
'36': 'cyan',
'37': 'white',
'40': 'bg-black',
'41': 'bg-red',
'42': 'bg-green',
'43': 'bg-yellow',
'44': 'bg-blue',
'45': 'bg-magenta',
'46': 'bg-cyan',
'47': 'bg-white',
},
parse: function(string) {
string = this.replace_escapes(string);
string = this.replace_styles(string);
string = this.remove_closings(string);
string = this.parse_linefeeds(string);
return string;
},
replace_escapes: function(string) {
return string.replace(String.fromCharCode(27), '');
},
replace_styles: function(string) {
var pattern = /\[(?:0;)?((?:1|4|30|31|32|33|34|35|36|37|40|41|42|43|44|45|46|47|;)+)m(.*?)(?=(\[(?:[\d;]?m\(B)?)|$)/gm;
return string.replace(pattern, function(match, styles, string) {
return '<span class="' + Deansi.to_styles(styles) + '">' + string + '</span>';
});
},
remove_closings: function(string) {
return string.replace(/\[0?m(?:\(B)?/gm, '');
},
parse_linefeeds: function(string) {
string = string.replace(/\[K\r/, "\r");
string = string.replace(/^.*\r(?!$)/gm, '');
return string;
},
to_styles: function(string) {
return _.compact(_.map(string.split(';'), function(number) { return Deansi.styles[number]; })).join(' ');
},
};
115 changes: 115 additions & 0 deletions public/javascripts/tests/lib/deansi_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
String.prototype.repeat = function(num) {
return new Array(num + 1).join(this);
}

describe('Deansi', function() {
function deansi(string) {
return Deansi.parse(string);
};

describe('colors', function() {
var sets = [
{ ansi: "[30m", class: 'black' },
{ ansi: "[31m", class: 'red' },
{ ansi: "[32m", class: 'green' },
{ ansi: "[33m", class: 'yellow' },
{ ansi: "[34m", class: 'blue' },
{ ansi: "[35m", class: 'magenta' },
{ ansi: "[36m", class: 'cyan' },
{ ansi: "[37m", class: 'white' },
{ ansi: "[30;1m", class: 'black bold' },
{ ansi: "[31;1m", class: 'red bold' },
{ ansi: "[32;1m", class: 'green bold' },
{ ansi: "[34;1m", class: 'blue bold' },
{ ansi: "[33;1m", class: 'yellow bold' },
{ ansi: "[35;1m", class: 'magenta bold' },
{ ansi: "[36;1m", class: 'cyan bold' },
{ ansi: "[37;1m", class: 'white bold' },
{ ansi: "[42;37;1m", class: 'bg-green white bold' },
];

it('replaces ANSI escape sequences having no closing sequence with a span having the respective css classes', function() {
for(ix in sets) {
var set = sets[ix];
source = (set.ansi + 'FOO').repeat(3);
expected = ('<span class="' + set.class +'">FOO</span>').repeat(3);
expect(deansi(source)).toEqual(expected);
}
});

it('replaces ANSI escape sequences having a closing sequence [m with a span having the respective css classes', function() {
for(ix in sets) {
var set = sets[ix];
source = (set.ansi + 'FOO[m').repeat(3);
expected = ('<span class="' + set.class +'">FOO</span>').repeat(3);
expect(deansi(source)).toEqual(expected);
}
});

it('replaces ANSI escape sequences having a closing sequence [0m with a span having the respective css classes', function() {
for(ix in sets) {
var set = sets[ix];
source = (set.ansi + 'FOO[0m').repeat(3);
expected = ('<span class="' + set.class +'">FOO</span>').repeat(3);
expect(deansi(source)).toEqual(expected);
}
});

// not sure where this comes from. can't find it in http://ascii-table.com/ansi-escape-sequences.php
it('replaces ANSI escape sequences having a closing sequence [m(B with a span having the respective css classes', function() {
for(ix in sets) {
var set = sets[ix];
source = (set.ansi + 'FOO[m(B').repeat(3);
expected = ('<span class="' + set.class +'">FOO</span>').repeat(3);
expect(deansi(source)).toEqual(expected);
}
});

it('replaces ANSI escape sequence (real examples)', function() {
var examples = [
{ source: '[42;37;1mPassed[0m', result: '<span class="bg-green white bold">Passed</span>' },
{ source: '[0;33;40mFailure', result: '<span class="yellow bg-black">Failure</span>' },
{ source: '[0;37;40mSuccess', result: '<span class="white bg-black">Success</span>' },
{ source: '[32m.[0m[31mF[0m', result: '<span class="green">.</span><span class="red">F</span>' },
{ source: '[31m2 failed[0m, [33m2 undefined[0m, [32m35 passed[0m', result: '<span class="red">2 failed</span>, <span class="yellow">2 undefined</span>, <span class="green">35 passed</span>' },
{ source: '[31m2 failed[0m, [36m1 skipped[0m, [33m7 undefined[0m, [32m212 passed[0m', result: '<span class="red">2 failed</span>, <span class="cyan">1 skipped</span>, <span class="yellow">7 undefined</span>, <span class="green">212 passed</span>' }
]
for(ix in examples) {
var example = examples[ix];
expect(deansi(example.source)).toEqual(example.result);
}
});
});

describe('carriage returns', function() {
it('replaces a line followed by a carriage return', function() {
source = 'remote: Compressing objects: 100% (21/21) \rremote: Compressing objects: 100% (21/21), done.';
expected = 'remote: Compressing objects: 100% (21/21), done.';
expect(deansi(source)).toEqual(expected);
});

it('replaces a line followed by an ansii clear line escape sequence and a carriage return', function() {
source = 'remote: Compressing objects: 100% (21/21) [K\rremote: Compressing objects: 100% (21/21), done.';
expected = 'remote: Compressing objects: 100% (21/21), done.';
expect(deansi(source)).toEqual(expected);
});

it('removes [K sequences preceeding a carriage return', function() {
source = 'remote: Compressing objects: 100% (21/21), done. [K\r';
expected = 'remote: Compressing objects: 100% (21/21), done. \r';
expect(deansi(source)).toEqual(expected);
});

it('does not replaces a line followed by a carriage return when this is the last character in the string', function() {
source = 'remote: Compressing objects: 100% (21/21) \r';
expected = 'remote: Compressing objects: 100% (21/21) \r';
expect(deansi(source)).toEqual(expected);
});

it('does not replace a line followed by a carriage return and a newline', function() {
source = 'remote: Counting objects: 31, done.\r\nremote: Compressing objects: 100% (21/21), done.';
expected = 'remote: Counting objects: 31, done.\r\nremote: Compressing objects: 100% (21/21), done.';
expect(deansi(source)).toEqual(expected);
});
});
});

0 comments on commit a88e446

Please sign in to comment.