@@ -19,7 +19,7 @@ var retrieveHeading = function (lines, callback) {
1919 }
2020
2121 // Generate and return the heading keys
22- return _ . map ( lines [ 0 ] . split ( options . DELIMITER . FIELD ) ,
22+ return _ . map ( splitLine ( lines [ 0 ] ) ,
2323 function ( headerKey , index ) {
2424 return {
2525 value : headerKey ,
@@ -64,7 +64,7 @@ var convertArrayRepresentation = function (arrayRepresentation) {
6464 * @returns {Object } created json document
6565 */
6666var createDocument = function ( keys , line ) {
67- var line = line . split ( options . DELIMITER . FIELD ) , // Split the line using the given field delimiter after trimming whitespace
67+ var line = splitLine ( line ) , // Split the line using the given field delimiter after trimming whitespace
6868 val ; // Temporary variable to set the current key's value to
6969
7070 // Reduce the keys into a JSON document representing the given line
@@ -102,6 +102,83 @@ var convertCSV = function (lines, callback) {
102102 } , [ ] ) ;
103103} ;
104104
105+ /**
106+ * Helper function that splits a line so that we can handle wrapped fields
107+ * @param line
108+ */
109+ var splitLine = function ( line ) {
110+ // If the fields are not wrapped, return the line split by the field delimiter
111+ if ( ! options . DELIMITER . WRAP ) { return line . split ( options . DELIMITER . FIELD ) ; }
112+
113+ // Parse out the line...
114+ var splitLine = [ ] ,
115+ character ,
116+ charBefore ,
117+ charAfter ,
118+ lastCharacterIndex = line . length - 1 ,
119+ stateVariables = {
120+ insideWrapDelimiter : false ,
121+ parsingValue : true ,
122+ startIndex : 0
123+ } ,
124+ index = 0 ;
125+
126+ // Loop through each character in the line to identify where to split the values
127+ while ( index < line . length ) {
128+ // Current character
129+ character = line [ index ] ;
130+ // Previous character
131+ charBefore = index ? line [ index - 1 ] : '' ;
132+ // Next character
133+ charAfter = index < lastCharacterIndex ? line [ index + 1 ] : '' ;
134+
135+ // If we reached the end of the line, add the remaining value
136+ if ( index === lastCharacterIndex ) {
137+ splitLine . push ( line . substring ( stateVariables . startIndex , stateVariables . insideWrapDelimiter ? index : undefined ) ) ;
138+ }
139+ // If the line starts with a wrap delimiter
140+ else if ( character === options . DELIMITER . WRAP && index === 0 ) {
141+ stateVariables . insideWrapDelimiter = true ;
142+ stateVariables . parsingValue = true ;
143+ stateVariables . startIndex = index + 1 ;
144+ }
145+
146+ // If we reached a wrap delimiter with a field delimiter after it (ie. *",)
147+ else if ( character === options . DELIMITER . WRAP && charAfter === options . DELIMITER . FIELD ) {
148+ splitLine . push ( line . substring ( stateVariables . startIndex , index ) ) ;
149+ stateVariables . startIndex = index + 2 ; // next value starts after the field delimiter
150+ stateVariables . insideWrapDelimiter = false ;
151+ stateVariables . parsingValue = false ;
152+ }
153+ // If we reached a wrap delimiter with a field delimiter after it (ie. ,"*)
154+ else if ( character === options . DELIMITER . WRAP && charBefore === options . DELIMITER . FIELD ) {
155+ if ( stateVariables . parsingValue ) {
156+ splitLine . push ( line . substring ( stateVariables . startIndex , index - 1 ) ) ;
157+ }
158+ stateVariables . insideWrapDelimiter = true ;
159+ stateVariables . parsingValue = true ;
160+ stateVariables . startIndex = index + 1 ;
161+ }
162+ // If we reached a field delimiter and are not inside the wrap delimiters (ie. *,*)
163+ else if ( character === options . DELIMITER . FIELD && charBefore !== options . DELIMITER . WRAP
164+ && charAfter !== options . DELIMITER . WRAP && ! stateVariables . insideWrapDelimiter
165+ && stateVariables . parsingValue ) {
166+ splitLine . push ( line . substring ( stateVariables . startIndex , index ) ) ;
167+ stateVariables . startIndex = index + 1 ;
168+ }
169+ else if ( character === options . DELIMITER . FIELD && charBefore === options . DELIMITER . WRAP
170+ && charAfter !== options . DELIMITER . WRAP ) {
171+ stateVariables . insideWrapDelimiter = false ;
172+ stateVariables . parsingValue = true ;
173+ stateVariables . startIndex = index + 1 ;
174+ }
175+ // Otherwise increment to the next character
176+ index ++ ;
177+ }
178+
179+ return splitLine ;
180+ } ;
181+
105182module . exports = {
106183
107184 /**
@@ -116,11 +193,11 @@ module.exports = {
116193 if ( ! callback ) { throw new Error ( constants . Errors . callbackRequired ) ; }
117194
118195 // Shouldn't happen, but just in case
119- if ( ! opts ) { return callback ( new Error ( constants . Errors . optionsRequired ) ) ; return null ; }
196+ if ( ! opts ) { return callback ( new Error ( constants . Errors . optionsRequired ) ) ; }
120197 options = opts ; // Options were passed, set the global options value
121198
122199 // If we don't receive data, report an error
123- if ( ! data ) { return callback ( new Error ( constants . Errors . csv2json . cannotCallCsv2JsonOn + data + '.' ) ) ; return null ; }
200+ if ( ! data ) { return callback ( new Error ( constants . Errors . csv2json . cannotCallCsv2JsonOn + data + '.' ) ) ; }
124201
125202 // The data provided is not a string
126203 if ( ! _ . isString ( data ) ) {
0 commit comments