Built motion from commit 76eb00b9e.|1.0.24
[motion.git] / public / bower_components / papaparse / papaparse.js
1 /*!
2         Papa Parse
3         v4.1.4
4         https://github.com/mholt/PapaParse
5 */
6 (function(root, factory)
7 {
8         if (typeof define === 'function' && define.amd)
9         {
10                 // AMD. Register as an anonymous module.
11                 define([], factory);
12         }
13         else if (typeof module === 'object' && module.exports)
14         {
15                 // Node. Does not work with strict CommonJS, but
16                 // only CommonJS-like environments that support module.exports,
17                 // like Node.
18                 module.exports = factory();
19         }
20         else
21         {
22                 // Browser globals (root is window)
23                 root.Papa = factory();
24         }
25 }(this, function()
26 {
27         'use strict';
28
29         var global = (function () {
30                 // alternative method, similar to `Function('return this')()`
31                 // but without using `eval` (which is disabled when
32                 // using Content Security Policy).
33
34                 if (typeof self !== 'undefined') { return self; }
35                 if (typeof window !== 'undefined') { return window; }
36                 if (typeof global !== 'undefined') { return global; }
37
38         // When running tests none of the above have been defined
39         return {};
40         })();
41
42
43         var IS_WORKER = !global.document && !!global.postMessage,
44                 IS_PAPA_WORKER = IS_WORKER && /(\?|&)papaworker(=|&|$)/.test(global.location.search),
45                 LOADED_SYNC = false, AUTO_SCRIPT_PATH;
46         var workers = {}, workerIdCounter = 0;
47
48         var Papa = {};
49
50         Papa.parse = CsvToJson;
51         Papa.unparse = JsonToCsv;
52
53         Papa.RECORD_SEP = String.fromCharCode(30);
54         Papa.UNIT_SEP = String.fromCharCode(31);
55         Papa.BYTE_ORDER_MARK = '\ufeff';
56         Papa.BAD_DELIMITERS = ['\r', '\n', '"', Papa.BYTE_ORDER_MARK];
57         Papa.WORKERS_SUPPORTED = !IS_WORKER && !!global.Worker;
58         Papa.SCRIPT_PATH = null;        // Must be set by your code if you use workers and this lib is loaded asynchronously
59
60         // Configurable chunk sizes for local and remote files, respectively
61         Papa.LocalChunkSize = 1024 * 1024 * 10; // 10 MB
62         Papa.RemoteChunkSize = 1024 * 1024 * 5; // 5 MB
63         Papa.DefaultDelimiter = ',';                    // Used if not specified and detection fails
64
65         // Exposed for testing and development only
66         Papa.Parser = Parser;
67         Papa.ParserHandle = ParserHandle;
68         Papa.NetworkStreamer = NetworkStreamer;
69         Papa.FileStreamer = FileStreamer;
70         Papa.StringStreamer = StringStreamer;
71
72         if (global.jQuery)
73         {
74                 var $ = global.jQuery;
75                 $.fn.parse = function(options)
76                 {
77                         var config = options.config || {};
78                         var queue = [];
79
80                         this.each(function(idx)
81                         {
82                                 var supported = $(this).prop('tagName').toUpperCase() === 'INPUT'
83                                                                 && $(this).attr('type').toLowerCase() === 'file'
84                                                                 && global.FileReader;
85
86                                 if (!supported || !this.files || this.files.length === 0)
87                                         return true;    // continue to next input element
88
89                                 for (var i = 0; i < this.files.length; i++)
90                                 {
91                                         queue.push({
92                                                 file: this.files[i],
93                                                 inputElem: this,
94                                                 instanceConfig: $.extend({}, config)
95                                         });
96                                 }
97                         });
98
99                         parseNextFile();        // begin parsing
100                         return this;            // maintains chainability
101
102
103                         function parseNextFile()
104                         {
105                                 if (queue.length === 0)
106                                 {
107                                         if (isFunction(options.complete))
108                                                 options.complete();
109                                         return;
110                                 }
111
112                                 var f = queue[0];
113
114                                 if (isFunction(options.before))
115                                 {
116                                         var returned = options.before(f.file, f.inputElem);
117
118                                         if (typeof returned === 'object')
119                                         {
120                                                 if (returned.action === 'abort')
121                                                 {
122                                                         error('AbortError', f.file, f.inputElem, returned.reason);
123                                                         return; // Aborts all queued files immediately
124                                                 }
125                                                 else if (returned.action === 'skip')
126                                                 {
127                                                         fileComplete(); // parse the next file in the queue, if any
128                                                         return;
129                                                 }
130                                                 else if (typeof returned.config === 'object')
131                                                         f.instanceConfig = $.extend(f.instanceConfig, returned.config);
132                                         }
133                                         else if (returned === 'skip')
134                                         {
135                                                 fileComplete(); // parse the next file in the queue, if any
136                                                 return;
137                                         }
138                                 }
139
140                                 // Wrap up the user's complete callback, if any, so that ours also gets executed
141                                 var userCompleteFunc = f.instanceConfig.complete;
142                                 f.instanceConfig.complete = function(results)
143                                 {
144                                         if (isFunction(userCompleteFunc))
145                                                 userCompleteFunc(results, f.file, f.inputElem);
146                                         fileComplete();
147                                 };
148
149                                 Papa.parse(f.file, f.instanceConfig);
150                         }
151
152                         function error(name, file, elem, reason)
153                         {
154                                 if (isFunction(options.error))
155                                         options.error({name: name}, file, elem, reason);
156                         }
157
158                         function fileComplete()
159                         {
160                                 queue.splice(0, 1);
161                                 parseNextFile();
162                         }
163                 }
164         }
165
166
167         if (IS_PAPA_WORKER)
168         {
169                 global.onmessage = workerThreadReceivedMessage;
170         }
171         else if (Papa.WORKERS_SUPPORTED)
172         {
173                 AUTO_SCRIPT_PATH = getScriptPath();
174
175                 // Check if the script was loaded synchronously
176                 if (!document.body)
177                 {
178                         // Body doesn't exist yet, must be synchronous
179                         LOADED_SYNC = true;
180                 }
181                 else
182                 {
183                         document.addEventListener('DOMContentLoaded', function () {
184                                 LOADED_SYNC = true;
185                         }, true);
186                 }
187         }
188
189
190
191
192         function CsvToJson(_input, _config)
193         {
194                 _config = _config || {};
195                 _config.dynamicTyping = _config.dynamicTyping || false;
196
197                 if (_config.worker && Papa.WORKERS_SUPPORTED)
198                 {
199                         var w = newWorker();
200
201                         w.userStep = _config.step;
202                         w.userChunk = _config.chunk;
203                         w.userComplete = _config.complete;
204                         w.userError = _config.error;
205
206                         _config.step = isFunction(_config.step);
207                         _config.chunk = isFunction(_config.chunk);
208                         _config.complete = isFunction(_config.complete);
209                         _config.error = isFunction(_config.error);
210                         delete _config.worker;  // prevent infinite loop
211
212                         w.postMessage({
213                                 input: _input,
214                                 config: _config,
215                                 workerId: w.id
216                         });
217
218                         return;
219                 }
220
221                 var streamer = null;
222                 if (typeof _input === 'string')
223                 {
224                         if (_config.download)
225                                 streamer = new NetworkStreamer(_config);
226                         else
227                                 streamer = new StringStreamer(_config);
228                 }
229                 else if ((global.File && _input instanceof File) || _input instanceof Object)   // ...Safari. (see issue #106)
230                         streamer = new FileStreamer(_config);
231
232                 return streamer.stream(_input);
233         }
234
235
236
237
238
239
240         function JsonToCsv(_input, _config)
241         {
242                 var _output = '';
243                 var _fields = [];
244
245                 // Default configuration
246
247                 /** whether to surround every datum with quotes */
248                 var _quotes = false;
249
250                 /** whether to write headers */
251                 var _writeHeader = true;
252
253                 /** delimiting character */
254                 var _delimiter = ',';
255
256                 /** newline character(s) */
257                 var _newline = '\r\n';
258
259                 /** quote character */
260                 var _quoteChar = '"';
261
262                 unpackConfig();
263
264                 var quoteCharRegex = new RegExp(_quoteChar, 'g');
265
266                 if (typeof _input === 'string')
267                         _input = JSON.parse(_input);
268
269                 if (_input instanceof Array)
270                 {
271                         if (!_input.length || _input[0] instanceof Array)
272                                 return serialize(null, _input);
273                         else if (typeof _input[0] === 'object')
274                                 return serialize(objectKeys(_input[0]), _input);
275                 }
276                 else if (typeof _input === 'object')
277                 {
278                         if (typeof _input.data === 'string')
279                                 _input.data = JSON.parse(_input.data);
280
281                         if (_input.data instanceof Array)
282                         {
283                                 if (!_input.fields)
284                                         _input.fields =  _input.meta && _input.meta.fields;
285
286                                 if (!_input.fields)
287                                         _input.fields =  _input.data[0] instanceof Array
288                                                                         ? _input.fields
289                                                                         : objectKeys(_input.data[0]);
290
291                                 if (!(_input.data[0] instanceof Array) && typeof _input.data[0] !== 'object')
292                                         _input.data = [_input.data];    // handles input like [1,2,3] or ['asdf']
293                         }
294
295                         return serialize(_input.fields || [], _input.data || []);
296                 }
297
298                 // Default (any valid paths should return before this)
299                 throw 'exception: Unable to serialize unrecognized input';
300
301
302                 function unpackConfig()
303                 {
304                         if (typeof _config !== 'object')
305                                 return;
306
307                         if (typeof _config.delimiter === 'string'
308                                 && _config.delimiter.length === 1
309                                 && Papa.BAD_DELIMITERS.indexOf(_config.delimiter) === -1)
310                         {
311                                 _delimiter = _config.delimiter;
312                         }
313
314                         if (typeof _config.quotes === 'boolean'
315                                 || _config.quotes instanceof Array)
316                                 _quotes = _config.quotes;
317
318                         if (typeof _config.newline === 'string')
319                                 _newline = _config.newline;
320
321                         if (typeof _config.quoteChar === 'string')
322                                 _quoteChar = _config.quoteChar;
323
324                         if (typeof _config.header === 'boolean')
325                                 _writeHeader = _config.header;
326                 }
327
328
329                 /** Turns an object's keys into an array */
330                 function objectKeys(obj)
331                 {
332                         if (typeof obj !== 'object')
333                                 return [];
334                         var keys = [];
335                         for (var key in obj)
336                                 keys.push(key);
337                         return keys;
338                 }
339
340                 /** The double for loop that iterates the data and writes out a CSV string including header row */
341                 function serialize(fields, data)
342                 {
343                         var csv = '';
344
345                         if (typeof fields === 'string')
346                                 fields = JSON.parse(fields);
347                         if (typeof data === 'string')
348                                 data = JSON.parse(data);
349
350                         var hasHeader = fields instanceof Array && fields.length > 0;
351                         var dataKeyedByField = !(data[0] instanceof Array);
352
353                         // If there a header row, write it first
354                         if (hasHeader && _writeHeader)
355                         {
356                                 for (var i = 0; i < fields.length; i++)
357                                 {
358                                         if (i > 0)
359                                                 csv += _delimiter;
360                                         csv += safe(fields[i], i);
361                                 }
362                                 if (data.length > 0)
363                                         csv += _newline;
364                         }
365
366                         // Then write out the data
367                         for (var row = 0; row < data.length; row++)
368                         {
369                                 var maxCol = hasHeader ? fields.length : data[row].length;
370
371                                 for (var col = 0; col < maxCol; col++)
372                                 {
373                                         if (col > 0)
374                                                 csv += _delimiter;
375                                         var colIdx = hasHeader && dataKeyedByField ? fields[col] : col;
376                                         csv += safe(data[row][colIdx], col);
377                                 }
378
379                                 if (row < data.length - 1)
380                                         csv += _newline;
381                         }
382
383                         return csv;
384                 }
385
386                 /** Encloses a value around quotes if needed (makes a value safe for CSV insertion) */
387                 function safe(str, col)
388                 {
389                         if (typeof str === 'undefined' || str === null)
390                                 return '';
391
392                         str = str.toString().replace(quoteCharRegex, _quoteChar+_quoteChar);
393
394                         var needsQuotes = (typeof _quotes === 'boolean' && _quotes)
395                                                         || (_quotes instanceof Array && _quotes[col])
396                                                         || hasAny(str, Papa.BAD_DELIMITERS)
397                                                         || str.indexOf(_delimiter) > -1
398                                                         || str.charAt(0) === ' '
399                                                         || str.charAt(str.length - 1) === ' ';
400
401                         return needsQuotes ? _quoteChar + str + _quoteChar : str;
402                 }
403
404                 function hasAny(str, substrings)
405                 {
406                         for (var i = 0; i < substrings.length; i++)
407                                 if (str.indexOf(substrings[i]) > -1)
408                                         return true;
409                         return false;
410                 }
411         }
412
413         /** ChunkStreamer is the base prototype for various streamer implementations. */
414         function ChunkStreamer(config)
415         {
416                 this._handle = null;
417                 this._paused = false;
418                 this._finished = false;
419                 this._input = null;
420                 this._baseIndex = 0;
421                 this._partialLine = '';
422                 this._rowCount = 0;
423                 this._start = 0;
424                 this._nextChunk = null;
425                 this.isFirstChunk = true;
426                 this._completeResults = {
427                         data: [],
428                         errors: [],
429                         meta: {}
430                 };
431                 replaceConfig.call(this, config);
432
433                 this.parseChunk = function(chunk)
434                 {
435                         // First chunk pre-processing
436                         if (this.isFirstChunk && isFunction(this._config.beforeFirstChunk))
437                         {
438                                 var modifiedChunk = this._config.beforeFirstChunk(chunk);
439                                 if (modifiedChunk !== undefined)
440                                         chunk = modifiedChunk;
441                         }
442                         this.isFirstChunk = false;
443
444                         // Rejoin the line we likely just split in two by chunking the file
445                         var aggregate = this._partialLine + chunk;
446                         this._partialLine = '';
447
448                         var results = this._handle.parse(aggregate, this._baseIndex, !this._finished);
449
450                         if (this._handle.paused() || this._handle.aborted())
451                                 return;
452
453                         var lastIndex = results.meta.cursor;
454
455                         if (!this._finished)
456                         {
457                                 this._partialLine = aggregate.substring(lastIndex - this._baseIndex);
458                                 this._baseIndex = lastIndex;
459                         }
460
461                         if (results && results.data)
462                                 this._rowCount += results.data.length;
463
464                         var finishedIncludingPreview = this._finished || (this._config.preview && this._rowCount >= this._config.preview);
465
466                         if (IS_PAPA_WORKER)
467                         {
468                                 global.postMessage({
469                                         results: results,
470                                         workerId: Papa.WORKER_ID,
471                                         finished: finishedIncludingPreview
472                                 });
473                         }
474                         else if (isFunction(this._config.chunk))
475                         {
476                                 this._config.chunk(results, this._handle);
477                                 if (this._paused)
478                                         return;
479                                 results = undefined;
480                                 this._completeResults = undefined;
481                         }
482
483                         if (!this._config.step && !this._config.chunk) {
484                                 this._completeResults.data = this._completeResults.data.concat(results.data);
485                                 this._completeResults.errors = this._completeResults.errors.concat(results.errors);
486                                 this._completeResults.meta = results.meta;
487                         }
488
489                         if (finishedIncludingPreview && isFunction(this._config.complete) && (!results || !results.meta.aborted))
490                                 this._config.complete(this._completeResults, this._input);
491
492                         if (!finishedIncludingPreview && (!results || !results.meta.paused))
493                                 this._nextChunk();
494
495                         return results;
496                 };
497
498                 this._sendError = function(error)
499                 {
500                         if (isFunction(this._config.error))
501                                 this._config.error(error);
502                         else if (IS_PAPA_WORKER && this._config.error)
503                         {
504                                 global.postMessage({
505                                         workerId: Papa.WORKER_ID,
506                                         error: error,
507                                         finished: false
508                                 });
509                         }
510                 };
511
512                 function replaceConfig(config)
513                 {
514                         // Deep-copy the config so we can edit it
515                         var configCopy = copy(config);
516                         configCopy.chunkSize = parseInt(configCopy.chunkSize);  // parseInt VERY important so we don't concatenate strings!
517                         if (!config.step && !config.chunk)
518                                 configCopy.chunkSize = null;  // disable Range header if not streaming; bad values break IIS - see issue #196
519                         this._handle = new ParserHandle(configCopy);
520                         this._handle.streamer = this;
521                         this._config = configCopy;      // persist the copy to the caller
522                 }
523         }
524
525
526         function NetworkStreamer(config)
527         {
528                 config = config || {};
529                 if (!config.chunkSize)
530                         config.chunkSize = Papa.RemoteChunkSize;
531                 ChunkStreamer.call(this, config);
532
533                 var xhr;
534
535                 if (IS_WORKER)
536                 {
537                         this._nextChunk = function()
538                         {
539                                 this._readChunk();
540                                 this._chunkLoaded();
541                         };
542                 }
543                 else
544                 {
545                         this._nextChunk = function()
546                         {
547                                 this._readChunk();
548                         };
549                 }
550
551                 this.stream = function(url)
552                 {
553                         this._input = url;
554                         this._nextChunk();      // Starts streaming
555                 };
556
557                 this._readChunk = function()
558                 {
559                         if (this._finished)
560                         {
561                                 this._chunkLoaded();
562                                 return;
563                         }
564
565                         xhr = new XMLHttpRequest();
566
567                         if (this._config.withCredentials)
568                         {
569                                 xhr.withCredentials = this._config.withCredentials;
570                         }
571
572                         if (!IS_WORKER)
573                         {
574                                 xhr.onload = bindFunction(this._chunkLoaded, this);
575                                 xhr.onerror = bindFunction(this._chunkError, this);
576                         }
577
578                         xhr.open('GET', this._input, !IS_WORKER);
579
580                         if (this._config.chunkSize)
581                         {
582                                 var end = this._start + this._config.chunkSize - 1;     // minus one because byte range is inclusive
583                                 xhr.setRequestHeader('Range', 'bytes='+this._start+'-'+end);
584                                 xhr.setRequestHeader('If-None-Match', 'webkit-no-cache'); // https://bugs.webkit.org/show_bug.cgi?id=82672
585                         }
586
587                         try {
588                                 xhr.send();
589                         }
590                         catch (err) {
591                                 this._chunkError(err.message);
592                         }
593
594                         if (IS_WORKER && xhr.status === 0)
595                                 this._chunkError();
596                         else
597                                 this._start += this._config.chunkSize;
598                 }
599
600                 this._chunkLoaded = function()
601                 {
602                         if (xhr.readyState != 4)
603                                 return;
604
605                         if (xhr.status < 200 || xhr.status >= 400)
606                         {
607                                 this._chunkError();
608                                 return;
609                         }
610
611                         this._finished = !this._config.chunkSize || this._start > getFileSize(xhr);
612                         this.parseChunk(xhr.responseText);
613                 }
614
615                 this._chunkError = function(errorMessage)
616                 {
617                         var errorText = xhr.statusText || errorMessage;
618                         this._sendError(errorText);
619                 }
620
621                 function getFileSize(xhr)
622                 {
623                         var contentRange = xhr.getResponseHeader('Content-Range');
624                         if (contentRange === null) { // no content range, then finish!
625                                 return -1;
626                         }
627                         return parseInt(contentRange.substr(contentRange.lastIndexOf('/') + 1));
628                 }
629         }
630         NetworkStreamer.prototype = Object.create(ChunkStreamer.prototype);
631         NetworkStreamer.prototype.constructor = NetworkStreamer;
632
633
634         function FileStreamer(config)
635         {
636                 config = config || {};
637                 if (!config.chunkSize)
638                         config.chunkSize = Papa.LocalChunkSize;
639                 ChunkStreamer.call(this, config);
640
641                 var reader, slice;
642
643                 // FileReader is better than FileReaderSync (even in worker) - see http://stackoverflow.com/q/24708649/1048862
644                 // But Firefox is a pill, too - see issue #76: https://github.com/mholt/PapaParse/issues/76
645                 var usingAsyncReader = typeof FileReader !== 'undefined';       // Safari doesn't consider it a function - see issue #105
646
647                 this.stream = function(file)
648                 {
649                         this._input = file;
650                         slice = file.slice || file.webkitSlice || file.mozSlice;
651
652                         if (usingAsyncReader)
653                         {
654                                 reader = new FileReader();              // Preferred method of reading files, even in workers
655                                 reader.onload = bindFunction(this._chunkLoaded, this);
656                                 reader.onerror = bindFunction(this._chunkError, this);
657                         }
658                         else
659                                 reader = new FileReaderSync();  // Hack for running in a web worker in Firefox
660
661                         this._nextChunk();      // Starts streaming
662                 };
663
664                 this._nextChunk = function()
665                 {
666                         if (!this._finished && (!this._config.preview || this._rowCount < this._config.preview))
667                                 this._readChunk();
668                 }
669
670                 this._readChunk = function()
671                 {
672                         var input = this._input;
673                         if (this._config.chunkSize)
674                         {
675                                 var end = Math.min(this._start + this._config.chunkSize, this._input.size);
676                                 input = slice.call(input, this._start, end);
677                         }
678                         var txt = reader.readAsText(input, this._config.encoding);
679                         if (!usingAsyncReader)
680                                 this._chunkLoaded({ target: { result: txt } }); // mimic the async signature
681                 }
682
683                 this._chunkLoaded = function(event)
684                 {
685                         // Very important to increment start each time before handling results
686                         this._start += this._config.chunkSize;
687                         this._finished = !this._config.chunkSize || this._start >= this._input.size;
688                         this.parseChunk(event.target.result);
689                 }
690
691                 this._chunkError = function()
692                 {
693                         this._sendError(reader.error);
694                 }
695
696         }
697         FileStreamer.prototype = Object.create(ChunkStreamer.prototype);
698         FileStreamer.prototype.constructor = FileStreamer;
699
700
701         function StringStreamer(config)
702         {
703                 config = config || {};
704                 ChunkStreamer.call(this, config);
705
706                 var string;
707                 var remaining;
708                 this.stream = function(s)
709                 {
710                         string = s;
711                         remaining = s;
712                         return this._nextChunk();
713                 }
714                 this._nextChunk = function()
715                 {
716                         if (this._finished) return;
717                         var size = this._config.chunkSize;
718                         var chunk = size ? remaining.substr(0, size) : remaining;
719                         remaining = size ? remaining.substr(size) : '';
720                         this._finished = !remaining;
721                         return this.parseChunk(chunk);
722                 }
723         }
724         StringStreamer.prototype = Object.create(StringStreamer.prototype);
725         StringStreamer.prototype.constructor = StringStreamer;
726
727
728
729         // Use one ParserHandle per entire CSV file or string
730         function ParserHandle(_config)
731         {
732                 // One goal is to minimize the use of regular expressions...
733                 var FLOAT = /^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i;
734
735                 var self = this;
736                 var _stepCounter = 0;   // Number of times step was called (number of rows parsed)
737                 var _input;                             // The input being parsed
738                 var _parser;                    // The core parser being used
739                 var _paused = false;    // Whether we are paused or not
740                 var _aborted = false;   // Whether the parser has aborted or not
741                 var _delimiterError;    // Temporary state between delimiter detection and processing results
742                 var _fields = [];               // Fields are from the header row of the input, if there is one
743                 var _results = {                // The last results returned from the parser
744                         data: [],
745                         errors: [],
746                         meta: {}
747                 };
748
749                 if (isFunction(_config.step))
750                 {
751                         var userStep = _config.step;
752                         _config.step = function(results)
753                         {
754                                 _results = results;
755
756                                 if (needsHeaderRow())
757                                         processResults();
758                                 else    // only call user's step function after header row
759                                 {
760                                         processResults();
761
762                                         // It's possbile that this line was empty and there's no row here after all
763                                         if (_results.data.length === 0)
764                                                 return;
765
766                                         _stepCounter += results.data.length;
767                                         if (_config.preview && _stepCounter > _config.preview)
768                                                 _parser.abort();
769                                         else
770                                                 userStep(_results, self);
771                                 }
772                         };
773                 }
774
775                 /**
776                  * Parses input. Most users won't need, and shouldn't mess with, the baseIndex
777                  * and ignoreLastRow parameters. They are used by streamers (wrapper functions)
778                  * when an input comes in multiple chunks, like from a file.
779                  */
780                 this.parse = function(input, baseIndex, ignoreLastRow)
781                 {
782                         if (!_config.newline)
783                                 _config.newline = guessLineEndings(input);
784
785                         _delimiterError = false;
786                         if (!_config.delimiter)
787                         {
788                                 var delimGuess = guessDelimiter(input, _config.newline);
789                                 if (delimGuess.successful)
790                                         _config.delimiter = delimGuess.bestDelimiter;
791                                 else
792                                 {
793                                         _delimiterError = true; // add error after parsing (otherwise it would be overwritten)
794                                         _config.delimiter = Papa.DefaultDelimiter;
795                                 }
796                                 _results.meta.delimiter = _config.delimiter;
797                         }
798                         else if(typeof _config.delimiter === 'function')
799                         {
800                                 _config.delimiter = _config.delimiter(input);
801                                 _results.meta.delimiter = _config.delimiter;
802                         }
803
804                         var parserConfig = copy(_config);
805                         if (_config.preview && _config.header)
806                                 parserConfig.preview++; // to compensate for header row
807
808                         _input = input;
809                         _parser = new Parser(parserConfig);
810                         _results = _parser.parse(_input, baseIndex, ignoreLastRow);
811                         processResults();
812                         return _paused ? { meta: { paused: true } } : (_results || { meta: { paused: false } });
813                 };
814
815                 this.paused = function()
816                 {
817                         return _paused;
818                 };
819
820                 this.pause = function()
821                 {
822                         _paused = true;
823                         _parser.abort();
824                         _input = _input.substr(_parser.getCharIndex());
825                 };
826
827                 this.resume = function()
828                 {
829                         _paused = false;
830                         self.streamer.parseChunk(_input);
831                 };
832
833                 this.aborted = function ()
834                 {
835                         return _aborted;
836                 };
837
838                 this.abort = function()
839                 {
840                         _aborted = true;
841                         _parser.abort();
842                         _results.meta.aborted = true;
843                         if (isFunction(_config.complete))
844                                 _config.complete(_results);
845                         _input = '';
846                 };
847
848                 function processResults()
849                 {
850                         if (_results && _delimiterError)
851                         {
852                                 addError('Delimiter', 'UndetectableDelimiter', 'Unable to auto-detect delimiting character; defaulted to \''+Papa.DefaultDelimiter+'\'');
853                                 _delimiterError = false;
854                         }
855
856                         if (_config.skipEmptyLines)
857                         {
858                                 for (var i = 0; i < _results.data.length; i++)
859                                         if (_results.data[i].length === 1 && _results.data[i][0] === '')
860                                                 _results.data.splice(i--, 1);
861                         }
862
863                         if (needsHeaderRow())
864                                 fillHeaderFields();
865
866                         return applyHeaderAndDynamicTyping();
867                 }
868
869                 function needsHeaderRow()
870                 {
871                         return _config.header && _fields.length === 0;
872                 }
873
874                 function fillHeaderFields()
875                 {
876                         if (!_results)
877                                 return;
878                         for (var i = 0; needsHeaderRow() && i < _results.data.length; i++)
879                                 for (var j = 0; j < _results.data[i].length; j++)
880                                         _fields.push(_results.data[i][j]);
881                         _results.data.splice(0, 1);
882                 }
883
884                 function parseDynamic(field, value)
885                 {
886                         if ((_config.dynamicTyping[field] || _config.dynamicTyping) === true)
887                         {
888                                 if (value === 'true' || value === 'TRUE')
889                                         return true;
890                                 else if (value === 'false' || value === 'FALSE')
891                                         return false;
892                                 else
893                                         return tryParseFloat(value);
894                         }
895                         return value;
896                 }
897
898                 function applyHeaderAndDynamicTyping()
899                 {
900                         if (!_results || (!_config.header && !_config.dynamicTyping))
901                                 return _results;
902
903                         for (var i = 0; i < _results.data.length; i++)
904                         {
905                                 var row = _config.header ? {} : [];
906
907                                 for (var j = 0; j < _results.data[i].length; j++)
908                                 {
909                                         var field = j;
910                                         var value = _results.data[i][j];
911
912                                         if (_config.header)
913                                                 field = j >= _fields.length ? '__parsed_extra' : _fields[j];
914
915                                         value = parseDynamic(field, value);
916
917                                         if (field === '__parsed_extra')
918                                         {
919                                                 row[field] = row[field] || [];
920                                                 row[field].push(value);
921                                         }
922                                         else
923                                                 row[field] = value;
924                                 }
925
926                                 _results.data[i] = row;
927
928                                 if (_config.header)
929                                 {
930                                         if (j > _fields.length)
931                                                 addError('FieldMismatch', 'TooManyFields', 'Too many fields: expected ' + _fields.length + ' fields but parsed ' + j, i);
932                                         else if (j < _fields.length)
933                                                 addError('FieldMismatch', 'TooFewFields', 'Too few fields: expected ' + _fields.length + ' fields but parsed ' + j, i);
934                                 }
935                         }
936
937                         if (_config.header && _results.meta)
938                                 _results.meta.fields = _fields;
939                         return _results;
940                 }
941
942                 function guessDelimiter(input, newline)
943                 {
944                         var delimChoices = [',', '\t', '|', ';', Papa.RECORD_SEP, Papa.UNIT_SEP];
945                         var bestDelim, bestDelta, fieldCountPrevRow;
946
947                         for (var i = 0; i < delimChoices.length; i++)
948                         {
949                                 var delim = delimChoices[i];
950                                 var delta = 0, avgFieldCount = 0;
951                                 fieldCountPrevRow = undefined;
952
953                                 var preview = new Parser({
954                                         delimiter: delim,
955                                         newline: newline,
956                                         preview: 10
957                                 }).parse(input);
958
959                                 for (var j = 0; j < preview.data.length; j++)
960                                 {
961                                         var fieldCount = preview.data[j].length;
962                                         avgFieldCount += fieldCount;
963
964                                         if (typeof fieldCountPrevRow === 'undefined')
965                                         {
966                                                 fieldCountPrevRow = fieldCount;
967                                                 continue;
968                                         }
969                                         else if (fieldCount > 1)
970                                         {
971                                                 delta += Math.abs(fieldCount - fieldCountPrevRow);
972                                                 fieldCountPrevRow = fieldCount;
973                                         }
974                                 }
975
976                                 if (preview.data.length > 0)
977                                         avgFieldCount /= preview.data.length;
978
979                                 if ((typeof bestDelta === 'undefined' || delta < bestDelta)
980                                         && avgFieldCount > 1.99)
981                                 {
982                                         bestDelta = delta;
983                                         bestDelim = delim;
984                                 }
985                         }
986
987                         _config.delimiter = bestDelim;
988
989                         return {
990                                 successful: !!bestDelim,
991                                 bestDelimiter: bestDelim
992                         }
993                 }
994
995                 function guessLineEndings(input)
996                 {
997                         input = input.substr(0, 1024*1024);     // max length 1 MB
998
999                         var r = input.split('\r');
1000
1001                         var n = input.split('\n');
1002
1003                         var nAppearsFirst = (n.length > 1 && n[0].length < r[0].length);
1004
1005                         if (r.length === 1 || nAppearsFirst)
1006                                 return '\n';
1007
1008                         var numWithN = 0;
1009                         for (var i = 0; i < r.length; i++)
1010                         {
1011                                 if (r[i][0] === '\n')
1012                                         numWithN++;
1013                         }
1014
1015                         return numWithN >= r.length / 2 ? '\r\n' : '\r';
1016                 }
1017
1018                 function tryParseFloat(val)
1019                 {
1020                         var isNumber = FLOAT.test(val);
1021                         return isNumber ? parseFloat(val) : val;
1022                 }
1023
1024                 function addError(type, code, msg, row)
1025                 {
1026                         _results.errors.push({
1027                                 type: type,
1028                                 code: code,
1029                                 message: msg,
1030                                 row: row
1031                         });
1032                 }
1033         }
1034
1035
1036
1037
1038
1039         /** The core parser implements speedy and correct CSV parsing */
1040         function Parser(config)
1041         {
1042                 // Unpack the config object
1043                 config = config || {};
1044                 var delim = config.delimiter;
1045                 var newline = config.newline;
1046                 var comments = config.comments;
1047                 var step = config.step;
1048                 var preview = config.preview;
1049                 var fastMode = config.fastMode;
1050                 var quoteChar = config.quoteChar || '"';
1051
1052                 // Delimiter must be valid
1053                 if (typeof delim !== 'string'
1054                         || Papa.BAD_DELIMITERS.indexOf(delim) > -1)
1055                         delim = ',';
1056
1057                 // Comment character must be valid
1058                 if (comments === delim)
1059                         throw 'Comment character same as delimiter';
1060                 else if (comments === true)
1061                         comments = '#';
1062                 else if (typeof comments !== 'string'
1063                         || Papa.BAD_DELIMITERS.indexOf(comments) > -1)
1064                         comments = false;
1065
1066                 // Newline must be valid: \r, \n, or \r\n
1067                 if (newline != '\n' && newline != '\r' && newline != '\r\n')
1068                         newline = '\n';
1069
1070                 // We're gonna need these at the Parser scope
1071                 var cursor = 0;
1072                 var aborted = false;
1073
1074                 this.parse = function(input, baseIndex, ignoreLastRow)
1075                 {
1076                         // For some reason, in Chrome, this speeds things up (!?)
1077                         if (typeof input !== 'string')
1078                                 throw 'Input must be a string';
1079
1080                         // We don't need to compute some of these every time parse() is called,
1081                         // but having them in a more local scope seems to perform better
1082                         var inputLen = input.length,
1083                                 delimLen = delim.length,
1084                                 newlineLen = newline.length,
1085                                 commentsLen = comments.length;
1086                         var stepIsFunction = typeof step === 'function';
1087
1088                         // Establish starting state
1089                         cursor = 0;
1090                         var data = [], errors = [], row = [], lastCursor = 0;
1091
1092                         if (!input)
1093                                 return returnable();
1094
1095                         if (fastMode || (fastMode !== false && input.indexOf(quoteChar) === -1))
1096                         {
1097                                 var rows = input.split(newline);
1098                                 for (var i = 0; i < rows.length; i++)
1099                                 {
1100                                         var row = rows[i];
1101                                         cursor += row.length;
1102                                         if (i !== rows.length - 1)
1103                                                 cursor += newline.length;
1104                                         else if (ignoreLastRow)
1105                                                 return returnable();
1106                                         if (comments && row.substr(0, commentsLen) === comments)
1107                                                 continue;
1108                                         if (stepIsFunction)
1109                                         {
1110                                                 data = [];
1111                                                 pushRow(row.split(delim));
1112                                                 doStep();
1113                                                 if (aborted)
1114                                                         return returnable();
1115                                         }
1116                                         else
1117                                                 pushRow(row.split(delim));
1118                                         if (preview && i >= preview)
1119                                         {
1120                                                 data = data.slice(0, preview);
1121                                                 return returnable(true);
1122                                         }
1123                                 }
1124                                 return returnable();
1125                         }
1126
1127                         var nextDelim = input.indexOf(delim, cursor);
1128                         var nextNewline = input.indexOf(newline, cursor);
1129                         var quoteCharRegex = new RegExp(quoteChar+quoteChar, 'g');
1130
1131                         // Parser loop
1132                         for (;;)
1133                         {
1134                                 // Field has opening quote
1135                                 if (input[cursor] === quoteChar)
1136                                 {
1137                                         // Start our search for the closing quote where the cursor is
1138                                         var quoteSearch = cursor;
1139
1140                                         // Skip the opening quote
1141                                         cursor++;
1142
1143                                         for (;;)
1144                                         {
1145                                                 // Find closing quote
1146                                                 var quoteSearch = input.indexOf(quoteChar, quoteSearch+1);
1147
1148                                                 if (quoteSearch === -1)
1149                                                 {
1150                                                         if (!ignoreLastRow) {
1151                                                                 // No closing quote... what a pity
1152                                                                 errors.push({
1153                                                                         type: 'Quotes',
1154                                                                         code: 'MissingQuotes',
1155                                                                         message: 'Quoted field unterminated',
1156                                                                         row: data.length,       // row has yet to be inserted
1157                                                                         index: cursor
1158                                                                 });
1159                                                         }
1160                                                         return finish();
1161                                                 }
1162
1163                                                 if (quoteSearch === inputLen-1)
1164                                                 {
1165                                                         // Closing quote at EOF
1166                                                         var value = input.substring(cursor, quoteSearch).replace(quoteCharRegex, quoteChar);
1167                                                         return finish(value);
1168                                                 }
1169
1170                                                 // If this quote is escaped, it's part of the data; skip it
1171                                                 if (input[quoteSearch+1] === quoteChar)
1172                                                 {
1173                                                         quoteSearch++;
1174                                                         continue;
1175                                                 }
1176
1177                                                 if (input[quoteSearch+1] === delim)
1178                                                 {
1179                                                         // Closing quote followed by delimiter
1180                                                         row.push(input.substring(cursor, quoteSearch).replace(quoteCharRegex, quoteChar));
1181                                                         cursor = quoteSearch + 1 + delimLen;
1182                                                         nextDelim = input.indexOf(delim, cursor);
1183                                                         nextNewline = input.indexOf(newline, cursor);
1184                                                         break;
1185                                                 }
1186
1187                                                 if (input.substr(quoteSearch+1, newlineLen) === newline)
1188                                                 {
1189                                                         // Closing quote followed by newline
1190                                                         row.push(input.substring(cursor, quoteSearch).replace(quoteCharRegex, quoteChar));
1191                                                         saveRow(quoteSearch + 1 + newlineLen);
1192                                                         nextDelim = input.indexOf(delim, cursor);       // because we may have skipped the nextDelim in the quoted field
1193
1194                                                         if (stepIsFunction)
1195                                                         {
1196                                                                 doStep();
1197                                                                 if (aborted)
1198                                                                         return returnable();
1199                                                         }
1200
1201                                                         if (preview && data.length >= preview)
1202                                                                 return returnable(true);
1203
1204                                                         break;
1205                                                 }
1206                                         }
1207
1208                                         continue;
1209                                 }
1210
1211                                 // Comment found at start of new line
1212                                 if (comments && row.length === 0 && input.substr(cursor, commentsLen) === comments)
1213                                 {
1214                                         if (nextNewline === -1) // Comment ends at EOF
1215                                                 return returnable();
1216                                         cursor = nextNewline + newlineLen;
1217                                         nextNewline = input.indexOf(newline, cursor);
1218                                         nextDelim = input.indexOf(delim, cursor);
1219                                         continue;
1220                                 }
1221
1222                                 // Next delimiter comes before next newline, so we've reached end of field
1223                                 if (nextDelim !== -1 && (nextDelim < nextNewline || nextNewline === -1))
1224                                 {
1225                                         row.push(input.substring(cursor, nextDelim));
1226                                         cursor = nextDelim + delimLen;
1227                                         nextDelim = input.indexOf(delim, cursor);
1228                                         continue;
1229                                 }
1230
1231                                 // End of row
1232                                 if (nextNewline !== -1)
1233                                 {
1234                                         row.push(input.substring(cursor, nextNewline));
1235                                         saveRow(nextNewline + newlineLen);
1236
1237                                         if (stepIsFunction)
1238                                         {
1239                                                 doStep();
1240                                                 if (aborted)
1241                                                         return returnable();
1242                                         }
1243
1244                                         if (preview && data.length >= preview)
1245                                                 return returnable(true);
1246
1247                                         continue;
1248                                 }
1249
1250                                 break;
1251                         }
1252
1253
1254                         return finish();
1255
1256
1257                         function pushRow(row)
1258                         {
1259                                 data.push(row);
1260                                 lastCursor = cursor;
1261                         }
1262
1263                         /**
1264                          * Appends the remaining input from cursor to the end into
1265                          * row, saves the row, calls step, and returns the results.
1266                          */
1267                         function finish(value)
1268                         {
1269                                 if (ignoreLastRow)
1270                                         return returnable();
1271                                 if (typeof value === 'undefined')
1272                                         value = input.substr(cursor);
1273                                 row.push(value);
1274                                 cursor = inputLen;      // important in case parsing is paused
1275                                 pushRow(row);
1276                                 if (stepIsFunction)
1277                                         doStep();
1278                                 return returnable();
1279                         }
1280
1281                         /**
1282                          * Appends the current row to the results. It sets the cursor
1283                          * to newCursor and finds the nextNewline. The caller should
1284                          * take care to execute user's step function and check for
1285                          * preview and end parsing if necessary.
1286                          */
1287                         function saveRow(newCursor)
1288                         {
1289                                 cursor = newCursor;
1290                                 pushRow(row);
1291                                 row = [];
1292                                 nextNewline = input.indexOf(newline, cursor);
1293                         }
1294
1295                         /** Returns an object with the results, errors, and meta. */
1296                         function returnable(stopped)
1297                         {
1298                                 return {
1299                                         data: data,
1300                                         errors: errors,
1301                                         meta: {
1302                                                 delimiter: delim,
1303                                                 linebreak: newline,
1304                                                 aborted: aborted,
1305                                                 truncated: !!stopped,
1306                                                 cursor: lastCursor + (baseIndex || 0)
1307                                         }
1308                                 };
1309                         }
1310
1311                         /** Executes the user's step function and resets data & errors. */
1312                         function doStep()
1313                         {
1314                                 step(returnable());
1315                                 data = [], errors = [];
1316                         }
1317                 };
1318
1319                 /** Sets the abort flag */
1320                 this.abort = function()
1321                 {
1322                         aborted = true;
1323                 };
1324
1325                 /** Gets the cursor position */
1326                 this.getCharIndex = function()
1327                 {
1328                         return cursor;
1329                 };
1330         }
1331
1332
1333         // If you need to load Papa Parse asynchronously and you also need worker threads, hard-code
1334         // the script path here. See: https://github.com/mholt/PapaParse/issues/87#issuecomment-57885358
1335         function getScriptPath()
1336         {
1337                 var scripts = document.getElementsByTagName('script');
1338                 return scripts.length ? scripts[scripts.length - 1].src : '';
1339         }
1340
1341         function newWorker()
1342         {
1343                 if (!Papa.WORKERS_SUPPORTED)
1344                         return false;
1345                 if (!LOADED_SYNC && Papa.SCRIPT_PATH === null)
1346                         throw new Error(
1347                                 'Script path cannot be determined automatically when Papa Parse is loaded asynchronously. ' +
1348                                 'You need to set Papa.SCRIPT_PATH manually.'
1349                         );
1350                 var workerUrl = Papa.SCRIPT_PATH || AUTO_SCRIPT_PATH;
1351                 // Append 'papaworker' to the search string to tell papaparse that this is our worker.
1352                 workerUrl += (workerUrl.indexOf('?') !== -1 ? '&' : '?') + 'papaworker';
1353                 var w = new global.Worker(workerUrl);
1354                 w.onmessage = mainThreadReceivedMessage;
1355                 w.id = workerIdCounter++;
1356                 workers[w.id] = w;
1357                 return w;
1358         }
1359
1360         /** Callback when main thread receives a message */
1361         function mainThreadReceivedMessage(e)
1362         {
1363                 var msg = e.data;
1364                 var worker = workers[msg.workerId];
1365                 var aborted = false;
1366
1367                 if (msg.error)
1368                         worker.userError(msg.error, msg.file);
1369                 else if (msg.results && msg.results.data)
1370                 {
1371                         var abort = function() {
1372                                 aborted = true;
1373                                 completeWorker(msg.workerId, { data: [], errors: [], meta: { aborted: true } });
1374                         };
1375
1376                         var handle = {
1377                                 abort: abort,
1378                                 pause: notImplemented,
1379                                 resume: notImplemented
1380                         };
1381
1382                         if (isFunction(worker.userStep))
1383                         {
1384                                 for (var i = 0; i < msg.results.data.length; i++)
1385                                 {
1386                                         worker.userStep({
1387                                                 data: [msg.results.data[i]],
1388                                                 errors: msg.results.errors,
1389                                                 meta: msg.results.meta
1390                                         }, handle);
1391                                         if (aborted)
1392                                                 break;
1393                                 }
1394                                 delete msg.results;     // free memory ASAP
1395                         }
1396                         else if (isFunction(worker.userChunk))
1397                         {
1398                                 worker.userChunk(msg.results, handle, msg.file);
1399                                 delete msg.results;
1400                         }
1401                 }
1402
1403                 if (msg.finished && !aborted)
1404                         completeWorker(msg.workerId, msg.results);
1405         }
1406
1407         function completeWorker(workerId, results) {
1408                 var worker = workers[workerId];
1409                 if (isFunction(worker.userComplete))
1410                         worker.userComplete(results);
1411                 worker.terminate();
1412                 delete workers[workerId];
1413         }
1414
1415         function notImplemented() {
1416                 throw 'Not implemented.';
1417         }
1418
1419         /** Callback when worker thread receives a message */
1420         function workerThreadReceivedMessage(e)
1421         {
1422                 var msg = e.data;
1423
1424                 if (typeof Papa.WORKER_ID === 'undefined' && msg)
1425                         Papa.WORKER_ID = msg.workerId;
1426
1427                 if (typeof msg.input === 'string')
1428                 {
1429                         global.postMessage({
1430                                 workerId: Papa.WORKER_ID,
1431                                 results: Papa.parse(msg.input, msg.config),
1432                                 finished: true
1433                         });
1434                 }
1435                 else if ((global.File && msg.input instanceof File) || msg.input instanceof Object)     // thank you, Safari (see issue #106)
1436                 {
1437                         var results = Papa.parse(msg.input, msg.config);
1438                         if (results)
1439                                 global.postMessage({
1440                                         workerId: Papa.WORKER_ID,
1441                                         results: results,
1442                                         finished: true
1443                                 });
1444                 }
1445         }
1446
1447         /** Makes a deep copy of an array or object (mostly) */
1448         function copy(obj)
1449         {
1450                 if (typeof obj !== 'object')
1451                         return obj;
1452                 var cpy = obj instanceof Array ? [] : {};
1453                 for (var key in obj)
1454                         cpy[key] = copy(obj[key]);
1455                 return cpy;
1456         }
1457
1458         function bindFunction(f, self)
1459         {
1460                 return function() { f.apply(self, arguments); };
1461         }
1462
1463         function isFunction(func)
1464         {
1465                 return typeof func === 'function';
1466         }
1467
1468         return Papa;
1469 }));