'use strict' var repeat = require('repeat-string') module.exports = markdownTable var trailingWhitespace = / +$/ // Characters. var space = ' ' var lineFeed = '\n' var dash = '-' var colon = ':' var verticalBar = '|' var x = 0 var C = 67 var L = 76 var R = 82 var c = 99 var l = 108 var r = 114 // Create a table from a matrix of strings. function markdownTable(table, options) { var settings = options || {} var padding = settings.padding !== false var start = settings.delimiterStart !== false var end = settings.delimiterEnd !== false var align = (settings.align || []).concat() var alignDelimiters = settings.alignDelimiters !== false var alignments = [] var stringLength = settings.stringLength || defaultStringLength var rowIndex = -1 var rowLength = table.length var cellMatrix = [] var sizeMatrix = [] var row = [] var sizes = [] var longestCellByColumn = [] var mostCellsPerRow = 0 var cells var columnIndex var columnLength var largest var size var cell var lines var line var before var after var code // This is a superfluous loop if we don’t align delimiters, but otherwise we’d // do superfluous work when aligning, so optimize for aligning. while (++rowIndex < rowLength) { cells = table[rowIndex] columnIndex = -1 columnLength = cells.length row = [] sizes = [] if (columnLength > mostCellsPerRow) { mostCellsPerRow = columnLength } while (++columnIndex < columnLength) { cell = serialize(cells[columnIndex]) if (alignDelimiters === true) { size = stringLength(cell) sizes[columnIndex] = size largest = longestCellByColumn[columnIndex] if (largest === undefined || size > largest) { longestCellByColumn[columnIndex] = size } } row.push(cell) } cellMatrix[rowIndex] = row sizeMatrix[rowIndex] = sizes } // Figure out which alignments to use. columnIndex = -1 columnLength = mostCellsPerRow if (typeof align === 'object' && 'length' in align) { while (++columnIndex < columnLength) { alignments[columnIndex] = toAlignment(align[columnIndex]) } } else { code = toAlignment(align) while (++columnIndex < columnLength) { alignments[columnIndex] = code } } // Inject the alignment row. columnIndex = -1 columnLength = mostCellsPerRow row = [] sizes = [] while (++columnIndex < columnLength) { code = alignments[columnIndex] before = '' after = '' if (code === l) { before = colon } else if (code === r) { after = colon } else if (code === c) { before = colon after = colon } // There *must* be at least one hyphen-minus in each alignment cell. size = alignDelimiters ? Math.max( 1, longestCellByColumn[columnIndex] - before.length - after.length ) : 1 cell = before + repeat(dash, size) + after if (alignDelimiters === true) { size = before.length + size + after.length if (size > longestCellByColumn[columnIndex]) { longestCellByColumn[columnIndex] = size } sizes[columnIndex] = size } row[columnIndex] = cell } // Inject the alignment row. cellMatrix.splice(1, 0, row) sizeMatrix.splice(1, 0, sizes) rowIndex = -1 rowLength = cellMatrix.length lines = [] while (++rowIndex < rowLength) { row = cellMatrix[rowIndex] sizes = sizeMatrix[rowIndex] columnIndex = -1 columnLength = mostCellsPerRow line = [] while (++columnIndex < columnLength) { cell = row[columnIndex] || '' before = '' after = '' if (alignDelimiters === true) { size = longestCellByColumn[columnIndex] - (sizes[columnIndex] || 0) code = alignments[columnIndex] if (code === r) { before = repeat(space, size) } else if (code === c) { if (size % 2 === 0) { before = repeat(space, size / 2) after = before } else { before = repeat(space, size / 2 + 0.5) after = repeat(space, size / 2 - 0.5) } } else { after = repeat(space, size) } } if (start === true && columnIndex === 0) { line.push(verticalBar) } if ( padding === true && // Don’t add the opening space if we’re not aligning and the cell is // empty: there will be a closing space. !(alignDelimiters === false && cell === '') && (start === true || columnIndex !== 0) ) { line.push(space) } if (alignDelimiters === true) { line.push(before) } line.push(cell) if (alignDelimiters === true) { line.push(after) } if (padding === true) { line.push(space) } if (end === true || columnIndex !== columnLength - 1) { line.push(verticalBar) } } line = line.join('') if (end === false) { line = line.replace(trailingWhitespace, '') } lines.push(line) } return lines.join(lineFeed) } function serialize(value) { return value === null || value === undefined ? '' : String(value) } function defaultStringLength(value) { return value.length } function toAlignment(value) { var code = typeof value === 'string' ? value.charCodeAt(0) : x return code === L || code === l ? l : code === R || code === r ? r : code === C || code === c ? c : x }