UNPKG

vdata-parser

Version:

Parse vdata (ics, vcal, vcard) to javscript objects.

191 lines (162 loc) 5.94 kB
'use strict'; var fs = require('fs'); var parser = module.exports = {}; /** * Helper function to recursively get inline parameters * @param {String} str The content of the parameters, eg. TYPE=INTERNET;TYPE=HOME * @return {Array} The parsed data in the form of an array for each key/value, which are objects in the array. */ var extractParams = function(str) { if(!(str)) {return undefined;} var sep = str.indexOf('='); var paramKey = str.substr(0, sep); var paramValue = str.substr(sep + 1); var newData = {}; sep = paramValue.indexOf(';'); if(sep !== -1) { var currentValue = paramValue.substr(0, sep); var recursive = extractParams(paramValue.substr(sep + 1)); var arr = []; newData[paramKey] = currentValue; arr.push(newData); return arr.concat(recursive); } else { newData[paramKey] = paramValue; return [newData]; } }; /** * Parse lines and build a javascript object * @param {Array} lines The lines which should be parsed * @return {Object} The parsed data */ function parse (lines) { var block, subLines = [], key, data = {}; // check if lines are an array if (!(lines instanceof Array)) { throw new TypeError('Argument must be instanceof Array'); } // loop through the lines for (var i = 0, l = lines.length; i < l; i++) { var line = lines[i]; // check if block is set and line begins with BEGIN: if (typeof block === 'undefined' && !!~line.indexOf('BEGIN:')) { block = line.substr(6); } // check if block is set and line is end of current block else if (typeof block === 'string' && line === 'END:' + block) { // parse the sublines which are collected var recursive = parse(subLines); // check if data for block is not set if (typeof data[block] === 'undefined') { data[block] = recursive; } // check if data is already an object but not an array else if (typeof data[block] === 'object' && !(data[block] instanceof Array)) { var arr = []; // push existing data into array arr.push(data[block]); // push new data arr.push(recursive); // set array as new block value data[block] = arr; } else { // push new data data[block].push(recursive); } block = undefined; subLines = []; } else { // check if block is set if (typeof block === 'string') { // push current line to sublines array subLines.push(line); } else { // if line begins with a space if (line.match(/^\ /)) { // add on to the previous value data[key] += line.substr(1); } else { // detect key/value seperator var sep = line.indexOf(':'); key = line.substr(0, sep); var value = line.substr(sep + 1); // check for type=XXXX values within the key sep = key.indexOf(';'); var k,v; if(sep !== -1) { var superKey = key.substr(0, sep); var otherParams = extractParams(key.substr(sep + 1)); var newData = {value: value, params: otherParams}; k = superKey; v = newData; } else { k = key; v = value; } if(data[k] === undefined) { // set key and value data[k] = v; } else if (!(data[k] instanceof Array)) { var arr2 = []; arr2.push(data[k]); arr2.push(v); data[k] = arr2; } else { data[k].push(v); } } } } } return data; } /** * Parse a string * @param {String} string The string which should be parsed * @return {Object} The parsed data */ parser.fromString = function (string) { var lines; // check if argument is an string if (typeof string !== 'string') { throw new TypeError('Argument must be of type string'); } // replace any CR or CRLF with just LF, then split lines on LF lines = string.replace(/\r?\n|\r/g, '\n').split('\n'); return parse(lines); }; /** * Parse a file asynchrone and call the callback when is finished * @param {String} filename The file which should be parsed * @param {Function} callback The function which is called if file is * successful finished or an error was occurred * @return {Object} The parsed data */ parser.fromFile = function (filename, callback) { return fs.readFile(filename, function (err, buffer) { if (err) { return callback(err); } // catch the error which are thrown by parser.fromString try { // convert buffer to string var data = parser.fromString(buffer.toString()); return callback(null, data); } catch (err) { return callback(err); } }); }; /** * Parse a file synchronic * @param {String} filename The file which should be parsed * @return {Object} The parsed data */ parser.fromFileSync = function (filename) { var buffer = fs.readFileSync(filename); return parser.fromString(buffer.toString()); };