dsv.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. var EOL = {},
  2. EOF = {},
  3. QUOTE = 34,
  4. NEWLINE = 10,
  5. RETURN = 13;
  6. function objectConverter(columns) {
  7. return new Function("d", "return {" + columns.map(function(name, i) {
  8. return JSON.stringify(name) + ": d[" + i + "] || \"\"";
  9. }).join(",") + "}");
  10. }
  11. function customConverter(columns, f) {
  12. var object = objectConverter(columns);
  13. return function(row, i) {
  14. return f(object(row), i, columns);
  15. };
  16. }
  17. // Compute unique columns in order of discovery.
  18. function inferColumns(rows) {
  19. var columnSet = Object.create(null),
  20. columns = [];
  21. rows.forEach(function(row) {
  22. for (var column in row) {
  23. if (!(column in columnSet)) {
  24. columns.push(columnSet[column] = column);
  25. }
  26. }
  27. });
  28. return columns;
  29. }
  30. function pad(value, width) {
  31. var s = value + "", length = s.length;
  32. return length < width ? new Array(width - length + 1).join(0) + s : s;
  33. }
  34. function formatYear(year) {
  35. return year < 0 ? "-" + pad(-year, 6)
  36. : year > 9999 ? "+" + pad(year, 6)
  37. : pad(year, 4);
  38. }
  39. function formatDate(date) {
  40. var hours = date.getUTCHours(),
  41. minutes = date.getUTCMinutes(),
  42. seconds = date.getUTCSeconds(),
  43. milliseconds = date.getUTCMilliseconds();
  44. return isNaN(date) ? "Invalid Date"
  45. : formatYear(date.getUTCFullYear(), 4) + "-" + pad(date.getUTCMonth() + 1, 2) + "-" + pad(date.getUTCDate(), 2)
  46. + (milliseconds ? "T" + pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + "." + pad(milliseconds, 3) + "Z"
  47. : seconds ? "T" + pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + "Z"
  48. : minutes || hours ? "T" + pad(hours, 2) + ":" + pad(minutes, 2) + "Z"
  49. : "");
  50. }
  51. export default function(delimiter) {
  52. var reFormat = new RegExp("[\"" + delimiter + "\n\r]"),
  53. DELIMITER = delimiter.charCodeAt(0);
  54. function parse(text, f) {
  55. var convert, columns, rows = parseRows(text, function(row, i) {
  56. if (convert) return convert(row, i - 1);
  57. columns = row, convert = f ? customConverter(row, f) : objectConverter(row);
  58. });
  59. rows.columns = columns || [];
  60. return rows;
  61. }
  62. function parseRows(text, f) {
  63. var rows = [], // output rows
  64. N = text.length,
  65. I = 0, // current character index
  66. n = 0, // current line number
  67. t, // current token
  68. eof = N <= 0, // current token followed by EOF?
  69. eol = false; // current token followed by EOL?
  70. // Strip the trailing newline.
  71. if (text.charCodeAt(N - 1) === NEWLINE) --N;
  72. if (text.charCodeAt(N - 1) === RETURN) --N;
  73. function token() {
  74. if (eof) return EOF;
  75. if (eol) return eol = false, EOL;
  76. // Unescape quotes.
  77. var i, j = I, c;
  78. if (text.charCodeAt(j) === QUOTE) {
  79. while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE);
  80. if ((i = I) >= N) eof = true;
  81. else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true;
  82. else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
  83. return text.slice(j + 1, i - 1).replace(/""/g, "\"");
  84. }
  85. // Find next delimiter or newline.
  86. while (I < N) {
  87. if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true;
  88. else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
  89. else if (c !== DELIMITER) continue;
  90. return text.slice(j, i);
  91. }
  92. // Return last token before EOF.
  93. return eof = true, text.slice(j, N);
  94. }
  95. while ((t = token()) !== EOF) {
  96. var row = [];
  97. while (t !== EOL && t !== EOF) row.push(t), t = token();
  98. if (f && (row = f(row, n++)) == null) continue;
  99. rows.push(row);
  100. }
  101. return rows;
  102. }
  103. function preformatBody(rows, columns) {
  104. return rows.map(function(row) {
  105. return columns.map(function(column) {
  106. return formatValue(row[column]);
  107. }).join(delimiter);
  108. });
  109. }
  110. function format(rows, columns) {
  111. if (columns == null) columns = inferColumns(rows);
  112. return [columns.map(formatValue).join(delimiter)].concat(preformatBody(rows, columns)).join("\n");
  113. }
  114. function formatBody(rows, columns) {
  115. if (columns == null) columns = inferColumns(rows);
  116. return preformatBody(rows, columns).join("\n");
  117. }
  118. function formatRows(rows) {
  119. return rows.map(formatRow).join("\n");
  120. }
  121. function formatRow(row) {
  122. return row.map(formatValue).join(delimiter);
  123. }
  124. function formatValue(value) {
  125. return value == null ? ""
  126. : value instanceof Date ? formatDate(value)
  127. : reFormat.test(value += "") ? "\"" + value.replace(/"/g, "\"\"") + "\""
  128. : value;
  129. }
  130. return {
  131. parse: parse,
  132. parseRows: parseRows,
  133. format: format,
  134. formatBody: formatBody,
  135. formatRows: formatRows,
  136. formatRow: formatRow,
  137. formatValue: formatValue
  138. };
  139. }