diff --git a/docs/docs.html b/docs/docs.html
index 647164e..e7911c9 100644
--- a/docs/docs.html
+++ b/docs/docs.html
@@ -259,7 +259,7 @@
delimiter: ",",
header: true,
newline: "\r\n",
- skipEmptyLines: false, //or 'greedy',
+ skipEmptyLines: false, //other option is 'greedy', meaning skip delimiters, quotes, and whitespace.
columns: null //or array of strings
}
@@ -318,7 +318,7 @@
newline
"\r"
, "\n"
, or "\r\n"
.
+ The character used to determine newline sequence. It defaults to "\r\n"
.
data
is an array of objects this option can be used to manually specify the keys (columns) you expect in the objects. If not set the keys of the first objects are used as column.
escapeFormulae
+ true
, field values that begin with =
, +
, -
, or @
, will be prepended with a '
to defend against injection attacks, because Excel and LibreOffice will automatically parse such cells as formulae.
+ transformHeader
header
to be true
. The function receives the header as its first argument.header
to be true
. The function receives the header as its first argument and the index as second.downloadRequestBody
+ skipEmptyLines
@@ -614,6 +632,14 @@ var csv = Papa.unparse({
A callback function, identical to step, which activates streaming. However, this function is executed after every chunk of the file is loaded and parsed rather than every row. Works only with local and remote files. Do not use both chunk and step callbacks together. For the function signature, see the documentation for the step function.
chunkSize
+ Papa.LocalChunkSize
and Papa.RemoteChunkSize
. See configurable section to know the usage of both parameters.
+ fastMode
diff --git a/docs/resources/js/lovers.js b/docs/resources/js/lovers.js
index ea21149..530a164 100644
--- a/docs/resources/js/lovers.js
+++ b/docs/resources/js/lovers.js
@@ -102,6 +102,29 @@ var peopleLovePapa = [
name: "Hua Explore",
description: "The premier destination for information on Contemporary Chinese Art.",
quote: "Papa makes processing data that galleries send us totally seamless."
+ },
+ {
+ link: "https://monei.net",
+ name: "MONEI",
+ description: "Digital payments made easy.",
+ quote: "With Papa life became much easier for us to manage huge csv payments files of our merchants."
+ },
+ {
+ link: "https://moonmail.io",
+ name: "MoonMail",
+ description: "OmniChannel Communication Platform powered by AWS PinPoint",
+ quote: "Papa makes contact imports a plain sailing."
+ },
+ {
+ link: "https://apps.shopify.com/wholesaler",
+ name: "Wholesaler for Shopify",
+ description: "Shopify App to offer Wholesaling within one unique Shopify store",
+ quote: "Super fast bulk Wholesale product price uploads. Love Papa!."
+ },
+ {
+ link: "https://www.unnitmetaliya.com/sop-sample/",
+ name: "Visa SOP Sample",
+ description: "Providing free guide to international students.",
+ quote: "Use Papa Parse for many of side projects. Super fast and works all the time. Love it!"
}
-
];
diff --git a/package.json b/package.json
index 264f72f..6404a35 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "papaparse",
- "version": "5.0.2",
+ "version": "5.3.0",
"description": "Fast and powerful CSV parser for the browser that supports web workers and streaming large files. Converts CSV to JSON and JSON to CSV.",
"keywords": [
"csv",
@@ -44,7 +44,7 @@
"grunt-contrib-uglify": "^3.3.0",
"mocha": "^5.2.0",
"mocha-headless-chrome": "^2.0.1",
- "open": "0.0.5",
+ "open": "7.0.0",
"serve-static": "^1.7.1"
},
"scripts": {
diff --git a/papaparse.js b/papaparse.js
index ae4b70d..e1825d1 100755
--- a/papaparse.js
+++ b/papaparse.js
@@ -1,6 +1,6 @@
/* @license
Papa Parse
-v5.0.2
+v5.3.0
https://github.com/mholt/PapaParse
License: MIT
*/
@@ -282,6 +282,9 @@ License: MIT
/** the columns (keys) we expect when we unparse objects */
var _columns = null;
+ /** whether to prevent outputting cells that can be parsed as formulae by spreadsheet software (Excel and LibreOffice) */
+ var _escapeFormulae = false;
+
unpackConfig();
var quoteCharRegex = new RegExp(escapeRegExp(_quoteChar), 'g');
@@ -361,6 +364,9 @@ License: MIT
if (_config.escapeChar !== undefined) {
_escapedQuote = _config.escapeChar + _quoteChar;
}
+
+ if (typeof _config.escapeFormulae === 'boolean')
+ _escapeFormulae = _config.escapeFormulae;
}
@@ -447,6 +453,10 @@ License: MIT
if (str.constructor === Date)
return JSON.stringify(str).slice(1, 25);
+ if (_escapeFormulae === true && typeof str === "string" && (str.match(/^[=+\-@].*$/) !== null)) {
+ str = "'" + str;
+ }
+
var escapedQuoteStr = str.toString().replace(quoteCharRegex, _escapedQuote);
var needsQuotes = (typeof _quotes === 'boolean' && _quotes)
@@ -642,7 +652,7 @@ License: MIT
xhr.onerror = bindFunction(this._chunkError, this);
}
- xhr.open('GET', this._input, !IS_WORKER);
+ xhr.open(this._config.downloadRequestBody ? 'POST' : 'GET', this._input, !IS_WORKER);
// Headers can only be set when once the request state is OPENED
if (this._config.downloadRequestHeaders)
{
@@ -661,7 +671,7 @@ License: MIT
}
try {
- xhr.send();
+ xhr.send(this._config.downloadRequestBody);
}
catch (err) {
this._chunkError(err.message);
@@ -669,8 +679,6 @@ License: MIT
if (IS_WORKER && xhr.status === 0)
this._chunkError();
- else
- this._start += this._config.chunkSize;
};
this._chunkLoaded = function()
@@ -684,7 +692,9 @@ License: MIT
return;
}
- this._finished = !this._config.chunkSize || this._start > getFileSize(xhr);
+ // Use chunckSize as it may be a diference on reponse lentgh due to characters with more than 1 byte
+ this._start += this._config.chunkSize ? this._config.chunkSize : xhr.responseText.length;
+ this._finished = !this._config.chunkSize || this._start >= getFileSize(xhr);
this.parseChunk(xhr.responseText);
};
@@ -700,7 +710,7 @@ License: MIT
if (contentRange === null) { // no content range, then finish!
return -1;
}
- return parseInt(contentRange.substr(contentRange.lastIndexOf('/') + 1));
+ return parseInt(contentRange.substring(contentRange.lastIndexOf('/') + 1));
}
}
NetworkStreamer.prototype = Object.create(ChunkStreamer.prototype);
@@ -789,8 +799,14 @@ License: MIT
{
if (this._finished) return;
var size = this._config.chunkSize;
- var chunk = size ? remaining.substr(0, size) : remaining;
- remaining = size ? remaining.substr(size) : '';
+ var chunk;
+ if(size) {
+ chunk = remaining.substring(0, size);
+ remaining = remaining.substring(size);
+ } else {
+ chunk = remaining;
+ remaining = '';
+ }
this._finished = !remaining;
return this.parseChunk(chunk);
};
@@ -1000,7 +1016,7 @@ License: MIT
// One goal is to minimize the use of regular expressions...
var MAX_FLOAT = Math.pow(2, 53);
var MIN_FLOAT = -MAX_FLOAT;
- var FLOAT = /^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i;
+ var FLOAT = /^\s*-?(\d+\.?|\.\d+|\d+\.\d+)(e[-+]?\d+)?\s*$/;
var ISO_DATE = /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/;
var self = this;
var _stepCounter = 0; // Number of times step was called (number of rows parsed)
@@ -1037,8 +1053,10 @@ License: MIT
_stepCounter += results.data.length;
if (_config.preview && _stepCounter > _config.preview)
_parser.abort();
- else
+ else {
+ _results.data = _results.data[0];
userStep(_results, self);
+ }
}
};
}
@@ -1093,7 +1111,10 @@ License: MIT
{
_paused = true;
_parser.abort();
- _input = _input.substr(_parser.getCharIndex());
+
+ // If it is streaming via "chunking", the reader will start appending correctly already so no need to substring,
+ // otherwise we can get duplicate content within a row
+ _input = isFunction(_config.chunk) ? "" : _input.substring(_parser.getCharIndex());
};
this.resume = function()
@@ -1104,7 +1125,7 @@ License: MIT
} else {
// Bugfix: #636 In case the processing hasn't halted yet
// wait for it to halt in order to resume
- setTimeout(this.resume, 3);
+ setTimeout(self.resume, 3);
}
};
@@ -1168,10 +1189,10 @@ License: MIT
if (!_results)
return;
- function addHeder(header)
+ function addHeader(header, i)
{
if (isFunction(_config.transformHeader))
- header = _config.transformHeader(header);
+ header = _config.transformHeader(header, i);
_fields.push(header);
}
@@ -1179,13 +1200,13 @@ License: MIT
if (Array.isArray(_results.data[0]))
{
for (var i = 0; needsHeaderRow() && i < _results.data.length; i++)
- _results.data[i].forEach(addHeder);
+ _results.data[i].forEach(addHeader);
_results.data.splice(0, 1);
}
// if _results.data[0] is not an array, we are in a step where _results.data is the row.
else
- _results.data.forEach(addHeder);
+ _results.data.forEach(addHeader);
}
function shouldApplyDynamicTyping(field) {
@@ -1331,7 +1352,7 @@ License: MIT
function guessLineEndings(input, quoteChar)
{
- input = input.substr(0, 1024 * 1024); // max length 1 MB
+ input = input.substring(0, 1024 * 1024); // max length 1 MB
// Replace all the text inside quotes
var re = new RegExp(escapeRegExp(quoteChar) + '([^]*?)' + escapeRegExp(quoteChar), 'gm');
input = input.replace(re, '');
@@ -1357,12 +1378,15 @@ License: MIT
function addError(type, code, msg, row)
{
- _results.errors.push({
+ var error = {
type: type,
code: code,
- message: msg,
- row: row
- });
+ message: msg
+ };
+ if(row !== undefined) {
+ error.row = row;
+ }
+ _results.errors.push(error);
}
}
@@ -1449,7 +1473,7 @@ License: MIT
cursor += newline.length;
else if (ignoreLastRow)
return returnable();
- if (comments && row.substr(0, commentsLen) === comments)
+ if (comments && row.substring(0, commentsLen) === comments)
continue;
if (stepIsFunction)
{
@@ -1529,6 +1553,12 @@ License: MIT
continue;
}
+ if(nextDelim !== -1 && nextDelim < (quoteSearch + 1)) {
+ nextDelim = input.indexOf(delim, (quoteSearch + 1));
+ }
+ if(nextNewline !== -1 && nextNewline < (quoteSearch + 1)) {
+ nextNewline = input.indexOf(newline, (quoteSearch + 1));
+ }
// Check up to nextDelim or nextNewline, whichever is closest
var checkUpTo = nextNewline === -1 ? nextDelim : Math.min(nextDelim, nextNewline);
var spacesBetweenQuoteAndDelimiter = extraSpaces(checkUpTo);
@@ -1552,7 +1582,7 @@ License: MIT
var spacesBetweenQuoteAndNewLine = extraSpaces(nextNewline);
// Closing quote followed by newline or 'unnecessary spaces + newLine'
- if (input.substr(quoteSearch + 1 + spacesBetweenQuoteAndNewLine, newlineLen) === newline)
+ if (input.substring(quoteSearch + 1 + spacesBetweenQuoteAndNewLine, quoteSearch + 1 + spacesBetweenQuoteAndNewLine + newlineLen) === newline)
{
row.push(input.substring(cursor, quoteSearch).replace(quoteCharRegex, quoteChar));
saveRow(quoteSearch + 1 + spacesBetweenQuoteAndNewLine + newlineLen);
@@ -1591,7 +1621,7 @@ License: MIT
}
// Comment found at start of new line
- if (comments && row.length === 0 && input.substr(cursor, commentsLen) === comments)
+ if (comments && row.length === 0 && input.substring(cursor, cursor + commentsLen) === comments)
{
if (nextNewline === -1) // Comment ends at EOF
return returnable();
@@ -1607,7 +1637,7 @@ License: MIT
// we check, if we have quotes, because delimiter char may be part of field enclosed in quotes
if (quoteSearch > nextDelim) {
// we have quotes, so we try to find the next delimiter not enclosed in quotes and also next starting quote char
- var nextDelimObj = getNextUnqotedDelimiter(nextDelim, quoteSearch, nextNewline);
+ var nextDelimObj = getNextUnquotedDelimiter(nextDelim, quoteSearch, nextNewline);
// if we have next delimiter char which is not enclosed in quotes
if (nextDelimObj && typeof nextDelimObj.nextDelim !== 'undefined') {
@@ -1683,7 +1713,7 @@ License: MIT
if (ignoreLastRow)
return returnable();
if (typeof value === 'undefined')
- value = input.substr(cursor);
+ value = input.substring(cursor);
row.push(value);
cursor = inputLen; // important in case parsing is paused
pushRow(row);
@@ -1707,11 +1737,10 @@ License: MIT
}
/** Returns an object with the results, errors, and meta. */
- function returnable(stopped, step)
+ function returnable(stopped)
{
- var isStep = step || false;
return {
- data: isStep ? data[0] : data,
+ data: data,
errors: errors,
meta: {
delimiter: delim,
@@ -1726,13 +1755,13 @@ License: MIT
/** Executes the user's step function and resets data & errors. */
function doStep()
{
- step(returnable(undefined, true));
+ step(returnable());
data = [];
errors = [];
}
/** Gets the delimiter character, which is not inside the quoted field */
- function getNextUnqotedDelimiter(nextDelim, quoteSearch, newLine) {
+ function getNextUnquotedDelimiter(nextDelim, quoteSearch, newLine) {
var result = {
nextDelim: undefined,
quoteSearch: undefined
@@ -1754,7 +1783,7 @@ License: MIT
nextQuoteSearch = input.indexOf(quoteChar, nextQuoteSearch + 1);
}
// try to get the next delimiter position
- result = getNextUnqotedDelimiter(nextNextDelim, nextQuoteSearch, newLine);
+ result = getNextUnquotedDelimiter(nextNextDelim, nextQuoteSearch, newLine);
} else {
result = {
nextDelim: nextDelim,
diff --git a/papaparse.min.js b/papaparse.min.js
index 14c98ff..a92afc1 100644
--- a/papaparse.min.js
+++ b/papaparse.min.js
@@ -1,7 +1,7 @@
/* @license
Papa Parse
-v5.0.2
+v5.3.0
https://github.com/mholt/PapaParse
License: MIT
*/
-!function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof module&&"undefined"!=typeof exports?module.exports=t():e.Papa=t()}(this,function s(){"use strict";var f="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==f?f:{};var n=!f.document&&!!f.postMessage,o=n&&/blob:/i.test((f.location||{}).protocol),a={},h=0,b={parse:function(e,t){var r=(t=t||{}).dynamicTyping||!1;q(r)&&(t.dynamicTypingFunction=r,r={});if(t.dynamicTyping=r,t.transform=!!q(t.transform)&&t.transform,t.worker&&b.WORKERS_SUPPORTED){var i=function(){if(!b.WORKERS_SUPPORTED)return!1;var e=(r=f.URL||f.webkitURL||null,i=s.toString(),b.BLOB_URL||(b.BLOB_URL=r.createObjectURL(new Blob(["(",i,")();"],{type:"text/javascript"})))),t=new f.Worker(e);var r,i;return t.onmessage=_,t.id=h++,a[t.id]=t}();return i.userStep=t.step,i.userChunk=t.chunk,i.userComplete=t.complete,i.userError=t.error,t.step=q(t.step),t.chunk=q(t.chunk),t.complete=q(t.complete),t.error=q(t.error),delete t.worker,void i.postMessage({input:e,config:t,workerId:i.id})}var n=null;b.NODE_STREAM_INPUT,"string"==typeof e?n=t.download?new l(t):new p(t):!0===e.readable&&q(e.read)&&q(e.on)?n=new m(t):(f.File&&e instanceof File||e instanceof Object)&&(n=new c(t));return n.stream(e)},unparse:function(e,t){var i=!1,_=!0,g=",",v="\r\n",n='"',s=n+n,r=!1,a=null;!function(){if("object"!=typeof t)return;"string"!=typeof t.delimiter||b.BAD_DELIMITERS.filter(function(e){return-1!==t.delimiter.indexOf(e)}).length||(g=t.delimiter);("boolean"==typeof t.quotes||Array.isArray(t.quotes))&&(i=t.quotes);"boolean"!=typeof t.skipEmptyLines&&"string"!=typeof t.skipEmptyLines||(r=t.skipEmptyLines);"string"==typeof t.newline&&(v=t.newline);"string"==typeof t.quoteChar&&(n=t.quoteChar);"boolean"==typeof t.header&&(_=t.header);if(Array.isArray(t.columns)){if(0===t.columns.length)throw new Error("Option columns is empty");a=t.columns}void 0!==t.escapeChar&&(s=t.escapeChar+n)}();var o=new RegExp(U(n),"g");"string"==typeof e&&(e=JSON.parse(e));if(Array.isArray(e)){if(!e.length||Array.isArray(e[0]))return u(null,e,r);if("object"==typeof e[0])return u(a||h(e[0]),e,r)}else if("object"==typeof e)return"string"==typeof e.data&&(e.data=JSON.parse(e.data)),Array.isArray(e.data)&&(e.fields||(e.fields=e.meta&&e.meta.fields),e.fields||(e.fields=Array.isArray(e.data[0])?e.fields:h(e.data[0])),Array.isArray(e.data[0])||"object"==typeof e.data[0]||(e.data=[e.data])),u(e.fields||[],e.data||[],r);throw new Error("Unable to serialize unrecognized input");function h(e){if("object"!=typeof e)return[];var t=[];for(var r in e)t.push(r);return t}function u(e,t,r){var i="";"string"==typeof e&&(e=JSON.parse(e)),"string"==typeof t&&(t=JSON.parse(t));var n=Array.isArray(e)&&0