| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- var EOL = {},
- EOF = {},
- QUOTE = 34,
- NEWLINE = 10,
- RETURN = 13;
- function objectConverter(columns) {
- return new Function("d", "return {" + columns.map(function(name, i) {
- return JSON.stringify(name) + ": d[" + i + "] || \"\"";
- }).join(",") + "}");
- }
- function customConverter(columns, f) {
- var object = objectConverter(columns);
- return function(row, i) {
- return f(object(row), i, columns);
- };
- }
- // Compute unique columns in order of discovery.
- function inferColumns(rows) {
- var columnSet = Object.create(null),
- columns = [];
- rows.forEach(function(row) {
- for (var column in row) {
- if (!(column in columnSet)) {
- columns.push(columnSet[column] = column);
- }
- }
- });
- return columns;
- }
- function pad(value, width) {
- var s = value + "", length = s.length;
- return length < width ? new Array(width - length + 1).join(0) + s : s;
- }
- function formatYear(year) {
- return year < 0 ? "-" + pad(-year, 6)
- : year > 9999 ? "+" + pad(year, 6)
- : pad(year, 4);
- }
- function formatDate(date) {
- var hours = date.getUTCHours(),
- minutes = date.getUTCMinutes(),
- seconds = date.getUTCSeconds(),
- milliseconds = date.getUTCMilliseconds();
- return isNaN(date) ? "Invalid Date"
- : formatYear(date.getUTCFullYear(), 4) + "-" + pad(date.getUTCMonth() + 1, 2) + "-" + pad(date.getUTCDate(), 2)
- + (milliseconds ? "T" + pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + "." + pad(milliseconds, 3) + "Z"
- : seconds ? "T" + pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + "Z"
- : minutes || hours ? "T" + pad(hours, 2) + ":" + pad(minutes, 2) + "Z"
- : "");
- }
- export default function(delimiter) {
- var reFormat = new RegExp("[\"" + delimiter + "\n\r]"),
- DELIMITER = delimiter.charCodeAt(0);
- function parse(text, f) {
- var convert, columns, rows = parseRows(text, function(row, i) {
- if (convert) return convert(row, i - 1);
- columns = row, convert = f ? customConverter(row, f) : objectConverter(row);
- });
- rows.columns = columns || [];
- return rows;
- }
- function parseRows(text, f) {
- var rows = [], // output rows
- N = text.length,
- I = 0, // current character index
- n = 0, // current line number
- t, // current token
- eof = N <= 0, // current token followed by EOF?
- eol = false; // current token followed by EOL?
- // Strip the trailing newline.
- if (text.charCodeAt(N - 1) === NEWLINE) --N;
- if (text.charCodeAt(N - 1) === RETURN) --N;
- function token() {
- if (eof) return EOF;
- if (eol) return eol = false, EOL;
- // Unescape quotes.
- var i, j = I, c;
- if (text.charCodeAt(j) === QUOTE) {
- while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE);
- if ((i = I) >= N) eof = true;
- else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true;
- else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
- return text.slice(j + 1, i - 1).replace(/""/g, "\"");
- }
- // Find next delimiter or newline.
- while (I < N) {
- if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true;
- else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
- else if (c !== DELIMITER) continue;
- return text.slice(j, i);
- }
- // Return last token before EOF.
- return eof = true, text.slice(j, N);
- }
- while ((t = token()) !== EOF) {
- var row = [];
- while (t !== EOL && t !== EOF) row.push(t), t = token();
- if (f && (row = f(row, n++)) == null) continue;
- rows.push(row);
- }
- return rows;
- }
- function preformatBody(rows, columns) {
- return rows.map(function(row) {
- return columns.map(function(column) {
- return formatValue(row[column]);
- }).join(delimiter);
- });
- }
- function format(rows, columns) {
- if (columns == null) columns = inferColumns(rows);
- return [columns.map(formatValue).join(delimiter)].concat(preformatBody(rows, columns)).join("\n");
- }
- function formatBody(rows, columns) {
- if (columns == null) columns = inferColumns(rows);
- return preformatBody(rows, columns).join("\n");
- }
- function formatRows(rows) {
- return rows.map(formatRow).join("\n");
- }
- function formatRow(row) {
- return row.map(formatValue).join(delimiter);
- }
- function formatValue(value) {
- return value == null ? ""
- : value instanceof Date ? formatDate(value)
- : reFormat.test(value += "") ? "\"" + value.replace(/"/g, "\"\"") + "\""
- : value;
- }
- return {
- parse: parse,
- parseRows: parseRows,
- format: format,
- formatBody: formatBody,
- formatRows: formatRows,
- formatRow: formatRow,
- formatValue: formatValue
- };
- }
|