|
|
|
/*
|
|
|
|
jQuery Parse plugin
|
|
|
|
v0.5.0
|
|
|
|
https://github.com/mholt/jquery.parse
|
|
|
|
*/
|
|
|
|
|
|
|
|
(function($)
|
|
|
|
{
|
|
|
|
var defaults = {
|
|
|
|
delimiter: ",",
|
|
|
|
header: true
|
|
|
|
};
|
|
|
|
|
|
|
|
$.parse = function(input, options)
|
|
|
|
{
|
|
|
|
options = verifyOptions(options);
|
|
|
|
var parser = new Parser(input, options);
|
|
|
|
return {
|
|
|
|
results: parser.parse(),
|
|
|
|
errors: parser.getErrors()
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
function verifyOptions(opt)
|
|
|
|
{
|
|
|
|
opt.delimeter = opt.delimiter || defaults.delimiter;
|
|
|
|
opt.header = typeof opt.header === 'undefined' ? defaults.header : opt.header;
|
|
|
|
|
|
|
|
if (opt.delimiter == '"' || opt.delimiter == "\n")
|
|
|
|
opt.delimiter = defaults.delimiter;
|
|
|
|
|
|
|
|
if (opt.delimiter.length > 1)
|
|
|
|
opt.delimiter = opt.delimiter[0];
|
|
|
|
|
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
|
|
|
|
function Parser(input, config)
|
|
|
|
{
|
|
|
|
var self = this;
|
|
|
|
var _input = input;
|
|
|
|
var _config = config;
|
|
|
|
var _errors = [];
|
|
|
|
var _state = emptyState();
|
|
|
|
|
|
|
|
this.parse = function(arg)
|
|
|
|
{
|
|
|
|
if (typeof arg === 'object')
|
|
|
|
self.setConfig(arg)
|
|
|
|
else if (typeof arg === 'string')
|
|
|
|
self.setInput(arg);
|
|
|
|
|
|
|
|
_errors = [];
|
|
|
|
_state = emptyState();
|
|
|
|
|
|
|
|
for (_state.i = 0; _state.i < _input.length; _state.i++)
|
|
|
|
{
|
|
|
|
_state.ch = _input[_state.i];
|
|
|
|
|
|
|
|
if (_state.ch == '"')
|
|
|
|
handleQuote();
|
|
|
|
else if (_state.inQuotes)
|
|
|
|
inQuotes();
|
|
|
|
else
|
|
|
|
notInQuotes();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Treat the last line and its last field
|
|
|
|
saveField();
|
|
|
|
trimEmptyLastLine();
|
|
|
|
inspectFieldCount();
|
|
|
|
|
|
|
|
if (_state.inQuotes)
|
|
|
|
addError("Unescaped or mismatched quotes");
|
|
|
|
|
|
|
|
return self.getParsed();
|
|
|
|
};
|
|
|
|
|
|
|
|
this.getDelimiter = function()
|
|
|
|
{
|
|
|
|
return config.delimiter;
|
|
|
|
};
|
|
|
|
|
|
|
|
this.setDelimiter = function(delim)
|
|
|
|
{
|
|
|
|
var comma = ",";
|
|
|
|
delim = delim
|
|
|
|
? (delim == '"' || delim == "\n" ? comma : delim)
|
|
|
|
: comma;
|
|
|
|
_config.delimiter = delim[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
this.setConfig = function(opt)
|
|
|
|
{
|
|
|
|
if ((typeof opt.header !== 'undefined'
|
|
|
|
&& opt.header != config.header)
|
|
|
|
|| (typeof opt.delimiter !== 'undefined'
|
|
|
|
&& opt.delimiter != config.delimiter))
|
|
|
|
{
|
|
|
|
_state.parsed = emptyParsed(opt.header);
|
|
|
|
}
|
|
|
|
|
|
|
|
_config = opt;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.getInput = function()
|
|
|
|
{
|
|
|
|
return _input;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setInput = function(input)
|
|
|
|
{
|
|
|
|
_input = input;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.getParsed = function()
|
|
|
|
{
|
|
|
|
return _state.parsed;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.getErrors = function()
|
|
|
|
{
|
|
|
|
return _errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function emptyParsed(header)
|
|
|
|
{
|
|
|
|
return header ? { fields: [], rows: [] } : [ [] ];
|
|
|
|
}
|
|
|
|
|
|
|
|
function emptyState()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
i: 0,
|
|
|
|
line: 1,
|
|
|
|
field: 0,
|
|
|
|
fieldVal: "",
|
|
|
|
ch: "",
|
|
|
|
inQuotes: false,
|
|
|
|
parsed: emptyParsed(config.header)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleQuote()
|
|
|
|
{
|
|
|
|
if (_state.i < _input.length - 1)
|
|
|
|
{
|
|
|
|
if (_input[_state.i+1] == '"' && _state.inQuotes)
|
|
|
|
{
|
|
|
|
_state.fieldVal += '"'
|
|
|
|
_state.i++;
|
|
|
|
}
|
|
|
|
else if (_input[_state.i+1] != _config.delimiter && _state.inQuotes)
|
|
|
|
return addError("Unescaped quote in field value");
|
|
|
|
}
|
|
|
|
|
|
|
|
_state.inQuotes = !_state.inQuotes;
|
|
|
|
}
|
|
|
|
|
|
|
|
function inQuotes()
|
|
|
|
{
|
|
|
|
appendCharToField();
|
|
|
|
}
|
|
|
|
|
|
|
|
function appendCharToField()
|
|
|
|
{
|
|
|
|
_state.fieldVal += _state.ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
function notInQuotes()
|
|
|
|
{
|
|
|
|
if (_state.ch == _config.delimiter)
|
|
|
|
{
|
|
|
|
saveField();
|
|
|
|
}
|
|
|
|
else if (_state.ch == "\n")
|
|
|
|
{
|
|
|
|
saveField();
|
|
|
|
newRow();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
appendCharToField();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function saveField()
|
|
|
|
{
|
|
|
|
if (_config.header)
|
|
|
|
{
|
|
|
|
if (_state.line == 1)
|
|
|
|
{
|
|
|
|
_state.parsed.fields.push(_state.fieldVal)
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var currentRow = _state.parsed.rows[_state.parsed.rows.length - 1];
|
|
|
|
var fieldName = _state.parsed.fields[_state.field];
|
|
|
|
if (fieldName)
|
|
|
|
currentRow[fieldName] = _state.fieldVal;
|
|
|
|
else
|
|
|
|
addError("Too many fields; expected " + _state.parsed.fields.length + " fields, found extra value: '" + _state.fieldVal + "'");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
_state.parsed[_state.parsed.length - 1].push(_state.fieldVal);
|
|
|
|
|
|
|
|
_state.fieldVal = "";
|
|
|
|
_state.field ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
function newRow()
|
|
|
|
{
|
|
|
|
trimEmptyLastLine();
|
|
|
|
|
|
|
|
if (_config.header)
|
|
|
|
{
|
|
|
|
inspectFieldCount();
|
|
|
|
if (_state.line > 0)
|
|
|
|
_state.parsed.rows.push({});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
_state.parsed.push([]);
|
|
|
|
|
|
|
|
_state.line ++;
|
|
|
|
_state.field = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function trimEmptyLastLine()
|
|
|
|
{
|
|
|
|
if (_config.header)
|
|
|
|
{
|
|
|
|
if (_state.line == 1)
|
|
|
|
{
|
|
|
|
if (_state.parsed.fields.length == 1
|
|
|
|
&& _state.parsed.fields[0].length == 0)
|
|
|
|
{
|
|
|
|
_state.parsed.fields = [];
|
|
|
|
_state.line --;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var lastRow = _state.parsed.rows[_state.parsed.rows.length - 1];
|
|
|
|
if (!lastRow[_state.parsed.fields[0]])
|
|
|
|
_state.parsed.rows.splice(_state.parsed.rows.length - 1, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var lastRow = _state.parsed[_state.parsed.length - 1];
|
|
|
|
if (lastRow.length == 0 || (lastRow[0].length == 0))
|
|
|
|
_state.parsed.splice(_state.parsed.length - 1, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function inspectFieldCount()
|
|
|
|
{
|
|
|
|
if (!_config.header)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (_state.parsed.rows.length == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
var expected = _state.parsed.fields.length;
|
|
|
|
var actual = Object.keys(_state.parsed.rows[_state.parsed.rows.length - 1]).length;
|
|
|
|
|
|
|
|
if (expected != actual)
|
|
|
|
return addError("Too few fields; expected " + expected + " fields, parsed " + actual);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function addError(msg)
|
|
|
|
{
|
|
|
|
_errors.push({
|
|
|
|
message: msg,
|
|
|
|
line: _state.line,
|
|
|
|
row: _config.header ? _state.parsed.rows.length - 1 : _state.parsed.length - 1,
|
|
|
|
index: _state.i
|
|
|
|
});
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})(jQuery);
|