commonmark
Version:
a strongly specified, highly compatible variant of Markdown
172 lines (150 loc) • 4.87 kB
JavaScript
;
var escapeXml = require('./common').escapeXml;
// Helper function to produce an XML tag.
var tag = function(name, attrs, selfclosing) {
var result = '<' + name;
if (attrs && attrs.length > 0) {
var i = 0;
var attrib;
while ((attrib = attrs[i]) !== undefined) {
result += ' ' + attrib[0] + '="' + attrib[1] + '"';
i++;
}
}
if (selfclosing) {
result += ' /';
}
result += '>';
return result;
};
var reXMLTag = /\<[^>]*\>/;
var toTagName = function(s) {
return s.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
};
var renderNodes = function(block) {
var attrs;
var tagname;
var walker = block.walker();
var event, node, entering;
var buffer = "";
var lastOut = "\n";
var disableTags = 0;
var indentLevel = 0;
var indent = ' ';
var unescapedContents;
var container;
var selfClosing;
var nodetype;
var out = function(s) {
if (disableTags > 0) {
buffer += s.replace(reXMLTag, '');
} else {
buffer += s;
}
lastOut = s;
};
var esc = this.escape;
var cr = function() {
if (lastOut !== '\n') {
buffer += '\n';
lastOut = '\n';
for (var i = indentLevel; i--;) {
buffer += indent;
}
}
};
var options = this.options;
if (options.time) { console.time("rendering"); }
buffer += '<?xml version="1.0" encoding="UTF-8"?>\n';
buffer += '<!DOCTYPE CommonMark SYSTEM "CommonMark.dtd">\n';
while ((event = walker.next())) {
entering = event.entering;
node = event.node;
nodetype = node.type;
container = node.isContainer;
selfClosing = nodetype === 'HorizontalRule' || nodetype === 'Hardbreak' ||
nodetype === 'Softbreak' || nodetype === 'Image';
unescapedContents = nodetype === 'Html' || nodetype === 'HtmlInline';
tagname = toTagName(nodetype);
if (entering) {
attrs = [];
switch (nodetype) {
case 'List':
if (node.listType !== null) {
attrs.push(['type', node.listType.toLowerCase()]);
}
if (node.listStart !== null) {
attrs.push(['start', String(node.listStart)]);
}
if (node.listTight !== null) {
attrs.push(['tight', (node.listTight ? 'true' : 'false')]);
}
var delim = node.listDelimiter;
if (delim !== null) {
var delimword = '';
if (delim === '.') {
delimword = 'period';
} else {
delimword = 'paren';
}
attrs.push(['delimiter', delimword]);
}
break;
case 'CodeBlock':
if (node.info) {
attrs.push(['info', node.info]);
}
break;
case 'Header':
attrs.push(['level', String(node.level)]);
break;
case 'Link':
case 'Image':
attrs.push(['destination', node.destination]);
attrs.push(['title', node.title]);
break;
default:
break;
}
if (options.sourcepos) {
var pos = node.sourcepos;
if (pos) {
attrs.push(['sourcepos', String(pos[0][0]) + ':' +
String(pos[0][1]) + '-' + String(pos[1][0]) + ':' +
String(pos[1][1])]);
}
}
cr();
out(tag(tagname, attrs, selfClosing));
if (container) {
indentLevel += 1;
} else if (!container && !selfClosing) {
var lit = node.literal;
if (lit) {
out(unescapedContents ? lit : esc(lit));
}
out(tag('/' + tagname));
}
} else {
indentLevel -= 1;
cr();
out(tag('/' + tagname));
}
}
if (options.time) { console.timeEnd("rendering"); }
buffer += '\n';
return buffer;
};
// The XmlRenderer object.
function XmlRenderer(options){
return {
// default options:
softbreak: '\n', // by default, soft breaks are rendered as newlines in HTML
// set to "<br />" to make them hard breaks
// set to " " if you want to ignore line wrapping in source
escape: escapeXml,
options: options || {},
render: renderNodes
};
}
module.exports = XmlRenderer;