@@ -19,7 +19,7 @@ var retrieveHeading = function (lines, callback) {
19
19
}
20
20
21
21
// Generate and return the heading keys
22
- return _ . map ( lines [ 0 ] . split ( options . DELIMITER . FIELD ) ,
22
+ return _ . map ( splitLine ( lines [ 0 ] ) ,
23
23
function ( headerKey , index ) {
24
24
return {
25
25
value : headerKey ,
@@ -64,7 +64,7 @@ var convertArrayRepresentation = function (arrayRepresentation) {
64
64
* @returns {Object } created json document
65
65
*/
66
66
var 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
68
68
val ; // Temporary variable to set the current key's value to
69
69
70
70
// Reduce the keys into a JSON document representing the given line
@@ -102,6 +102,83 @@ var convertCSV = function (lines, callback) {
102
102
} , [ ] ) ;
103
103
} ;
104
104
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
+
105
182
module . exports = {
106
183
107
184
/**
@@ -116,11 +193,11 @@ module.exports = {
116
193
if ( ! callback ) { throw new Error ( constants . Errors . callbackRequired ) ; }
117
194
118
195
// 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 ) ) ; }
120
197
options = opts ; // Options were passed, set the global options value
121
198
122
199
// 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 + '.' ) ) ; }
124
201
125
202
// The data provided is not a string
126
203
if ( ! _ . isString ( data ) ) {
0 commit comments