/*jslint node:true */ var common = require('./common'); function validateOptions (userOptions) { var options = common.copyOptions(userOptions); common.ensureFlagExists('ignoreDeclaration', options); common.ensureFlagExists('ignoreAttributes', options); common.ensureFlagExists('ignoreText', options); common.ensureFlagExists('ignoreComment', options); common.ensureFlagExists('ignoreCdata', options); common.ensureFlagExists('compact', options); common.ensureFlagExists('fullTagEmptyElement', options); common.ensureSpacesExists(options); if (typeof options.spaces === 'number') { options.spaces = Array(options.spaces + 1).join(' '); } common.ensureKeyExists('declaration', options); common.ensureKeyExists('attributes', options); common.ensureKeyExists('text', options); common.ensureKeyExists('comment', options); common.ensureKeyExists('cdata', options); common.ensureKeyExists('type', options); common.ensureKeyExists('name', options); common.ensureKeyExists('elements', options); return options; } function writeIndentation (options, depth, firstLine) { return (!firstLine && options.spaces ? '\n' : '') + Array(depth + 1).join(options.spaces); } function writeAttributes (attributes) { var key, result = ''; for (key in attributes) { if (attributes.hasOwnProperty(key)) { result += ' ' + key + '="' + attributes[key] + '"'; } } return result; } function writeDeclaration (declaration, options) { return ''; } function writeComment (element, options) { return options.ignoreComment ? '' : ''; } function writeCdata (element, options) { return options.ignoreCdata ? '' : ''; } function writeText (element, options) { return options.ignoreText ? '' : element[options.textKey].replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } function writeElement (element, options, depth) { var xml = ''; xml += '<' + element.name; if (element[options.attributesKey]) { xml += writeAttributes(element[options.attributesKey]); } if (options.fullTagEmptyElement || (element[options.elementsKey] && element[options.elementsKey].length) || (element[options.attributesKey] && element[options.attributesKey]['xml:space'] === 'preserve')) { xml += '>'; if (element[options.elementsKey] && element[options.elementsKey].length) { xml += writeElements(element[options.elementsKey], options, depth + 1); } xml += (options.spaces && element[options.elementsKey] && element[options.elementsKey].length && (element[options.elementsKey].length > 1 || element[options.elementsKey][0].type !== 'text') ? '\n' : '') + Array(depth + 1).join(options.spaces); xml += ''; } else { xml += '/>'; } return xml; } function writeElements (elements, options, depth, firstLine) { var indent = writeIndentation(options, depth, firstLine); return elements.reduce(function (xml, element) { switch (element.type) { case 'element': return xml + indent + writeElement(element, options, depth); case 'comment': return xml + indent + writeComment(element, options); case 'cdata': return xml + indent + writeCdata(element, options); case 'text': return xml + writeText(element, options); } }, ''); } function hasContent (element, options, skipText) { var key; for (key in element) { if (element.hasOwnProperty(key)) { switch (key) { case options.textKey: if (!skipText) { return true; } break; // skip to next key case options.parentKey: case options.attributesKey: break; // skip to next key case options.cdataKey: case options.commentKey: case options.declarationKey: return true; default: return true; } } } return false; } function writeElementCompact (element, name, options, depth, indent) { var xml = ''; if (name) { xml += '<' + name; if (element[options.attributesKey]) { xml += writeAttributes(element[options.attributesKey]); } if (options.fullTagEmptyElement || hasContent(element, options) || element[options.attributesKey] && element[options.attributesKey]['xml:space'] === 'preserve') { xml += '>'; } else { xml += '/>'; return xml; } } xml += writeElementsCompact(element, options, depth + 1, false); if (name) { xml += (indent ? writeIndentation(options, depth, false) : '') + ''; } return xml; } function writeElementsCompact (element, options, depth, firstLine) { var key, xml = ''; for (key in element) { if (element.hasOwnProperty(key)) { switch (key) { case options.declarationKey: xml += writeDeclaration(element[options.declarationKey], options); break; case options.attributesKey: case options.parentKey: break; // skip case options.textKey: xml += writeText(element, options); break; case options.cdataKey: xml += writeIndentation(options, depth, firstLine) + writeCdata(element, options); break; case options.commentKey: xml += writeIndentation(options, depth, firstLine) + writeComment(element, options); break; default: xml += writeIndentation(options, depth, firstLine) + writeElementCompact(element[key], key, options, depth, hasContent(element[key], options, true)); } firstLine = firstLine && !xml; } } return xml; } module.exports = function (js, options) { 'use strict'; options = validateOptions(options); var xml = ''; if (options.compact) { xml = writeElementsCompact(js, options, 0, true); } else { if (js[options.declarationKey]) { xml += writeDeclaration(js[options.declarationKey], options); } if (js[options.elementsKey] && js[options.elementsKey].length) { xml += writeElements(js[options.elementsKey], options, 0, !xml); } } return xml; };