var fs = require("fs"), extended = require("../extended"), Buffer = require('safer-buffer').Buffer, has = extended.has, isBoolean = extended.isBoolean, isUndefinedOrNull = extended.isUndefinedOrNull, escape = extended.escape, isArray = extended.isArray, keys = extended.keys, stream = require("stream"), LINE_BREAK = extended.LINE_BREAK; function createQuoteChecker(stream, quoteColumns, quoteHeaders) { var shouldQuote; if (isBoolean(quoteColumns)) { if (isBoolean(quoteHeaders)) { shouldQuote = function shouldQuote(index, isHeader) { return (isHeader ? quoteHeaders : quoteColumns); }; } else if (isArray(quoteHeaders)) { shouldQuote = function shouldQuote(index, isHeader) { return isHeader ? quoteHeaders[index] : quoteColumns; }; } else { shouldQuote = function shouldQuote(index, isHeader) { return isHeader ? quoteHeaders[stream.headers[index]] : quoteColumns; }; } } else if (isArray(quoteColumns)) { if (isBoolean(quoteHeaders)) { shouldQuote = function shouldQuote(index, isHeader) { return isHeader ? quoteHeaders : quoteColumns[index]; }; } else { shouldQuote = function shouldQuote(index, isHeader) { return isHeader ? quoteHeaders[index] : quoteColumns[index]; }; } } else { if (isBoolean(quoteHeaders)) { shouldQuote = function shouldQuote(index, isHeader) { return isHeader ? quoteHeaders : quoteColumns[stream.headers[index]]; }; } else { shouldQuote = function shouldQuote(index, isHeader) { return isHeader ? quoteHeaders[stream.headers[index]] : quoteColumns[stream.headers[index]]; }; } } return shouldQuote; } function createFormatter(options, stream) { options = options || {}; var delimiter = options.delimiter || ",", ESCAPE_REGEXP = new RegExp("[" + delimiter + escape(options.rowDelimiter || LINE_BREAK) + "']"), QUOTE = options.quote || '"', ESCAPE = options.escape || '"', REPLACE_REGEXP = new RegExp(QUOTE, "g"), quoteColumns = has(options, "quoteColumns") ? options.quoteColumns : false, quoteHeaders = has(options, "quoteHeaders") ? options.quoteHeaders : quoteColumns, shouldQuote = createQuoteChecker(stream, quoteColumns, quoteHeaders); function escapeField(field, index, isHeader) { var escape; field = field.replace(/\0/g, ''); if ((escape = field.indexOf(QUOTE) !== -1)) { field = field.replace(REPLACE_REGEXP, ESCAPE + QUOTE); escape = true; } else { escape = field.search(ESCAPE_REGEXP) !== -1; } escape = escape || shouldQuote(index, isHeader); if (escape) { field = [QUOTE + field + QUOTE]; } else { field = [field]; } return field.join(""); } return function escapeFields(fields, isHeader) { var i = -1, l = fields.length, ret = [], field; while (++i < l) { field = fields[i]; field = (isUndefinedOrNull(field) ? "" : field) + ""; ret.push(escapeField(field, i, isHeader)); } return ret.join(delimiter); }; } function defaultTransform(row, cb) { return cb(null, row); } function isHashArray(arr) { return isArray(arr) && isArray(arr[0]) && arr[0].length === 2; } //get headers from a row item function gatherHeaders(item) { var ret, i, l; if (isHashArray(item)) { //lets assume a multidimesional array with item 0 bing the title i = -1; l = item.length; ret = []; while (++i < l) { ret[i] = item[i][0]; } } else if (isArray(item)) { ret = item; } else { ret = keys(item); } return ret; } //check if we need to write header return true if we should also write a row //could be false if headers is true and the header row(first item) is passed in function checkHeaders(stream, item) { var headers, ret = true; if (!stream.parsedHeaders) { stream.parsedHeaders = true; headers = stream.headers = gatherHeaders(item); stream.headersLength = headers.length; } if (!stream.hasWrittenHeaders) { stream.totalCount++; stream.push(Buffer.from(stream.formatter(stream.headers, true), "utf8")); stream.hasWrittenHeaders = true; ret = isHashArray(item) || !isArray(item); } return ret; } //transform an object into a CSV row function transformHashData(stream, item) { var vals = [], row = [], headers = stream.headers, i = -1, headersLength = stream.headersLength; if (stream.totalCount++) { row.push(stream.rowDelimiter); } while (++i < headersLength) { vals[i] = item[headers[i]]; } row.push(stream.formatter(vals)); return row.join(""); } //transform an array into a CSV row function transformArrayData(stream, item, cb) { var row = []; if (stream.totalCount++) { row.push(stream.rowDelimiter); } row.push(stream.formatter(item)); return row.join(""); } //transform an array of two item arrays into a CSV row function transformHashArrayData(stream, item) { var vals = [], row = [], i = -1, headersLength = stream.headersLength; if (stream.totalCount++) { row.push(stream.rowDelimiter); } while (++i < headersLength) { vals[i] = item[i][1]; } row.push(stream.formatter(vals)); return row.join(""); } //wrapper to determin what transform to run function transformItem(stream, item) { var ret; if (isArray(item)) { if (isHashArray(item)) { ret = transformHashArrayData(stream, item); } else { ret = transformArrayData(stream, item); } } else { ret = transformHashData(stream, item); } return ret; } exports.createFormatter = createFormatter; exports.transformItem = transformItem; exports.checkHeaders = checkHeaders; exports.defaultTransform = defaultTransform;