Repository: mholt/PapaParse
Branch: master
Commit: cc8c801f83fa
Files: 43
Total size: 48.5 MB
Directory structure:
gitextract_8bywzl85/
├── .eslintrc.js
├── .github/
│ └── workflows/
│ └── node.js.yml
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── docs/
│ ├── CNAME
│ ├── Caddyfile
│ ├── demo.html
│ ├── docs.html
│ ├── faq.html
│ ├── index.html
│ └── resources/
│ ├── css/
│ │ ├── common.css
│ │ ├── demo.css
│ │ ├── home.css
│ │ ├── tomorrow.highlight.css
│ │ └── unsemantic.css
│ ├── files/
│ │ ├── big.csv
│ │ ├── malformed.tsv
│ │ └── normal.csv
│ └── js/
│ ├── common.js
│ ├── demo.js
│ ├── home.js
│ ├── lovers.js
│ └── papaparse.js
├── package.json
├── papaparse.js
├── player/
│ ├── player.css
│ ├── player.html
│ └── player.js
└── tests/
├── .eslintrc.js
├── long-sample.csv
├── node-tests.js
├── sample-header.csv
├── sample.csv
├── test-cases.js
├── test.js
├── tests.html
├── utf-8-bom-sample.csv
└── verylong-sample.csv
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc.js
================================================
module.exports = {
"parserOptions": {
"ecmaVersion": 6
},
"env": {
"es6": true,
"browser": true,
"worker": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
"accessor-pairs": "error",
"array-bracket-newline": ["error", "consistent"],
"array-bracket-spacing": [
"error",
"never"
],
"array-callback-return": "error",
"array-element-newline": "off",
"arrow-body-style": "error",
"arrow-parens": "error",
"arrow-spacing": "error",
"block-scoped-var": "error",
"block-spacing": "error",
"brace-style": "off",
"callback-return": "error",
"camelcase": ["error", {"properties": "never"}],
"capitalized-comments": "off",
"class-methods-use-this": "error",
"comma-dangle": "off",
"comma-spacing": "off",
"comma-style": [
"error",
"last"
],
"complexity": "off",
"computed-property-spacing": [
"error",
"never"
],
"consistent-return": "off",
"consistent-this": "off",
"curly": "off",
"default-case": "error",
"dot-location": "error",
"dot-notation": "error",
"eol-last": "error",
"eqeqeq": "error",
"for-direction": "error",
"func-call-spacing": "error",
"func-name-matching": "error",
"func-names": [
"error",
"never"
],
"func-style": "off",
"function-paren-newline": "off",
"generator-star-spacing": "error",
"getter-return": "error",
"global-require": "off",
"guard-for-in": "off",
"handle-callback-err": "error",
"id-blacklist": "error",
"id-length": "off",
"id-match": "error",
"implicit-arrow-linebreak": "error",
"indent": [
"error",
"tab"
],
"indent-legacy": "off",
"init-declarations": "off",
"jsx-quotes": "error",
"key-spacing": "error",
"keyword-spacing": "off",
"line-comment-position": "off",
"linebreak-style": [
"error",
"unix"
],
"lines-around-comment": "off",
"lines-around-directive": "off",
"lines-between-class-members": "error",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
"max-nested-callbacks": "error",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "off",
"multiline-comment-style": "off",
"multiline-ternary": [
"error",
"always-multiline"
],
"new-parens": "error",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "off",
"no-alert": "error",
"no-array-constructor": "error",
"no-await-in-loop": "error",
"no-bitwise": "error",
"no-buffer-constructor": "error",
"no-caller": "error",
"no-catch-shadow": "off",
"no-cond-assign": [
"error",
"except-parens"
],
"no-confusing-arrow": "error",
"no-console": "off",
"no-continue": "off",
"no-div-regex": "error",
"no-duplicate-imports": "error",
"no-else-return": "off",
"no-empty": ["error", {"allowEmptyCatch": true}],
"no-empty-function": "off",
"no-eq-null": "error",
"no-eval": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-label": "error",
"no-extra-parens": "off",
"no-floating-decimal": "error",
"no-implicit-globals": "error",
"no-implied-eval": "error",
"no-inline-comments": "off",
"no-inner-declarations": [
"error",
"functions"
],
"no-invalid-this": "off",
"no-iterator": "error",
"no-label-var": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-loop-func": "error",
"no-magic-numbers": "off",
"no-mixed-operators": "off",
"no-mixed-requires": "error",
"no-multi-assign": "error",
"no-multi-spaces": "off",
"no-multi-str": "error",
"no-multiple-empty-lines": "off",
"no-native-reassign": "error",
"no-negated-condition": "off",
"no-negated-in-lhs": "error",
"no-nested-ternary": "off",
"no-new": "error",
"no-new-func": "error",
"no-new-object": "error",
"no-new-require": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-param-reassign": "off",
"no-path-concat": "off",
"no-plusplus": "off",
"no-process-env": "error",
"no-process-exit": "error",
"no-proto": "error",
"no-prototype-builtins": "error",
"no-restricted-globals": "error",
"no-restricted-imports": "error",
"no-restricted-modules": "error",
"no-restricted-properties": "error",
"no-restricted-syntax": "error",
"no-return-assign": "error",
"no-return-await": "error",
"no-script-url": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow": "off",
"no-shadow-restricted-names": "error",
"no-spaced-func": "error",
"no-sync": ["error", {"allowAtRootLevel": true}],
"no-tabs": "off",
"no-template-curly-in-string": "error",
"no-ternary": "off",
"no-throw-literal": "error",
"no-trailing-spaces": "error",
"no-undef-init": "error",
"no-undefined": "off",
"no-underscore-dangle": "off",
"no-unmodified-loop-condition": "off",
"no-unneeded-ternary": "error",
"no-unused-expressions": "off",
"no-unused-vars": ["error", {"args": "none"}],
"no-use-before-define": "off",
"no-useless-call": "error",
"no-useless-computed-key": "error",
"no-useless-concat": "error",
"no-useless-constructor": "error",
"no-useless-rename": "error",
"no-useless-return": "off",
"no-var": "off",
"no-void": "error",
"no-warning-comments": "error",
"no-whitespace-before-property": "error",
"no-with": "error",
"nonblock-statement-body-position": [
"error",
"any"
],
"object-curly-newline": ["error", {"consistent": true}],
"object-curly-spacing": "off",
"object-shorthand": "off",
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": [
"error",
"always"
],
"operator-linebreak": "off",
"padded-blocks": "off",
"padding-line-between-statements": "error",
"prefer-arrow-callback": "off",
"prefer-const": "error",
"prefer-destructuring": "off",
"prefer-numeric-literals": "error",
"prefer-promise-reject-errors": "error",
"prefer-reflect": "off",
"prefer-rest-params": "off",
"prefer-spread": "error",
"prefer-template": "off",
"quote-props": "off",
"quotes": "off",
"radix": [
"error",
"as-needed"
],
"require-await": "error",
"require-jsdoc": "off",
"rest-spread-spacing": "error",
"semi": "error",
"semi-spacing": "error",
"semi-style": [
"error",
"last"
],
"sort-imports": "error",
"sort-keys": "off",
"sort-vars": "off",
"space-before-blocks": "error",
"space-before-function-paren": [
"error",
"never"
],
"space-in-parens": [
"error",
"never"
],
"space-infix-ops": "error",
"space-unary-ops": [
"error",
{
"nonwords": false,
"words": false
}
],
"spaced-comment": "off",
"strict": "off",
"switch-colon-spacing": "error",
"symbol-description": "error",
"template-curly-spacing": "error",
"template-tag-spacing": "error",
"unicode-bom": [
"error",
"never"
],
"valid-jsdoc": "off",
"vars-on-top": "off",
"wrap-iife": "off",
"wrap-regex": "off",
"yield-star-spacing": "error",
"yoda": "off"
}
};
================================================
FILE: .github/workflows/node.js.yml
================================================
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x, 22.x, 24.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run lint && npm run test-node && npm run build
================================================
FILE: .gitignore
================================================
_gitignore/
bower_components/
node_modules/
package-lock.json
yarn.lock
================================================
FILE: .npmignore
================================================
_gitignore/
bower_components/
node_modules/
docs/
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## 5.5.3
### Bug Fixes
- Avoid infinite loop with duplicate header counting (#1095)
## 5.5.2
### Bug Fixes
- Only attempt to parse headers once, fixing performance issue (#1086)
### Maintenance
- Do not run headless tests on CI (#1087)
- Fix package URL in package.json
## 5.5.1
### Maintenance
- Revert "Remove ES6 features to allow minifying papaparse file" — updated `grunt-contrib-uglify` instead to support ES6+
- Update grunt-contrib-uglify version
- Run build script in CI
## 5.5.0
### Features
- Add `skipFirstNLines` option to skip first N lines before parsing (#1021, #738)
- Add `renamedHeaders` to parse result meta, reporting original-to-renamed header mappings (#990)
### Bug Fixes
- Fix `escapeFormulae` option to handle boolean values correctly (#1025)
- Fix cursor position when encountering duplicated headers (#997)
- Only skip first N lines in the first chunk and don't incorrectly consume the header line (#1045, #1046)
- Refactor header renaming logic to correctly handle duplicates (#1058, #1052, #1007)
### Performance
- Faster duplicate header detection using a header map (#991)
- Use `for` loop instead of `for...in` for header parsing to only iterate over array elements (#987)
### Maintenance
- Update minimum ES version to 6
- Documentation and README improvements (#1002, #1034, #1041, #1044, #1060)
## 5.4.1
### Bug Fixes
- Remove jsperf.com links from README.md. (#986)
- Only test duplicate headers on first row
- Rename duplicated headers
================================================
FILE: Gruntfile.js
================================================
module.exports = function(grunt) {
grunt.initConfig({
uglify: {
options: {
compress: {
global_defs: {
'PAPA_BROWSER_CONTEXT': true
},
dead_code: true
},
output: {
comments: 'some',
},
},
min: {
files: {
'papaparse.min.js': ['papaparse.js']
},
},
},
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('build', ['uglify']);
grunt.registerTask('default', ['uglify']);
};
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Matthew Holt
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
Parse CSV with JavaScript
========================================
Papa Parse is the fastest in-browser CSV (or delimited text) parser for JavaScript. It is reliable and correct according to [RFC 4180](https://tools.ietf.org/html/rfc4180), and it comes with these features:
- Easy to use
- Parse CSV files directly (local or over the network)
- Fast mode
- Stream large files (even via HTTP)
- Reverse parsing (converts JSON to CSV)
- Auto-detect delimiter
- Worker threads to keep your web page reactive
- Header row support
- Pause, resume, abort
- Can convert numbers and booleans to their types
- Optional jQuery integration to get files from `` elements
- One of the only parsers that correctly handles line-breaks and quotations
Papa Parse has **no dependencies** - not even jQuery.
Install
-------
papaparse is available on [npm](https://www.npmjs.com/package/papaparse). It
can be installed with the following command:
```shell
npm install papaparse
```
If you don't want to use npm, [papaparse.min.js](https://unpkg.com/papaparse@latest/papaparse.min.js) can be downloaded to your project source.
Usage
-----
```js
import Papa from 'papaparse';
Papa.parse(file, config);
const csv = Papa.unparse(data[, config]);
```
Homepage & Demo
----------------
- [Homepage](https://www.papaparse.com)
- [Demo](https://www.papaparse.com/demo)
To learn how to use Papa Parse:
- [Documentation](https://www.papaparse.com/docs)
The website is hosted on [Github Pages](https://pages.github.com/). Its content is also included in the docs folder of this repository. If you want to contribute on it just clone the master of this repository and open a pull request.
Papa Parse for Node
--------------------
Papa Parse can parse a [Readable Stream](https://nodejs.org/api/stream.html#stream_readable_streams) instead of a [File](https://www.w3.org/TR/FileAPI/) when used in Node.js environments (in addition to plain strings). In this mode, `encoding` must, if specified, be a Node-supported character encoding. The `Papa.LocalChunkSize`, `Papa.RemoteChunkSize` , `download`, `withCredentials` and `worker` config options are unavailable.
Papa Parse can also parse in a node streaming style which makes `.pipe` available. Simply pipe the [Readable Stream](https://nodejs.org/api/stream.html#stream_readable_streams) to the stream returned from `Papa.parse(Papa.NODE_STREAM_INPUT, options)`. The `Papa.LocalChunkSize`, `Papa.RemoteChunkSize` , `download`, `withCredentials`, `worker`, `step`, and `complete` config options are unavailable. To register a callback with the stream to process data, use the `data` event like so: `stream.on('data', callback)` and to signal the end of stream, use the 'end' event like so: `stream.on('end', callback)`.
Get Started
-----------
For usage instructions, see the [homepage](https://www.papaparse.com) and, for more detail, the [documentation](https://www.papaparse.com/docs).
Tests
-----
Papa Parse is under test. Download this repository, run `npm install`, then `npm test` to run the tests.
Contributing
------------
To discuss a new feature or ask a question, open an issue. To fix a bug, submit a pull request to be credited with the [contributors](https://github.com/mholt/PapaParse/graphs/contributors)! Remember, a pull request, *with test*, is best. You may also discuss on Twitter with [#PapaParse](https://twitter.com/search?q=%23PapaParse&src=typd&f=realtime) or directly to me, [@mholt6](https://twitter.com/mholt6).
If you contribute a patch, ensure the tests suite is running correctly. We run continuous integration on each pull request and will not accept a patch that breaks the tests.
================================================
FILE: bower.json
================================================
{
"name": "papaparse",
"main": "papaparse.js",
"homepage": "http://papaparse.com",
"authors": [
"Matthew Holt"
],
"description": "Fast and powerful CSV parser for the browser. Converts CSV->JSON and JSON->CSV. Supports web workers and streaming large files.",
"keywords": [
"csv",
"parse",
"parsing",
"parser",
"delimited",
"text",
"data",
"auto-detect",
"comma",
"tab",
"pipe",
"file",
"filereader",
"stream",
"worker",
"workers",
"ajax",
"thread",
"threading",
"multi-threaded"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests",
"player"
]
}
================================================
FILE: docs/CNAME
================================================
www.papaparse.com
================================================
FILE: docs/Caddyfile
================================================
localhost
ext .html
================================================
FILE: docs/demo.html
================================================
Delimited data can be parsed out of strings or files. Files that are parsed can be local or remote. Local files are opened with FileReader, and remote files are downloaded with XMLHttpRequest.
config is a config object which contains a callback.
Doesn't return anything. Results are provided asynchronously to a callback function.
Parse remote file
Papa.parse(url, {
download: true,
// rest of config ...
})
url is the path or URL to the file to download.
The second argument is a config object where download: true is set.
Doesn't return anything. Results are provided asynchronously to a callback function.
Using jQuery to select files
$('input[type=file]').parse({
config: {
// base config to use for each file
},
before: function(file, inputElem)
{
// executed before parsing each file begins;
// what you return here controls the flow
},
error: function(err, file, inputElem, reason)
{
// executed if an error occurs while loading the file,
// or if before callback aborted for some reason
},
complete: function()
{
// executed after all files are complete
}
});
Select the file input elements with files you want to parse.
before is an optional callback that lets you inspect each file before parsing begins. Return an object like:
to alter the flow of parsing. Actions can be "abort" to skip this and all other files in the queue, "skip" to skip just this file, or "continue" to carry on (equivalent to returning nothing). reason can be a reason for aborting. config can be a modified configuration for parsing just this file.
The complete callback shown here is executed after all files are finished and does not receive any data. Use the complete callback in config for per-file results.
Convert JSON to CSV
Papa's unparse utility writes out correct delimited text strings given an array of arrays or an array of objects.
{
quotes: false, //or array of booleans
quoteChar: '"',
escapeChar: '"',
delimiter: ",",
header: true,
newline: "\r\n",
skipEmptyLines: false, //other option is 'greedy', meaning skip delimiters, quotes, and whitespace.
columns: null //or array of strings
}
Unparse Config Options
Option
Explanation
quotes
If true, forces all fields to be enclosed in quotes. If an array of true/false values, specifies which fields should be force-quoted (first boolean is for the first column, second boolean for the second column, ...). A function that returns a boolean values can be used to determine the quotes value of a cell. This function accepts the cell value and column index as parameters.
Note that this option is ignored for undefined, null and date-object values. The option escapeFormulae also takes precedence over this.
quoteChar
The character used to quote fields.
escapeChar
The character used to escape quoteChar inside field values.
delimiter
The delimiting character. Multi-character delimiters are supported. It must not be found in Papa.BAD_DELIMITERS.
header
If false, will omit the header row. If data is an array of arrays this option is ignored. If data is an array of objects the keys of the first object are the header row. If data is an object with the keys fields and data the fields are the header row.
newline
The character used to determine newline sequence. It defaults to "\r\n".
skipEmptyLines
If true, lines that are completely empty (those which evaluate to an empty string) will be skipped. If set to 'greedy', lines that don't have any content (those which have only whitespace after parsing) will also be skipped.
columns
If 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
If true, field values that begin with =, +, -, @, \t, or \r, will be prepended with a ' to defend against injection attacks, because Excel and LibreOffice will automatically parse such cells as formulae. You can override those values by setting this option to a regular expression
// With implicit header row
// (keys of first object populate header row)
var csv = Papa.unparse([
{
"Column 1": "foo",
"Column 2": "bar"
},
{
"Column 1": "abc",
"Column 2": "def"
}
]);
// Specifying fields and data explicitly
var csv = Papa.unparse({
"fields": ["Column 1", "Column 2"],
"data": [
["foo", "bar"],
["abc", "def"]
]
});
The Parse Config Object
The parse function may be passed a configuration object. It defines settings, behavior, and callbacks used during parsing. Any properties left unspecified will resort to their default values.
The delimiting character. Leave blank to auto-detect from a list of most common delimiters, or any values passed in through delimitersToGuess. It can be a string or a function. If a string, it can be of any length (so multi-character delimiters are supported). If a function, it must accept the input as first parameter and it must return a string which will be used as delimiter. In both cases it cannot be found in Papa.BAD_DELIMITERS.
newline
The newline sequence. Leave blank to auto-detect. Must be one of \r, \n, or \r\n.
quoteChar
The character used to quote fields. The quoting of all fields is not mandatory. Any field which is not quoted will correctly read.
escapeChar
The character used to escape the quote character within a field. If not set, this option will default to the value of quoteChar, meaning that the default escaping of quote character within a quoted field is using the quote character two times. (e.g. "column with ""quotes"" in text")
header
If true, the first row of parsed data will be interpreted as field names. An array of field names will be returned in meta, and each row of data will be an object of values keyed by field name instead of a simple array. Rows with a different number of fields from the header row will produce an error.
Warning: Duplicated field names will be automatically renamed to avoid values in previous fields having the same name to be overwritten. Renamed fields with original (or transformed by transformHeader) are stored in ParseResult.meta.renamedHeaders
transformHeader
A function to apply on each header. Requires header to be true. The function receives the header as its first argument and the index as second.
Only available starting with version 5.0.
dynamicTyping
If true, numeric and boolean data will be converted to their type instead of remaining strings. Numeric data must conform to the definition of a decimal literal. Numerical values greater than 2^53 or less than -2^53 will not be converted to numbers to preserve precision. European-formatted numbers must have commas and dots swapped. It also accepts an object or a function. If it's an object, its values should be a boolean to indicate if dynamic typing should be applied for each column number (or header name if using headers). If it's a function, it should return a boolean value for each field number (or name if using headers) which will be passed as first argument.
preview
If > 0, only that many rows will be parsed.
encoding
The encoding to use when opening local files. If specified, it must be a value supported by the FileReader API.
worker
Whether or not to use a worker thread. Using a worker will keep your page reactive, but may be slightly slower.
comments
A string that indicates a comment (for example, "#" or "//"). When Papa encounters a line starting with this string, it will skip the line.
Streaming is necessary for large files which would otherwise crash the browser. You can call parser.abort() to abort parsing. And, except when using a Web Worker, you can call parser.pause() to pause it, and parser.resume() to resume.
complete
The callback to execute when parsing is complete. It receives the parse results. If parsing a local file, the File is passed in, too:
When streaming, parse results are not available in this callback.
error
A callback to execute if FileReader encounters an error. The function is passed two arguments: the error and the File.
download
If true, this indicates that the string you passed as the first argument to parse() is actually a URL from which to download a file and parse its contents.
downloadRequestHeaders
If defined, should be an object that describes the headers, example:
Use POST request on the URL of the download option. The value passed will be set as the body of the request.
skipEmptyLines
If true, lines that are completely empty (those which evaluate to an empty string) will be skipped. If set to 'greedy', lines that don't have any content (those which have only whitespace after parsing) will also be skipped.
chunk
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
Overrides Papa.LocalChunkSize and Papa.RemoteChunkSize. See configurable section to know the usage of both parameters.
fastMode
Fast mode speeds up parsing significantly for large inputs. However, it only works when the input has no quoted fields. Fast mode will automatically be enabled if no " characters appear in the input. You can force fast mode either way by setting it to true or false.
beforeFirstChunk
A function to execute before parsing the first chunk. Can be used with chunk or step streaming modes. The function receives as an argument the chunk about to be parsed, and it may return a modified chunk to parse. This is useful for stripping header lines (as long as the header fits in a single chunk).
withCredentials
A boolean value passed directly into XMLHttpRequest's "withCredentials" property.
transform
A function to apply on each value. The function receives the value as its first argument and the column number or header name when enabled as its second argument. The return value of the function will replace the value it received. The transform function is applied before dynamicTyping.
delimitersToGuess
An array of delimiters to guess from if the delimiter option is not set.
skipFirstNLines
To skip first N number of lines when converting a CSV file to JSON
The Parse Result Object
A parse result always contains three objects: data, errors, and meta. Data and errors are arrays, and meta is an object. In the step callback, the data array will only contain one element.
Result Structure
{
data: // array of parsed data
errors: // array of errors
meta: // object with extra info
}
data is an array of rows. If header is false, rows are arrays; otherwise they are objects of data keyed by the field name.
meta contains extra information about the parse, such as delimiter used, the newline sequence, whether the process was aborted, etc. Properties in this object are not guaranteed to exist in all situations.
If header row is enabled and more fields are found on a row of data than in the header row, an extra field will appear in that row called __parsed_extra. It contains an array of all data parsed from that row that extended beyond the header row.
Errors
// Error structure
{
type: "", // A generalization of the error
code: "", // Standardized error code
message: "", // Human-readable details
row: 0, // Row index of parsed data where error is
}
The error type will be one of "Quotes", "Delimiter", or "FieldMismatch".
The code may be "MissingQuotes", "UndetectableDelimiter", "TooFewFields", or "TooManyFields" (depending on the error type).
Just because errors are generated does not necessarily mean that parsing failed. The worst error you can get is probably MissingQuotes.
Meta
{
delimiter: // Delimiter used
linebreak: // Line break sequence used
aborted: // Whether process was aborted
fields: // Array of field names
truncated: // Whether preview consumed all input
renamedHeaders: // Headers that are automatically renamed by the library to avoid duplication. {Column 1_1: 'Column 1' // the later header 'Column 1' was renamed to 'Column 1_1'}
}
Not all meta properties will always be available. For instance, fields is only given when header row is enabled.
Extras
There's a few other things that Papa exposes to you that weren't explained above.
Read-Only
Read-Only Property
Explanation
Papa.BAD_DELIMITERS
An array of characters that are not allowed as delimiters (\r, \n, ", \ufeff).
There's a thousand CSV libraries for Javascript. Papa is different. It's written with correctness and performance in mind. Papa is the first (and so far only) multi-threaded CSV parser that runs on web pages. It can parse files gigabytes in size without crashing the browser. It correctly handles malformed or edge-case CSV text. It can parse files on the local file system or download them over the Internet. Papa is boss.
Privacy advocates also use Papa Parse to avoid having to transmit sensitive files over the Internet. Now all the processing can be done locally on the client's computer. This is especially significant considering some organizations' policies.
As of version 4, Papa Parse is the fastest CSV parser for the browser, whereas it used to be the slowest.
Can I use Papa Parse server-side with Node.js?
Yes, Paparse supports Node. See our README for further details.
Does Papa Parse have any dependencies?
No. Papa Parse has no dependencies. If jQuery is present, however, it plugs in to make it easier to select files from the DOM.
Can I put other libraries in the same file as Papa Parse?
Yes.
Which browsers is it compatible with?
All modern, competent browsers should support all of the features. However, as usual, use IE at your own risk. It looks like IE 10+ and Safari 6+ should support all the features. Firefox and Chrome should work with all features back to versions 3 and 4. Opera 11 and up should be fine. If you really need to use Papa in old IE or Opera, then keep the fancy features off and you may be in luck.
Can Papa Parse be loaded asynchronously (after the page loads)?
Yes.
Is it open source? (Can I contribute something?)
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.
What's the deal with fast mode?
Fast mode makes Papa Parse screaming fast, but you wouldn't want to use it if there are quoted fields in your input. Fast mode is fast because it makes one major assumption: no quoted fields. If you don't specify fastMode either way, fast mode will be turned on automatically if there are no quote characters in the input. With fast mode on, 1 GB files can be parsed in about 20 seconds.
Why do non-ASCII characters look weird?
It's probably an encoding issue. The FileReader API allows you to specify an encoding, which you can do using the encoding configuration property. This property only works with local files and does not apply to strings or remote files. Also see issues #64 and #169 if you're having trouble parsing CSV files generated from Excel.
Streaming
Can Papa load and parse huge files?
Yes. Parsing huge text files is facilitated by streaming, where the file is loaded a little bit at a time, parsed, and the results are sent to your step callback function, row-by-row. You can also get results chunk-by-chunk (which is usually faster) by using the chunk callback function in the same way.
How do I stream my input?
Just specify a step callback function. Results will not be available after parsing is finished, however. You have to inspect the results one row at a time.
What if I want more than 1 row at a time?
Use the chunk callback instead. It works just like step, but you get an entire chunk of the file at a time, rather than a single row. Don't try to use step and chunk together (the behavior is undefined).
What is a stream and when should I stream files?
A stream is a unique data structure which, given infinite time, gives you infinite space.
So if you're short on memory (as browsers often are), use a stream.
Wait, does that mean streaming takes more time?
Yes and no. Typically, when we gain speed, we pay with space. The opposite is true, too. Streaming uses significantly less memory with large inputs, but since the reading happens in chunks and results are processed at each row instead of at the very end, yes, it can be slower.
But consider the alternative: upload the file to a remote server, open and process it there, then compress the output and have the client download the results. How long does it take you to upload a 500 MB or 1 GB file? Then consider that the server still has to open the file and read its contents, which is what the client would have done minutes ago. The server might parse it faster with natively-compiled binaries, but only if its resources are dedicated to the task and isn't already parsing files for many other users.
So unless your clients have a fiber line and you have a scalable cloud application, local parsing by streaming is nearly guaranteed to be faster.
How do I get all the results together after streaming?
You don't. Unless you assemble it manually. And really, don't do that... it defeats the purpose of using a stream. Just take the parts you need as they come through.
Does Papa use a true stream?
Papa uses HTML5's FileReader API which uses a stream. FileReader doesn't technically allow us to hook into the underlying stream, but it does let us load the file in pieces. But fortunately you don't have to worry about that; it's all taken care of for you. Just take the results one row at a time.
Can I stream files over a network or the Internet?
Yes, Papa Parse supports this. It will download a file in pieces using HTTP's standard Range header, then pass the parsed results to your step function just like a local file. However, these requests may not work cross-origin (different domain/hostname), depending on the server's configuration.
Streaming remote files also requires the Content-Range header in the server's response. Most production-ready servers support this header, but Python's SimpleHTTPServer does not. If you need a quick and easy server, Caddy will do the trick: $ caddy
Can I pause and resume parsing?
Yes, as long as you are streaming and not using a worker. Your step callback (same with the chunk callback) is passed a parser which has pause, resume, and abort functions. This is exceptionally useful when performing asynchronous actions during parsing, for example, AJAX requests. You can always abort parsing in your callback, even when using workers, but pause and resume is only available without a worker.
Multi-Threading (Workers)
Can I use workers for unparsing?
No, unparsing CSV files with workers is not currently available. Workers can only be used for parsing CSV files.
What is a web worker? Why use one?
HTML5 Web Workers facilitate basic multi-threading in the browser. This means that a web page can spawn a new thread in the operating system that runs Javascript code. This is highly beneficial for long-running scripts that would otherwise lock up the web page.
How do I use a worker?
Just specify worker: true in your config. You'll also need to make a complete callback (unless you're streaming) so that you can get the results, because using a worker makes the parse function asynchronous.
Can I use a worker if I combine/concatenate my Javascript files?
Yes.
When should I use a worker?
That's up to you. The most typical reason to use a web worker is if your web page becomes unresponsive during parsing. In other words, if it freezes and you can't click things or the scrolling becomes choppy. If that happens, some browsers (like Firefox) will warn the user that a script has become unresponsive or is taking a long time (even if it's working properly). If this happens to you or some of your users, consider using a web worker, at least for the large inputs.
However, read the next answer for more info. Using workers has performance implications (both good and bad).
What are the performance implications of using a worker thread?
Using a worker will be a little slower. In Javascript, threads don't share memory. That's really annoying because sharing memory is the primary reason for multi-threading. As such, all parse results in a worker thread need to be copied to the main thread. And if you're parsing a string in a worker thread, that string also needs to be copied into the worker in the first place. (Files will be opened or downloaded by the worker itself, so the input doesn't need to be copied from the main thread in those cases.)
The process of sending data between the page and the worker thread can stall the main page for just a moment. Each thread must also wait for the data to finish sending before un-blocking.
Basically: if you don't have much time, don't use a worker. If you can afford a little extra time, use a worker. It will keep your page from appearing unresponsive and give users an overall better experience.
Can I stream and use a worker at the same time?
Yup. If the input is too large to fit in memory (or large enough to crash the browser), streaming is always the answer, even in a worker thread. Workers keep the page reactive. Streaming makes it able to fit in memory. Use both if you need to.
Can I pause/resume workers?
No. This would drastically slow down parsing, as it would require the worker to wait after every chunk for a "continue" signal from the main thread. But you can abort workers by calling .abort() on the parser that gets passed to your callback function.
I set worker:true and now I'm getting an error: "window is not defined." How do I fix it?
This is a fairly common issue with configuration and it appears to be related to the use of React or other third party tools. Since this is a configuration issue, the exact steps needed to solve it may vary. See Issue #655 on GitHub for a solution that worked for one person, and for links to other related issues.
================================================
FILE: docs/index.html
================================================
Papa Parse - Powerful CSV Parser for JavaScript
Papa Parse
The powerful, in-browser CSV parser for big boys and girls
SmartyStreets verifies addresses, many of which are in CSV files. Papa Parse can process huge files in the browser. "We rapidly built an awesome client-side file processor with Papa Parse."
MetaReader helps you see your data from a meta level before you start detailed analysis. "Papa Parse made it very easy to load and ready user CSV files in the browser on the client side."
EpiML is an agent-based mathematical model for the web, still in its early stages of development. "Papa makes it so easy to use CSV, which is good for scientists."
That's what streaming is for. Specify a step callback to receive the results row-by-row. This way, you won't load the whole file into memory and crash the browser.
That happens when a long-running script is executing in the same thread as the page. Use a Worker thread by specifying worker: true. It may take slightly longer, but your page will stay reactive.
If you tell Papa there is a header row, each row will be organized by field name instead of index.
// Key data by field name instead of index/position
var results = Papa.parse(csv, {
header: true
});
Type Conversion
"Hey, these numbers are parsed as strings."
Everything is parsed as strings. If you want numbers and booleans, you can enable dynamic typing to do the conversion for you.
// Converts numeric/boolean data
var results = Papa.parse(csv, {
dynamicTyping: true
});
Comments
"I forgot to mention: my CSV files have comments in them."
Okay, first off: that's really weird. But fortunately, you can skip those lines... just specify the comment string.
// Mostly found in academia, some CSV files
// may have commented lines in them
var results = Papa.parse(csv, {
comments: "#"
});
Error Handling
"Aw, shoot. Errors."
Papa handles errors pretty well. The CSV standard is somewhat loose ambiguous, so Papa is designed for edge cases. For example, mismatched fields won't break parsing.
// Example error:
{
type: "FieldMismatch",
code: "TooManyFields",
message: "Expected 3 fields, but parsed 4",
row: 1
}
jQuery Plugin
"Can I use Papa with jQuery?"
Sure, but it's not required. You can use jQuery to select file input elements and then parse their files. Papa exposes its file parsing API as a jQuery plugin only when jQuery is defined. Papa Parse has no dependencies.
Open the Console in your browser's inspector tools to see results.
================================================
FILE: player/player.js
================================================
var stepped = 0, chunks = 0, rows = 0;
var start, end;
var parser;
var pauseChecked = false;
var printStepChecked = false;
$(function()
{
$('#submit-parse').click(function()
{
stepped = 0;
chunks = 0;
rows = 0;
var txt = $('#input').val();
var localChunkSize = $('#localChunkSize').val();
var remoteChunkSize = $('#remoteChunkSize').val();
var files = $('#files')[0].files;
var config = buildConfig();
// NOTE: Chunk size does not get reset if changed and then set back to empty/default value
if (localChunkSize)
Papa.LocalChunkSize = localChunkSize;
if (remoteChunkSize)
Papa.RemoteChunkSize = remoteChunkSize;
pauseChecked = $('#step-pause').prop('checked');
printStepChecked = $('#print-steps').prop('checked');
if (files.length > 0)
{
if (!$('#stream').prop('checked') && !$('#chunk').prop('checked'))
{
for (var i = 0; i < files.length; i++)
{
if (files[i].size > 1024 * 1024 * 10)
{
alert("A file you've selected is larger than 10 MB; please choose to stream or chunk the input to prevent the browser from crashing.");
return;
}
}
}
start = performance.now();
$('#files').parse({
config: config,
before: function(file, inputElem)
{
console.log("Parsing file:", file);
},
complete: function()
{
console.log("Done with all files.");
}
});
}
else
{
start = performance.now();
var results = Papa.parse(txt, config);
console.log("Synchronous parse results:", results);
}
});
$('#submit-unparse').click(function()
{
var input = $('#input').val();
var delim = $('#delimiter').val();
var header = $('#header').prop('checked');
var results = Papa.unparse(input, {
delimiter: delim,
header: header,
});
console.log("Unparse complete!");
console.log("--------------------------------------");
console.log(results);
console.log("--------------------------------------");
});
$('#insert-tab').click(function()
{
$('#delimiter').val('\t');
});
});
function buildConfig()
{
return {
delimiter: $('#delimiter').val(),
newline: getLineEnding(),
header: $('#header').prop('checked'),
dynamicTyping: $('#dynamicTyping').prop('checked'),
preview: parseInt($('#preview').val() || 0),
step: $('#stream').prop('checked') ? stepFn : undefined,
encoding: $('#encoding').val(),
worker: $('#worker').prop('checked'),
comments: $('#comments').val(),
complete: completeFn,
error: errorFn,
download: $('#download').prop('checked'),
fastMode: $('#fastmode').prop('checked'),
skipEmptyLines: $('#skipEmptyLines').prop('checked'),
chunk: $('#chunk').prop('checked') ? chunkFn : undefined,
beforeFirstChunk: undefined,
skipFirstNLines: $('#skipFirstNLines').val()
};
function getLineEnding()
{
if ($('#newline-n').is(':checked'))
return "\n";
else if ($('#newline-r').is(':checked'))
return "\r";
else if ($('#newline-rn').is(':checked'))
return "\r\n";
else
return "";
}
}
function stepFn(results, parserHandle)
{
stepped++;
rows += results.data.length;
parser = parserHandle;
if (pauseChecked)
{
console.log(results, results.data[0]);
parserHandle.pause();
return;
}
if (printStepChecked)
console.log(results, results.data[0]);
}
function chunkFn(results, streamer, file)
{
if (!results)
return;
chunks++;
rows += results.data.length;
parser = streamer;
if (printStepChecked)
console.log("Chunk data:", results.data.length, results);
if (pauseChecked)
{
console.log("Pausing; " + results.data.length + " rows in chunk; file:", file);
streamer.pause();
return;
}
}
function errorFn(error, file)
{
console.log("ERROR:", error, file);
}
function completeFn()
{
end = performance.now();
if (!$('#stream').prop('checked')
&& !$('#chunk').prop('checked')
&& arguments[0]
&& arguments[0].data)
rows = arguments[0].data.length;
console.log("Finished input (async). Time:", end-start, arguments);
console.log("Rows:", rows, "Stepped:", stepped, "Chunks:", chunks);
}
================================================
FILE: tests/.eslintrc.js
================================================
module.exports = {
"extends": ["../.eslintrc.js"],
"parserOptions": {
"ecmaVersion": 8
},
"env": {
"mocha": true
},
"rules": {
}
};
================================================
FILE: tests/long-sample.csv
================================================
Grant,Dyer,Donec.elementum@orciluctuset.example,2013-11-23T02:30:31-08:00,2014-05-31T01:06:56-07:00,Magna Ut Associates,ljenkins
Cherokee,Shields,Nulla.Semper.Tellus@duinec.example,2014-11-22T16:43:51-08:00,2013-09-26T11:47:15-07:00,Pede Corporation,Donec.elementum@orciluctuset.example
Catherine,Parrish,lorem@feugiatnon.example,2015-02-11T12:01:10-08:00,2015-02-26T00:29:40-08:00,Phasellus Fermentum Convallis PC,Donec.elementum@orciluctuset.example
Destiny,Shannon,libero@Aenean.example,2015-07-14T09:38:11-07:00,2014-01-11T14:53:04-08:00,Pretium Et Inc.,Donec.elementum@orciluctuset.example
Callum,Underwood,Phasellus@Quisquetincidunt.example,2013-09-13T18:49:35-07:00,2014-12-04T23:04:19-08:00,Sed Turpis Nec LLP,ljenkins
Elliott,Wright,cursus@nibh.example,2015-04-20T14:35:19-07:00,2015-03-05T12:56:46-08:00,Dolor Associate,Phasellus@Quisquetincidunt.example
Galvin,Foley,nisi.Aenean.eget@atauctorullamcorper.example,2014-03-20T23:20:15-07:00,2014-06-11T15:00:23-07:00,Adipiscing Industrie,Phasellus@Quisquetincidunt.example
Talon,Salinas,posuere.vulputate.lacus@Donecsollicitudin.example,2015-01-31T09:19:02-08:00,2014-12-17T04:59:18-08:00,Aliquam Iaculis Incorporate,Phasellus@Quisquetincidunt.example
================================================
FILE: tests/node-tests.js
================================================
"use strict";
var Papa = require("../papaparse.js");
var fs = require('fs');
var assert = require('assert');
var longSampleRawCsv = fs.readFileSync(__dirname + '/long-sample.csv', 'utf8');
var utf8BomSampleRawCsv = fs.readFileSync(__dirname + '/utf-8-bom-sample.csv', 'utf8');
function assertLongSampleParsedCorrectly(parsedCsv) {
assert.equal(8, parsedCsv.data.length);
assert.deepEqual(parsedCsv.data[0], [
'Grant',
'Dyer',
'Donec.elementum@orciluctuset.example',
'2013-11-23T02:30:31-08:00',
'2014-05-31T01:06:56-07:00',
'Magna Ut Associates',
'ljenkins'
]);
assert.deepEqual(parsedCsv.data[7], [
'Talon',
'Salinas',
'posuere.vulputate.lacus@Donecsollicitudin.example',
'2015-01-31T09:19:02-08:00',
'2014-12-17T04:59:18-08:00',
'Aliquam Iaculis Incorporate',
'Phasellus@Quisquetincidunt.example'
]);
assert.deepEqual(parsedCsv.meta, {
"delimiter": ",",
"linebreak": "\n",
"aborted": false,
"truncated": false,
renamedHeaders: null,
"cursor": 1209
});
assert.equal(parsedCsv.errors.length, 0);
}
describe('PapaParse', function() {
it('synchronously parsed CSV should be correctly parsed', function() {
assertLongSampleParsedCorrectly(Papa.parse(longSampleRawCsv));
});
it('Pause and resume works (Regression Test for Bug #636)', function(done) {
this.timeout(30000);
var mod200Rows = [
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Lorem ipsum dolor sit","42","ABC"],
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Etiam a dolor vitae est vestibulum","84"],
["Lorem ipsum dolor sit","42","ABC"],
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Lorem ipsum dolor sit","42","ABC"],
["Lorem ipsum dolor sit","42"]
];
var stepped = 0;
var dataRows = [];
Papa.parse(fs.createReadStream(__dirname + '/verylong-sample.csv'), {
step: function(results, parser) {
stepped++;
if (results)
{
parser.pause();
parser.resume();
if (results.data && stepped % 200 === 0) {
dataRows.push(results.data);
}
}
},
complete: function() {
assert.strictEqual(2001, stepped);
assert.deepEqual(mod200Rows, dataRows);
done();
}
});
});
it('asynchronously parsed CSV should be correctly parsed', function(done) {
Papa.parse(longSampleRawCsv, {
complete: function(parsedCsv) {
assertLongSampleParsedCorrectly(parsedCsv);
done();
},
});
});
it('asynchronously parsed streaming CSV should be correctly parsed', function(done) {
Papa.parse(fs.createReadStream(__dirname + '/long-sample.csv', 'utf8'), {
complete: function(parsedCsv) {
assertLongSampleParsedCorrectly(parsedCsv);
done();
},
});
});
it('reports the correct row number on FieldMismatch errors', function(done) {
Papa.parse(fs.createReadStream(__dirname + '/verylong-sample.csv'), {
header: true,
fastMode: true,
complete: function(parsedCsv) {
assert.deepEqual(parsedCsv.errors, [
{
"type": "FieldMismatch",
"code": "TooFewFields",
"message": "Too few fields: expected 3 fields but parsed 2",
"row": 498
},
{
"type": "FieldMismatch",
"code": "TooFewFields",
"message": "Too few fields: expected 3 fields but parsed 2",
"row": 998
},
{
"type": "FieldMismatch",
"code": "TooFewFields",
"message": "Too few fields: expected 3 fields but parsed 2",
"row": 1498
},
{
"type": "FieldMismatch",
"code": "TooFewFields",
"message": "Too few fields: expected 3 fields but parsed 2",
"row": 1998
}
]);
assert.strictEqual(2000, parsedCsv.data.length);
done();
},
});
});
it('piped streaming CSV should be correctly parsed', function(done) {
var data = [];
var readStream = fs.createReadStream(__dirname + '/long-sample.csv', 'utf8');
var csvStream = readStream.pipe(Papa.parse(Papa.NODE_STREAM_INPUT));
csvStream.on('data', function(item) {
data.push(item);
});
csvStream.on('end', function() {
assert.deepEqual(data[0], [
'Grant',
'Dyer',
'Donec.elementum@orciluctuset.example',
'2013-11-23T02:30:31-08:00',
'2014-05-31T01:06:56-07:00',
'Magna Ut Associates',
'ljenkins'
]);
assert.deepEqual(data[7], [
'Talon',
'Salinas',
'posuere.vulputate.lacus@Donecsollicitudin.example',
'2015-01-31T09:19:02-08:00',
'2014-12-17T04:59:18-08:00',
'Aliquam Iaculis Incorporate',
'Phasellus@Quisquetincidunt.example'
]);
done();
});
});
it('piped streaming CSV should be correctly parsed when header is true', function(done) {
var data = [];
var readStream = fs.createReadStream(__dirname + '/sample-header.csv', 'utf8');
var csvStream = readStream.pipe(Papa.parse(Papa.NODE_STREAM_INPUT, {header: true}));
csvStream.on('data', function(item) {
data.push(item);
});
csvStream.on('end', function() {
assert.deepEqual(data[0], { title: 'test title 01', name: 'test name 01' });
assert.deepEqual(data[1], { title: '', name: 'test name 02' });
done();
});
});
it('should support pausing and resuming on same tick when streaming', function(done) {
var rows = [];
Papa.parse(fs.createReadStream(__dirname + '/long-sample.csv', 'utf8'), {
chunk: function(results, parser) {
rows = rows.concat(results.data);
parser.pause();
parser.resume();
},
error: function(err) {
done(new Error(err));
},
complete: function() {
assert.deepEqual(rows[0], [
'Grant',
'Dyer',
'Donec.elementum@orciluctuset.example',
'2013-11-23T02:30:31-08:00',
'2014-05-31T01:06:56-07:00',
'Magna Ut Associates',
'ljenkins'
]);
assert.deepEqual(rows[7], [
'Talon',
'Salinas',
'posuere.vulputate.lacus@Donecsollicitudin.example',
'2015-01-31T09:19:02-08:00',
'2014-12-17T04:59:18-08:00',
'Aliquam Iaculis Incorporate',
'Phasellus@Quisquetincidunt.example'
]);
done();
}
});
});
it('should support pausing and resuming asynchronously when streaming', function(done) {
var rows = [];
Papa.parse(fs.createReadStream(__dirname + '/long-sample.csv', 'utf8'), {
chunk: function(results, parser) {
rows = rows.concat(results.data);
parser.pause();
setTimeout(function() {
parser.resume();
}, 200);
},
error: function(err) {
done(new Error(err));
},
complete: function() {
assert.deepEqual(rows[0], [
'Grant',
'Dyer',
'Donec.elementum@orciluctuset.example',
'2013-11-23T02:30:31-08:00',
'2014-05-31T01:06:56-07:00',
'Magna Ut Associates',
'ljenkins'
]);
assert.deepEqual(rows[7], [
'Talon',
'Salinas',
'posuere.vulputate.lacus@Donecsollicitudin.example',
'2015-01-31T09:19:02-08:00',
'2014-12-17T04:59:18-08:00',
'Aliquam Iaculis Incorporate',
'Phasellus@Quisquetincidunt.example'
]);
done();
}
});
});
it('handles errors in beforeFirstChunk', function(done) {
var expectedError = new Error('test');
Papa.parse(fs.createReadStream(__dirname + '/long-sample.csv', 'utf8'), {
beforeFirstChunk: function() {
throw expectedError;
},
error: function(err) {
assert.deepEqual(err, expectedError);
done();
}
});
});
it('handles errors in chunk', function(done) {
var expectedError = new Error('test');
Papa.parse(fs.createReadStream(__dirname + '/long-sample.csv', 'utf8'), {
chunk: function() {
throw expectedError;
},
error: function(err) {
assert.deepEqual(err, expectedError);
done();
}
});
});
it('handles errors in step', function(done) {
var expectedError = new Error('test');
Papa.parse(fs.createReadStream(__dirname + '/long-sample.csv', 'utf8'), {
step: function() {
throw expectedError;
},
error: function(err) {
assert.deepEqual(err, expectedError);
done();
}
});
});
it('handles utf-8 BOM encoded files', function(done) {
Papa.parse(utf8BomSampleRawCsv, {
header: true,
complete: function(parsedCsv) {
assert.deepEqual(parsedCsv.data[0], { A: 'X', B: 'Y', C: 'Z' });
done();
}
});
});
});
================================================
FILE: tests/sample-header.csv
================================================
title,name
test title 01,test name 01
,test name 02
================================================
FILE: tests/sample.csv
================================================
A,B,C
X,Y,Z
================================================
FILE: tests/test-cases.js
================================================
var chai;
var Papa;
if (typeof module !== 'undefined' && module.exports) {
chai = require('chai');
Papa = require('../papaparse.js');
}
var assert = chai.assert;
var BASE_PATH = (typeof document === 'undefined') ? './' : document.getElementById('test-cases').src.replace(/test-cases\.js$/, '');
var RECORD_SEP = String.fromCharCode(30);
var UNIT_SEP = String.fromCharCode(31);
var FILES_ENABLED = false;
try {
new File([""], ""); // eslint-disable-line no-new
// Required since Node20 as it ads a FileGlobal but not a FileReaderSync
new FileReaderSync(); // eslint-disable-line no-new
FILES_ENABLED = true;
} catch (e) {} // safari, ie
var XHR_ENABLED = false;
try {
new XMLHttpRequest(); // eslint-disable-line no-new
XHR_ENABLED = true;
} catch (e) {} // safari, ie
// Tests for the core parser using new Papa.Parser().parse() (CSV to JSON)
var CORE_PARSER_TESTS = [
{
description: "One row",
input: 'A,b,c',
expected: {
data: [['A', 'b', 'c']],
errors: [],
meta: {delimiter: ',', renamedHeaders: null}
}
},
{
description: "Two rows",
input: 'A,b,c\nd,E,f',
expected: {
data: [['A', 'b', 'c'], ['d', 'E', 'f']],
errors: []
}
},
{
description: "Three rows",
input: 'A,b,c\nd,E,f\nG,h,i',
expected: {
data: [['A', 'b', 'c'], ['d', 'E', 'f'], ['G', 'h', 'i']],
errors: []
}
},
{
description: "Whitespace at edges of unquoted field",
input: 'a, b ,c',
notes: "Extra whitespace should graciously be preserved",
expected: {
data: [['a', ' b ', 'c']],
errors: []
}
},
{
description: "Quoted field",
input: 'A,"B",C',
expected: {
data: [['A', 'B', 'C']],
errors: []
}
},
{
description: "Quoted field with extra whitespace on edges",
input: 'A," B ",C',
expected: {
data: [['A', ' B ', 'C']],
errors: []
}
},
{
description: "Quoted field with delimiter",
input: 'A,"B,B",C',
expected: {
data: [['A', 'B,B', 'C']],
errors: []
}
},
{
description: "Quoted field with line break",
input: 'A,"B\nB",C',
expected: {
data: [['A', 'B\nB', 'C']],
errors: []
}
},
{
description: "Quoted fields with line breaks",
input: 'A,"B\nB","C\nC\nC"',
expected: {
data: [['A', 'B\nB', 'C\nC\nC']],
errors: []
}
},
{
description: "Quoted fields at end of row with delimiter and line break",
input: 'a,b,"c,c\nc"\nd,e,f',
expected: {
data: [['a', 'b', 'c,c\nc'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Quoted field with escaped quotes",
input: 'A,"B""B""B",C',
expected: {
data: [['A', 'B"B"B', 'C']],
errors: []
}
},
{
description: "Quoted field with escaped quotes at boundaries",
input: 'A,"""B""",C',
expected: {
data: [['A', '"B"', 'C']],
errors: []
}
},
{
description: "Unquoted field with quotes at end of field",
notes: "The quotes character is misplaced, but shouldn't generate an error or break the parser",
input: 'A,B",C',
expected: {
data: [['A', 'B"', 'C']],
errors: []
}
},
{
description: "Quoted field with quotes around delimiter",
input: 'A,""",""",C',
notes: "For a boundary to exist immediately before the quotes, we must not already be in quotes",
expected: {
data: [['A', '","', 'C']],
errors: []
}
},
{
description: "Quoted field with quotes on right side of delimiter",
input: 'A,",""",C',
notes: "Similar to the test above but with quotes only after the comma",
expected: {
data: [['A', ',"', 'C']],
errors: []
}
},
{
description: "Quoted field with quotes on left side of delimiter",
input: 'A,""",",C',
notes: "Similar to the test above but with quotes only before the comma",
expected: {
data: [['A', '",', 'C']],
errors: []
}
},
{
description: "Quoted field with 5 quotes in a row and a delimiter in there, too",
input: '"1","cnonce="""",nc=""""","2"',
notes: "Actual input reported in issue #121",
expected: {
data: [['1', 'cnonce="",nc=""', '2']],
errors: []
}
},
{
description: "Quoted field with whitespace around quotes",
input: 'A, "B" ,C',
notes: "The quotes must be immediately adjacent to the delimiter to indicate a quoted field",
expected: {
data: [['A', ' "B" ', 'C']],
errors: []
}
},
{
description: "Misplaced quotes in data, not as opening quotes",
input: 'A,B "B",C',
notes: "The input is technically malformed, but this syntax should not cause an error",
expected: {
data: [['A', 'B "B"', 'C']],
errors: []
}
},
{
description: "Quoted field has no closing quote",
input: 'a,"b,c\nd,e,f',
expected: {
data: [['a', 'b,c\nd,e,f']],
errors: [{
"type": "Quotes",
"code": "MissingQuotes",
"message": "Quoted field unterminated",
"row": 0,
"index": 3
}]
}
},
{
description: "Quoted field has invalid trailing quote after delimiter with a valid closer",
input: '"a,"b,c"\nd,e,f',
notes: "The input is malformed, opening quotes identified, trailing quote is malformed. Trailing quote should be escaped or followed by valid new line or delimiter to be valid",
expected: {
data: [['a,"b,c'], ['d', 'e', 'f']],
errors: [{
"type": "Quotes",
"code": "InvalidQuotes",
"message": "Trailing quote on quoted field is malformed",
"row": 0,
"index": 1
}]
}
},
{
description: "Quoted field has invalid trailing quote after delimiter",
input: 'a,"b,"c\nd,e,f',
notes: "The input is malformed, opening quotes identified, trailing quote is malformed. Trailing quote should be escaped or followed by valid new line or delimiter to be valid",
expected: {
data: [['a', 'b,"c\nd,e,f']],
errors: [{
"type": "Quotes",
"code": "InvalidQuotes",
"message": "Trailing quote on quoted field is malformed",
"row": 0,
"index": 3
},
{
"type": "Quotes",
"code": "MissingQuotes",
"message": "Quoted field unterminated",
"row": 0,
"index": 3
}]
}
},
{
description: "Quoted field has invalid trailing quote before delimiter",
input: 'a,"b"c,d\ne,f,g',
notes: "The input is malformed, opening quotes identified, trailing quote is malformed. Trailing quote should be escaped or followed by valid new line or delimiter to be valid",
expected: {
data: [['a', 'b"c,d\ne,f,g']],
errors: [{
"type": "Quotes",
"code": "InvalidQuotes",
"message": "Trailing quote on quoted field is malformed",
"row": 0,
"index": 3
},
{
"type": "Quotes",
"code": "MissingQuotes",
"message": "Quoted field unterminated",
"row": 0,
"index": 3
}]
}
},
{
description: "Quoted field has invalid trailing quote after new line",
input: 'a,"b,c\nd"e,f,g',
notes: "The input is malformed, opening quotes identified, trailing quote is malformed. Trailing quote should be escaped or followed by valid new line or delimiter to be valid",
expected: {
data: [['a', 'b,c\nd"e,f,g']],
errors: [{
"type": "Quotes",
"code": "InvalidQuotes",
"message": "Trailing quote on quoted field is malformed",
"row": 0,
"index": 3
},
{
"type": "Quotes",
"code": "MissingQuotes",
"message": "Quoted field unterminated",
"row": 0,
"index": 3
}]
}
},
{
description: "Quoted field has valid trailing quote via delimiter",
input: 'a,"b",c\nd,e,f',
notes: "Trailing quote is valid due to trailing delimiter",
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Quoted field has valid trailing quote via \\n",
input: 'a,b,"c"\nd,e,f',
notes: "Trailing quote is valid due to trailing new line delimiter",
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Quoted field has valid trailing quote via EOF",
input: 'a,b,c\nd,e,"f"',
notes: "Trailing quote is valid due to EOF",
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Quoted field contains delimiters and \\n with valid trailing quote",
input: 'a,"b,c\nd,e,f"',
notes: "Trailing quote is valid due to trailing delimiter",
expected: {
data: [['a', 'b,c\nd,e,f']],
errors: []
}
},
{
description: "Line starts with quoted field",
input: 'a,b,c\n"d",e,f',
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Line starts with unquoted empty field",
input: ',b,c\n"d",e,f',
expected: {
data: [['', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Line ends with quoted field",
input: 'a,b,c\nd,e,f\n"g","h","i"\n"j","k","l"',
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'], ['j', 'k', 'l']],
errors: []
}
},
{
description: "Line ends with quoted field, first field of next line is empty, \\n",
input: 'a,b,c\n,e,f\n,"h","i"\n,"k","l"',
config: {
newline: '\n',
},
expected: {
data: [['a', 'b', 'c'], ['', 'e', 'f'], ['', 'h', 'i'], ['', 'k', 'l']],
errors: []
}
},
{
description: "Quoted field at end of row (but not at EOF) has quotes",
input: 'a,b,"c""c"""\nd,e,f',
expected: {
data: [['a', 'b', 'c"c"'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Empty quoted field at EOF is empty",
input: 'a,b,""\na,b,""',
expected: {
data: [['a', 'b', ''], ['a', 'b', '']],
errors: []
}
},
{
description: "Multiple consecutive empty fields",
input: 'a,b,,,c,d\n,,e,,,f',
expected: {
data: [['a', 'b', '', '', 'c', 'd'], ['', '', 'e', '', '', 'f']],
errors: []
}
},
{
description: "Empty input string",
input: '',
expected: {
data: [],
errors: []
}
},
{
description: "Input is just the delimiter (2 empty fields)",
input: ',',
expected: {
data: [['', '']],
errors: []
}
},
{
description: "Input is just empty fields",
input: ',,\n,,,',
expected: {
data: [['', '', ''], ['', '', '', '']],
errors: []
}
},
{
description: "Input is just a string (a single field)",
input: 'Abc def',
expected: {
data: [['Abc def']],
errors: []
}
},
{
description: "Commented line at beginning",
input: '# Comment!\na,b,c',
config: { comments: true },
expected: {
data: [['a', 'b', 'c']],
errors: []
}
},
{
description: "Commented line in middle",
input: 'a,b,c\n# Comment\nd,e,f',
config: { comments: true },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Commented line at end",
input: 'a,true,false\n# Comment',
config: { comments: true },
expected: {
data: [['a', 'true', 'false']],
errors: []
}
},
{
description: "Two comment lines consecutively",
input: 'a,b,c\n#comment1\n#comment2\nd,e,f',
config: { comments: true },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Two comment lines consecutively at end of file",
input: 'a,b,c\n#comment1\n#comment2',
config: { comments: true },
expected: {
data: [['a', 'b', 'c']],
errors: []
}
},
{
description: "Three comment lines consecutively at beginning of file",
input: '#comment1\n#comment2\n#comment3\na,b,c',
config: { comments: true },
expected: {
data: [['a', 'b', 'c']],
errors: []
}
},
{
description: "Entire file is comment lines",
input: '#comment1\n#comment2\n#comment3',
config: { comments: true },
expected: {
data: [],
errors: []
}
},
{
description: "Comment with non-default character",
input: 'a,b,c\n!Comment goes here\nd,e,f',
config: { comments: '!' },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Bad comments value specified",
notes: "Should silently disable comment parsing",
input: 'a,b,c\n5comment\nd,e,f',
config: { comments: 5 },
expected: {
data: [['a', 'b', 'c'], ['5comment'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Multi-character comment string",
input: 'a,b,c\n=N(Comment)\nd,e,f',
config: { comments: "=N(" },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Input with only a commented line",
input: '#commented line',
config: { comments: true, delimiter: ',' },
expected: {
data: [],
errors: []
}
},
{
description: "Input with only a commented line and blank line after",
input: '#commented line\n',
config: { comments: true, delimiter: ',' },
expected: {
data: [['']],
errors: []
}
},
{
description: "Input with only a commented line, without comments enabled",
input: '#commented line',
config: { delimiter: ',' },
expected: {
data: [['#commented line']],
errors: []
}
},
{
description: "Input without comments with line starting with whitespace",
input: 'a\n b\nc',
config: { delimiter: ',' },
notes: "\" \" == false, but \" \" !== false, so === comparison is required",
expected: {
data: [['a'], [' b'], ['c']],
errors: []
}
},
{
description: "Multiple rows, one column (no delimiter found)",
input: 'a\nb\nc\nd\ne',
expected: {
data: [['a'], ['b'], ['c'], ['d'], ['e']],
errors: []
}
},
{
description: "One column input with empty fields",
input: 'a\nb\n\n\nc\nd\ne\n',
expected: {
data: [['a'], ['b'], [''], [''], ['c'], ['d'], ['e'], ['']],
errors: []
}
},
{
description: "Fast mode, basic",
input: 'a,b,c\nd,e,f',
config: { fastMode: true },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Fast mode with comments",
input: '// Commented line\na,b,c',
config: { fastMode: true, comments: "//" },
expected: {
data: [['a', 'b', 'c']],
errors: []
}
},
{
description: "Fast mode with preview",
input: 'a,b,c\nd,e,f\nh,j,i\n',
config: { fastMode: true, preview: 2 },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Fast mode with blank line at end",
input: 'a,b,c\n',
config: { fastMode: true },
expected: {
data: [['a', 'b', 'c'], ['']],
errors: []
}
},
{
description: "Simple duplicated header names",
input: 'A,A,A,A\n1,2,3,4',
config: { header: true },
expected: {
data: [['A', 'A_1', 'A_2', 'A_3'], ['1', '2', '3', '4']],
errors: [],
meta: {
renamedHeaders: {A_1: 'A', A_2: 'A', A_3: 'A'},
cursor: 15
}
}
},
{
description: "Duplicated header names with headerTransform",
input: 'A,A,A,A\n1,2,3,4',
config: { header: true, transformHeader: function(header) { return header.toLowerCase(); } },
expected: {
data: [['a', 'a_1', 'a_2', 'a_3'], ['1', '2', '3', '4']],
errors: [],
meta: {
renamedHeaders: {a_1: 'a', a_2: 'a', a_3: 'a'},
cursor: 15
}
}
},
{
description: "Duplicated header names existing column",
input: 'c,c,c,c_1\n1,2,3,4',
config: { header: true },
expected: {
data: [['c', 'c_2', 'c_3', 'c_1'], ['1', '2', '3', '4']],
errors: [],
meta: {
renamedHeaders: {c_2: 'c', c_3: 'c'},
cursor: 17
}
}
},
{
description: "Duplicate header names with __proto__ field",
input: '__proto__,__proto__,__proto__\n1,2,3',
config: { header: true },
expected: {
data: [['__proto__', '__proto___1', '__proto___2'], ['1', '2', '3']],
errors: [],
meta: {
renamedHeaders: {__proto___1: '__proto__', __proto___2: '__proto__'},
cursor: 35
}
}
},
];
describe('Core Parser Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function() {
var actual = new Papa.Parser(test.config).parse(test.input);
assert.deepEqual(actual.errors, test.expected.errors);
assert.deepEqual(actual.data, test.expected.data);
assert.deepNestedInclude(actual.meta, test.expected.meta || {});
});
}
for (var i = 0; i < CORE_PARSER_TESTS.length; i++) {
generateTest(CORE_PARSER_TESTS[i]);
}
});
// Tests for Papa.parse() function -- high-level wrapped parser (CSV to JSON)
var PARSE_TESTS = [
{
description: "Two rows, just \\r",
input: 'A,b,c\rd,E,f',
expected: {
data: [['A', 'b', 'c'], ['d', 'E', 'f']],
errors: []
}
},
{
description: "Two rows, \\r\\n",
input: 'A,b,c\r\nd,E,f',
expected: {
data: [['A', 'b', 'c'], ['d', 'E', 'f']],
errors: []
}
},
{
description: "Quoted field with \\r\\n",
input: 'A,"B\r\nB",C',
expected: {
data: [['A', 'B\r\nB', 'C']],
errors: []
}
},
{
description: "Quoted field with \\r",
input: 'A,"B\rB",C',
expected: {
data: [['A', 'B\rB', 'C']],
errors: []
}
},
{
description: "Quoted field with \\n",
input: 'A,"B\nB",C',
expected: {
data: [['A', 'B\nB', 'C']],
errors: []
}
},
{
description: "Quoted fields with spaces between closing quote and next delimiter",
input: 'A,"B" ,C,D\r\nE,F,"G" ,H',
expected: {
data: [['A', 'B', 'C','D'],['E', 'F', 'G','H']],
errors: []
}
},
{
description: "Quoted fields with spaces between closing quote and next new line",
input: 'A,B,C,"D" \r\nE,F,G,"H" \r\nQ,W,E,R',
expected: {
data: [['A', 'B', 'C','D'],['E', 'F', 'G','H'],['Q', 'W', 'E','R']],
errors: []
}
},
{
description: "Quoted fields with spaces after closing quote",
input: 'A,"B" ,C,"D" \r\nE,F,"G" ,"H" \r\nQ,W,"E" ,R',
expected: {
data: [['A', 'B', 'C','D'],['E', 'F', 'G','H'],['Q', 'W', 'E','R']],
errors: []
}
},
{
description: "Misplaced quotes in data twice, not as opening quotes",
input: 'A,B",C\nD,E",F',
expected: {
data: [['A', 'B"', 'C'], ['D', 'E"', 'F']],
errors: []
}
},
{
description: "Mixed slash n and slash r should choose first as precident",
input: 'a,b,c\nd,e,f\rg,h,i\n',
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f\rg', 'h', 'i'], ['']],
errors: []
}
},
{
description: "Header row with one row of data",
input: 'A,B,C\r\na,b,c',
config: { header: true },
expected: {
data: [{"A": "a", "B": "b", "C": "c"}],
errors: []
}
},
{
description: "Header row only",
input: 'A,B,C',
config: { header: true },
expected: {
data: [],
errors: []
}
},
{
description: "Row with too few fields",
input: 'A,B,C\r\na,b',
config: { header: true },
expected: {
data: [{"A": "a", "B": "b"}],
errors: [{
"type": "FieldMismatch",
"code": "TooFewFields",
"message": "Too few fields: expected 3 fields but parsed 2",
"row": 0
}]
}
},
{
description: "Row with too many fields",
input: 'A,B,C\r\na,b,c,d,e\r\nf,g,h',
config: { header: true },
expected: {
data: [{"A": "a", "B": "b", "C": "c", "__parsed_extra": ["d", "e"]}, {"A": "f", "B": "g", "C": "h"}],
errors: [{
"type": "FieldMismatch",
"code": "TooManyFields",
"message": "Too many fields: expected 3 fields but parsed 5",
"row": 0
}]
}
},
{
description: "Row with enough fields but blank field in the begining",
input: 'A,B,C\r\n,b1,c1\r\na2,b2,c2',
expected: {
data: [["A", "B", "C"], ['', 'b1', 'c1'], ['a2', 'b2', 'c2']],
errors: []
}
},
{
description: "Row with enough fields but blank field in the begining using headers",
input: 'A,B,C\r\n,b1,c1\r\n,b2,c2',
config: { header: true },
expected: {
data: [{"A": "", "B": "b1", "C": "c1"}, {"A": "", "B": "b2", "C": "c2"}],
errors: []
}
},
{
description: "Row with enough fields but blank field at end",
input: 'A,B,C\r\na,b,',
config: { header: true },
expected: {
data: [{"A": "a", "B": "b", "C": ""}],
errors: []
}
},
{
description: "Header rows are transformed when transformHeader function is provided",
input: 'A,B,C\r\na,b,c',
config: { header: true, transformHeader: function(header) { return header.toLowerCase(); } },
expected: {
data: [{"a": "a", "b": "b", "c": "c"}],
errors: []
}
},
{
description: "transformHeader accepts and optional index attribute",
input: 'A,B,C\r\na,b,c',
config: { header: true, transformHeader: function(header, i) { return i % 2 ? header.toLowerCase() : header; } },
expected: {
data: [{"A": "a", "b": "b", "C": "c"}],
errors: []
}
},
{
description: "Line ends with quoted field, first field of next line is empty using headers",
input: 'a,b,"c"\r\nd,e,"f"\r\n,"h","i"\r\n,"k","l"',
config: {
header: true,
newline: '\r\n',
},
expected: {
data: [
{a: 'd', b: 'e', c: 'f'},
{a: '', b: 'h', c: 'i'},
{a: '', b: 'k', c: 'l'}
],
errors: []
}
},
{
description: "Tab delimiter",
input: 'a\tb\tc\r\nd\te\tf',
config: { delimiter: "\t" },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Pipe delimiter",
input: 'a|b|c\r\nd|e|f',
config: { delimiter: "|" },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "ASCII 30 delimiter",
input: 'a' + RECORD_SEP + 'b' + RECORD_SEP + 'c\r\nd' + RECORD_SEP + 'e' + RECORD_SEP + 'f',
config: { delimiter: RECORD_SEP },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "ASCII 31 delimiter",
input: 'a' + UNIT_SEP + 'b' + UNIT_SEP + 'c\r\nd' + UNIT_SEP + 'e' + UNIT_SEP + 'f',
config: { delimiter: UNIT_SEP },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Bad delimiter (\\n)",
input: 'a,b,c',
config: { delimiter: "\n" },
notes: "Should silently default to comma",
expected: {
data: [['a', 'b', 'c']],
errors: []
}
},
{
description: "Multi-character delimiter",
input: 'a, b, c',
config: { delimiter: ", " },
expected: {
data: [['a', 'b', 'c']],
errors: []
}
},
{
description: "Multi-character delimiter (length 2) with quoted field",
input: 'a, b, "c, e", d',
config: { delimiter: ", " },
notes: "The quotes must be immediately adjacent to the delimiter to indicate a quoted field",
expected: {
data: [['a', 'b', 'c, e', 'd']],
errors: []
}
},
{
description: "Callback delimiter",
input: 'a$ b$ c',
config: { delimiter: function(input) { return input[1] + ' '; } },
expected: {
data: [['a', 'b', 'c']],
errors: []
}
},
{
description: "Dynamic typing converts numeric literals and maintains precision",
input: '1,2.2,1e3\r\n-4,-4.5,-4e-5\r\n-,5a,5-2\r\n16142028098527942586,9007199254740991,-9007199254740992',
config: { dynamicTyping: true },
expected: {
data: [[1, 2.2, 1000], [-4, -4.5, -0.00004], ["-", "5a", "5-2"], ["16142028098527942586", 9007199254740991, "-9007199254740992"]],
errors: []
}
},
{
description: "Dynamic typing converts boolean literals",
input: 'true,false,T,F,TRUE,FALSE,True,False',
config: { dynamicTyping: true },
expected: {
data: [[true, false, "T", "F", true, false, "True", "False"]],
errors: []
}
},
{
description: "Dynamic typing doesn't convert other types",
input: 'A,B,C\r\nundefined,null,[\r\nvar,float,if',
config: { dynamicTyping: true },
expected: {
data: [["A", "B", "C"], ["undefined", "null", "["], ["var", "float", "if"]],
errors: []
}
},
{
description: "Dynamic typing applies to specific columns",
input: 'A,B,C\r\n1,2.2,1e3\r\n-4,-4.5,-4e-5',
config: { header: true, dynamicTyping: { A: true, C: true } },
expected: {
data: [{"A": 1, "B": "2.2", "C": 1000}, {"A": -4, "B": "-4.5", "C": -0.00004}],
errors: []
}
},
{
description: "Dynamic typing applies to specific columns by index",
input: '1,2.2,1e3\r\n-4,-4.5,-4e-5\r\n-,5a,5-2',
config: { dynamicTyping: { 1: true } },
expected: {
data: [["1", 2.2, "1e3"], ["-4", -4.5, "-4e-5"], ["-", "5a", "5-2"]],
errors: []
}
},
{
description: "Dynamic typing can be applied to `__parsed_extra`",
input: 'A,B,C\r\n1,2.2,1e3,5.5\r\n-4,-4.5,-4e-5',
config: { header: true, dynamicTyping: { A: true, C: true, __parsed_extra: true } },
expected: {
data: [{"A": 1, "B": "2.2", "C": 1000, "__parsed_extra": [5.5]}, {"A": -4, "B": "-4.5", "C": -0.00004}],
errors: [{
"type": "FieldMismatch",
"code": "TooManyFields",
"message": "Too many fields: expected 3 fields but parsed 4",
"row": 0
}]
}
},
{
description: "Dynamic typing by indices can be determined by function",
input: '001,002,003',
config: { dynamicTyping: function(field) { return (field % 2) === 0; } },
expected: {
data: [[1, "002", 3]],
errors: []
}
},
{
description: "Dynamic typing by headers can be determined by function",
input: 'A_as_int,B,C_as_int\r\n001,002,003',
config: { header: true, dynamicTyping: function(field) { return /_as_int$/.test(field); } },
expected: {
data: [{"A_as_int": 1, "B": "002", "C_as_int": 3}],
errors: []
}
},
{
description: "Dynamic typing converts empty values into NULL",
input: '1,2.2,1e3\r\n,NULL,\r\n-,5a,null',
config: { dynamicTyping: true },
expected: {
data: [[1, 2.2, 1000], [null, "NULL", null], ["-", "5a", "null"]],
errors: []
}
},
{
description: "Custom transform function is applied to values",
input: 'A,B,C\r\nd,e,f',
config: {
transform: function(value) {
return value.toLowerCase();
}
},
expected: {
data: [["a","b","c"], ["d","e","f"]],
errors: []
}
},
{
description: "Custom transform accepts column number also",
input: 'A,B,C\r\nd,e,f',
config: {
transform: function(value, column) {
if (column % 2) {
value = value.toLowerCase();
}
return value;
}
},
expected: {
data: [["A","b","C"], ["d","e","f"]],
errors: []
}
},
{
description: "Custom transform accepts header name when using header",
input: 'A,B,C\r\nd,e,f',
config: {
header: true,
transform: function(value, name) {
if (name === 'B') {
value = value.toUpperCase();
}
return value;
}
},
expected: {
data: [{'A': "d", 'B': "E", 'C': "f"}],
errors: []
}
},
{
description: "Dynamic typing converts ISO date strings to Dates",
input: 'ISO date,long date\r\n2018-05-04T21:08:03.269Z,Fri May 04 2018 14:08:03 GMT-0700 (PDT)\r\n2018-05-08T15:20:22.642Z,Tue May 08 2018 08:20:22 GMT-0700 (PDT)',
config: { dynamicTyping: true },
expected: {
data: [["ISO date", "long date"], [new Date("2018-05-04T21:08:03.269Z"), "Fri May 04 2018 14:08:03 GMT-0700 (PDT)"], [new Date("2018-05-08T15:20:22.642Z"), "Tue May 08 2018 08:20:22 GMT-0700 (PDT)"]],
errors: []
}
},
{
description: "Dynamic typing skips ISO date strings ocurring in other strings",
input: 'ISO date,String with ISO date\r\n2018-05-04T21:08:03.269Z,The date is 2018-05-04T21:08:03.269Z\r\n2018-05-08T15:20:22.642Z,The date is 2018-05-08T15:20:22.642Z',
config: { dynamicTyping: true },
expected: {
data: [["ISO date", "String with ISO date"], [new Date("2018-05-04T21:08:03.269Z"), "The date is 2018-05-04T21:08:03.269Z"], [new Date("2018-05-08T15:20:22.642Z"), "The date is 2018-05-08T15:20:22.642Z"]],
errors: []
}
},
{
description: "Blank line at beginning",
input: '\r\na,b,c\r\nd,e,f',
config: { newline: '\r\n' },
expected: {
data: [[''], ['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Blank line in middle",
input: 'a,b,c\r\n\r\nd,e,f',
config: { newline: '\r\n' },
expected: {
data: [['a', 'b', 'c'], [''], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Blank lines at end",
input: 'a,b,c\nd,e,f\n\n',
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f'], [''], ['']],
errors: []
}
},
{
description: "Blank line in middle with whitespace",
input: 'a,b,c\r\n \r\nd,e,f',
expected: {
data: [['a', 'b', 'c'], [" "], ['d', 'e', 'f']],
errors: []
}
},
{
description: "First field of a line is empty",
input: 'a,b,c\r\n,e,f',
expected: {
data: [['a', 'b', 'c'], ['', 'e', 'f']],
errors: []
}
},
{
description: "Last field of a line is empty",
input: 'a,b,\r\nd,e,f',
expected: {
data: [['a', 'b', ''], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Other fields are empty",
input: 'a,,c\r\n,,',
expected: {
data: [['a', '', 'c'], ['', '', '']],
errors: []
}
},
{
description: "Empty input string",
input: '',
expected: {
data: [],
errors: [{
"type": "Delimiter",
"code": "UndetectableDelimiter",
"message": "Unable to auto-detect delimiting character; defaulted to ','"
}]
}
},
{
description: "Input is just the delimiter (2 empty fields)",
input: ',',
expected: {
data: [['', '']],
errors: []
}
},
{
description: "Input is just a string (a single field)",
input: 'Abc def',
expected: {
data: [['Abc def']],
errors: [
{
"type": "Delimiter",
"code": "UndetectableDelimiter",
"message": "Unable to auto-detect delimiting character; defaulted to ','"
}
]
}
},
{
description: "Preview 0 rows should default to parsing all",
input: 'a,b,c\r\nd,e,f\r\ng,h,i',
config: { preview: 0 },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
errors: []
}
},
{
description: "Preview 1 row",
input: 'a,b,c\r\nd,e,f\r\ng,h,i',
config: { preview: 1 },
expected: {
data: [['a', 'b', 'c']],
errors: []
}
},
{
description: "Preview 2 rows",
input: 'a,b,c\r\nd,e,f\r\ng,h,i',
config: { preview: 2 },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Preview all (3) rows",
input: 'a,b,c\r\nd,e,f\r\ng,h,i',
config: { preview: 3 },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
errors: []
}
},
{
description: "Preview more rows than input has",
input: 'a,b,c\r\nd,e,f\r\ng,h,i',
config: { preview: 4 },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
errors: []
}
},
{
description: "Preview should count rows, not lines",
input: 'a,b,c\r\nd,e,"f\r\nf",g,h,i',
config: { preview: 2 },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f\r\nf', 'g', 'h', 'i']],
errors: []
}
},
{
description: "Preview with header row",
notes: "Preview is defined to be number of rows of input not including header row",
input: 'a,b,c\r\nd,e,f\r\ng,h,i\r\nj,k,l',
config: { header: true, preview: 2 },
expected: {
data: [{"a": "d", "b": "e", "c": "f"}, {"a": "g", "b": "h", "c": "i"}],
errors: []
}
},
{
description: "Empty lines",
input: '\na,b,c\n\nd,e,f\n\n',
config: { delimiter: ',' },
expected: {
data: [[''], ['a', 'b', 'c'], [''], ['d', 'e', 'f'], [''], ['']],
errors: []
}
},
{
description: "Skip empty lines",
input: 'a,b,c\n\nd,e,f',
config: { skipEmptyLines: true },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Skip empty lines, with newline at end of input",
input: 'a,b,c\r\n\r\nd,e,f\r\n',
config: { skipEmptyLines: true },
expected: {
data: [['a', 'b', 'c'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Skip empty lines, with empty input",
input: '',
config: { skipEmptyLines: true },
expected: {
data: [],
errors: [
{
"type": "Delimiter",
"code": "UndetectableDelimiter",
"message": "Unable to auto-detect delimiting character; defaulted to ','"
}
]
}
},
{
description: "Skip empty lines, with first line only whitespace",
notes: "A line must be absolutely empty to be considered empty",
input: ' \na,b,c',
config: { skipEmptyLines: true, delimiter: ',' },
expected: {
data: [[" "], ['a', 'b', 'c']],
errors: []
}
},
{
description: "Skip empty lines while detecting delimiter",
notes: "Parsing correctly newline-terminated short data with delimiter:auto and skipEmptyLines:true",
input: 'a,b\n1,2\n3,4\n',
config: { header: true, skipEmptyLines: true },
expected: {
data: [{'a': '1', 'b': '2'}, {'a': '3', 'b': '4'}],
errors: []
}
},
{
description: "Lines with comments are not used when guessing the delimiter in an escaped file",
notes: "Guessing the delimiter should work even if there are many lines of comments at the start of the file",
input: '#1\n#2\n#3\n#4\n#5\n#6\n#7\n#8\n#9\n#10\none,"t,w,o",three\nfour,five,six',
config: { comments: '#' },
expected: {
data: [['one','t,w,o','three'],['four','five','six']],
errors: []
}
},
{
description: "Lines with comments are not used when guessing the delimiter in a non-escaped file",
notes: "Guessing the delimiter should work even if there are many lines of comments at the start of the file",
input: '#1\n#2\n#3\n#4\n#5\n#6\n#7\n#8\n#9\n#10\n#11\none,two,three\nfour,five,six',
config: { comments: '#' },
expected: {
data: [['one','two','three'],['four','five','six']],
errors: []
}
},
{
description: "Pipe delimiter is guessed correctly when mixed with comas",
notes: "Guessing the delimiter should work even if there are many lines of comments at the start of the file",
input: 'one|two,two|three\nfour|five,five|six',
config: {},
expected: {
data: [['one','two,two','three'],['four','five,five','six']],
errors: []
}
},
{
description: "Pipe delimiter is guessed correctly choose avgFildCount max one",
notes: "Guessing the delimiter should work choose the min delta one and the max one",
config: {},
input: 'a,b,c\na,b,c|d|e|f',
expected: {
data: [['a', 'b', 'c'], ['a','b','c|d|e|f']],
errors: []
}
},
{
description: "Pipe delimiter is guessed correctly when first field are enclosed in quotes and contain delimiter characters",
notes: "Guessing the delimiter should work if the first field is enclosed in quotes, but others are not",
input: '"Field1,1,1";Field2;"Field3";Field4;Field5;Field6',
config: {},
expected: {
data: [['Field1,1,1','Field2','Field3', 'Field4', 'Field5', 'Field6']],
errors: []
}
},
{
description: "Pipe delimiter is guessed correctly when some fields are enclosed in quotes and contain delimiter characters and escaoped quotes",
notes: "Guessing the delimiter should work even if the first field is not enclosed in quotes, but others are",
input: 'Field1;Field2;"Field,3,""3,3";Field4;Field5;"Field6,6"',
config: {},
expected: {
data: [['Field1','Field2','Field,3,"3,3', 'Field4', 'Field5', 'Field6,6']],
errors: []
}
},
{
description: "Single quote as quote character",
notes: "Must parse correctly when single quote is specified as a quote character",
input: "a,b,'c,d'",
config: { quoteChar: "'" },
expected: {
data: [['a', 'b', 'c,d']],
errors: []
}
},
{
description: "Custom escape character in the middle",
notes: "Must parse correctly if the backslash sign (\\) is configured as a custom escape character",
input: 'a,b,"c\\"d\\"f"',
config: { escapeChar: '\\' },
expected: {
data: [['a', 'b', 'c"d"f']],
errors: []
}
},
{
description: "Custom escape character at the end",
notes: "Must parse correctly if the backslash sign (\\) is configured as a custom escape character and the escaped quote character appears at the end of the column",
input: 'a,b,"c\\"d\\""',
config: { escapeChar: '\\' },
expected: {
data: [['a', 'b', 'c"d"']],
errors: []
}
},
{
description: "Custom escape character not used for escaping",
notes: "Must parse correctly if the backslash sign (\\) is configured as a custom escape character and appears as regular character in the text",
input: 'a,b,"c\\d"',
config: { escapeChar: '\\' },
expected: {
data: [['a', 'b', 'c\\d']],
errors: []
}
},
{
description: "Header row with preceding comment",
notes: "Must parse correctly headers if they are preceded by comments",
input: '#Comment\na,b\nc,d\n',
config: { header: true, comments: '#', skipEmptyLines: true, delimiter: ',' },
expected: {
data: [{'a': 'c', 'b': 'd'}],
errors: []
}
},
{
description: "Carriage return in header inside quotes, with line feed endings",
input: '"a\r\na","b"\n"c","d"\n"e","f"\n"g","h"\n"i","j"',
config: {},
expected: {
data: [['a\r\na', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: []
}
},
{
description: "Line feed in header inside quotes, with carriage return + line feed endings",
input: '"a\na","b"\r\n"c","d"\r\n"e","f"\r\n"g","h"\r\n"i","j"',
config: {},
expected: {
data: [['a\na', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: []
}
},
{
description: "Using \\r\\n endings uses \\r\\n linebreak",
input: 'a,b\r\nc,d\r\ne,f\r\ng,h\r\ni,j',
config: {},
expected: {
data: [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: [],
meta: {
linebreak: '\r\n',
delimiter: ',',
cursor: 23,
aborted: false,
truncated: false,
renamedHeaders: null
}
}
},
{
description: "Using \\n endings uses \\n linebreak",
input: 'a,b\nc,d\ne,f\ng,h\ni,j',
config: {},
expected: {
data: [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: [],
meta: {
linebreak: '\n',
delimiter: ',',
cursor: 19,
aborted: false,
truncated: false,
renamedHeaders: null
}
}
},
{
description: "Using \\r\\n endings with \\r\\n in header field uses \\r\\n linebreak",
input: '"a\r\na",b\r\nc,d\r\ne,f\r\ng,h\r\ni,j',
config: {},
expected: {
data: [['a\r\na', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: [],
meta: {
linebreak: '\r\n',
delimiter: ',',
cursor: 28,
aborted: false,
truncated: false,
renamedHeaders: null
}
}
},
{
description: "Using \\r\\n endings with \\n in header field uses \\r\\n linebreak",
input: '"a\na",b\r\nc,d\r\ne,f\r\ng,h\r\ni,j',
config: {},
expected: {
data: [['a\na', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: [],
meta: {
linebreak: '\r\n',
delimiter: ',',
cursor: 27,
aborted: false,
truncated: false,
renamedHeaders: null
}
}
},
{
description: "Using \\r\\n endings with \\n in header field with skip empty lines uses \\r\\n linebreak",
input: '"a\na",b\r\nc,d\r\ne,f\r\ng,h\r\ni,j\r\n',
config: {skipEmptyLines: true},
expected: {
data: [['a\na', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: [],
meta: {
linebreak: '\r\n',
delimiter: ',',
cursor: 29,
aborted: false,
truncated: false,
renamedHeaders: null
}
}
},
{
description: "Using \\n endings with \\r\\n in header field uses \\n linebreak",
input: '"a\r\na",b\nc,d\ne,f\ng,h\ni,j',
config: {},
expected: {
data: [['a\r\na', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: [],
meta: {
linebreak: '\n',
delimiter: ',',
cursor: 24,
aborted: false,
truncated: false,
renamedHeaders: null
}
}
},
{
description: "Using reserved regex character . as quote character",
input: '.a\na.,b\r\nc,d\r\ne,f\r\ng,h\r\ni,j',
config: { quoteChar: '.' },
expected: {
data: [['a\na', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: [],
meta: {
linebreak: '\r\n',
delimiter: ',',
cursor: 27,
aborted: false,
truncated: false,
renamedHeaders: null
}
}
},
{
description: "Using reserved regex character | as quote character",
input: '|a\na|,b\r\nc,d\r\ne,f\r\ng,h\r\ni,j',
config: { quoteChar: '|' },
expected: {
data: [['a\na', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h'], ['i', 'j']],
errors: [],
meta: {
linebreak: '\r\n',
delimiter: ',',
cursor: 27,
aborted: false,
truncated: false,
renamedHeaders: null
}
}
},
{
description: "UTF-8 BOM encoded input is stripped from invisible BOM character",
input: '\ufeffA,B\nX,Y',
config: {},
expected: {
data: [['A', 'B'], ['X', 'Y']],
errors: [],
}
},
{
description: "UTF-8 BOM encoded input with header produces column key stripped from invisible BOM character",
input: '\ufeffA,B\nX,Y',
config: { header: true },
expected: {
data: [{A: 'X', B: 'Y'}],
errors: [],
}
},
{
description: "Parsing with skipEmptyLines set to 'greedy'",
notes: "Must parse correctly without lines with no content",
input: 'a,b\n\n,\nc,d\n , \n""," "\n , \n,,,,\n',
config: { skipEmptyLines: 'greedy' },
expected: {
data: [['a', 'b'], ['c', 'd']],
errors: []
}
},
{
description: "Parsing with skipEmptyLines set to 'greedy' with quotes and delimiters as content",
notes: "Must include lines with escaped delimiters and quotes",
input: 'a,b\n\n,\nc,d\n" , ",","\n""" """,""""""\n\n\n',
config: { skipEmptyLines: 'greedy' },
expected: {
data: [['a', 'b'], ['c', 'd'], [' , ', ','], ['" "', '""']],
errors: []
}
},
{
description: "Quoted fields with spaces between closing quote and next delimiter and contains delimiter",
input: 'A,",B" ,C,D\nE,F,G,H',
expected: {
data: [['A', ',B', 'C', 'D'],['E', 'F', 'G', 'H']],
errors: []
}
},
{
description: "Quoted fields with spaces between closing quote and newline and contains newline",
input: 'a,b,"c\n" \nd,e,f',
expected: {
data: [['a', 'b', 'c\n'], ['d', 'e', 'f']],
errors: []
}
},
{
description: "Skip First N number of lines , with header and 2 rows",
input: 'a,b,c,d\n1,2,3,4',
config: { header: true, skipFirstNLines: 1 },
expected: {
data: [],
errors: []
}
},
{
description: "Skip First N number of lines , with header and 2 rows",
input: 'to-be-ignored\na,b,c,d\n1,2,3,4',
config: { header: true, skipFirstNLines: 1 },
expected: {
data: [{a: '1', b: '2', c: '3', d: '4'}],
errors: []
}
},
{
description: "Skip First N number of lines , with header false",
input: 'a,b,c,d\n1,2,3,4\n4,5,6,7',
config: { header: false, skipFirstNLines: 1 },
expected: {
data: [['1','2','3','4'],['4','5','6','7']],
errors: []
}
},
{
description: "Skip First N number of lines , with header false and skipFirstNLines as negative value",
input: 'a,b,c,d\n1,2,3,4\n4,5,6,7',
config: { header: false, skipFirstNLines: -2 },
expected: {
data: [['a','b','c','d'],['1','2','3','4'],['4','5','6','7']],
errors: []
}
},
{
description: "Skip first 2 lines , with custom newline character",
input: 'skip-this\rskip-this\r1,2,3,4',
config: { header: false, skipFirstNLines: 2, newline: '\r' },
expected: {
data: [['1','2','3','4']],
errors: []
}
},
];
describe('Parse Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function() {
var actual = Papa.parse(test.input, test.config);
// allows for testing the meta object if present in the test
if (test.expected.meta) {
assert.deepEqual(actual.meta, test.expected.meta);
}
assert.deepEqual(actual.errors, test.expected.errors);
assert.deepEqual(actual.data, test.expected.data);
});
}
for (var i = 0; i < PARSE_TESTS.length; i++) {
generateTest(PARSE_TESTS[i]);
}
// Custom test for Issue 1024 - renamedHeaders regression test
it('Issue 1024: renamedHeaders returned for simple duplicate headers (regression test)', function() {
var result = Papa.parse('Column,Column\n1-1,1-2\n2-1,2-2\n3-1,3-2', { header: true });
// Test data structure
assert.deepEqual(result.data, [
{Column: '1-1', Column_1: '1-2'},
{Column: '2-1', Column_1: '2-2'},
{Column: '3-1', Column_1: '3-2'}
]);
// Test errors
assert.deepEqual(result.errors, []);
// Test that renamedHeaders is present and correct
assert.isNotNull(result.meta.renamedHeaders, 'renamedHeaders should not be null');
assert.isObject(result.meta.renamedHeaders, 'renamedHeaders should be an object');
assert.deepEqual(result.meta.renamedHeaders, {Column_1: 'Column'}, 'renamedHeaders should contain the renamed header mapping');
});
});
// Tests for Papa.parse() that involve asynchronous operation
var PARSE_ASYNC_TESTS = [
{
description: "Simple worker",
input: "A,B,C\nX,Y,Z",
config: {
worker: true,
},
expected: {
data: [['A','B','C'],['X','Y','Z']],
errors: []
}
},
{
description: "Simple download",
input: BASE_PATH + "sample.csv",
config: {
download: true
},
disabled: !XHR_ENABLED,
expected: {
data: [['A','B','C'],['X','Y','Z']],
errors: []
}
},
{
description: "Simple download + worker",
input: BASE_PATH + "sample.csv",
config: {
worker: true,
download: true
},
disabled: !XHR_ENABLED,
expected: {
data: [['A','B','C'],['X','Y','Z']],
errors: []
}
},
{
description: "Simple file",
disabled: !FILES_ENABLED,
input: FILES_ENABLED ? new File(["A,B,C\nX,Y,Z"], "sample.csv") : false,
config: {
},
expected: {
data: [['A','B','C'],['X','Y','Z']],
errors: []
}
},
{
description: "Simple file + worker",
disabled: !FILES_ENABLED,
input: FILES_ENABLED ? new File(["A,B,C\nX,Y,Z"], "sample.csv") : false,
config: {
worker: true,
},
expected: {
data: [['A','B','C'],['X','Y','Z']],
errors: []
}
},
{
description: "File with a few regular and lots of empty lines",
disabled: !FILES_ENABLED,
input: FILES_ENABLED ? new File(["A,B,C\nX,Y,Z\n" + new Array(500000).fill(",,").join("\n")], "sample.csv") : false,
config: {
skipEmptyLines: "greedy"
},
expected: {
data: [['A','B','C'],['X','Y','Z']],
errors: []
}
},
{
description: "File with a few regular and lots of empty lines + worker",
disabled: !FILES_ENABLED,
input: FILES_ENABLED ? new File(["A,B,C\nX,Y,Z\n" + new Array(500000).fill(",,").join("\n")], "sample.csv") : false,
config: {
worker: true,
skipEmptyLines: "greedy"
},
expected: {
data: [['A','B','C'],['X','Y','Z']],
errors: []
}
}
];
describe('Parse Async Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function(done) {
var config = test.config;
config.complete = function(actual) {
assert.deepEqual(actual.errors, test.expected.errors);
assert.deepEqual(actual.data, test.expected.data);
done();
};
config.error = function(err) {
throw err;
};
Papa.parse(test.input, config);
});
}
for (var i = 0; i < PARSE_ASYNC_TESTS.length; i++) {
generateTest(PARSE_ASYNC_TESTS[i]);
}
});
// Tests for Papa.unparse() function (JSON to CSV)
var UNPARSE_TESTS = [
{
description: "A simple row",
notes: "Comma should be default delimiter",
input: [['A', 'b', 'c']],
expected: 'A,b,c'
},
{
description: "Two rows",
input: [['A', 'b', 'c'], ['d', 'E', 'f']],
expected: 'A,b,c\r\nd,E,f'
},
{
description: "Data with quotes",
input: [['a', '"b"', 'c'], ['"d"', 'e', 'f']],
expected: 'a,"""b""",c\r\n"""d""",e,f'
},
{
description: "Data with newlines",
input: [['a', 'b\nb', 'c'], ['d', 'e', 'f\r\nf']],
expected: 'a,"b\nb",c\r\nd,e,"f\r\nf"'
},
{
description: "Array of objects (header row)",
input: [{ "Col1": "a", "Col2": "b", "Col3": "c" }, { "Col1": "d", "Col2": "e", "Col3": "f" }],
expected: 'Col1,Col2,Col3\r\na,b,c\r\nd,e,f'
},
{
description: "With header row, missing a field in a row",
input: [{ "Col1": "a", "Col2": "b", "Col3": "c" }, { "Col1": "d", "Col3": "f" }],
expected: 'Col1,Col2,Col3\r\na,b,c\r\nd,,f'
},
{
description: "With header row, with extra field in a row",
notes: "Extra field should be ignored; first object in array dictates header row",
input: [{ "Col1": "a", "Col2": "b", "Col3": "c" }, { "Col1": "d", "Col2": "e", "Extra": "g", "Col3": "f" }],
expected: 'Col1,Col2,Col3\r\na,b,c\r\nd,e,f'
},
{
description: "Specifying column names and data separately",
input: { fields: ["Col1", "Col2", "Col3"], data: [["a", "b", "c"], ["d", "e", "f"]] },
expected: 'Col1,Col2,Col3\r\na,b,c\r\nd,e,f'
},
{
description: "Specifying column names only (no data)",
notes: "Papa should add a data property that is an empty array to prevent errors (no copy is made)",
input: { fields: ["Col1", "Col2", "Col3"] },
expected: 'Col1,Col2,Col3'
},
{
description: "Specifying data only (no field names), improperly",
notes: "A single array for a single row is wrong, but it can be compensated. Papa should add empty fields property to prevent errors.",
input: { data: ["abc", "d", "ef"] },
expected: 'abc,d,ef'
},
{
description: "Specifying data only (no field names), properly",
notes: "An array of arrays, even if just a single row. Papa should add empty fields property to prevent errors.",
input: { data: [["a", "b", "c"]] },
expected: 'a,b,c'
},
{
description: "Custom delimiter (semicolon)",
input: [['A', 'b', 'c'], ['d', 'e', 'f']],
config: { delimiter: ';' },
expected: 'A;b;c\r\nd;e;f'
},
{
description: "Custom delimiter (tab)",
input: [['Ab', 'cd', 'ef'], ['g', 'h', 'ij']],
config: { delimiter: '\t' },
expected: 'Ab\tcd\tef\r\ng\th\tij'
},
{
description: "Custom delimiter (ASCII 30)",
input: [['a', 'b', 'c'], ['d', 'e', 'f']],
config: { delimiter: RECORD_SEP },
expected: 'a' + RECORD_SEP + 'b' + RECORD_SEP + 'c\r\nd' + RECORD_SEP + 'e' + RECORD_SEP + 'f'
},
{
description: "Custom delimiter (Multi-character)",
input: [['A', 'b', 'c'], ['d', 'e', 'f']],
config: { delimiter: ', ' },
expected: 'A, b, c\r\nd, e, f'
},
{
description: "Custom delimiter (Multi-character), field contains custom delimiter",
input: [['A', 'b', 'c'], ['d', 'e', 'f, g']],
config: { delimiter: ', ' },
expected: 'A, b, c\r\nd, e, "f, g"'
},
{
description: "Bad delimiter (\\n)",
notes: "Should default to comma",
input: [['a', 'b', 'c'], ['d', 'e', 'f']],
config: { delimiter: '\n' },
expected: 'a,b,c\r\nd,e,f'
},
{
description: "Custom line ending (\\r)",
input: [['a', 'b', 'c'], ['d', 'e', 'f']],
config: { newline: '\r' },
expected: 'a,b,c\rd,e,f'
},
{
description: "Custom line ending (\\n)",
input: [['a', 'b', 'c'], ['d', 'e', 'f']],
config: { newline: '\n' },
expected: 'a,b,c\nd,e,f'
},
{
description: "Custom, but strange, line ending ($)",
input: [['a', 'b', 'c'], ['d', 'e', 'f']],
config: { newline: '$' },
expected: 'a,b,c$d,e,f'
},
{
description: "Force quotes around all fields",
input: [['a', 'b', 'c'], ['d', 'e', 'f']],
config: { quotes: true },
expected: '"a","b","c"\r\n"d","e","f"'
},
{
description: "Force quotes around all fields (with header row)",
input: [{ "Col1": "a", "Col2": "b", "Col3": "c" }, { "Col1": "d", "Col2": "e", "Col3": "f" }],
config: { quotes: true },
expected: '"Col1","Col2","Col3"\r\n"a","b","c"\r\n"d","e","f"'
},
{
description: "Force quotes around certain fields only",
input: [['a', 'b', 'c'], ['d', 'e', 'f']],
config: { quotes: [true, false, true] },
expected: '"a",b,"c"\r\n"d",e,"f"'
},
{
description: "Force quotes around certain fields only (with header row)",
input: [{ "Col1": "a", "Col2": "b", "Col3": "c" }, { "Col1": "d", "Col2": "e", "Col3": "f" }],
config: { quotes: [true, false, true] },
expected: '"Col1",Col2,"Col3"\r\n"a",b,"c"\r\n"d",e,"f"'
},
{
description: "Force quotes around string fields only",
input: [['a', 'b', 'c'], ['d', 10, true]],
config: { quotes: function(value) { return typeof value === 'string'; } },
expected: '"a","b","c"\r\n"d",10,true'
},
{
description: "Force quotes around string fields only (with header row)",
input: [{ "Col1": "a", "Col2": "b", "Col3": "c" }, { "Col1": "d", "Col2": 10, "Col3": true }],
config: { quotes: function(value) { return typeof value === 'string'; } },
expected: '"Col1","Col2","Col3"\r\n"a","b","c"\r\n"d",10,true'
},
{
description: "Empty input",
input: [],
expected: ''
},
{
description: "Mismatched field counts in rows",
input: [['a', 'b', 'c'], ['d', 'e'], ['f']],
expected: 'a,b,c\r\nd,e\r\nf'
},
{
description: "JSON null is treated as empty value",
input: [{ "Col1": "a", "Col2": null, "Col3": "c" }],
expected: 'Col1,Col2,Col3\r\na,,c'
},
{
description: "Custom quote character (single quote)",
input: [['a,d','b','c']],
config: { quoteChar: "'"},
expected: "'a,d',b,c"
},
{
description: "Don't print header if header:false option specified",
input: [{"Col1": "a", "Col2": "b", "Col3": "c"}, {"Col1": "d", "Col2": "e", "Col3": "f"}],
config: {header: false},
expected: 'a,b,c\r\nd,e,f'
},
{
description: "Date objects are exported in its ISO representation",
input: [{date: new Date("2018-05-04T21:08:03.269Z"), "not a date": 16}, {date: new Date("Tue May 08 2018 08:20:22 GMT-0700 (PDT)"), "not a date": 32}],
expected: 'date,not a date\r\n2018-05-04T21:08:03.269Z,16\r\n2018-05-08T15:20:22.000Z,32'
},
{
description: "Returns empty rows when empty rows are passed and skipEmptyLines is false",
input: [[null, ' '], [], ['1', '2']],
config: {skipEmptyLines: false},
expected: '," "\r\n\r\n1,2'
},
{
description: "Returns without empty rows when skipEmptyLines is true",
input: [[null, ' '], [], ['1', '2']],
config: {skipEmptyLines: true},
expected: '," "\r\n1,2'
},
{
description: "Returns without rows with no content when skipEmptyLines is 'greedy'",
input: [[null, ' '], [], ['1', '2']].concat(new Array(500000).fill(['', ''])).concat([['3', '4']]),
config: {skipEmptyLines: 'greedy'},
expected: '1,2\r\n3,4'
},
{
description: "Returns empty rows when empty rows are passed and skipEmptyLines is false with headers",
input: [{a: null, b: ' '}, {}, {a: '1', b: '2'}],
config: {skipEmptyLines: false, header: true},
expected: 'a,b\r\n," "\r\n\r\n1,2'
},
{
description: "Returns without empty rows when skipEmptyLines is true with headers",
input: [{a: null, b: ' '}, {}, {a: '1', b: '2'}],
config: {skipEmptyLines: true, header: true},
expected: 'a,b\r\n," "\r\n1,2'
},
{
description: "Returns without rows with no content when skipEmptyLines is 'greedy' with headers",
input: [{a: null, b: ' '}, {}, {a: '1', b: '2'}],
config: {skipEmptyLines: 'greedy', header: true},
expected: 'a,b\r\n1,2'
},
{
description: "Column option used to manually specify keys",
notes: "Should not throw any error when attempting to serialize key not present in object. Columns are different than keys of the first object. When an object is missing a key then the serialized value should be an empty string.",
input: [{a: 1, b: '2'}, {}, {a: 3, d: 'd', c: 4,}],
config: {columns: ['a', 'b', 'c']},
expected: 'a,b,c\r\n1,2,\r\n\r\n3,,4'
},
{
description: "Column option used to manually specify keys with input type object",
notes: "Should not throw any error when attempting to serialize key not present in object. Columns are different than keys of the first object. When an object is missing a key then the serialized value should be an empty string.",
input: { data: [{a: 1, b: '2'}, {}, {a: 3, d: 'd', c: 4,}] },
config: {columns: ['a', 'b', 'c']},
expected: 'a,b,c\r\n1,2,\r\n\r\n3,,4'
},
{
description: "Use different escapeChar",
input: [{a: 'foo', b: '"quoted"'}],
config: {header: false, escapeChar: '\\'},
expected: 'foo,"\\"quoted\\""'
},
{
description: "test defeault escapeChar",
input: [{a: 'foo', b: '"quoted"'}],
config: {header: false},
expected: 'foo,"""quoted"""'
},
{
description: "Escape formulae",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
config: { escapeFormulae: true },
expected: 'Col1,Col2,Col3\r\n"\'=danger","\'@danger",safe\r\nsafe=safe,"\'+danger","\'-danger, danger"\r\n\'+safe,\'@safe,"safe, safe"'
},
{
description: "Don't escape formulae by default",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
expected: 'Col1,Col2,Col3\r\n=danger,@danger,safe\r\nsafe=safe,+danger,"-danger, danger"\r\n\'+safe,\'@safe,"safe, safe"'
},
{
description: "Escape formulae with forced quotes",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
config: { escapeFormulae: true, quotes: true },
expected: '"Col1","Col2","Col3"\r\n"\'=danger","\'@danger","safe"\r\n"safe=safe","\'+danger","\'-danger, danger"\r\n"\'+safe","\'@safe","safe, safe"'
},
{
description: "Escape formulae with single-quote quoteChar and escapeChar",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
config: { escapeFormulae: true, quoteChar: "'", escapeChar: "'" },
expected: 'Col1,Col2,Col3\r\n\'\'\'=danger\',\'\'\'@danger\',safe\r\nsafe=safe,\'\'\'+danger\',\'\'\'-danger, danger\'\r\n\'\'+safe,\'\'@safe,\'safe, safe\''
},
{
description: "Escape formulae with single-quote quoteChar and escapeChar and forced quotes",
input: [{ "Col1": "=danger", "Col2": "@danger", "Col3": "safe" }, { "Col1": "safe=safe", "Col2": "+danger", "Col3": "-danger, danger" }, { "Col1": "'+safe", "Col2": "'@safe", "Col3": "safe, safe" }],
config: { escapeFormulae: true, quotes: true, quoteChar: "'", escapeChar: "'" },
expected: '\'Col1\',\'Col2\',\'Col3\'\r\n\'\'\'=danger\',\'\'\'@danger\',\'safe\'\r\n\'safe=safe\',\'\'\'+danger\',\'\'\'-danger, danger\'\r\n\'\'\'+safe\',\'\'\'@safe\',\'safe, safe\''
},
// new escapeFormulae values:
{
description: "Escape formulae with tab and carriage-return",
input: [{ "Col1": "\tdanger", "Col2": "\rdanger,", "Col3": "safe\t\r" }],
config: { escapeFormulae: true },
expected: 'Col1,Col2,Col3\r\n"\'\tdanger","\'\rdanger,","safe\t\r"'
},
{
description: "Escape formulae with tab and carriage-return, with forced quotes",
input: [{ "Col1": " danger", "Col2": "\rdanger,", "Col3": "safe\t\r" }],
config: { escapeFormulae: true, quotes: true },
expected: '"Col1","Col2","Col3"\r\n"\'\tdanger","\'\rdanger,","safe\t\r"'
},
{
description: "Escape formulae with tab and carriage-return, with single-quote quoteChar and escapeChar",
input: [{ "Col1": " danger", "Col2": "\rdanger,", "Col3": "safe, \t\r" }],
config: { escapeFormulae: true, quoteChar: "'", escapeChar: "'" },
expected: 'Col1,Col2,Col3\r\n\'\'\'\tdanger\',\'\'\'\rdanger,\',\'safe, \t\r\''
},
{
description: "Escape formulae with tab and carriage-return, with single-quote quoteChar and escapeChar and forced quotes",
input: [{ "Col1": " danger", "Col2": "\rdanger,", "Col3": "safe, \t\r" }],
config: { escapeFormulae: true, quotes: true, quoteChar: "'", escapeChar: "'" },
expected: '\'Col1\',\'Col2\',\'Col3\'\r\n\'\'\'\tdanger\',\'\'\'\rdanger,\',\'safe, \t\r\''
},
];
describe('Unparse Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function() {
var actual;
try {
actual = Papa.unparse(test.input, test.config);
} catch (e) {
if (e instanceof Error) {
throw e;
}
actual = e;
}
assert.strictEqual(actual, test.expected);
});
}
for (var i = 0; i < UNPARSE_TESTS.length; i++) {
generateTest(UNPARSE_TESTS[i]);
}
});
var CUSTOM_TESTS = [
{
description: "Pause and resume works (Regression Test for Bug #636)",
disabled: !XHR_ENABLED,
timeout: 30000,
expected: [2001, [
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Lorem ipsum dolor sit","42","ABC"],
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Etiam a dolor vitae est vestibulum","84"],
["Lorem ipsum dolor sit","42","ABC"],
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Etiam a dolor vitae est vestibulum","84","DEF"],
["Lorem ipsum dolor sit","42","ABC"],
["Lorem ipsum dolor sit","42"]
], 0],
run: function(callback) {
var stepped = 0;
var dataRows = [];
var errorCount = 0;
var output = [];
Papa.parse(BASE_PATH + "verylong-sample.csv", {
download: true,
step: function(results, parser) {
stepped++;
if (results)
{
parser.pause();
parser.resume();
if (results.data && stepped % 200 === 0) {
dataRows.push(results.data);
}
}
},
complete: function() {
output.push(stepped);
output.push(dataRows);
output.push(errorCount);
callback(output);
}
});
}
},
{
description: "Pause and resume works for chunks with NetworkStreamer",
disabled: !XHR_ENABLED,
timeout: 30000,
expected: ["Etiam a dolor vitae est vestibulum", "84", "DEF"],
run: function(callback) {
var chunkNum = 0;
Papa.parse(BASE_PATH + "verylong-sample.csv", {
download: true,
chunkSize: 1000,
chunk: function(results, parser) {
chunkNum++;
parser.pause();
if (chunkNum === 2) {
callback(results.data[0]);
return;
}
parser.resume();
},
complete: function() {
callback(new Error("Should have found matched row before parsing whole file"));
}
});
}
},
{
description: "Pause and resume works for chunks with FileStreamer",
disabled: !XHR_ENABLED,
timeout: 30000,
expected: ["Etiam a dolor vitae est vestibulum", "84", "DEF"],
run: function(callback) {
var chunkNum = 0;
var xhr = new XMLHttpRequest();
xhr.onload = function() {
Papa.parse(new File([xhr.responseText], './verylong-sample.csv'), {
chunkSize: 1000,
chunk: function(results, parser) {
chunkNum++;
parser.pause();
if (chunkNum === 2) {
callback(results.data[0]);
return;
}
parser.resume();
},
complete: function() {
callback(new Error("Should have found matched row before parsing whole file"));
}
});
};
xhr.open("GET", BASE_PATH + "verylong-sample.csv");
try {
xhr.send();
} catch (err) {
callback(err);
return;
}
}
},
{
description: "Pause and resume works for chunks with StringStreamer",
disabled: !XHR_ENABLED,
timeout: 30000,
// Test also with string as byte size may be diferent
expected: ["Etiam a dolor vitae est vestibulum", "84", "DEF"],
run: function(callback) {
var chunkNum = 0;
var xhr = new XMLHttpRequest();
xhr.onload = function() {
Papa.parse(xhr.responseText, {
chunkSize: 1000,
chunk: function(results, parser) {
chunkNum++;
parser.pause();
if (chunkNum === 2) {
callback(results.data[0]);
return;
}
parser.resume();
},
complete: function() {
callback(new Error("Should have found matched row before parsing whole file"));
}
});
};
xhr.open("GET", BASE_PATH + "verylong-sample.csv");
try {
xhr.send();
} catch (err) {
callback(err);
return;
}
}
},
{
description: "Complete is called with all results if neither step nor chunk is defined",
expected: [['A', 'b', 'c'], ['d', 'E', 'f'], ['G', 'h', 'i']],
disabled: !FILES_ENABLED,
run: function(callback) {
Papa.parse(new File(['A,b,c\nd,E,f\nG,h,i'], 'sample.csv'), {
chunkSize: 3,
complete: function(response) {
callback(response.data);
}
});
}
},
{
description: "Step is called for each row",
expected: 2,
run: function(callback) {
var callCount = 0;
Papa.parse('A,b,c\nd,E,f', {
step: function() {
callCount++;
},
complete: function() {
callback(callCount);
}
});
}
},
{
description: "Data is correctly parsed with steps",
expected: [['A', 'b', 'c'], ['d', 'E', 'f']],
run: function(callback) {
var data = [];
Papa.parse('A,b,c\nd,E,f', {
step: function(results) {
data.push(results.data);
},
complete: function() {
callback(data);
}
});
}
},
{
description: "Data is correctly parsed with steps (headers)",
expected: [{One: 'A', Two: 'b', Three: 'c'}, {One: 'd', Two: 'E', Three: 'f'}],
run: function(callback) {
var data = [];
Papa.parse('One,Two,Three\nA,b,c\nd,E,f', {
header: true,
step: function(results) {
data.push(results.data);
},
complete: function() {
callback(data);
}
});
}
},
{
description: "Data is correctly parsed with steps and worker (headers)",
expected: [{One: 'A', Two: 'b', Three: 'c'}, {One: 'd', Two: 'E', Three: 'f'}],
run: function(callback) {
var data = [];
Papa.parse('One,Two,Three\nA,b,c\nd,E,f', {
header: true,
worker: true,
step: function(results) {
data.push(results.data);
},
complete: function() {
callback(data);
}
});
}
},
{
description: "Data is correctly parsed with steps and worker",
expected: [['A', 'b', 'c'], ['d', 'E', 'f']],
run: function(callback) {
var data = [];
Papa.parse('A,b,c\nd,E,f', {
worker: true,
step: function(results) {
data.push(results.data);
},
complete: function() {
callback(data);
}
});
}
},
{
description: "Data is correctly parsed with steps when skipping empty lines",
expected: [['A', 'b', 'c'], ['d', 'E', 'f']],
run: function(callback) {
var data = [];
Papa.parse('A,b,c\n\nd,E,f', {
skipEmptyLines: true,
step: function(results) {
data.push(results.data);
},
complete: function() {
callback(data);
}
});
}
},
{
description: "Data is correctly parsed with steps when there are empty values",
expected: [{A: 'a', B: 'b', C: 'c', D: 'd'}, {A: 'a', B: '', C: '', D: ''}],
run: function(callback) {
var data = [];
Papa.parse('A,B,C,D\na,b,c,d\na,,,', {
header: true,
step: function(results) {
data.push(results.data);
},
complete: function() {
callback(data);
}
});
}
},
{
description: "Step is called with the contents of the row",
expected: ['A', 'b', 'c'],
run: function(callback) {
Papa.parse('A,b,c', {
step: function(response) {
callback(response.data);
}
});
}
},
{
description: "Step is called with the last cursor position",
expected: [6, 12, 17],
run: function(callback) {
var updates = [];
Papa.parse('A,b,c\nd,E,f\nG,h,i', {
step: function(response) {
updates.push(response.meta.cursor);
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Step exposes cursor for downloads",
expected: [129, 287, 452, 595, 727, 865, 1031, 1209],
disabled: !XHR_ENABLED,
run: function(callback) {
var updates = [];
Papa.parse(BASE_PATH + "long-sample.csv", {
download: true,
step: function(response) {
updates.push(response.meta.cursor);
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Step exposes cursor for chunked downloads",
expected: [129, 287, 452, 595, 727, 865, 1031, 1209],
disabled: !XHR_ENABLED,
run: function(callback) {
var updates = [];
Papa.parse(BASE_PATH + "long-sample.csv", {
download: true,
chunkSize: 500,
step: function(response) {
updates.push(response.meta.cursor);
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Step exposes cursor for workers",
expected: [452, 452, 452, 865, 865, 865, 1209, 1209],
disabled: !XHR_ENABLED,
run: function(callback) {
var updates = [];
Papa.parse(BASE_PATH + "long-sample.csv", {
download: true,
chunkSize: 500,
worker: true,
step: function(response) {
updates.push(response.meta.cursor);
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Chunk is called for each chunk",
expected: [3, 3, 2],
disabled: !XHR_ENABLED,
run: function(callback) {
var updates = [];
Papa.parse(BASE_PATH + "long-sample.csv", {
download: true,
chunkSize: 500,
chunk: function(response) {
updates.push(response.data.length);
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Chunk is called with cursor position",
expected: [452, 865, 1209],
disabled: !XHR_ENABLED,
run: function(callback) {
var updates = [];
Papa.parse(BASE_PATH + "long-sample.csv", {
download: true,
chunkSize: 500,
chunk: function(response) {
updates.push(response.meta.cursor);
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Chunk functions can pause parsing",
expected: [
[['A', 'b', 'c']]
],
run: function(callback) {
var updates = [];
Papa.parse('A,b,c\nd,E,f\nG,h,i', {
chunkSize: 10,
chunk: function(response, handle) {
updates.push(response.data);
handle.pause();
callback(updates);
},
complete: function() {
callback(new Error('incorrect complete callback'));
}
});
}
},
{
description: "Chunk functions can resume parsing",
expected: [
[['A', 'b', 'c']],
[['d', 'E', 'f'], ['G', 'h', 'i']]
],
run: function(callback) {
var updates = [];
var handle = null;
var first = true;
Papa.parse('A,b,c\nd,E,f\nG,h,i', {
chunkSize: 10,
chunk: function(response, h) {
updates.push(response.data);
if (!first) return;
handle = h;
handle.pause();
first = false;
},
complete: function() {
callback(updates);
}
});
setTimeout(function() {
handle.resume();
}, 500);
}
},
{
description: "Chunk functions can abort parsing",
expected: [
[['A', 'b', 'c']]
],
run: function(callback) {
var updates = [];
Papa.parse('A,b,c\nd,E,f\nG,h,i', {
chunkSize: 1,
chunk: function(response, handle) {
if (response.data.length) {
updates.push(response.data);
handle.abort();
}
},
complete: function(response) {
callback(updates);
}
});
}
},
{
description: "Step exposes indexes for files",
expected: [6, 12, 17],
disabled: !FILES_ENABLED,
run: function(callback) {
var updates = [];
Papa.parse(new File(['A,b,c\nd,E,f\nG,h,i'], 'sample.csv'), {
download: true,
step: function(response) {
updates.push(response.meta.cursor);
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Step exposes indexes for chunked files",
expected: [6, 12, 17],
disabled: !FILES_ENABLED,
run: function(callback) {
var updates = [];
Papa.parse(new File(['A,b,c\nd,E,f\nG,h,i'], 'sample.csv'), {
chunkSize: 3,
step: function(response) {
updates.push(response.meta.cursor);
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Quoted line breaks near chunk boundaries are handled",
expected: [['A', 'B', 'C'], ['X', 'Y\n1\n2\n3', 'Z']],
disabled: !FILES_ENABLED,
run: function(callback) {
var updates = [];
Papa.parse(new File(['A,B,C\nX,"Y\n1\n2\n3",Z'], 'sample.csv'), {
chunkSize: 3,
step: function(response) {
updates.push(response.data);
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Step functions can abort parsing",
expected: [['A', 'b', 'c']],
run: function(callback) {
var updates = [];
Papa.parse('A,b,c\nd,E,f\nG,h,i', {
step: function(response, handle) {
updates.push(response.data);
handle.abort();
callback(updates);
},
chunkSize: 6
});
}
},
{
description: "Complete is called after aborting",
expected: true,
run: function(callback) {
Papa.parse('A,b,c\nd,E,f\nG,h,i', {
step: function(response, handle) {
handle.abort();
},
chunkSize: 6,
complete: function(response) {
callback(response.meta.aborted);
}
});
}
},
{
description: "Step functions can pause parsing",
expected: [['A', 'b', 'c']],
run: function(callback) {
var updates = [];
Papa.parse('A,b,c\nd,E,f\nG,h,i', {
step: function(response, handle) {
updates.push(response.data);
handle.pause();
callback(updates);
},
complete: function() {
callback('incorrect complete callback');
}
});
}
},
{
description: "Step functions can resume parsing",
expected: [['A', 'b', 'c'], ['d', 'E', 'f'], ['G', 'h', 'i']],
run: function(callback) {
var updates = [];
var handle = null;
var first = true;
Papa.parse('A,b,c\nd,E,f\nG,h,i', {
step: function(response, h) {
updates.push(response.data);
if (!first) return;
handle = h;
handle.pause();
first = false;
},
complete: function() {
callback(updates);
}
});
setTimeout(function() {
handle.resume();
}, 500);
}
},
{
description: "Step functions can abort workers",
expected: 1,
disabled: !XHR_ENABLED,
run: function(callback) {
var updates = 0;
Papa.parse(BASE_PATH + "long-sample.csv", {
worker: true,
download: true,
chunkSize: 500,
step: function(response, handle) {
updates++;
handle.abort();
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "beforeFirstChunk manipulates only first chunk",
expected: 7,
disabled: !XHR_ENABLED,
run: function(callback) {
var updates = 0;
Papa.parse(BASE_PATH + "long-sample.csv", {
download: true,
chunkSize: 500,
beforeFirstChunk: function(chunk) {
return chunk.replace(/.*?\n/, '');
},
step: function(response) {
updates++;
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "First chunk not modified if beforeFirstChunk returns nothing",
expected: 8,
disabled: !XHR_ENABLED,
run: function(callback) {
var updates = 0;
Papa.parse(BASE_PATH + "long-sample.csv", {
download: true,
chunkSize: 500,
beforeFirstChunk: function(chunk) {
},
step: function(response) {
updates++;
},
complete: function() {
callback(updates);
}
});
}
},
{
description: "Should correctly guess custom delimiter when passed delimiters to guess.",
expected: "~",
run: function(callback) {
var results = Papa.parse('"A"~"B"~"C"~"D"', {
delimitersToGuess: ['~', '@', '%']
});
callback(results.meta.delimiter);
}
},
{
description: "Should still correctly guess default delimiters when delimiters to guess are not given.",
expected: ",",
run: function(callback) {
var results = Papa.parse('"A","B","C","D"');
callback(results.meta.delimiter);
}
},
{
description: "Data is correctly parsed with chunks and duplicated headers",
expected: [{h0: 'a', h1: 'a'}, {h0: 'b', h1: 'b'}],
run: function(callback) {
var data = [];
Papa.parse('h0,h1\na,a\nb,b', {
header: true,
chunkSize: 10,
chunk: function(results) {
data.push(results.data[0]);
},
complete: function() {
callback(data);
}
});
}
},
];
describe('Custom Tests', function() {
function generateTest(test) {
(test.disabled ? it.skip : it)(test.description, function(done) {
if(test.timeout) {
this.timeout(test.timeout);
}
test.run(function(actual) {
assert.deepEqual(actual, test.expected);
done();
});
});
}
for (var i = 0; i < CUSTOM_TESTS.length; i++) {
generateTest(CUSTOM_TESTS[i]);
}
});
(typeof window !== "undefined" ? describe : describe.skip)("Browser Tests", () => {
it("When parsing synchronously inside a web-worker not owned by PapaParse we should not invoke postMessage", async() => {
// Arrange
const papaParseScriptPath = new URL("../papaparse.js", window.document.baseURI).href;
// Define our custom web-worker that loads PapaParse and executes a synchronous parse
const blob = new Blob([
`
importScripts('${papaParseScriptPath}');
self.addEventListener("message", function(event) {
if (event.data === "ExecuteParse") {
// Perform our synchronous parse, as requested
const results = Papa.parse('x\\ny\\n');
postMessage({type: "ParseExecutedSuccessfully", results});
} else {
// Otherwise, send whatever we received back. We shouldn't be hitting this (!) If we're reached
// this it means PapaParse thinks it is running inside a web-worker that it owns
postMessage(event.data);
}
});
`
], {type: 'text/javascript'});
const blobURL = window.URL.createObjectURL(blob);
const webWorker = new Worker(blobURL);
const receiveMessagePromise = new Promise((resolve, reject) => {
webWorker.addEventListener("message", (event) => {
if (event.data.type === "ParseExecutedSuccessfully") {
resolve(event.data);
} else {
const error = new Error(`Received unexpected message: ${JSON.stringify(event.data, null, 2)}`);
error.data = event.data;
reject(error);
}
});
});
// Act
webWorker.postMessage("ExecuteParse");
const webWorkerMessage = await receiveMessagePromise;
// Assert
assert.equal("ParseExecutedSuccessfully", webWorkerMessage.type);
assert.equal(3, webWorkerMessage.results.data.length);
});
});
================================================
FILE: tests/test.js
================================================
var connect = require('connect');
var serveStatic = require('serve-static');
var open = require('open');
var path = require('path');
var childProcess = require('child_process');
var server = connect().use(serveStatic(path.join(__dirname, '/..'))).listen(8071, function() {
if (process.argv.indexOf('--mocha-headless-chrome') !== -1) {
childProcess.spawn('node_modules/.bin/mocha-headless-chrome', ['-f', 'http://localhost:8071/tests/tests.html'], {
stdio: 'inherit'
}).on('exit', function(code) {
server.close();
process.exit(code); // eslint-disable-line no-process-exit
});
} else {
open('http://localhost:8071/tests/tests.html');
console.log('Serving tests...');
}
});
================================================
FILE: tests/tests.html
================================================
Papa Parse Tests
================================================
FILE: tests/utf-8-bom-sample.csv
================================================
A,B,C
X,Y,Z
================================================
FILE: tests/verylong-sample.csv
================================================
placeholder,meaning of life,TLD
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
"Lorem ipsum dolor sit",42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42,ABC
Lorem ipsum dolor sit,42,ABC
Etiam a dolor vitae est vestibulum,84,DEF
Lorem ipsum dolor sit,42
Lorem ipsum dolor sit,42,ABC
Comments
"I forgot to mention: my CSV files have comments in them."
Okay, first off: that's really weird. But fortunately, you can skip those lines... just specify the comment string.