UNPKG

6-mils

Version:

A JS library for sending, receiving, and parsing cXML messages.

106 lines (88 loc) 3.27 kB
/** * Pretty-prints a cXML document with indentation. Only intended for creating * human-readable representations. * * @param {String} src The cXML to format (no whitespace is expected * between tags). * * @return {String} */ function humanizeXml (src) { /** * Validate input. */ src = src || '' if (src.length === 0) { return '' } // nothing to do! /** * As the source XML is parsed for formatting, it will be added to this array * line by line. * @type {Array} */ const output = [] /** * The characters to use for indentation. * @type {String} */ const indent = ' ' /** * A list of elements being traversed by the parser. This enables the parser * to keep track of where it is in the document tree. * @type {Array} */ const currentTagHierarchy = ['cXML'] /** * The input to this function, separated by tag (either opening or closing). * @type {Array} */ const lines = src.split('>') /** * The last line to parse. This is purposefully set as the second-to-last * because it is easier (processing-wise) to simply add a trailing "</cXML>" * outside the loop, since that will always be a constant feature. * @type {Number} */ const lastIndex = lines.length - 2 for (let i = 0; i < lastIndex; i++) { const currentLine = lines[i] // the first three lines are all left-aligned (xml, doctype, cXML) if (i < 3) { output.push(`${currentLine}>`) continue } const previousLine = output[output.length - 1] const openingTagMatches = /^<(\w+)/.exec(currentLine) const closingTagMatches = /^<\/(\w+)/.exec(currentLine) if (openingTagMatches) { // start each new element on a separate line, indented appropriately output.push(`${indent.repeat(currentTagHierarchy.length)}${currentLine}>`) // if this isn't a self-closing tag, then assume there may be children // elements if (!currentLine.endsWith('/')) { currentTagHierarchy.push(openingTagMatches[1]) } } else if (closingTagMatches) { const previousOpeningTagMatches = /<(\w+)/.exec(previousLine) if (previousOpeningTagMatches && previousOpeningTagMatches[1] === closingTagMatches[1]) { // since the closing tag is the same as the opening one on the previous // line, keep them together output[output.length - 1] = `${previousLine}${currentLine}>` currentTagHierarchy.pop() continue } // otherwise, place the closing tag on a new line, but update the // hierarchy first to align the indentation with the matching opening tag currentTagHierarchy.pop() output.push(`${indent.repeat(currentTagHierarchy.length)}${currentLine}>`) } else { // this line does not start with either an opening tag nor a closing one, // therefore it should be merged with the previous line output[output.length - 1] = `${previousLine}${currentLine}>` // owing to the way the lines were split originally, we know this line // must end in a closing tag, so update the hierarchy currentTagHierarchy.pop() } } output.push('</cXML>') return output.join('\n') } module.exports = humanizeXml