UNPKG

ale_parser.js

Version:

Avid Log Exchange (ALE) Document Parser in JavaScript

147 lines (132 loc) 5.17 kB
/* Takes a string `buffer` to parse and expose the heading, column, and data * Avid Log Exchange sections as JSON for easy consumption by your application */ function AleParser(buffer) { this.raw = buffer.toString(); this.heading = {}; this.column = []; this.data = []; var d = "\t"; // Default delimiter /* The following documentation for parseRows and subsequent * implementation of CSV parsing are from the csv2json and d3 projects. * ----------------------------------------------------------------------- * Parses the specified string, which is the contents of a CSV file, * returning an array of arrays representing the parsed rows. The string * is assumed to be RFC4180-compliant. Unlike the parse method, this * method treats the header line as a standard row, and should be used * whenever the CSV file does not contain a header. Each row is represented * as an array rather than an object. Rows may have variable length. * For example, consider the following CSV file: * 1997,Ford,E350,2.34 * 2000,Mercury,Cougar,2.38 * The resulting JavaScript array is: * [ * ["1997", "Ford", "E350", "2.34"], * ["2000", "Mercury", "Cougar", "2.38"] * ] * Note that the values themselves are always strings; they will not be automatically converted to numbers. See parse for details. * An optional accessor function may be specified as the second argument. * This function is invoked for each row in the CSV file, being passed the * current row and index as two arguments. The return value of the function * replaces the element in the returned array of rows; if the function * returns null, the row is stripped from the returned array of rows. * In effect, the accessor is similar to applying a map and filter operator * to the returned rows. The accessor function is used by parse to convert * each row to an object with named attributes. */ this.parseRows = function(delim, text, f) { var delimiterCode = delim.charCodeAt(0); var EOL = {}, // sentinel value for end-of-line EOF = {}, // sentinel value for end-of-file rows = [], // output rows N = text.length, I = 0, // current character index n = 0, // the current line number t, // the current token eol; // is the current token followed by EOL? function token() { if (I >= N) return EOF; // special case: end of file if (eol) return eol = false, EOL; // special case: end of line // special case: quotes var j = I; if (text.charCodeAt(j) === 34) { var i = j; while (i++ < N) { if (text.charCodeAt(i) === 34) { if (text.charCodeAt(i + 1) !== 34) break; ++i; } } I = i + 2; var c = text.charCodeAt(i + 1); if (c === 13) { eol = true; if (text.charCodeAt(i + 2) === 10) ++I; } else if (c === 10) { eol = true; } return text.substring(j + 1, i).replace(/""/g, "\""); } // common case: find next delimiter or newline while (I < N) { var c = text.charCodeAt(I++), k = 1; if (c === 10) eol = true; // \n else if (c === 13) { eol = true; if (text.charCodeAt(I) === 10) ++I, ++k; } // \r|\r\n else if (c !== delimiterCode) continue; return text.substring(j, I - k); } // special case: last token before EOF return text.substring(j); } while ((t = token()) !== EOF) { var a = []; while (t !== EOL && t !== EOF) { a.push(t); t = token(); } if (typeof(f)==='function' && !(a = f(a, n++))) continue; rows.push(a); } return rows; } /* Here's the ALE-specific logic that actually extracts the sections * it makes heavy use of the parseRows method above and can be expanded * pretty easily. I certainly do not claim to know the ALE format so well * but if you have issues, please fork and improve this :) */ var currentSection; var lines = this.raw.split('\n'); for (var i = 0, l = lines.length; i < l; i ++) { var line = lines[i].replace(/^\s+|\s+$/g, ''); if (line == "Heading") currentSection = line; else if (line == "Column") currentSection = line; else if (line == "Data") currentSection = line; else { if (currentSection == "Heading") { var headRow = this.parseRows(d, line); if(headRow.length > 0) this.heading[headRow[0][0].toString()] = headRow[0][1]; } else if (currentSection == "Column") { var colRow = this.parseRows(d, line); if(colRow.length > 0) this.column = colRow[0]; } else if (currentSection == "Data") { var dataRow = this.parseRows(d, line); if(dataRow.length > 0) { var clipMeta = {}; for(var k = 0; k < dataRow[0].length; k++) { clipMeta[this.column[k]] = dataRow[0][k]; } this.data.push(clipMeta); } } } } } // Support Node.js if (typeof(module) !== "undefined") module.exports = AleParser;