|
(function(Aspect) { |
|
Aspect.Renderer = new Class({ |
|
defaults: { |
|
"fg": 7, |
|
"bg": 0, |
|
}, |
|
|
|
initialize: function(world, element) { |
|
this.world = world; |
|
this.e = element; |
|
this.resetAttributes(); |
|
}, |
|
|
|
display: function(data) { |
|
var buf = [this.newStyleRun()]; |
|
var invalidated = false; |
|
|
|
for (var l = data.length, i = 0; i < l; ++i) { |
|
var entry = data[i]; |
|
|
|
if (entry instanceof Array) { |
|
if (entry[0] == "m") { |
|
this.setANSI(entry[1]); |
|
invalidated = true; |
|
} |
|
} else { |
|
if (invalidated) { |
|
buf.push("</span>", this.newStyleRun()); |
|
invalidated = false; |
|
} |
|
buf.push(entry.replace(/&/g, "&").replace(/</g, "<")); |
|
} |
|
} |
|
|
|
buf.push("</span>"); |
|
var span = document.createElement("span"); |
|
span.innerHTML = buf.join(""); |
|
this.e.appendChild(span); |
|
}, |
|
|
|
setANSI: function(commands) { |
|
// default case: \e[m |
|
if (commands.length == 0) { |
|
this.resetAttributes(); |
|
return; |
|
} |
|
|
|
// xterm-256: \e[38;5;Xm for FG or \e[48;5;Xm for BG, |
|
// where X is a number in the interval [0, 255]. |
|
if (commands.length == 3 && |
|
(commands[0] === 38 || commands[0] === 48) && |
|
commands[1] === 5) { |
|
var num = commands[2]; |
|
|
|
if (num !== null && (0 <= num && num <= 255)) { |
|
if (commands[0] === 38) { |
|
this.fg = num; |
|
this.xterm = true; |
|
} else { |
|
this.bg = num; |
|
} |
|
|
|
return; |
|
} |
|
} |
|
|
|
// Standard commands (well, mostly) |
|
for (var l = commands.length, i = 0; i < l; ++i) { |
|
var command = commands[i]; |
|
switch (command) { |
|
// Color sequences are used the most, so they come first |
|
case 30: case 31: case 32: case 33: |
|
case 34: case 35: case 36: case 37: |
|
this.fg = command - 30; |
|
this.xterm = false; |
|
break; |
|
|
|
case 40: case 41: case 42: case 43: |
|
case 44: case 45: case 46: case 47: |
|
this.bg = command - 40; |
|
break; |
|
|
|
case 0: this.resetAttributes(); break; |
|
case 1: this.bright = true; break; |
|
case 3: this.italic = true; break; |
|
case 4: this.underline = true; break; |
|
case 7: this.negative = true; break; |
|
case 8: this.concealed = true; break; |
|
case 9: this.strike = true; break; |
|
case 22: this.bright = false; break; |
|
case 23: this.italic = false; break; |
|
case 24: this.underline = false; break; |
|
case 27: this.negative = false; break; |
|
case 28: this.concealed = false; break; |
|
case 29: this.strike = false; break; |
|
|
|
case 39: |
|
this.fg = this.defaults.fg; |
|
this.xterm = false; |
|
break; |
|
|
|
case 49: |
|
this.bg = this.defaults.bg; |
|
break; |
|
|
|
case 90: case 91: case 92: case 93: |
|
case 94: case 95: case 96: case 97: |
|
this.fg = command - 90 + 8; |
|
this.xterm = true; |
|
break; |
|
case 100: case 101: case 102: case 103: |
|
case 104: case 105: case 106: case 107: |
|
this.bg = command - 100 + 8; |
|
this.xterm = true; |
|
break; |
|
} |
|
} |
|
}, |
|
|
|
newStyleRun: function() { |
|
var fg = this.fg; |
|
if (this.bright && !this.xterm) { |
|
fg += 8; |
|
} |
|
|
|
var bg = this.bg; |
|
if (this.negative) { |
|
bg = fg; |
|
fg = this.bg; |
|
} |
|
|
|
if (this.concealed) { |
|
fg = bg; |
|
} |
|
|
|
var span = ["<span class=\"fg" + fg + " bg" + bg]; |
|
if (this.italic) { span.push(" italic"); } |
|
if (this.strike) { span.push(" strike"); } |
|
if (this.underline) { span.push(" underline"); } |
|
span.push("\">"); |
|
|
|
return span.join(""); |
|
}, |
|
|
|
resetAttributes: function() { |
|
this.fg = this.defaults.fg; |
|
this.bg = this.defaults.bg; |
|
this.negative = this.italic = this.bright = this.strike = this.underline = this.concealed = this.xterm = false; |
|
} |
|
}); |
|
})(Aspect); |