UNPKG

commonmark

Version:

a strongly specified, highly compatible variant of Markdown

183 lines (155 loc) 4.51 kB
"use strict"; var Renderer = require('./renderer'); var reXMLTag = /\<[^>]*\>/; function toTagName(s) { return s.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase(); } function XmlRenderer(options) { options = options || {}; this.disableTags = 0; this.lastOut = "\n"; this.indentLevel = 0; this.indent = ' '; this.options = options; } function render(ast) { this.buffer = ''; var attrs; var tagname; var walker = ast.walker(); var event, node, entering; var container; var selfClosing; var nodetype; var options = this.options; if (options.time) { console.time("rendering"); } this.buffer += '<?xml version="1.0" encoding="UTF-8"?>\n'; this.buffer += '<!DOCTYPE document SYSTEM "CommonMark.dtd">\n'; while ((event = walker.next())) { entering = event.entering; node = event.node; nodetype = node.type; container = node.isContainer; selfClosing = nodetype === 'thematic_break' || nodetype === 'linebreak' || nodetype === 'softbreak'; tagname = toTagName(nodetype); if (entering) { attrs = []; switch (nodetype) { case 'document': attrs.push(['xmlns', 'http://commonmark.org/xml/1.0']); break; 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 'code_block': if (node.info) { attrs.push(['info', node.info]); } break; case 'heading': attrs.push(['level', String(node.level)]); break; case 'link': case 'image': attrs.push(['destination', node.destination]); attrs.push(['title', node.title]); break; case 'custom_inline': case 'custom_block': attrs.push(['on_enter', node.onEnter]); attrs.push(['on_exit', node.onExit]); 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])]); } } this.cr(); this.out(this.tag(tagname, attrs, selfClosing)); if (container) { this.indentLevel += 1; } else if (!container && !selfClosing) { var lit = node.literal; if (lit) { this.out(this.esc(lit)); } this.out(this.tag('/' + tagname)); } } else { this.indentLevel -= 1; this.cr(); this.out(this.tag('/' + tagname)); } } if (options.time) { console.timeEnd("rendering"); } this.buffer += '\n'; return this.buffer; } function out(s) { if(this.disableTags > 0) { this.buffer += s.replace(reXMLTag, ''); }else{ this.buffer += s; } this.lastOut = s; } function cr() { if(this.lastOut !== '\n') { this.buffer += '\n'; this.lastOut = '\n'; for(var i = this.indentLevel; i > 0; i--) { this.buffer += this.indent; } } } // Helper function to produce an XML tag. function tag(name, attrs, selfclosing) { var result = '<' + name; if(attrs && attrs.length > 0) { var i = 0; var attrib; while ((attrib = attrs[i]) !== undefined) { result += ' ' + attrib[0] + '="' + this.esc(attrib[1]) + '"'; i++; } } if(selfclosing) { result += ' /'; } result += '>'; return result; } // quick browser-compatible inheritance XmlRenderer.prototype = Object.create(Renderer.prototype); XmlRenderer.prototype.render = render; XmlRenderer.prototype.out = out; XmlRenderer.prototype.cr = cr; XmlRenderer.prototype.tag = tag; XmlRenderer.prototype.esc = require('../common').escapeXml; module.exports = XmlRenderer;