- /**
- * References:
- *
- * - http://en.wikipedia.org/wiki/ANSI_escape_code
- * - http://www.termsys.demon.co.uk/vtansi.htm
- *
- */
- /**
- * Module dependencies.
- */
- var emitNewlineEvents = require('./newlines')
- , prefix = '\x1b[' // For all escape codes
- , suffix = 'm' // Only for color codes
- /**
- * The ANSI escape sequences.
- */
- var codes = {
- up: 'A'
- , down: 'B'
- , forward: 'C'
- , back: 'D'
- , nextLine: 'E'
- , previousLine: 'F'
- , horizontalAbsolute: 'G'
- , eraseData: 'J'
- , eraseLine: 'K'
- , scrollUp: 'S'
- , scrollDown: 'T'
- , savePosition: 's'
- , restorePosition: 'u'
- , queryPosition: '6n'
- , hide: '?25l'
- , show: '?25h'
- }
- /**
- * Rendering ANSI codes.
- */
- var styles = {
- bold: 1
- , italic: 3
- , underline: 4
- , inverse: 7
- }
- /**
- * The negating ANSI code for the rendering modes.
- */
- var reset = {
- bold: 22
- , italic: 23
- , underline: 24
- , inverse: 27
- }
- /**
- * The standard, styleable ANSI colors.
- */
- var colors = {
- white: 37
- , black: 30
- , blue: 34
- , cyan: 36
- , green: 32
- , magenta: 35
- , red: 31
- , yellow: 33
- , grey: 90
- , brightBlack: 90
- , brightRed: 91
- , brightGreen: 92
- , brightYellow: 93
- , brightBlue: 94
- , brightMagenta: 95
- , brightCyan: 96
- , brightWhite: 97
- }
- /**
- * Creates a Cursor instance based off the given `writable stream` instance.
- */
- function ansi (stream, options) {
- if (stream._ansicursor) {
- return stream._ansicursor
- } else {
- return stream._ansicursor = new Cursor(stream, options)
- }
- }
- module.exports = exports = ansi
- /**
- * The `Cursor` class.
- */
- function Cursor (stream, options) {
- if (!(this instanceof Cursor)) {
- return new Cursor(stream, options)
- }
- if (typeof stream != 'object' || typeof stream.write != 'function') {
- throw new Error('a valid Stream instance must be passed in')
- }
- // the stream to use
- this.stream = stream
- // when 'enabled' is false then all the functions are no-ops except for write()
- this.enabled = options && options.enabled
- if (typeof this.enabled === 'undefined') {
- this.enabled = stream.isTTY
- }
- this.enabled = !!this.enabled
- // then `buffering` is true, then `write()` calls are buffered in
- // memory until `flush()` is invoked
- this.buffering = !!(options && options.buffering)
- this._buffer = []
- // controls the foreground and background colors
- this.fg = this.foreground = new Colorer(this, 0)
- this.bg = this.background = new Colorer(this, 10)
- // defaults
- this.Bold = false
- this.Italic = false
- this.Underline = false
- this.Inverse = false
- // keep track of the number of "newlines" that get encountered
- this.newlines = 0
- emitNewlineEvents(stream)
- stream.on('newline', function () {
- this.newlines++
- }.bind(this))
- }
- exports.Cursor = Cursor
- /**
- * Helper function that calls `write()` on the underlying Stream.
- * Returns `this` instead of the write() return value to keep
- * the chaining going.
- */
- Cursor.prototype.write = function (data) {
- if (this.buffering) {
- this._buffer.push(arguments)
- } else {
- this.stream.write.apply(this.stream, arguments)
- }
- return this
- }
- /**
- * Buffer `write()` calls into memory.
- *
- * @api public
- */
- Cursor.prototype.buffer = function () {
- this.buffering = true
- return this
- }
- /**
- * Write out the in-memory buffer.
- *
- * @api public
- */
- Cursor.prototype.flush = function () {
- this.buffering = false
- var str = this._buffer.map(function (args) {
- if (args.length != 1) throw new Error('unexpected args length! ' + args.length);
- return args[0];
- }).join('');
- this._buffer.splice(0); // empty
- this.write(str);
- return this
- }
- /**
- * The `Colorer` class manages both the background and foreground colors.
- */
- function Colorer (cursor, base) {
- this.current = null
- this.cursor = cursor
- this.base = base
- }
- exports.Colorer = Colorer
- /**
- * Write an ANSI color code, ensuring that the same code doesn't get rewritten.
- */
- Colorer.prototype._setColorCode = function setColorCode (code) {
- var c = String(code)
- if (this.current === c) return
- this.cursor.enabled && this.cursor.write(prefix + c + suffix)
- this.current = c
- return this
- }
- /**
- * Set up the positional ANSI codes.
- */
- Object.keys(codes).forEach(function (name) {
- var code = String(codes[name])
- Cursor.prototype[name] = function () {
- var c = code
- if (arguments.length > 0) {
- c = toArray(arguments).map(Math.round).join(';') + code
- }
- this.enabled && this.write(prefix + c)
- return this
- }
- })
- /**
- * Set up the functions for the rendering ANSI codes.
- */
- Object.keys(styles).forEach(function (style) {
- var name = style[0].toUpperCase() + style.substring(1)
- , c = styles[style]
- , r = reset[style]
- Cursor.prototype[style] = function () {
- if (this[name]) return
- this.enabled && this.write(prefix + c + suffix)
- this[name] = true
- return this
- }
- Cursor.prototype['reset' + name] = function () {
- if (!this[name]) return
- this.enabled && this.write(prefix + r + suffix)
- this[name] = false
- return this
- }
- })
- /**
- * Setup the functions for the standard colors.
- */
- Object.keys(colors).forEach(function (color) {
- var code = colors[color]
- Colorer.prototype[color] = function () {
- this._setColorCode(this.base + code)
- return this.cursor
- }
- Cursor.prototype[color] = function () {
- return this.foreground[color]()
- }
- })
- /**
- * Makes a beep sound!
- */
- Cursor.prototype.beep = function () {
- this.enabled && this.write('\x07')
- return this
- }
- /**
- * Moves cursor to specific position
- */
- Cursor.prototype.goto = function (x, y) {
- x = x | 0
- y = y | 0
- this.enabled && this.write(prefix + y + ';' + x + 'H')
- return this
- }
- /**
- * Resets the color.
- */
- Colorer.prototype.reset = function () {
- this._setColorCode(this.base + 39)
- return this.cursor
- }
- /**
- * Resets all ANSI formatting on the stream.
- */
- Cursor.prototype.reset = function () {
- this.enabled && this.write(prefix + '0' + suffix)
- this.Bold = false
- this.Italic = false
- this.Underline = false
- this.Inverse = false
- this.foreground.current = null
- this.background.current = null
- return this
- }
- /**
- * Sets the foreground color with the given RGB values.
- * The closest match out of the 216 colors is picked.
- */
- Colorer.prototype.rgb = function (r, g, b) {
- var base = this.base + 38
- , code = rgb(r, g, b)
- this._setColorCode(base + ';5;' + code)
- return this.cursor
- }
- /**
- * Same as `cursor.fg.rgb(r, g, b)`.
- */
- Cursor.prototype.rgb = function (r, g, b) {
- return this.foreground.rgb(r, g, b)
- }
- /**
- * Accepts CSS color codes for use with ANSI escape codes.
- * For example: `#FF000` would be bright red.
- */
- Colorer.prototype.hex = function (color) {
- return this.rgb.apply(this, hex(color))
- }
- /**
- * Same as `cursor.fg.hex(color)`.
- */
- Cursor.prototype.hex = function (color) {
- return this.foreground.hex(color)
- }
- // UTIL FUNCTIONS //
- /**
- * Translates a 255 RGB value to a 0-5 ANSI RGV value,
- * then returns the single ANSI color code to use.
- */
- function rgb (r, g, b) {
- var red = r / 255 * 5
- , green = g / 255 * 5
- , blue = b / 255 * 5
- return rgb5(red, green, blue)
- }
- /**
- * Turns rgb 0-5 values into a single ANSI color code to use.
- */
- function rgb5 (r, g, b) {
- var red = Math.round(r)
- , green = Math.round(g)
- , blue = Math.round(b)
- return 16 + (red*36) + (green*6) + blue
- }
- /**
- * Accepts a hex CSS color code string (# is optional) and
- * translates it into an Array of 3 RGB 0-255 values, which
- * can then be used with rgb().
- */
- function hex (color) {
- var c = color[0] === '#' ? color.substring(1) : color
- , r = c.substring(0, 2)
- , g = c.substring(2, 4)
- , b = c.substring(4, 6)
- return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)]
- }
- /**
- * Turns an array-like object into a real array.
- */
- function toArray (a) {
- var i = 0
- , l = a.length
- , rtn = []
- for (; i<l; i++) {
- rtn.push(a[i])
- }
- return rtn
- }
|