blob: 9a94af560e08fc1e19d5b18fc7cde4045234771c [file] [log] [blame]
'use strict'
module.exports = markdownTable
var dotRe = /\./
var lastDotRe = /\.[^.]*$/
// Characters.
var space = ' '
var lineFeed = '\n'
var dash = '-'
var dot = '.'
var colon = ':'
var lowercaseC = 'c'
var lowercaseL = 'l'
var lowercaseR = 'r'
var verticalBar = '|'
var minCellSize = 3
// Create a table from a matrix of strings.
function markdownTable(table, options) {
var settings = options || {}
var delimiter = settings.delimiter
var start = settings.start
var end = settings.end
var alignment = settings.align
var calculateStringLength = settings.stringLength || lengthNoop
var cellCount = 0
var rowIndex = -1
var rowLength = table.length
var sizes = []
var align
var rule
var rows
var row
var cells
var index
var position
var size
var value
var spacing
var before
var after
alignment = alignment ? alignment.concat() : []
if (delimiter === null || delimiter === undefined) {
delimiter = space + verticalBar + space
}
if (start === null || start === undefined) {
start = verticalBar + space
}
if (end === null || end === undefined) {
end = space + verticalBar
}
while (++rowIndex < rowLength) {
row = table[rowIndex]
index = -1
if (row.length > cellCount) {
cellCount = row.length
}
while (++index < cellCount) {
position = row[index] ? dotindex(row[index]) : null
if (!sizes[index]) {
sizes[index] = minCellSize
}
if (position > sizes[index]) {
sizes[index] = position
}
}
}
if (typeof alignment === 'string') {
alignment = pad(cellCount, alignment).split('')
}
// Make sure only valid alignments are used.
index = -1
while (++index < cellCount) {
align = alignment[index]
if (typeof align === 'string') {
align = align.charAt(0).toLowerCase()
}
if (
align !== lowercaseL &&
align !== lowercaseR &&
align !== lowercaseC &&
align !== dot
) {
align = ''
}
alignment[index] = align
}
rowIndex = -1
rows = []
while (++rowIndex < rowLength) {
row = table[rowIndex]
index = -1
cells = []
while (++index < cellCount) {
value = row[index]
value = stringify(value)
if (alignment[index] === dot) {
position = dotindex(value)
size =
sizes[index] +
(dotRe.test(value) ? 0 : 1) -
(calculateStringLength(value) - position)
cells[index] = value + pad(size - 1)
} else {
cells[index] = value
}
}
rows[rowIndex] = cells
}
sizes = []
rowIndex = -1
while (++rowIndex < rowLength) {
cells = rows[rowIndex]
index = -1
while (++index < cellCount) {
value = cells[index]
if (!sizes[index]) {
sizes[index] = minCellSize
}
size = calculateStringLength(value)
if (size > sizes[index]) {
sizes[index] = size
}
}
}
rowIndex = -1
while (++rowIndex < rowLength) {
cells = rows[rowIndex]
index = -1
if (settings.pad !== false) {
while (++index < cellCount) {
value = cells[index]
position = sizes[index] - (calculateStringLength(value) || 0)
spacing = pad(position)
if (alignment[index] === lowercaseR || alignment[index] === dot) {
value = spacing + value
} else if (alignment[index] === lowercaseC) {
position /= 2
if (position % 1 === 0) {
before = position
after = position
} else {
before = position + 0.5
after = position - 0.5
}
value = pad(before) + value + pad(after)
} else {
value += spacing
}
cells[index] = value
}
}
rows[rowIndex] = cells.join(delimiter)
}
if (settings.rule !== false) {
index = -1
rule = []
while (++index < cellCount) {
// When `pad` is false, make the rule the same size as the first row.
if (settings.pad === false) {
value = table[0][index]
spacing = calculateStringLength(stringify(value))
spacing = spacing > minCellSize ? spacing : minCellSize
} else {
spacing = sizes[index]
}
align = alignment[index]
// When `align` is left, don't add colons.
value = align === lowercaseR || align === '' ? dash : colon
value += pad(spacing - 2, dash)
value += align !== lowercaseL && align !== '' ? colon : dash
rule[index] = value
}
rows.splice(1, 0, rule.join(delimiter))
}
return start + rows.join(end + lineFeed + start) + end
}
function stringify(value) {
return value === null || value === undefined ? '' : String(value)
}
// Get the length of `value`.
function lengthNoop(value) {
return String(value).length
}
// Get a string consisting of `length` `character`s.
function pad(length, character) {
return new Array(length + 1).join(character || space)
}
// Get the position of the last dot in `value`.
function dotindex(value) {
var match = lastDotRe.exec(value)
return match ? match.index + 1 : value.length
}