From 995a0534006e27d6066a485c46e09661f6673d87 Mon Sep 17 00:00:00 2001 From: Rufus Pollock Date: Tue, 27 Mar 2012 08:27:10 +0100 Subject: [PATCH] [#36,backend/localcsv][s]: start on local 'backend' with CSV parser and test. --- src/backend/localcsv.js | 145 ++++++++++++++++++++++++++++++++++ test/backend.localcsv.test.js | 24 ++++++ 2 files changed, 169 insertions(+) create mode 100644 src/backend/localcsv.js create mode 100644 test/backend.localcsv.test.js diff --git a/src/backend/localcsv.js b/src/backend/localcsv.js new file mode 100644 index 00000000..7efb9a28 --- /dev/null +++ b/src/backend/localcsv.js @@ -0,0 +1,145 @@ +this.recline = this.recline || {}; +this.recline.Backend = this.recline.Backend || {}; + +(function($, my) { + my.loadFromCSVFile = function(file) { + var metadata = { + id: file.name, + file: file + }; + var reader = new FileReader(); + // TODO + reader.onload = function(e) { + // console.log(e.target.result); + }; + reader.onerror = function (e) { + alert('Failed to load file. Code: ' + e.target.error.code); + } + reader.readAsText(file); + }; + + // Converts a Comma Separated Values string into an array of arrays. + // Each line in the CSV becomes an array. + // + // Empty fields are converted to nulls and non-quoted numbers are converted to integers or floats. + // + // @return The CSV parsed as an array + // @type Array + // + // @param {String} s The string to convert + // @param {Boolean} [trm=false] If set to True leading and trailing whitespace is stripped off of each non-quoted field as it is imported + // + // Heavily based on uselesscode's JS CSV parser (MIT Licensed): + // thttp://www.uselesscode.org/javascript/csv/ + my.parseCSV= function(s, trm) { + // Get rid of any trailing \n + s = chomp(s); + + var cur = '', // The character we are currently processing. + inQuote = false, + fieldQuoted = false, + field = '', // Buffer for building up the current field + row = [], + out = [], + i, + processField; + + processField = function (field) { + if (fieldQuoted !== true) { + // If field is empty set to null + if (field === '') { + field = null; + // If the field was not quoted and we are trimming fields, trim it + } else if (trm === true) { + field = trim(field); + } + + // Convert unquoted numbers to their appropriate types + if (rxIsInt.test(field)) { + field = parseInt(field, 10); + } else if (rxIsFloat.test(field)) { + field = parseFloat(field, 10); + } + } + return field; + }; + + for (i = 0; i < s.length; i += 1) { + cur = s.charAt(i); + + // If we are at a EOF or EOR + if (inQuote === false && (cur === ',' || cur === "\n")) { + field = processField(field); + // Add the current field to the current row + row.push(field); + // If this is EOR append row to output and flush row + if (cur === "\n") { + out.push(row); + row = []; + } + // Flush the field buffer + field = ''; + fieldQuoted = false; + } else { + // If it's not a ", add it to the field buffer + if (cur !== '"') { + field += cur; + } else { + if (!inQuote) { + // We are not in a quote, start a quote + inQuote = true; + fieldQuoted = true; + } else { + // Next char is ", this is an escaped " + if (s.charAt(i + 1) === '"') { + field += '"'; + // Skip the next char + i += 1; + } else { + // It's not escaping, so end quote + inQuote = false; + } + } + } + } + } + + // Add the last field + field = processField(field); + row.push(field); + out.push(row); + + return out; + }; + + var rxIsInt = /^\d+$/, + rxIsFloat = /^\d*\.\d+$|^\d+\.\d*$/, + // If a string has leading or trailing space, + // contains a comma double quote or a newline + // it needs to be quoted in CSV output + rxNeedsQuoting = /^\s|\s$|,|"|\n/, + trim = (function () { + // Fx 3.1 has a native trim function, it's about 10x faster, use it if it exists + if (String.prototype.trim) { + return function (s) { + return s.trim(); + }; + } else { + return function (s) { + return s.replace(/^\s*/, '').replace(/\s*$/, ''); + }; + } + }()); + + function chomp(s) { + if (s.charAt(s.length - 1) !== "\n") { + // Does not end with \n, just return string + return s; + } else { + // Remove the \n + return s.substring(0, s.length - 1); + } + } + + +}(jQuery, this.recline.Backend)); diff --git a/test/backend.localcsv.test.js b/test/backend.localcsv.test.js new file mode 100644 index 00000000..c37fc138 --- /dev/null +++ b/test/backend.localcsv.test.js @@ -0,0 +1,24 @@ +(function ($) { +module("Backend Local CSV"); + +test("parseCSV", function() { + var csv = '"Jones, Jay",10\n' + + '"Xyz ""ABC"" O\'Brien",11:35\n' + + '"Other, AN",12:35\n'; + + var array = recline.Backend.parseCSV(csv); + var exp = [ + ['Jones, Jay', 10], + ['Xyz "ABC" O\'Brien', '11:35' ], + ['Other, AN', '12:35' ] + ]; + deepEqual(exp, array); + + var csv = '"Jones, Jay", 10\n' + + '"Xyz ""ABC"" O\'Brien", 11:35\n' + + '"Other, AN", 12:35\n'; + var array = recline.Backend.parseCSV(csv, true); + deepEqual(exp, array); +}); + +})(this.jQuery);