2017-03-02 10:07:55 +01:00

169 lines
6.6 KiB
JavaScript

/*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 '<?xml' + writeAttributes(declaration[options.attributesKey]) + '?>';
}
function writeComment (element, options) {
return options.ignoreComment ? '' : '<!--' + element[options.commentKey] + '-->';
}
function writeCdata (element, options) {
return options.ignoreCdata ? '' : '<![CDATA[' + element[options.cdataKey] + ']]>';
}
function writeText (element, options) {
return options.ignoreText ? '' : element[options.textKey].replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
}
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 += '</' + element.name + '>';
} 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) : '') + '</' + name + '>';
}
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;
};