mirror of https://github.com/keeweb/keeweb.git
109 lines
3.2 KiB
JavaScript
109 lines
3.2 KiB
JavaScript
class CsvParser {
|
|
next;
|
|
csv;
|
|
index;
|
|
line = [];
|
|
lines = [];
|
|
value = '';
|
|
error = undefined;
|
|
|
|
parse(csv) {
|
|
this.csv = csv.trim().replace(/\r\n/g, '\n');
|
|
this.result = [];
|
|
this.next = this.handleBeforeValue;
|
|
this.index = 0;
|
|
while (this.next && this.index <= this.csv.length) {
|
|
this.next = this.next(this);
|
|
}
|
|
if (this.lines.length <= 1) {
|
|
throw new Error('Empty CSV');
|
|
}
|
|
return { headers: this.lines[0], rows: this.lines.slice(1) };
|
|
}
|
|
|
|
handleBeforeValue() {
|
|
const isQuoted = this.csv[this.index] === '"';
|
|
if (isQuoted) {
|
|
this.index++;
|
|
this.value = '';
|
|
return this.handleQuotedValue;
|
|
}
|
|
return this.handleUnquotedValue;
|
|
}
|
|
|
|
handleUnquotedValue() {
|
|
const commaIndex = this.csv.indexOf(',', this.index);
|
|
const newLineIndex = this.csv.indexOf('\n', this.index);
|
|
|
|
let nextIndex;
|
|
if (commaIndex >= 0 && (newLineIndex < 0 || commaIndex < newLineIndex)) {
|
|
nextIndex = commaIndex;
|
|
} else if (newLineIndex >= 0) {
|
|
nextIndex = newLineIndex;
|
|
} else {
|
|
nextIndex = this.csv.length;
|
|
}
|
|
|
|
const value = this.csv.substr(this.index, nextIndex - this.index);
|
|
this.line.push(value);
|
|
|
|
this.index = nextIndex;
|
|
|
|
return this.handleAfterValue;
|
|
}
|
|
|
|
handleQuotedValue() {
|
|
const nextQuoteIndex = this.csv.indexOf('"', this.index);
|
|
const nextBackslashIndex = this.csv.indexOf('\\', this.index);
|
|
|
|
if (nextQuoteIndex < 0) {
|
|
this.index = this.csv.length;
|
|
this.error = 'Quoted value not closed';
|
|
return this.handleError;
|
|
}
|
|
|
|
if (nextBackslashIndex > 0 && nextBackslashIndex < nextQuoteIndex) {
|
|
const charAfterBackslash = this.csv[nextBackslashIndex + 1];
|
|
if (charAfterBackslash === '"' || charAfterBackslash === '\\') {
|
|
this.value +=
|
|
this.csv.substr(this.index, nextBackslashIndex - this.index) +
|
|
charAfterBackslash;
|
|
this.index = nextBackslashIndex + 2;
|
|
} else {
|
|
this.value += this.csv.substr(this.index, nextBackslashIndex - this.index + 1);
|
|
this.index = nextBackslashIndex + 1;
|
|
}
|
|
return this.handleQuotedValue;
|
|
}
|
|
|
|
if (this.csv[nextQuoteIndex + 1] === '"') {
|
|
this.value += this.csv.substr(this.index, nextQuoteIndex - this.index + 1);
|
|
this.index = nextQuoteIndex + 2;
|
|
return this.handleQuotedValue;
|
|
}
|
|
|
|
this.value += this.csv.substr(this.index, nextQuoteIndex - this.index);
|
|
this.index = nextQuoteIndex + 1;
|
|
this.line.push(this.value);
|
|
this.value = '';
|
|
|
|
return this.handleAfterValue;
|
|
}
|
|
|
|
handleAfterValue() {
|
|
const hasNextValueOnThisLine = this.csv[this.index] === ',';
|
|
this.index++;
|
|
if (!hasNextValueOnThisLine) {
|
|
this.lines.push(this.line);
|
|
this.line = [];
|
|
}
|
|
return this.handleBeforeValue;
|
|
}
|
|
|
|
handleError() {
|
|
throw new Error(this.error);
|
|
}
|
|
}
|
|
|
|
export { CsvParser };
|