/* 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