123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- /**
- * Module dependencies.
- */
- var colors = require('colors/safe')
- , utils = require('./utils')
- , repeat = utils.repeat
- , truncate = utils.truncate
- , pad = utils.pad;
- /**
- * Table constructor
- *
- * @param {Object} options
- * @api public
- */
- function Table (options){
- this.options = utils.options({
- 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: []
- , colAligns: []
- , style: {
- 'padding-left': 1
- , 'padding-right': 1
- , head: ['red']
- , border: ['grey']
- , compact : false
- }
- , head: []
- }, options);
- };
- /**
- * Inherit from Array.
- */
- Table.prototype.__proto__ = Array.prototype;
- /**
- * Width getter
- *
- * @return {Number} width
- * @api public
- */
- Table.prototype.__defineGetter__('width', function (){
- var str = this.toString().split("\n");
- if (str.length) return str[0].length;
- return 0;
- });
- /**
- * Render to a string.
- *
- * @return {String} table representation
- * @api public
- */
- Table.prototype.render
- Table.prototype.toString = function (){
- var ret = ''
- , options = this.options
- , style = options.style
- , head = options.head
- , chars = options.chars
- , truncater = options.truncate
- , colWidths = options.colWidths || new Array(this.head.length)
- , totalWidth = 0;
- if (!head.length && !this.length) return '';
- if (!colWidths.length){
- var all_rows = this.slice(0);
- if (head.length) { all_rows = all_rows.concat([head]) };
- all_rows.forEach(function(cells){
- // horizontal (arrays)
- if (typeof cells === 'object' && cells.length) {
- extractColumnWidths(cells);
- // vertical (objects)
- } else {
- var header_cell = Object.keys(cells)[0]
- , value_cell = cells[header_cell];
- colWidths[0] = Math.max(colWidths[0] || 0, get_width(header_cell) || 0);
- // cross (objects w/ array values)
- if (typeof value_cell === 'object' && value_cell.length) {
- extractColumnWidths(value_cell, 1);
- } else {
- colWidths[1] = Math.max(colWidths[1] || 0, get_width(value_cell) || 0);
- }
- }
- });
- };
- totalWidth = (colWidths.length == 1 ? colWidths[0] : colWidths.reduce(
- function (a, b){
- return a + b
- })) + colWidths.length + 1;
- function extractColumnWidths(arr, offset) {
- var offset = offset || 0;
- arr.forEach(function(cell, i){
- colWidths[i + offset] = Math.max(colWidths[i + offset] || 0, get_width(cell) || 0);
- });
- };
- function get_width(obj) {
- return typeof obj == 'object' && obj.width != undefined
- ? obj.width
- : ((typeof obj == 'object' ? utils.strlen(obj.text) : utils.strlen(obj)) + (style['padding-left'] || 0) + (style['padding-right'] || 0))
- }
- // draws a line
- function line (line, left, right, intersection){
- var width = 0
- , line =
- left
- + repeat(line, totalWidth - 2)
- + right;
- colWidths.forEach(function (w, i){
- if (i == colWidths.length - 1) return;
- width += w + 1;
- line = line.substr(0, width) + intersection + line.substr(width + 1);
- });
- return applyStyles(options.style.border, line);
- };
- // draws the top line
- function lineTop (){
- var l = line(chars.top
- , chars['top-left'] || chars.top
- , chars['top-right'] || chars.top
- , chars['top-mid']);
- if (l)
- ret += l + "\n";
- };
- function generateRow (items, style) {
- var cells = []
- , max_height = 0;
- // prepare vertical and cross table data
- if (!Array.isArray(items) && typeof items === "object") {
- var key = Object.keys(items)[0]
- , value = items[key]
- , first_cell_head = true;
- if (Array.isArray(value)) {
- items = value;
- items.unshift(key);
- } else {
- items = [key, value];
- }
- }
- // transform array of item strings into structure of cells
- items.forEach(function (item, i) {
- var contents = item.toString().split("\n").reduce(function (memo, l) {
- memo.push(string(l, i));
- return memo;
- }, [])
- var height = contents.length;
- if (height > max_height) { max_height = height };
- cells.push({ contents: contents , height: height });
- });
- // transform vertical cells into horizontal lines
- var lines = new Array(max_height);
- cells.forEach(function (cell, i) {
- cell.contents.forEach(function (line, j) {
- if (!lines[j]) { lines[j] = [] };
- if (style || (first_cell_head && i === 0 && options.style.head)) {
- line = applyStyles(options.style.head, line)
- }
- lines[j].push(line);
- });
- // populate empty lines in cell
- for (var j = cell.height, l = max_height; j < l; j++) {
- if (!lines[j]) { lines[j] = [] };
- lines[j].push(string('', i));
- }
- });
- var ret = "";
- lines.forEach(function (line, index) {
- if (ret.length > 0) {
- ret += "\n" + applyStyles(options.style.border, chars.left);
- }
- ret += line.join(applyStyles(options.style.border, chars.middle)) + applyStyles(options.style.border, chars.right);
- });
- return applyStyles(options.style.border, chars.left) + ret;
- };
- function applyStyles(styles, subject) {
- if (!subject)
- return '';
- styles.forEach(function(style) {
- subject = colors[style](subject);
- });
- return subject;
- };
- // renders a string, by padding it or truncating it
- function string (str, index){
- var str = String(typeof str == 'object' && str.text ? str.text : str)
- , length = utils.strlen(str)
- , width = colWidths[index]
- - (style['padding-left'] || 0)
- - (style['padding-right'] || 0)
- , align = options.colAligns[index] || 'left';
- return repeat(' ', style['padding-left'] || 0)
- + (length == width ? str :
- (length < width
- ? pad(str, ( width + (str.length - length) ), ' ', align == 'left' ? 'right' :
- (align == 'middle' ? 'both' : 'left'))
- : (truncater ? truncate(str, width, truncater) : str))
- )
- + repeat(' ', style['padding-right'] || 0);
- };
- if (head.length){
- lineTop();
- ret += generateRow(head, style.head) + "\n"
- }
- if (this.length)
- this.forEach(function (cells, i){
- if (!head.length && i == 0)
- lineTop();
- else {
- if (!style.compact || i<(!!head.length) ?1:0 || cells.length == 0){
- var l = line(chars.mid
- , chars['left-mid']
- , chars['right-mid']
- , chars['mid-mid']);
- if (l)
- ret += l + "\n"
- }
- }
- if (cells.hasOwnProperty("length") && !cells.length) {
- return
- } else {
- ret += generateRow(cells) + "\n";
- };
- });
- var l = line(chars.bottom
- , chars['bottom-left'] || chars.bottom
- , chars['bottom-right'] || chars.bottom
- , chars['bottom-mid']);
- if (l)
- ret += l;
- else
- // trim the last '\n' if we didn't add the bottom decoration
- ret = ret.slice(0, -1);
- return ret;
- };
- /**
- * Module exports.
- */
- module.exports = Table;
- module.exports.version = '0.0.1';
|