index.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. 'use strict'
  2. module.exports = markdownTable
  3. var dotRe = /\./
  4. var lastDotRe = /\.[^.]*$/
  5. // Characters.
  6. var space = ' '
  7. var lineFeed = '\n'
  8. var dash = '-'
  9. var dot = '.'
  10. var colon = ':'
  11. var lowercaseC = 'c'
  12. var lowercaseL = 'l'
  13. var lowercaseR = 'r'
  14. var verticalBar = '|'
  15. var minCellSize = 3
  16. // Create a table from a matrix of strings.
  17. function markdownTable(table, options) {
  18. var settings = options || {}
  19. var delimiter = settings.delimiter
  20. var start = settings.start
  21. var end = settings.end
  22. var alignment = settings.align
  23. var calculateStringLength = settings.stringLength || lengthNoop
  24. var cellCount = 0
  25. var rowIndex = -1
  26. var rowLength = table.length
  27. var sizes = []
  28. var align
  29. var rule
  30. var rows
  31. var row
  32. var cells
  33. var index
  34. var position
  35. var size
  36. var value
  37. var spacing
  38. var before
  39. var after
  40. alignment = alignment ? alignment.concat() : []
  41. if (delimiter === null || delimiter === undefined) {
  42. delimiter = space + verticalBar + space
  43. }
  44. if (start === null || start === undefined) {
  45. start = verticalBar + space
  46. }
  47. if (end === null || end === undefined) {
  48. end = space + verticalBar
  49. }
  50. while (++rowIndex < rowLength) {
  51. row = table[rowIndex]
  52. index = -1
  53. if (row.length > cellCount) {
  54. cellCount = row.length
  55. }
  56. while (++index < cellCount) {
  57. position = row[index] ? dotindex(row[index]) : null
  58. if (!sizes[index]) {
  59. sizes[index] = minCellSize
  60. }
  61. if (position > sizes[index]) {
  62. sizes[index] = position
  63. }
  64. }
  65. }
  66. if (typeof alignment === 'string') {
  67. alignment = pad(cellCount, alignment).split('')
  68. }
  69. // Make sure only valid alignments are used.
  70. index = -1
  71. while (++index < cellCount) {
  72. align = alignment[index]
  73. if (typeof align === 'string') {
  74. align = align.charAt(0).toLowerCase()
  75. }
  76. if (
  77. align !== lowercaseL &&
  78. align !== lowercaseR &&
  79. align !== lowercaseC &&
  80. align !== dot
  81. ) {
  82. align = ''
  83. }
  84. alignment[index] = align
  85. }
  86. rowIndex = -1
  87. rows = []
  88. while (++rowIndex < rowLength) {
  89. row = table[rowIndex]
  90. index = -1
  91. cells = []
  92. while (++index < cellCount) {
  93. value = row[index]
  94. value = stringify(value)
  95. if (alignment[index] === dot) {
  96. position = dotindex(value)
  97. size =
  98. sizes[index] +
  99. (dotRe.test(value) ? 0 : 1) -
  100. (calculateStringLength(value) - position)
  101. cells[index] = value + pad(size - 1)
  102. } else {
  103. cells[index] = value
  104. }
  105. }
  106. rows[rowIndex] = cells
  107. }
  108. sizes = []
  109. rowIndex = -1
  110. while (++rowIndex < rowLength) {
  111. cells = rows[rowIndex]
  112. index = -1
  113. while (++index < cellCount) {
  114. value = cells[index]
  115. if (!sizes[index]) {
  116. sizes[index] = minCellSize
  117. }
  118. size = calculateStringLength(value)
  119. if (size > sizes[index]) {
  120. sizes[index] = size
  121. }
  122. }
  123. }
  124. rowIndex = -1
  125. while (++rowIndex < rowLength) {
  126. cells = rows[rowIndex]
  127. index = -1
  128. if (settings.pad !== false) {
  129. while (++index < cellCount) {
  130. value = cells[index]
  131. position = sizes[index] - (calculateStringLength(value) || 0)
  132. spacing = pad(position)
  133. if (alignment[index] === lowercaseR || alignment[index] === dot) {
  134. value = spacing + value
  135. } else if (alignment[index] === lowercaseC) {
  136. position /= 2
  137. if (position % 1 === 0) {
  138. before = position
  139. after = position
  140. } else {
  141. before = position + 0.5
  142. after = position - 0.5
  143. }
  144. value = pad(before) + value + pad(after)
  145. } else {
  146. value += spacing
  147. }
  148. cells[index] = value
  149. }
  150. }
  151. rows[rowIndex] = cells.join(delimiter)
  152. }
  153. if (settings.rule !== false) {
  154. index = -1
  155. rule = []
  156. while (++index < cellCount) {
  157. // When `pad` is false, make the rule the same size as the first row.
  158. if (settings.pad === false) {
  159. value = table[0][index]
  160. spacing = calculateStringLength(stringify(value))
  161. spacing = spacing > minCellSize ? spacing : minCellSize
  162. } else {
  163. spacing = sizes[index]
  164. }
  165. align = alignment[index]
  166. // When `align` is left, don't add colons.
  167. value = align === lowercaseR || align === '' ? dash : colon
  168. value += pad(spacing - 2, dash)
  169. value += align !== lowercaseL && align !== '' ? colon : dash
  170. rule[index] = value
  171. }
  172. rows.splice(1, 0, rule.join(delimiter))
  173. }
  174. return start + rows.join(end + lineFeed + start) + end
  175. }
  176. function stringify(value) {
  177. return value === null || value === undefined ? '' : String(value)
  178. }
  179. // Get the length of `value`.
  180. function lengthNoop(value) {
  181. return String(value).length
  182. }
  183. // Get a string consisting of `length` `character`s.
  184. function pad(length, character) {
  185. return new Array(length + 1).join(character || space)
  186. }
  187. // Get the position of the last dot in `value`.
  188. function dotindex(value) {
  189. var match = lastDotRe.exec(value)
  190. return match ? match.index + 1 : value.length
  191. }