5 * - http://en.wikipedia.org/wiki/ANSI_escape_code
6 * - http://www.termsys.demon.co.uk/vtansi.htm
11 * Module dependencies.
14 var emitNewlineEvents = require('./newlines')
15 , prefix = '\x1b[' // For all escape codes
16 , suffix = 'm' // Only for color codes
19 * The ANSI escape sequences.
29 , horizontalAbsolute: 'G'
35 , restorePosition: 'u'
42 * Rendering ANSI codes.
53 * The negating ANSI code for the rendering modes.
64 * The standard, styleable ANSI colors.
89 * Creates a Cursor instance based off the given `writable stream` instance.
92 function ansi (stream, options) {
93 if (stream._ansicursor) {
94 return stream._ansicursor
96 return stream._ansicursor = new Cursor(stream, options)
99 module.exports = exports = ansi
102 * The `Cursor` class.
105 function Cursor (stream, options) {
106 if (!(this instanceof Cursor)) {
107 return new Cursor(stream, options)
109 if (typeof stream != 'object' || typeof stream.write != 'function') {
110 throw new Error('a valid Stream instance must be passed in')
116 // when 'enabled' is false then all the functions are no-ops except for write()
117 this.enabled = options && options.enabled
118 if (typeof this.enabled === 'undefined') {
119 this.enabled = stream.isTTY
121 this.enabled = !!this.enabled
123 // then `buffering` is true, then `write()` calls are buffered in
124 // memory until `flush()` is invoked
125 this.buffering = !!(options && options.buffering)
128 // controls the foreground and background colors
129 this.fg = this.foreground = new Colorer(this, 0)
130 this.bg = this.background = new Colorer(this, 10)
135 this.Underline = false
138 // keep track of the number of "newlines" that get encountered
140 emitNewlineEvents(stream)
141 stream.on('newline', function () {
145 exports.Cursor = Cursor
148 * Helper function that calls `write()` on the underlying Stream.
149 * Returns `this` instead of the write() return value to keep
150 * the chaining going.
153 Cursor.prototype.write = function (data) {
154 if (this.buffering) {
155 this._buffer.push(arguments)
157 this.stream.write.apply(this.stream, arguments)
163 * Buffer `write()` calls into memory.
168 Cursor.prototype.buffer = function () {
169 this.buffering = true
174 * Write out the in-memory buffer.
179 Cursor.prototype.flush = function () {
180 this.buffering = false
181 var str = this._buffer.map(function (args) {
182 if (args.length != 1) throw new Error('unexpected args length! ' + args.length);
185 this._buffer.splice(0); // empty
192 * The `Colorer` class manages both the background and foreground colors.
195 function Colorer (cursor, base) {
200 exports.Colorer = Colorer
203 * Write an ANSI color code, ensuring that the same code doesn't get rewritten.
206 Colorer.prototype._setColorCode = function setColorCode (code) {
208 if (this.current === c) return
209 this.cursor.enabled && this.cursor.write(prefix + c + suffix)
216 * Set up the positional ANSI codes.
219 Object.keys(codes).forEach(function (name) {
220 var code = String(codes[name])
221 Cursor.prototype[name] = function () {
223 if (arguments.length > 0) {
224 c = toArray(arguments).map(Math.round).join(';') + code
226 this.enabled && this.write(prefix + c)
232 * Set up the functions for the rendering ANSI codes.
235 Object.keys(styles).forEach(function (style) {
236 var name = style[0].toUpperCase() + style.substring(1)
240 Cursor.prototype[style] = function () {
241 if (this[name]) return this
242 this.enabled && this.write(prefix + c + suffix)
247 Cursor.prototype['reset' + name] = function () {
248 if (!this[name]) return this
249 this.enabled && this.write(prefix + r + suffix)
256 * Setup the functions for the standard colors.
259 Object.keys(colors).forEach(function (color) {
260 var code = colors[color]
262 Colorer.prototype[color] = function () {
263 this._setColorCode(this.base + code)
267 Cursor.prototype[color] = function () {
268 return this.foreground[color]()
273 * Makes a beep sound!
276 Cursor.prototype.beep = function () {
277 this.enabled && this.write('\x07')
282 * Moves cursor to specific position
285 Cursor.prototype.goto = function (x, y) {
288 this.enabled && this.write(prefix + y + ';' + x + 'H')
296 Colorer.prototype.reset = function () {
297 this._setColorCode(this.base + 39)
302 * Resets all ANSI formatting on the stream.
305 Cursor.prototype.reset = function () {
306 this.enabled && this.write(prefix + '0' + suffix)
309 this.Underline = false
311 this.foreground.current = null
312 this.background.current = null
317 * Sets the foreground color with the given RGB values.
318 * The closest match out of the 216 colors is picked.
321 Colorer.prototype.rgb = function (r, g, b) {
322 var base = this.base + 38
323 , code = rgb(r, g, b)
324 this._setColorCode(base + ';5;' + code)
329 * Same as `cursor.fg.rgb(r, g, b)`.
332 Cursor.prototype.rgb = function (r, g, b) {
333 return this.foreground.rgb(r, g, b)
337 * Accepts CSS color codes for use with ANSI escape codes.
338 * For example: `#FF000` would be bright red.
341 Colorer.prototype.hex = function (color) {
342 return this.rgb.apply(this, hex(color))
346 * Same as `cursor.fg.hex(color)`.
349 Cursor.prototype.hex = function (color) {
350 return this.foreground.hex(color)
357 * Translates a 255 RGB value to a 0-5 ANSI RGV value,
358 * then returns the single ANSI color code to use.
361 function rgb (r, g, b) {
362 var red = r / 255 * 5
363 , green = g / 255 * 5
365 return rgb5(red, green, blue)
369 * Turns rgb 0-5 values into a single ANSI color code to use.
372 function rgb5 (r, g, b) {
373 var red = Math.round(r)
374 , green = Math.round(g)
375 , blue = Math.round(b)
376 return 16 + (red*36) + (green*6) + blue
380 * Accepts a hex CSS color code string (# is optional) and
381 * translates it into an Array of 3 RGB 0-255 values, which
382 * can then be used with rgb().
385 function hex (color) {
386 var c = color[0] === '#' ? color.substring(1) : color
387 , r = c.substring(0, 2)
388 , g = c.substring(2, 4)
389 , b = c.substring(4, 6)
390 return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)]
394 * Turns an array-like object into a real array.
397 function toArray (a) {