table.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. var each = require('./each');
  2. var strWidth = require('./strWidth');
  3. var map = require('./map');
  4. var repeat = require('./repeat');
  5. var cloneDeep = require('./cloneDeep');
  6. var defaults = require('./defaults');
  7. var trim = require('./trim');
  8. var rtrim = require('./rtrim');
  9. var filter = require('./filter');
  10. exports = function(rows) {
  11. var options =
  12. arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  13. rows = cloneDeep(rows);
  14. options.border = options.border || {};
  15. defaults(options.border, defBorder);
  16. options.columns = getColumns(rows);
  17. padData(rows, options);
  18. return render(rows, options);
  19. };
  20. function padData(rows, options) {
  21. var columnCount = options.columns.length;
  22. for (var i = 0, len = rows.length; i < len; i++) {
  23. while (rows[i].length < columnCount) {
  24. rows[i].push('');
  25. }
  26. }
  27. return loopData(rows, function(data, row, column) {
  28. var _options$columns$colu = options.columns[column],
  29. paddingLeft = _options$columns$colu.paddingLeft,
  30. width = _options$columns$colu.width,
  31. paddingRight = _options$columns$colu.paddingRight;
  32. return (
  33. repeat(' ', paddingLeft) +
  34. data +
  35. repeat(' ', width - strWidth(data) - paddingRight)
  36. );
  37. });
  38. }
  39. function loopData(rows, handler) {
  40. for (var i = 0, len = rows.length; i < len; i++) {
  41. var row = rows[i];
  42. for (var j = 0, _len = row.length; j < _len; j++) {
  43. var data = handler(row[j], i, j);
  44. if (data) {
  45. row[j] = data;
  46. }
  47. }
  48. }
  49. }
  50. function getColumns(rows) {
  51. var columns = [];
  52. var paddingLeft = 1;
  53. var paddingRight = 1;
  54. loopData(rows, function(data, row, column) {
  55. columns[column] = columns[column] || {
  56. width: paddingLeft + paddingRight,
  57. paddingLeft: paddingLeft,
  58. paddingRight: paddingRight
  59. };
  60. var width = strWidth(data) + paddingLeft + paddingRight;
  61. if (width > columns[column].width) {
  62. columns[column].width = width;
  63. }
  64. });
  65. return columns;
  66. }
  67. function render(rows, options) {
  68. var ret = '';
  69. ret += renderBorder('top', options);
  70. each(rows, function(row, idx) {
  71. ret += renderRow(row, options);
  72. if (idx === rows.length - 1) {
  73. ret += renderBorder('bottom', options);
  74. } else {
  75. ret += renderBorder('join', options);
  76. }
  77. });
  78. return ret;
  79. }
  80. function renderRow(columns, options) {
  81. var border = options.border;
  82. return (
  83. border.bodyLeft +
  84. columns.join(border.bodyJoin) +
  85. border.bodyRight +
  86. '\n'
  87. );
  88. }
  89. function renderBorder(type, options) {
  90. var border = options.border,
  91. columns = options.columns;
  92. var left = border[type + 'Left'];
  93. var right = border[type + 'Right'];
  94. var body = border[type + 'Body'];
  95. var join = border[type + 'Join'];
  96. var ret = map(columns, function(column) {
  97. return repeat(body, column.width);
  98. }).join(join);
  99. ret = left + ret + right;
  100. if (type !== 'bottom') {
  101. ret += '\n';
  102. }
  103. return ret;
  104. }
  105. exports.parse = function(table) {
  106. var options =
  107. arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  108. options.border = options.border || {};
  109. defaults(options.border, defBorder);
  110. var lines = splitLines(table, options.border);
  111. return parseLines(lines, options);
  112. };
  113. function splitLines(table, border) {
  114. var lines = table.split(/\n/);
  115. var trimLines = [];
  116. var chars = ' ';
  117. each(border, function(val) {
  118. return (chars += val);
  119. });
  120. each(lines, function(line, idx) {
  121. line = trim(line);
  122. line = trim(line, chars);
  123. trimLines[idx] = line;
  124. });
  125. return filter(lines, function(line, idx) {
  126. return trimLines[idx] !== '';
  127. });
  128. }
  129. function parseLines(lines, options) {
  130. var border = options.border;
  131. var maxLen = 0;
  132. each(lines, function(line) {
  133. var len = strWidth(line);
  134. if (len > maxLen) {
  135. maxLen = len;
  136. }
  137. });
  138. lines = map(lines, function(line) {
  139. return line + repeat(' ', maxLen - strWidth(line));
  140. });
  141. var start = -1;
  142. var end = -1;
  143. var firstLine = lines[0];
  144. if (border.bodyLeft) {
  145. start = firstLine.indexOf(border.bodyLeft);
  146. }
  147. if (border.bodyRight) {
  148. end = firstLine.lastIndexOf(border.bodyRight);
  149. }
  150. lines = map(lines, function(line) {
  151. if (start > -1) {
  152. if (end > -1) {
  153. line = line.slice(start + 1, end);
  154. } else {
  155. line = line.slice(start + 1);
  156. }
  157. }
  158. return line;
  159. });
  160. maxLen = lines[0].length;
  161. var rows = [];
  162. var rowCount = lines.length;
  163. var column = [];
  164. var _loop = function() {
  165. var isSeparator = true;
  166. var isFakeColumn = false;
  167. for (var r = 0; r < rowCount; r++) {
  168. column[r] = column[r] || '';
  169. var c = lines[r][i] || '';
  170. if (c !== border.bodyJoin) {
  171. isSeparator = false;
  172. }
  173. column[r] += lines[r][i];
  174. }
  175. if (isSeparator || i === maxLen - 1) {
  176. var emptyLineCount = 0;
  177. each(column, function(data) {
  178. data = rtrim(data, ' ' + border.bodyJoin);
  179. if (data === '') {
  180. emptyLineCount++;
  181. }
  182. });
  183. if (emptyLineCount >= rowCount - 1) {
  184. isFakeColumn = true;
  185. }
  186. if (isSeparator) {
  187. column = map(column, function(data) {
  188. return data.slice(0, data.length - 1);
  189. });
  190. }
  191. column = map(column, function(data) {
  192. return trim(data);
  193. });
  194. for (var _r = 0; _r < rowCount; _r++) {
  195. var row = rows[_r] || [];
  196. var data = column[_r];
  197. if (isFakeColumn) {
  198. if (row.length !== 0 && data) {
  199. row[row.length - 1] += border.bodyJoin + data;
  200. }
  201. } else {
  202. row.push(data);
  203. }
  204. rows[_r] = row;
  205. }
  206. column = [];
  207. }
  208. };
  209. for (var i = 0; i < maxLen; i++) {
  210. _loop();
  211. }
  212. return rows;
  213. }
  214. var defBorder = {
  215. topBody: '─',
  216. topJoin: '┬',
  217. topLeft: '┌',
  218. topRight: '┐',
  219. bottomBody: '─',
  220. bottomJoin: '┴',
  221. bottomLeft: '└',
  222. bottomRight: '┘',
  223. bodyLeft: '│',
  224. bodyRight: '│',
  225. bodyJoin: '│',
  226. joinBody: '─',
  227. joinLeft: '├',
  228. joinRight: '┤',
  229. joinJoin: '┼'
  230. };
  231. module.exports = exports;