| '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 |
| } |