| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 | 
							- var stringWidth = require('string-width')
 
- var stripAnsi = require('strip-ansi')
 
- var wrap = require('wrap-ansi')
 
- var align = {
 
-   right: alignRight,
 
-   center: alignCenter
 
- }
 
- var top = 0
 
- var right = 1
 
- var bottom = 2
 
- var left = 3
 
- function UI (opts) {
 
-   this.width = opts.width
 
-   this.wrap = opts.wrap
 
-   this.rows = []
 
- }
 
- UI.prototype.span = function () {
 
-   var cols = this.div.apply(this, arguments)
 
-   cols.span = true
 
- }
 
- UI.prototype.resetOutput = function () {
 
-   this.rows = []
 
- }
 
- UI.prototype.div = function () {
 
-   if (arguments.length === 0) this.div('')
 
-   if (this.wrap && this._shouldApplyLayoutDSL.apply(this, arguments)) {
 
-     return this._applyLayoutDSL(arguments[0])
 
-   }
 
-   var cols = []
 
-   for (var i = 0, arg; (arg = arguments[i]) !== undefined; i++) {
 
-     if (typeof arg === 'string') cols.push(this._colFromString(arg))
 
-     else cols.push(arg)
 
-   }
 
-   this.rows.push(cols)
 
-   return cols
 
- }
 
- UI.prototype._shouldApplyLayoutDSL = function () {
 
-   return arguments.length === 1 && typeof arguments[0] === 'string' &&
 
-     /[\t\n]/.test(arguments[0])
 
- }
 
- UI.prototype._applyLayoutDSL = function (str) {
 
-   var _this = this
 
-   var rows = str.split('\n')
 
-   var leftColumnWidth = 0
 
-   // simple heuristic for layout, make sure the
 
-   // second column lines up along the left-hand.
 
-   // don't allow the first column to take up more
 
-   // than 50% of the screen.
 
-   rows.forEach(function (row) {
 
-     var columns = row.split('\t')
 
-     if (columns.length > 1 && stringWidth(columns[0]) > leftColumnWidth) {
 
-       leftColumnWidth = Math.min(
 
-         Math.floor(_this.width * 0.5),
 
-         stringWidth(columns[0])
 
-       )
 
-     }
 
-   })
 
-   // generate a table:
 
-   //  replacing ' ' with padding calculations.
 
-   //  using the algorithmically generated width.
 
-   rows.forEach(function (row) {
 
-     var columns = row.split('\t')
 
-     _this.div.apply(_this, columns.map(function (r, i) {
 
-       return {
 
-         text: r.trim(),
 
-         padding: _this._measurePadding(r),
 
-         width: (i === 0 && columns.length > 1) ? leftColumnWidth : undefined
 
-       }
 
-     }))
 
-   })
 
-   return this.rows[this.rows.length - 1]
 
- }
 
- UI.prototype._colFromString = function (str) {
 
-   return {
 
-     text: str,
 
-     padding: this._measurePadding(str)
 
-   }
 
- }
 
- UI.prototype._measurePadding = function (str) {
 
-   // measure padding without ansi escape codes
 
-   var noAnsi = stripAnsi(str)
 
-   return [0, noAnsi.match(/\s*$/)[0].length, 0, noAnsi.match(/^\s*/)[0].length]
 
- }
 
- UI.prototype.toString = function () {
 
-   var _this = this
 
-   var lines = []
 
-   _this.rows.forEach(function (row, i) {
 
-     _this.rowToString(row, lines)
 
-   })
 
-   // don't display any lines with the
 
-   // hidden flag set.
 
-   lines = lines.filter(function (line) {
 
-     return !line.hidden
 
-   })
 
-   return lines.map(function (line) {
 
-     return line.text
 
-   }).join('\n')
 
- }
 
- UI.prototype.rowToString = function (row, lines) {
 
-   var _this = this
 
-   var padding
 
-   var rrows = this._rasterize(row)
 
-   var str = ''
 
-   var ts
 
-   var width
 
-   var wrapWidth
 
-   rrows.forEach(function (rrow, r) {
 
-     str = ''
 
-     rrow.forEach(function (col, c) {
 
-       ts = '' // temporary string used during alignment/padding.
 
-       width = row[c].width // the width with padding.
 
-       wrapWidth = _this._negatePadding(row[c]) // the width without padding.
 
-       ts += col
 
-       for (var i = 0; i < wrapWidth - stringWidth(col); i++) {
 
-         ts += ' '
 
-       }
 
-       // align the string within its column.
 
-       if (row[c].align && row[c].align !== 'left' && _this.wrap) {
 
-         ts = align[row[c].align](ts, wrapWidth)
 
-         if (stringWidth(ts) < wrapWidth) ts += new Array(width - stringWidth(ts)).join(' ')
 
-       }
 
-       // apply border and padding to string.
 
-       padding = row[c].padding || [0, 0, 0, 0]
 
-       if (padding[left]) str += new Array(padding[left] + 1).join(' ')
 
-       str += addBorder(row[c], ts, '| ')
 
-       str += ts
 
-       str += addBorder(row[c], ts, ' |')
 
-       if (padding[right]) str += new Array(padding[right] + 1).join(' ')
 
-       // if prior row is span, try to render the
 
-       // current row on the prior line.
 
-       if (r === 0 && lines.length > 0) {
 
-         str = _this._renderInline(str, lines[lines.length - 1])
 
-       }
 
-     })
 
-     // remove trailing whitespace.
 
-     lines.push({
 
-       text: str.replace(/ +$/, ''),
 
-       span: row.span
 
-     })
 
-   })
 
-   return lines
 
- }
 
- function addBorder (col, ts, style) {
 
-   if (col.border) {
 
-     if (/[.']-+[.']/.test(ts)) return ''
 
-     else if (ts.trim().length) return style
 
-     else return '  '
 
-   }
 
-   return ''
 
- }
 
- // if the full 'source' can render in
 
- // the target line, do so.
 
- UI.prototype._renderInline = function (source, previousLine) {
 
-   var leadingWhitespace = source.match(/^ */)[0].length
 
-   var target = previousLine.text
 
-   var targetTextWidth = stringWidth(target.trimRight())
 
-   if (!previousLine.span) return source
 
-   // if we're not applying wrapping logic,
 
-   // just always append to the span.
 
-   if (!this.wrap) {
 
-     previousLine.hidden = true
 
-     return target + source
 
-   }
 
-   if (leadingWhitespace < targetTextWidth) return source
 
-   previousLine.hidden = true
 
-   return target.trimRight() + new Array(leadingWhitespace - targetTextWidth + 1).join(' ') + source.trimLeft()
 
- }
 
- UI.prototype._rasterize = function (row) {
 
-   var _this = this
 
-   var i
 
-   var rrow
 
-   var rrows = []
 
-   var widths = this._columnWidths(row)
 
-   var wrapped
 
-   // word wrap all columns, and create
 
-   // a data-structure that is easy to rasterize.
 
-   row.forEach(function (col, c) {
 
-     // leave room for left and right padding.
 
-     col.width = widths[c]
 
-     if (_this.wrap) wrapped = wrap(col.text, _this._negatePadding(col), { hard: true }).split('\n')
 
-     else wrapped = col.text.split('\n')
 
-     if (col.border) {
 
-       wrapped.unshift('.' + new Array(_this._negatePadding(col) + 3).join('-') + '.')
 
-       wrapped.push("'" + new Array(_this._negatePadding(col) + 3).join('-') + "'")
 
-     }
 
-     // add top and bottom padding.
 
-     if (col.padding) {
 
-       for (i = 0; i < (col.padding[top] || 0); i++) wrapped.unshift('')
 
-       for (i = 0; i < (col.padding[bottom] || 0); i++) wrapped.push('')
 
-     }
 
-     wrapped.forEach(function (str, r) {
 
-       if (!rrows[r]) rrows.push([])
 
-       rrow = rrows[r]
 
-       for (var i = 0; i < c; i++) {
 
-         if (rrow[i] === undefined) rrow.push('')
 
-       }
 
-       rrow.push(str)
 
-     })
 
-   })
 
-   return rrows
 
- }
 
- UI.prototype._negatePadding = function (col) {
 
-   var wrapWidth = col.width
 
-   if (col.padding) wrapWidth -= (col.padding[left] || 0) + (col.padding[right] || 0)
 
-   if (col.border) wrapWidth -= 4
 
-   return wrapWidth
 
- }
 
- UI.prototype._columnWidths = function (row) {
 
-   var _this = this
 
-   var widths = []
 
-   var unset = row.length
 
-   var unsetWidth
 
-   var remainingWidth = this.width
 
-   // column widths can be set in config.
 
-   row.forEach(function (col, i) {
 
-     if (col.width) {
 
-       unset--
 
-       widths[i] = col.width
 
-       remainingWidth -= col.width
 
-     } else {
 
-       widths[i] = undefined
 
-     }
 
-   })
 
-   // any unset widths should be calculated.
 
-   if (unset) unsetWidth = Math.floor(remainingWidth / unset)
 
-   widths.forEach(function (w, i) {
 
-     if (!_this.wrap) widths[i] = row[i].width || stringWidth(row[i].text)
 
-     else if (w === undefined) widths[i] = Math.max(unsetWidth, _minWidth(row[i]))
 
-   })
 
-   return widths
 
- }
 
- // calculates the minimum width of
 
- // a column, based on padding preferences.
 
- function _minWidth (col) {
 
-   var padding = col.padding || []
 
-   var minWidth = 1 + (padding[left] || 0) + (padding[right] || 0)
 
-   if (col.border) minWidth += 4
 
-   return minWidth
 
- }
 
- function getWindowWidth () {
 
-   if (typeof process === 'object' && process.stdout && process.stdout.columns) return process.stdout.columns
 
- }
 
- function alignRight (str, width) {
 
-   str = str.trim()
 
-   var padding = ''
 
-   var strWidth = stringWidth(str)
 
-   if (strWidth < width) {
 
-     padding = new Array(width - strWidth + 1).join(' ')
 
-   }
 
-   return padding + str
 
- }
 
- function alignCenter (str, width) {
 
-   str = str.trim()
 
-   var padding = ''
 
-   var strWidth = stringWidth(str.trim())
 
-   if (strWidth < width) {
 
-     padding = new Array(parseInt((width - strWidth) / 2, 10) + 1).join(' ')
 
-   }
 
-   return padding + str
 
- }
 
- module.exports = function (opts) {
 
-   opts = opts || {}
 
-   return new UI({
 
-     width: (opts || {}).width || getWindowWidth() || 80,
 
-     wrap: typeof opts.wrap === 'boolean' ? opts.wrap : true
 
-   })
 
- }
 
 
  |