From 53cd6be35cafc33c6d0901ba1a572b96fab031cf Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 23:14:45 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- js/doc/cli-table.js | 306 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 js/doc/cli-table.js diff --git a/js/doc/cli-table.js b/js/doc/cli-table.js new file mode 100644 index 0000000..cc0ba3f --- /dev/null +++ b/js/doc/cli-table.js @@ -0,0 +1,306 @@ + +/** + * Module dependencies. + */ + +var colors = Require('doc/colors') + , utils = Require('doc/cli-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 + */ +/* Depricated + +Table.prototype.__defineGetter__('width', function (){ + var str = this.toString().split("\n"); + if (str.length) return str[0].length; + return 0; +}); +*/ +Object.defineProperty(Table.prototype,'width',{ + get: 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 && obj.width != undefined + ? obj.width + : ((typeof obj == 'object' && obj !== null ? 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 == null ? '' : 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';