diff --git a/demo.html b/demo.html index 78cee10..c85103b 100644 --- a/demo.html +++ b/demo.html @@ -41,7 +41,7 @@ Issues - + Donate diff --git a/docs.html b/docs.html index 085b3fb..6bed4fb 100644 --- a/docs.html +++ b/docs.html @@ -39,7 +39,7 @@ Issues - + Donate @@ -329,11 +329,11 @@ var csv = Papa.unparse({
preview
If > 0, only that many rows will be parsed.step
To stream the input, define a callback function to receive results row-by-row rather than together at the end:
-step: function(results, parser) {
+step: function(results, handle) {
console.log("Row data:", results.data);
console.log("Row errors:", results.errors);
}
- You can call parser.abort()
to halt parsing that input (not available if using a worker).
+ Except when using a worker, you can call handle.abort()
to stop parsing, handle.pause()
to pause it, or handle.resume()
to resume.
Papa.RemoteChunkSize
Same as LocalChunkSize, but for downloading files from remote locations. Default 5 MB.
+ Papa.DefaultDelimiter
+ The delimiter used when one is not specified and it cannot be detected automatically. Default is comma ","
.
+ diff --git a/faq.html b/faq.html index 3f4f763..fde4037 100644 --- a/faq.html +++ b/faq.html @@ -39,7 +39,7 @@ Issues - + Donate
- Yes, please! I don't want to do this all by myself. Head over to the GitHub project page and hack away. + Yes, please! I don't want to do this all by myself. Head over to the GitHub project page and hack away. If you're making a significant change, open an issue first so we can talk about it.
diff --git a/index.html b/index.html index f481c5e..9405f10 100644 --- a/index.html +++ b/index.html @@ -48,7 +48,7 @@ Issues - + Donate diff --git a/resources/js/papaparse.js b/resources/js/papaparse.js index fe11b81..00e37a2 100644 --- a/resources/js/papaparse.js +++ b/resources/js/papaparse.js @@ -1,6 +1,6 @@ /* Papa Parse - v3.0.1 + v3.1.0 https://github.com/mholt/PapaParse */ (function(global) @@ -21,6 +21,7 @@ worker: false, comments: false, complete: undefined, + error: undefined, download: false, chunk: undefined, keepEmptyRows: false @@ -40,6 +41,7 @@ // Configurable chunk sizes for local and remote files, respectively global.Papa.LocalChunkSize = 1024 * 1024 * 10; // 10 MB global.Papa.RemoteChunkSize = 1024 * 1024 * 5; // 5 MB + global.Papa.DefaultDelimiter = ","; // Used if not specified and detection fails // Exposed for testing and development only global.Papa.Parser = Parser; @@ -81,11 +83,7 @@ function parseNextFile() { if (queue.length == 0) - { - if (isFunction(options.complete)) - options.complete(); return; - } var f = queue[0]; @@ -189,8 +187,6 @@ { var ph = new ParserHandle(config); var results = ph.parse(_input); - if (isFunction(config.complete)) - config.complete(results); return results; } } @@ -218,8 +214,6 @@ { var ph = new ParserHandle(config); var results = ph.parse(event.target.result); - if (isFunction(config.complete)) - config.complete(results); }; reader.onerror = function() { @@ -364,7 +358,7 @@ // Encloses a value around quotes if needed (makes a value safe for CSV insertion) function safe(str, col) { - if (typeof str === "undefined") + if (typeof str === "undefined" || str === null) return ""; str = str.toString().replace(/"/g, '""'); @@ -390,7 +384,7 @@ - // NOTE/TODO: Many of the functions of NetworkStreamer and FileStreamer are the same. Consolidate? + // TODO: Many of the functions of NetworkStreamer and FileStreamer are similar or the same. Consolidate? function NetworkStreamer(config) { config = config || {}; @@ -498,15 +492,11 @@ } else if (isFunction(config.chunk)) { - config.chunk(results); // TODO: Implement abort? (like step) + config.chunk(results); results = undefined; } - - if (finishedWithEntireFile && isFunction(config.complete)) - config.complete(results); - else if (results && results.meta.aborted && isFunction(config.complete)) - config.complete(results); - else if (!finishedWithEntireFile) + + if (!finishedWithEntireFile && !results.meta.paused) nextChunk(); } @@ -545,7 +535,7 @@ config = config || {}; if (!config.chunkSize) config.chunkSize = Papa.LocalChunkSize; - + var start = 0; var aggregate = ""; var partialLine = ""; @@ -633,12 +623,8 @@ config.chunk(results, file); results = undefined; } - - if (finishedWithEntireFile && isFunction(config.complete)) - config.complete(undefined, file); - else if (results && results.meta.aborted && isFunction(config.complete)) // TODO: Abort needs reworking like pause/resume need it (if streaming, no results object is returned, so it has no meta to say aborted: true...) - config.complete(results, file); - else if (!finishedWithEntireFile) + + if (!finishedWithEntireFile && !results.meta.paused) nextChunk(); } @@ -669,6 +655,10 @@ // One goal is to minimize the use of regular expressions... var FLOAT = /^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i; + var self = this; + var _input; // The input being parsed + var _parser; // The core parser being used + var _paused = false; // Whether we are paused or not var _delimiterError; // Temporary state between delimiter detection and processing results var _fields = []; // Fields are from the header row of the input, if there is one var _results = { // The last results returned from the parser @@ -689,7 +679,7 @@ else { _delimiterError = true; // add error after parsing (otherwise it would be overwritten) - _config.delimiter = ","; + _config.delimiter = Papa.DefaultDelimiter; } _results.meta.delimiter = _config.delimiter; } @@ -697,25 +687,57 @@ if (isFunction(_config.step)) { var userStep = _config.step; - _config.step = function(results, parser) + _config.step = function(results) { _results = results; if (needsHeaderRow()) processResults(); else - userStep(processResults(), parser); + userStep(processResults(), self); }; } - _results = new Parser(_config).parse(input); - return processResults(); + if (_config.preview && _config.header) + _config.preview++; // to compensate for header row + + _input = input; + _parser = new Parser(_config); + _results = _parser.parse(_input); + processResults(); + if (isFunction(_config.complete) && !_paused) + _config.complete(_results); + return _paused ? { meta: { paused: true } } : _results; }; + this.pause = function() + { + _paused = true; + _parser.abort(); + _input = _input.substr(_parser.getCharIndex()); + }; + + this.resume = function() + { + _paused = false; + _parser = new Parser(_config); + _parser.parse(_input); + if (isFunction(_config.complete) && !_paused) + _config.complete(_results); + }; + + this.abort = function() + { + _parser.abort(); + if (isFunction(_config.complete)) + _config.complete(_results); + _input = ""; + } + function processResults() { if (_results && _delimiterError) { - addError("Delimiter", "UndetectableDelimiter", "Unable to auto-detect delimiting character; defaulted to comma"); + addError("Delimiter", "UndetectableDelimiter", "Unable to auto-detect delimiting character; defaulted to '"+Papa.DefaultDelimiter+"'"); _delimiterError = false; } @@ -869,7 +891,6 @@ function Parser(config) { - var self = this; var EMPTY = /^\s*$/; var _input; // The input text being parsed @@ -888,7 +909,6 @@ var _colIdx; // Current col index within result row (0-based) var _runningRowIdx; // Cumulative row index, used by the preview feature var _aborted = false; // Abort flag - var _paused = false; // Pause flag // Unpack the config object config = config || {}; @@ -920,35 +940,24 @@ reset(input); return parserLoop(); }; -/* - // TODO: Pause and resume just doesn't work well. - // I suspect this may need to be implemented at a higher-level - // scope than just this core Parser. - this.pause = function() - { - _paused = true; - }; - this.resume = function() - { - _paused = false; - if (_i < _input.length) - return parserLoop(); - }; -*/ this.abort = function() { _aborted = true; }; + this.getCharIndex = function() + { + return _i; + }; + function parserLoop() { while (_i < _input.length) { if (_aborted) break; if (_preview > 0 && _runningRowIdx >= _preview) break; - if (_paused) return finishParsing(); - + if (_ch == '"') parseQuotes(); else if (_inQuotes) @@ -1066,7 +1075,7 @@ if (isFunction(_step)) { if (_data[_rowIdx]) - _step(returnable(), self); + _step(returnable()); clearErrorsAndData(); } } @@ -1085,7 +1094,7 @@ function twoCharLineBreak(i) { - return i < _input.length - 1 && + return i < _input.length - 1 && ((_input[i] == "\r" && _input[i+1] == "\n") || (_input[i] == "\n" && _input[i+1] == "\r")) } @@ -1157,7 +1166,8 @@ meta: { lines: _lineNum, delimiter: _delimiter, - aborted: _aborted + aborted: _aborted, + truncated: _preview > 0 && _i < _input.length } }; } @@ -1277,6 +1287,9 @@ if (typeof config.complete !== 'function') config.complete = DEFAULTS.complete; + if (typeof config.error !== 'function') + config.error = DEFAULTS.error; + if (typeof config.encoding !== 'string') config.encoding = DEFAULTS.encoding;