-
Notifications
You must be signed in to change notification settings - Fork 2
/
jquery.csv.js
186 lines (162 loc) · 7.6 KB
/
jquery.csv.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* Usage:
* jQuery.csv()(csvtext) returns an array of arrays representing the CSV text.
* jQuery.csv("\t")(tsvtext) uses Tab as a delimiter (comma is the default)
* jQuery.csv("\t", "'")(tsvtext) uses single quotes as the quote character (double quotes is the default)
* jQuery.csv("\t", "'\"")(tsvtext) uses single & double quotes as the quote character
* Note: Use jQuery.csv(delim, 0) to speed up loading, if there are no quotes. Use with care: there might be quotes
*
* jQuery.csv2json()(csvtext) returns an array of hashes, whose keys are from the header row.
* Otherwise, syntax identical to jQuery.csv()
*/
(function($) {
// split() doesn't work properly on IE. "a,,b".split(",") returns ["a", "b"] and not ["a", "", "b"]
// On IE, you need to fix String.prototype.split first. See http://blog.stevenlevithan.com/archives/cross-browser-split
if ('a,,b'.split(',').length < 3) {
var nativeSplit = nativeSplit || String.prototype.split;
String.prototype.split = function (s /* separator */, limit) {
// If separator is not a regex, use the native split method
if (!(s instanceof RegExp)) {
return nativeSplit.apply(this, arguments);
}
/* Behavior for limit: If it's...
- Undefined: No limit
- NaN or zero: Return an empty array
- A positive number: Use limit after dropping any decimal
- A negative number: No limit
- Other: Type-convert, then use the above rules */
if (limit === undefined || +limit < 0) {
limit = false;
} else {
limit = Math.floor(+limit);
if (!limit) {
return [];
}
}
var flags = (s.global ? "g" : "") + (s.ignoreCase ? "i" : "") + (s.multiline ? "m" : ""),
s2 = new RegExp("^" + s.source + "$", flags),
output = [],
lastLastIndex = 0,
i = 0,
match;
if (!s.global) {
s = new RegExp(s.source, "g" + flags);
}
while ((!limit || i++ <= limit) && (match = s.exec(this))) {
var zeroLengthMatch = !match[0].length;
// Fix IE's infinite-loop-resistant but incorrect lastIndex
if (zeroLengthMatch && s.lastIndex > match.index) {
s.lastIndex = match.index; // The same as s.lastIndex--
}
if (s.lastIndex > lastLastIndex) {
// Fix browsers whose exec methods don't consistently return undefined for non-participating capturing groups
if (match.length > 1) {
match[0].replace(s2, function () {
for (var j = 1; j < arguments.length - 2; j++) {
if (arguments[j] === undefined) { match[j] = undefined; }
}
});
}
output = output.concat(this.slice(lastLastIndex, match.index), (match.index === this.length ? [] : match.slice(1)));
lastLastIndex = s.lastIndex;
}
if (zeroLengthMatch) {
s.lastIndex++;
}
}
return (lastLastIndex === this.length) ?
(s.test("") ? output : output.concat("")) :
(limit ? output : output.concat(this.slice(lastLastIndex)));
};
}
// Returns a function that splits a line into fields using a simple delimiter
// delimre is the regular expression for the delimiter, e.g. /,/
function simple_splitline(delimre) {
return function(v) { return v.split(delimre); };
}
// Returns a function that splits a line into fields using a delimiter, and accounts for quotes.
// For example, quoted_splitline(/,/, /"/, /""/) will break the following line:
// a,"b,c",d,"e,""f"",g"
// into
// ['a', 'b,c', 'd', 'e,"f",g']
function quoted_splitline(delim, delimre, quotere, doublequotere) {
return function(v) {
var arr = v.split(delimre);
for (var out=[], q, i=0, l=arr.length; i<l; i++) {
// If the value is within quotes, then from the point where the quote begins ...
if (q = arr[i].match(quotere)) {
q = q[0];
// ... to the point where the quote ends
for (var j=i; j<l; j++) {
if (arr[j].charAt(arr[j].length-1) == q) { break; }
}
// Join the pieces into a single piece
var s = arr.slice(i,j+1).join(delim);
// Double quoting is the escape sequence for quotes. ("" instead of ")
s = s.replace(doublequotere[q], q);
// Use this joined piece instead of the individual pieces
out.push(s.substr(1,s.length-2));
i = j;
}
else {
out.push(arr[i]);
}
}
return out;
};
}
function parse_param(delim, quote, lined) {
var undef = 'undefined';
delim = typeof delim == undef ? ',' : delim; // delimiter is comma by default
quote = typeof quote == undef ? '"' : quote; // quotes mark is "double quotes" by default
lined = typeof lined == undef ? '\r\n' : lined; // line delimiter is \r or \n or both
var quotes = quote ? quote.split('') : [], // get each character in the quote
delimre = new RegExp( '[' + delim + ']' ),
quotere = new RegExp('^[' + quote + ']' );
for (var i=0, doublequotere={}, q; q=quotes[i]; i++) { // and make a dictionary of doublequote possibilities
doublequotere[q] = new RegExp(q + q, 'g');
}
return [
new RegExp( '[' + lined + ']*$' ), // trailing line delimiter
new RegExp( '[' + lined + '][' + lined + ']*'), // line delimiter,
quote ? quoted_splitline(delim, delimre, quotere, doublequotere) : // splitline function
simple_splitline(delimre)
];
}
jQuery.extend({
csv: function(delim, quote, lined) {
var param = parse_param(delim, quote, lined),
trailing = param[0],
linedre = param[1],
splitline = param[2];
// The main function. Split into lines, and then call splitline repeatedly.
return function(text) {
var lines = text.replace(trailing, '').split(linedre);
for (var i=0, l=lines.length; i<l; i++) {
lines[i] = splitline(lines[i]);
}
return lines;
};
},
csv2json: function(delim, quote, lined) {
var param = parse_param(delim, quote, lined),
trailing = param[0],
linedre = param[1],
splitline = param[2];
// The main function. Split into lines, and then call splitline repeatedly.
return function(text) {
var lines = text.replace(trailing, '').split(linedre), // split into lines
header = splitline(lines[0]), // and get the first row (header)
nfields = header.length,
out = []; // put the results into this array
for (var i=1, l=lines.length; i<l; i++) {
var line = splitline(lines[i]); // split each subsequent row
for (var j=0, result={}; j<nfields; j++) { // and make it a hash of fields, using the header
result[header[j]] = line[j];
}
out.push(result); // add the hash
}
return out;
};
}
});
})(jQuery);