169 lines
6.6 KiB
JavaScript
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, "&").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 += '</' + 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;
|
|
}; |