250 lines
5.4 KiB
JavaScript
250 lines
5.4 KiB
JavaScript
|
'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
|
|||
|
}
|