summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/cli-table3/src
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/cli-table3/src')
-rw-r--r--deps/npm/node_modules/cli-table3/src/cell.js371
-rw-r--r--deps/npm/node_modules/cli-table3/src/layout-manager.js232
-rw-r--r--deps/npm/node_modules/cli-table3/src/table.js77
-rw-r--r--deps/npm/node_modules/cli-table3/src/utils.js303
4 files changed, 983 insertions, 0 deletions
diff --git a/deps/npm/node_modules/cli-table3/src/cell.js b/deps/npm/node_modules/cli-table3/src/cell.js
new file mode 100644
index 0000000000..2db7f74d72
--- /dev/null
+++ b/deps/npm/node_modules/cli-table3/src/cell.js
@@ -0,0 +1,371 @@
+const utils = require('./utils');
+
+class Cell {
+ /**
+ * A representation of a cell within the table.
+ * Implementations must have `init` and `draw` methods,
+ * as well as `colSpan`, `rowSpan`, `desiredHeight` and `desiredWidth` properties.
+ * @param options
+ * @constructor
+ */
+ constructor(options) {
+ this.setOptions(options);
+
+ /**
+ * Each cell will have it's `x` and `y` values set by the `layout-manager` prior to
+ * `init` being called;
+ * @type {Number}
+ */
+ this.x = null;
+ this.y = null;
+ }
+
+ setOptions(options) {
+ if (['boolean', 'number', 'string'].indexOf(typeof options) !== -1) {
+ options = { content: '' + options };
+ }
+ options = options || {};
+ this.options = options;
+ let content = options.content;
+ if (['boolean', 'number', 'string'].indexOf(typeof content) !== -1) {
+ this.content = String(content);
+ } else if (!content) {
+ this.content = '';
+ } else {
+ throw new Error('Content needs to be a primitive, got: ' + typeof content);
+ }
+ this.colSpan = options.colSpan || 1;
+ this.rowSpan = options.rowSpan || 1;
+ }
+
+ mergeTableOptions(tableOptions, cells) {
+ this.cells = cells;
+
+ let optionsChars = this.options.chars || {};
+ let tableChars = tableOptions.chars;
+ let chars = (this.chars = {});
+ CHAR_NAMES.forEach(function(name) {
+ setOption(optionsChars, tableChars, name, chars);
+ });
+
+ this.truncate = this.options.truncate || tableOptions.truncate;
+
+ let style = (this.options.style = this.options.style || {});
+ let tableStyle = tableOptions.style;
+ setOption(style, tableStyle, 'padding-left', this);
+ setOption(style, tableStyle, 'padding-right', this);
+ this.head = style.head || tableStyle.head;
+ this.border = style.border || tableStyle.border;
+
+ let fixedWidth = tableOptions.colWidths[this.x];
+ if (tableOptions.wordWrap && fixedWidth) {
+ fixedWidth -= this.paddingLeft + this.paddingRight;
+ if (this.colSpan) {
+ let i = 1;
+ while (i < this.colSpan) {
+ fixedWidth += tableOptions.colWidths[this.x + i];
+ i++;
+ }
+ }
+ this.lines = utils.colorizeLines(utils.wordWrap(fixedWidth, this.content));
+ } else {
+ this.lines = utils.colorizeLines(this.content.split('\n'));
+ }
+
+ this.desiredWidth = utils.strlen(this.content) + this.paddingLeft + this.paddingRight;
+ this.desiredHeight = this.lines.length;
+ }
+
+ /**
+ * Initializes the Cells data structure.
+ *
+ * @param tableOptions - A fully populated set of tableOptions.
+ * In addition to the standard default values, tableOptions must have fully populated the
+ * `colWidths` and `rowWidths` arrays. Those arrays must have lengths equal to the number
+ * of columns or rows (respectively) in this table, and each array item must be a Number.
+ *
+ */
+ init(tableOptions) {
+ let x = this.x;
+ let y = this.y;
+ this.widths = tableOptions.colWidths.slice(x, x + this.colSpan);
+ this.heights = tableOptions.rowHeights.slice(y, y + this.rowSpan);
+ this.width = this.widths.reduce(sumPlusOne, -1);
+ this.height = this.heights.reduce(sumPlusOne, -1);
+
+ this.hAlign = this.options.hAlign || tableOptions.colAligns[x];
+ this.vAlign = this.options.vAlign || tableOptions.rowAligns[y];
+
+ this.drawRight = x + this.colSpan == tableOptions.colWidths.length;
+ }
+
+ /**
+ * Draws the given line of the cell.
+ * This default implementation defers to methods `drawTop`, `drawBottom`, `drawLine` and `drawEmpty`.
+ * @param lineNum - can be `top`, `bottom` or a numerical line number.
+ * @param spanningCell - will be a number if being called from a RowSpanCell, and will represent how
+ * many rows below it's being called from. Otherwise it's undefined.
+ * @returns {String} The representation of this line.
+ */
+ draw(lineNum, spanningCell) {
+ if (lineNum == 'top') return this.drawTop(this.drawRight);
+ if (lineNum == 'bottom') return this.drawBottom(this.drawRight);
+ let padLen = Math.max(this.height - this.lines.length, 0);
+ let padTop;
+ switch (this.vAlign) {
+ case 'center':
+ padTop = Math.ceil(padLen / 2);
+ break;
+ case 'bottom':
+ padTop = padLen;
+ break;
+ default:
+ padTop = 0;
+ }
+ if (lineNum < padTop || lineNum >= padTop + this.lines.length) {
+ return this.drawEmpty(this.drawRight, spanningCell);
+ }
+ let forceTruncation = this.lines.length > this.height && lineNum + 1 >= this.height;
+ return this.drawLine(lineNum - padTop, this.drawRight, forceTruncation, spanningCell);
+ }
+
+ /**
+ * Renders the top line of the cell.
+ * @param drawRight - true if this method should render the right edge of the cell.
+ * @returns {String}
+ */
+ drawTop(drawRight) {
+ let content = [];
+ if (this.cells) {
+ //TODO: cells should always exist - some tests don't fill it in though
+ this.widths.forEach(function(width, index) {
+ content.push(this._topLeftChar(index));
+ content.push(utils.repeat(this.chars[this.y == 0 ? 'top' : 'mid'], width));
+ }, this);
+ } else {
+ content.push(this._topLeftChar(0));
+ content.push(utils.repeat(this.chars[this.y == 0 ? 'top' : 'mid'], this.width));
+ }
+ if (drawRight) {
+ content.push(this.chars[this.y == 0 ? 'topRight' : 'rightMid']);
+ }
+ return this.wrapWithStyleColors('border', content.join(''));
+ }
+
+ _topLeftChar(offset) {
+ let x = this.x + offset;
+ let leftChar;
+ if (this.y == 0) {
+ leftChar = x == 0 ? 'topLeft' : offset == 0 ? 'topMid' : 'top';
+ } else {
+ if (x == 0) {
+ leftChar = 'leftMid';
+ } else {
+ leftChar = offset == 0 ? 'midMid' : 'bottomMid';
+ if (this.cells) {
+ //TODO: cells should always exist - some tests don't fill it in though
+ let spanAbove = this.cells[this.y - 1][x] instanceof Cell.ColSpanCell;
+ if (spanAbove) {
+ leftChar = offset == 0 ? 'topMid' : 'mid';
+ }
+ if (offset == 0) {
+ let i = 1;
+ while (this.cells[this.y][x - i] instanceof Cell.ColSpanCell) {
+ i++;
+ }
+ if (this.cells[this.y][x - i] instanceof Cell.RowSpanCell) {
+ leftChar = 'leftMid';
+ }
+ }
+ }
+ }
+ }
+ return this.chars[leftChar];
+ }
+
+ wrapWithStyleColors(styleProperty, content) {
+ if (this[styleProperty] && this[styleProperty].length) {
+ try {
+ let colors = require('colors/safe');
+ for (let i = this[styleProperty].length - 1; i >= 0; i--) {
+ colors = colors[this[styleProperty][i]];
+ }
+ return colors(content);
+ } catch (e) {
+ return content;
+ }
+ } else {
+ return content;
+ }
+ }
+
+ /**
+ * Renders a line of text.
+ * @param lineNum - Which line of text to render. This is not necessarily the line within the cell.
+ * There may be top-padding above the first line of text.
+ * @param drawRight - true if this method should render the right edge of the cell.
+ * @param forceTruncationSymbol - `true` if the rendered text should end with the truncation symbol even
+ * if the text fits. This is used when the cell is vertically truncated. If `false` the text should
+ * only include the truncation symbol if the text will not fit horizontally within the cell width.
+ * @param spanningCell - a number of if being called from a RowSpanCell. (how many rows below). otherwise undefined.
+ * @returns {String}
+ */
+ drawLine(lineNum, drawRight, forceTruncationSymbol, spanningCell) {
+ let left = this.chars[this.x == 0 ? 'left' : 'middle'];
+ if (this.x && spanningCell && this.cells) {
+ let cellLeft = this.cells[this.y + spanningCell][this.x - 1];
+ while (cellLeft instanceof ColSpanCell) {
+ cellLeft = this.cells[cellLeft.y][cellLeft.x - 1];
+ }
+ if (!(cellLeft instanceof RowSpanCell)) {
+ left = this.chars['rightMid'];
+ }
+ }
+ let leftPadding = utils.repeat(' ', this.paddingLeft);
+ let right = drawRight ? this.chars['right'] : '';
+ let rightPadding = utils.repeat(' ', this.paddingRight);
+ let line = this.lines[lineNum];
+ let len = this.width - (this.paddingLeft + this.paddingRight);
+ if (forceTruncationSymbol) line += this.truncate || '…';
+ let content = utils.truncate(line, len, this.truncate);
+ content = utils.pad(content, len, ' ', this.hAlign);
+ content = leftPadding + content + rightPadding;
+ return this.stylizeLine(left, content, right);
+ }
+
+ stylizeLine(left, content, right) {
+ left = this.wrapWithStyleColors('border', left);
+ right = this.wrapWithStyleColors('border', right);
+ if (this.y === 0) {
+ content = this.wrapWithStyleColors('head', content);
+ }
+ return left + content + right;
+ }
+
+ /**
+ * Renders the bottom line of the cell.
+ * @param drawRight - true if this method should render the right edge of the cell.
+ * @returns {String}
+ */
+ drawBottom(drawRight) {
+ let left = this.chars[this.x == 0 ? 'bottomLeft' : 'bottomMid'];
+ let content = utils.repeat(this.chars.bottom, this.width);
+ let right = drawRight ? this.chars['bottomRight'] : '';
+ return this.wrapWithStyleColors('border', left + content + right);
+ }
+
+ /**
+ * Renders a blank line of text within the cell. Used for top and/or bottom padding.
+ * @param drawRight - true if this method should render the right edge of the cell.
+ * @param spanningCell - a number of if being called from a RowSpanCell. (how many rows below). otherwise undefined.
+ * @returns {String}
+ */
+ drawEmpty(drawRight, spanningCell) {
+ let left = this.chars[this.x == 0 ? 'left' : 'middle'];
+ if (this.x && spanningCell && this.cells) {
+ let cellLeft = this.cells[this.y + spanningCell][this.x - 1];
+ while (cellLeft instanceof ColSpanCell) {
+ cellLeft = this.cells[cellLeft.y][cellLeft.x - 1];
+ }
+ if (!(cellLeft instanceof RowSpanCell)) {
+ left = this.chars['rightMid'];
+ }
+ }
+ let right = drawRight ? this.chars['right'] : '';
+ let content = utils.repeat(' ', this.width);
+ return this.stylizeLine(left, content, right);
+ }
+}
+
+class ColSpanCell {
+ /**
+ * A Cell that doesn't do anything. It just draws empty lines.
+ * Used as a placeholder in column spanning.
+ * @constructor
+ */
+ constructor() {}
+
+ draw() {
+ return '';
+ }
+
+ init() {}
+
+ mergeTableOptions() {}
+}
+
+class RowSpanCell {
+ /**
+ * A placeholder Cell for a Cell that spans multiple rows.
+ * It delegates rendering to the original cell, but adds the appropriate offset.
+ * @param originalCell
+ * @constructor
+ */
+ constructor(originalCell) {
+ this.originalCell = originalCell;
+ }
+
+ init(tableOptions) {
+ let y = this.y;
+ let originalY = this.originalCell.y;
+ this.cellOffset = y - originalY;
+ this.offset = findDimension(tableOptions.rowHeights, originalY, this.cellOffset);
+ }
+
+ draw(lineNum) {
+ if (lineNum == 'top') {
+ return this.originalCell.draw(this.offset, this.cellOffset);
+ }
+ if (lineNum == 'bottom') {
+ return this.originalCell.draw('bottom');
+ }
+ return this.originalCell.draw(this.offset + 1 + lineNum);
+ }
+
+ mergeTableOptions() {}
+}
+
+// HELPER FUNCTIONS
+function setOption(objA, objB, nameB, targetObj) {
+ let nameA = nameB.split('-');
+ if (nameA.length > 1) {
+ nameA[1] = nameA[1].charAt(0).toUpperCase() + nameA[1].substr(1);
+ nameA = nameA.join('');
+ targetObj[nameA] = objA[nameA] || objA[nameB] || objB[nameA] || objB[nameB];
+ } else {
+ targetObj[nameB] = objA[nameB] || objB[nameB];
+ }
+}
+
+function findDimension(dimensionTable, startingIndex, span) {
+ let ret = dimensionTable[startingIndex];
+ for (let i = 1; i < span; i++) {
+ ret += 1 + dimensionTable[startingIndex + i];
+ }
+ return ret;
+}
+
+function sumPlusOne(a, b) {
+ return a + b + 1;
+}
+
+let CHAR_NAMES = [
+ 'top',
+ 'top-mid',
+ 'top-left',
+ 'top-right',
+ 'bottom',
+ 'bottom-mid',
+ 'bottom-left',
+ 'bottom-right',
+ 'left',
+ 'left-mid',
+ 'mid',
+ 'mid-mid',
+ 'right',
+ 'right-mid',
+ 'middle',
+];
+module.exports = Cell;
+module.exports.ColSpanCell = ColSpanCell;
+module.exports.RowSpanCell = RowSpanCell;
diff --git a/deps/npm/node_modules/cli-table3/src/layout-manager.js b/deps/npm/node_modules/cli-table3/src/layout-manager.js
new file mode 100644
index 0000000000..908040e7e5
--- /dev/null
+++ b/deps/npm/node_modules/cli-table3/src/layout-manager.js
@@ -0,0 +1,232 @@
+const objectAssign = require('object-assign');
+const Cell = require('./cell');
+const { ColSpanCell, RowSpanCell } = Cell;
+
+(function() {
+ function layoutTable(table) {
+ table.forEach(function(row, rowIndex) {
+ row.forEach(function(cell, columnIndex) {
+ cell.y = rowIndex;
+ cell.x = columnIndex;
+ for (let y = rowIndex; y >= 0; y--) {
+ let row2 = table[y];
+ let xMax = y === rowIndex ? columnIndex : row2.length;
+ for (let x = 0; x < xMax; x++) {
+ let cell2 = row2[x];
+ while (cellsConflict(cell, cell2)) {
+ cell.x++;
+ }
+ }
+ }
+ });
+ });
+ }
+
+ function maxWidth(table) {
+ let mw = 0;
+ table.forEach(function(row) {
+ row.forEach(function(cell) {
+ mw = Math.max(mw, cell.x + (cell.colSpan || 1));
+ });
+ });
+ return mw;
+ }
+
+ function maxHeight(table) {
+ return table.length;
+ }
+
+ function cellsConflict(cell1, cell2) {
+ let yMin1 = cell1.y;
+ let yMax1 = cell1.y - 1 + (cell1.rowSpan || 1);
+ let yMin2 = cell2.y;
+ let yMax2 = cell2.y - 1 + (cell2.rowSpan || 1);
+ let yConflict = !(yMin1 > yMax2 || yMin2 > yMax1);
+
+ let xMin1 = cell1.x;
+ let xMax1 = cell1.x - 1 + (cell1.colSpan || 1);
+ let xMin2 = cell2.x;
+ let xMax2 = cell2.x - 1 + (cell2.colSpan || 1);
+ let xConflict = !(xMin1 > xMax2 || xMin2 > xMax1);
+
+ return yConflict && xConflict;
+ }
+
+ function conflictExists(rows, x, y) {
+ let i_max = Math.min(rows.length - 1, y);
+ let cell = { x: x, y: y };
+ for (let i = 0; i <= i_max; i++) {
+ let row = rows[i];
+ for (let j = 0; j < row.length; j++) {
+ if (cellsConflict(cell, row[j])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ function allBlank(rows, y, xMin, xMax) {
+ for (let x = xMin; x < xMax; x++) {
+ if (conflictExists(rows, x, y)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function addRowSpanCells(table) {
+ table.forEach(function(row, rowIndex) {
+ row.forEach(function(cell) {
+ for (let i = 1; i < cell.rowSpan; i++) {
+ let rowSpanCell = new RowSpanCell(cell);
+ rowSpanCell.x = cell.x;
+ rowSpanCell.y = cell.y + i;
+ rowSpanCell.colSpan = cell.colSpan;
+ insertCell(rowSpanCell, table[rowIndex + i]);
+ }
+ });
+ });
+ }
+
+ function addColSpanCells(cellRows) {
+ for (let rowIndex = cellRows.length - 1; rowIndex >= 0; rowIndex--) {
+ let cellColumns = cellRows[rowIndex];
+ for (let columnIndex = 0; columnIndex < cellColumns.length; columnIndex++) {
+ let cell = cellColumns[columnIndex];
+ for (let k = 1; k < cell.colSpan; k++) {
+ let colSpanCell = new ColSpanCell();
+ colSpanCell.x = cell.x + k;
+ colSpanCell.y = cell.y;
+ cellColumns.splice(columnIndex + 1, 0, colSpanCell);
+ }
+ }
+ }
+ }
+
+ function insertCell(cell, row) {
+ let x = 0;
+ while (x < row.length && row[x].x < cell.x) {
+ x++;
+ }
+ row.splice(x, 0, cell);
+ }
+
+ function fillInTable(table) {
+ let h_max = maxHeight(table);
+ let w_max = maxWidth(table);
+ for (let y = 0; y < h_max; y++) {
+ for (let x = 0; x < w_max; x++) {
+ if (!conflictExists(table, x, y)) {
+ let opts = { x: x, y: y, colSpan: 1, rowSpan: 1 };
+ x++;
+ while (x < w_max && !conflictExists(table, x, y)) {
+ opts.colSpan++;
+ x++;
+ }
+ let y2 = y + 1;
+ while (y2 < h_max && allBlank(table, y2, opts.x, opts.x + opts.colSpan)) {
+ opts.rowSpan++;
+ y2++;
+ }
+
+ let cell = new Cell(opts);
+ cell.x = opts.x;
+ cell.y = opts.y;
+ insertCell(cell, table[y]);
+ }
+ }
+ }
+ }
+
+ function generateCells(rows) {
+ return rows.map(function(row) {
+ if (!Array.isArray(row)) {
+ let key = Object.keys(row)[0];
+ row = row[key];
+ if (Array.isArray(row)) {
+ row = row.slice();
+ row.unshift(key);
+ } else {
+ row = [key, row];
+ }
+ }
+ return row.map(function(cell) {
+ return new Cell(cell);
+ });
+ });
+ }
+
+ function makeTableLayout(rows) {
+ let cellRows = generateCells(rows);
+ layoutTable(cellRows);
+ fillInTable(cellRows);
+ addRowSpanCells(cellRows);
+ addColSpanCells(cellRows);
+ return cellRows;
+ }
+
+ module.exports = {
+ makeTableLayout: makeTableLayout,
+ layoutTable: layoutTable,
+ addRowSpanCells: addRowSpanCells,
+ maxWidth: maxWidth,
+ fillInTable: fillInTable,
+ computeWidths: makeComputeWidths('colSpan', 'desiredWidth', 'x', 1),
+ computeHeights: makeComputeWidths('rowSpan', 'desiredHeight', 'y', 1),
+ };
+})();
+
+function makeComputeWidths(colSpan, desiredWidth, x, forcedMin) {
+ return function(vals, table) {
+ let result = [];
+ let spanners = [];
+ table.forEach(function(row) {
+ row.forEach(function(cell) {
+ if ((cell[colSpan] || 1) > 1) {
+ spanners.push(cell);
+ } else {
+ result[cell[x]] = Math.max(result[cell[x]] || 0, cell[desiredWidth] || 0, forcedMin);
+ }
+ });
+ });
+
+ vals.forEach(function(val, index) {
+ if (typeof val === 'number') {
+ result[index] = val;
+ }
+ });
+
+ //spanners.forEach(function(cell){
+ for (let k = spanners.length - 1; k >= 0; k--) {
+ let cell = spanners[k];
+ let span = cell[colSpan];
+ let col = cell[x];
+ let existingWidth = result[col];
+ let editableCols = typeof vals[col] === 'number' ? 0 : 1;
+ for (let i = 1; i < span; i++) {
+ existingWidth += 1 + result[col + i];
+ if (typeof vals[col + i] !== 'number') {
+ editableCols++;
+ }
+ }
+ if (cell[desiredWidth] > existingWidth) {
+ let i = 0;
+ while (editableCols > 0 && cell[desiredWidth] > existingWidth) {
+ if (typeof vals[col + i] !== 'number') {
+ let dif = Math.round((cell[desiredWidth] - existingWidth) / editableCols);
+ existingWidth += dif;
+ result[col + i] += dif;
+ editableCols--;
+ }
+ i++;
+ }
+ }
+ }
+
+ objectAssign(vals, result);
+ for (let j = 0; j < vals.length; j++) {
+ vals[j] = Math.max(forcedMin, vals[j] || 0);
+ }
+ };
+}
diff --git a/deps/npm/node_modules/cli-table3/src/table.js b/deps/npm/node_modules/cli-table3/src/table.js
new file mode 100644
index 0000000000..af4f146463
--- /dev/null
+++ b/deps/npm/node_modules/cli-table3/src/table.js
@@ -0,0 +1,77 @@
+const utils = require('./utils');
+const tableLayout = require('./layout-manager');
+
+class Table extends Array {
+ constructor(options) {
+ super();
+
+ this.options = utils.mergeOptions(options);
+ }
+
+ toString() {
+ let array = this;
+ let headersPresent = this.options.head && this.options.head.length;
+ if (headersPresent) {
+ array = [this.options.head];
+ if (this.length) {
+ array.push.apply(array, this);
+ }
+ } else {
+ this.options.style.head = [];
+ }
+
+ let cells = tableLayout.makeTableLayout(array);
+
+ cells.forEach(function(row) {
+ row.forEach(function(cell) {
+ cell.mergeTableOptions(this.options, cells);
+ }, this);
+ }, this);
+
+ tableLayout.computeWidths(this.options.colWidths, cells);
+ tableLayout.computeHeights(this.options.rowHeights, cells);
+
+ cells.forEach(function(row) {
+ row.forEach(function(cell) {
+ cell.init(this.options);
+ }, this);
+ }, this);
+
+ let result = [];
+
+ for (let rowIndex = 0; rowIndex < cells.length; rowIndex++) {
+ let row = cells[rowIndex];
+ let heightOfRow = this.options.rowHeights[rowIndex];
+
+ if (rowIndex === 0 || !this.options.style.compact || (rowIndex == 1 && headersPresent)) {
+ doDraw(row, 'top', result);
+ }
+
+ for (let lineNum = 0; lineNum < heightOfRow; lineNum++) {
+ doDraw(row, lineNum, result);
+ }
+
+ if (rowIndex + 1 == cells.length) {
+ doDraw(row, 'bottom', result);
+ }
+ }
+
+ return result.join('\n');
+ }
+
+ get width() {
+ let str = this.toString().split('\n');
+ return str[0].length;
+ }
+}
+
+function doDraw(row, lineNum, result) {
+ let line = [];
+ row.forEach(function(cell) {
+ line.push(cell.draw(lineNum));
+ });
+ let str = line.join('');
+ if (str.length) result.push(str);
+}
+
+module.exports = Table;
diff --git a/deps/npm/node_modules/cli-table3/src/utils.js b/deps/npm/node_modules/cli-table3/src/utils.js
new file mode 100644
index 0000000000..c9149558e2
--- /dev/null
+++ b/deps/npm/node_modules/cli-table3/src/utils.js
@@ -0,0 +1,303 @@
+const objectAssign = require('object-assign');
+const stringWidth = require('string-width');
+
+function codeRegex(capture) {
+ return capture ? /\u001b\[((?:\d*;){0,5}\d*)m/g : /\u001b\[(?:\d*;){0,5}\d*m/g;
+}
+
+function strlen(str) {
+ let code = codeRegex();
+ let stripped = ('' + str).replace(code, '');
+ let split = stripped.split('\n');
+ return split.reduce(function(memo, s) {
+ return stringWidth(s) > memo ? stringWidth(s) : memo;
+ }, 0);
+}
+
+function repeat(str, times) {
+ return Array(times + 1).join(str);
+}
+
+function pad(str, len, pad, dir) {
+ let length = strlen(str);
+ if (len + 1 >= length) {
+ let padlen = len - length;
+ switch (dir) {
+ case 'right': {
+ str = repeat(pad, padlen) + str;
+ break;
+ }
+ case 'center': {
+ let right = Math.ceil(padlen / 2);
+ let left = padlen - right;
+ str = repeat(pad, left) + str + repeat(pad, right);
+ break;
+ }
+ default: {
+ str = str + repeat(pad, padlen);
+ break;
+ }
+ }
+ }
+ return str;
+}
+
+let codeCache = {};
+
+function addToCodeCache(name, on, off) {
+ on = '\u001b[' + on + 'm';
+ off = '\u001b[' + off + 'm';
+ codeCache[on] = { set: name, to: true };
+ codeCache[off] = { set: name, to: false };
+ codeCache[name] = { on: on, off: off };
+}
+
+//https://github.com/Marak/colors.js/blob/master/lib/styles.js
+addToCodeCache('bold', 1, 22);
+addToCodeCache('italics', 3, 23);
+addToCodeCache('underline', 4, 24);
+addToCodeCache('inverse', 7, 27);
+addToCodeCache('strikethrough', 9, 29);
+
+function updateState(state, controlChars) {
+ let controlCode = controlChars[1] ? parseInt(controlChars[1].split(';')[0]) : 0;
+ if ((controlCode >= 30 && controlCode <= 39) || (controlCode >= 90 && controlCode <= 97)) {
+ state.lastForegroundAdded = controlChars[0];
+ return;
+ }
+ if ((controlCode >= 40 && controlCode <= 49) || (controlCode >= 100 && controlCode <= 107)) {
+ state.lastBackgroundAdded = controlChars[0];
+ return;
+ }
+ if (controlCode === 0) {
+ for (let i in state) {
+ /* istanbul ignore else */
+ if (state.hasOwnProperty(i)) {
+ delete state[i];
+ }
+ }
+ return;
+ }
+ let info = codeCache[controlChars[0]];
+ if (info) {
+ state[info.set] = info.to;
+ }
+}
+
+function readState(line) {
+ let code = codeRegex(true);
+ let controlChars = code.exec(line);
+ let state = {};
+ while (controlChars !== null) {
+ updateState(state, controlChars);
+ controlChars = code.exec(line);
+ }
+ return state;
+}
+
+function unwindState(state, ret) {
+ let lastBackgroundAdded = state.lastBackgroundAdded;
+ let lastForegroundAdded = state.lastForegroundAdded;
+
+ delete state.lastBackgroundAdded;
+ delete state.lastForegroundAdded;
+
+ Object.keys(state).forEach(function(key) {
+ if (state[key]) {
+ ret += codeCache[key].off;
+ }
+ });
+
+ if (lastBackgroundAdded && lastBackgroundAdded != '\u001b[49m') {
+ ret += '\u001b[49m';
+ }
+ if (lastForegroundAdded && lastForegroundAdded != '\u001b[39m') {
+ ret += '\u001b[39m';
+ }
+
+ return ret;
+}
+
+function rewindState(state, ret) {
+ let lastBackgroundAdded = state.lastBackgroundAdded;
+ let lastForegroundAdded = state.lastForegroundAdded;
+
+ delete state.lastBackgroundAdded;
+ delete state.lastForegroundAdded;
+
+ Object.keys(state).forEach(function(key) {
+ if (state[key]) {
+ ret = codeCache[key].on + ret;
+ }
+ });
+
+ if (lastBackgroundAdded && lastBackgroundAdded != '\u001b[49m') {
+ ret = lastBackgroundAdded + ret;
+ }
+ if (lastForegroundAdded && lastForegroundAdded != '\u001b[39m') {
+ ret = lastForegroundAdded + ret;
+ }
+
+ return ret;
+}
+
+function truncateWidth(str, desiredLength) {
+ if (str.length === strlen(str)) {
+ return str.substr(0, desiredLength);
+ }
+
+ while (strlen(str) > desiredLength) {
+ str = str.slice(0, -1);
+ }
+
+ return str;
+}
+
+function truncateWidthWithAnsi(str, desiredLength) {
+ let code = codeRegex(true);
+ let split = str.split(codeRegex());
+ let splitIndex = 0;
+ let retLen = 0;
+ let ret = '';
+ let myArray;
+ let state = {};
+
+ while (retLen < desiredLength) {
+ myArray = code.exec(str);
+ let toAdd = split[splitIndex];
+ splitIndex++;
+ if (retLen + strlen(toAdd) > desiredLength) {
+ toAdd = truncateWidth(toAdd, desiredLength - retLen);
+ }
+ ret += toAdd;
+ retLen += strlen(toAdd);
+
+ if (retLen < desiredLength) {
+ if (!myArray) {
+ break;
+ } // full-width chars may cause a whitespace which cannot be filled
+ ret += myArray[0];
+ updateState(state, myArray);
+ }
+ }
+
+ return unwindState(state, ret);
+}
+
+function truncate(str, desiredLength, truncateChar) {
+ truncateChar = truncateChar || '…';
+ let lengthOfStr = strlen(str);
+ if (lengthOfStr <= desiredLength) {
+ return str;
+ }
+ desiredLength -= strlen(truncateChar);
+
+ let ret = truncateWidthWithAnsi(str, desiredLength);
+
+ return ret + truncateChar;
+}
+
+function defaultOptions() {
+ return {
+ chars: {
+ top: '─',
+ 'top-mid': '┬',
+ 'top-left': '┌',
+ 'top-right': '┐',
+ bottom: '─',
+ 'bottom-mid': '┴',
+ 'bottom-left': '└',
+ 'bottom-right': '┘',
+ left: '│',
+ 'left-mid': '├',
+ mid: '─',
+ 'mid-mid': '┼',
+ right: '│',
+ 'right-mid': '┤',
+ middle: '│',
+ },
+ truncate: '…',
+ colWidths: [],
+ rowHeights: [],
+ colAligns: [],
+ rowAligns: [],
+ style: {
+ 'padding-left': 1,
+ 'padding-right': 1,
+ head: ['red'],
+ border: ['grey'],
+ compact: false,
+ },
+ head: [],
+ };
+}
+
+function mergeOptions(options, defaults) {
+ options = options || {};
+ defaults = defaults || defaultOptions();
+ let ret = objectAssign({}, defaults, options);
+ ret.chars = objectAssign({}, defaults.chars, options.chars);
+ ret.style = objectAssign({}, defaults.style, options.style);
+ return ret;
+}
+
+function wordWrap(maxLength, input) {
+ let lines = [];
+ let split = input.split(/(\s+)/g);
+ let line = [];
+ let lineLength = 0;
+ let whitespace;
+ for (let i = 0; i < split.length; i += 2) {
+ let word = split[i];
+ let newLength = lineLength + strlen(word);
+ if (lineLength > 0 && whitespace) {
+ newLength += whitespace.length;
+ }
+ if (newLength > maxLength) {
+ if (lineLength !== 0) {
+ lines.push(line.join(''));
+ }
+ line = [word];
+ lineLength = strlen(word);
+ } else {
+ line.push(whitespace || '', word);
+ lineLength = newLength;
+ }
+ whitespace = split[i + 1];
+ }
+ if (lineLength) {
+ lines.push(line.join(''));
+ }
+ return lines;
+}
+
+function multiLineWordWrap(maxLength, input) {
+ let output = [];
+ input = input.split('\n');
+ for (let i = 0; i < input.length; i++) {
+ output.push.apply(output, wordWrap(maxLength, input[i]));
+ }
+ return output;
+}
+
+function colorizeLines(input) {
+ let state = {};
+ let output = [];
+ for (let i = 0; i < input.length; i++) {
+ let line = rewindState(state, input[i]);
+ state = readState(line);
+ let temp = objectAssign({}, state);
+ output.push(unwindState(temp, line));
+ }
+ return output;
+}
+
+module.exports = {
+ strlen: strlen,
+ repeat: repeat,
+ pad: pad,
+ truncate: truncate,
+ mergeOptions: mergeOptions,
+ wordWrap: multiLineWordWrap,
+ colorizeLines: colorizeLines,
+};