aboutsummaryrefslogtreecommitdiff
path: root/js/lib/xml.js
blob: d2d95e823f6fb2688fcb56816003cdd9f925800a (plain)
  1. "use strict";
  2. var escapeXml = require('./common').escapeXml;
  3. // Helper function to produce an XML tag.
  4. var tag = function(name, attrs, selfclosing) {
  5. var result = '<' + name;
  6. if (attrs && attrs.length > 0) {
  7. var i = 0;
  8. var attrib;
  9. while ((attrib = attrs[i]) !== undefined) {
  10. result += ' ' + attrib[0] + '="' + attrib[1] + '"';
  11. i++;
  12. }
  13. }
  14. if (selfclosing) {
  15. result += ' /';
  16. }
  17. result += '>';
  18. return result;
  19. };
  20. var reXMLTag = /\<[^>]*\>/;
  21. var toTagName = function(s) {
  22. return s.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
  23. };
  24. var renderNodes = function(block) {
  25. var attrs;
  26. var tagname;
  27. var walker = block.walker();
  28. var event, node, entering;
  29. var buffer = "";
  30. var lastOut = "\n";
  31. var disableTags = 0;
  32. var indentLevel = 0;
  33. var indent = ' ';
  34. var unescapedContents;
  35. var container;
  36. var selfClosing;
  37. var nodetype;
  38. var out = function(s) {
  39. if (disableTags > 0) {
  40. buffer += s.replace(reXMLTag, '');
  41. } else {
  42. buffer += s;
  43. }
  44. lastOut = s;
  45. };
  46. var esc = this.escape;
  47. var cr = function() {
  48. if (lastOut !== '\n') {
  49. buffer += '\n';
  50. lastOut = '\n';
  51. for (var i = indentLevel; i--;) {
  52. buffer += indent;
  53. }
  54. }
  55. };
  56. var options = this.options;
  57. if (options.time) { console.time("rendering"); }
  58. buffer += '<?xml version="1.0" encoding="UTF-8"?>\n';
  59. buffer += '<!DOCTYPE CommonMark SYSTEM "CommonMark.dtd">\n';
  60. while ((event = walker.next())) {
  61. entering = event.entering;
  62. node = event.node;
  63. nodetype = node.type;
  64. container = node.isContainer();
  65. selfClosing = nodetype === 'HorizontalRule' || nodetype === 'Hardbreak' ||
  66. nodetype === 'Softbreak' || nodetype === 'Image';
  67. unescapedContents = nodetype === 'Html' || nodetype === 'HtmlInline';
  68. tagname = toTagName(nodetype);
  69. if (entering) {
  70. attrs = [];
  71. switch (nodetype) {
  72. case 'List':
  73. if (node.listType !== null) {
  74. attrs.push(['type', node.listType.toLowerCase()]);
  75. }
  76. if (node.listStart !== null) {
  77. attrs.push(['start', String(node.listStart)]);
  78. }
  79. if (node.listTight !== null) {
  80. attrs.push(['tight', (node.listTight ? 'true' : 'false')]);
  81. }
  82. var delim = node.listDelimiter;
  83. if (delim !== null) {
  84. var delimword = '';
  85. if (delim === '.') {
  86. delimword = 'period';
  87. } else {
  88. delimword = 'paren';
  89. }
  90. attrs.push(['delimiter', delimword]);
  91. }
  92. break;
  93. case 'CodeBlock':
  94. if (node.info) {
  95. attrs.push(['info', node.info]);
  96. }
  97. break;
  98. case 'Header':
  99. attrs.push(['level', String(node.level)]);
  100. break;
  101. case 'Link':
  102. case 'Image':
  103. attrs.push(['destination', node.destination]);
  104. attrs.push(['title', node.title]);
  105. break;
  106. default:
  107. break;
  108. }
  109. if (options.sourcepos) {
  110. var pos = node.sourcepos;
  111. if (pos) {
  112. attrs.push(['data-sourcepos', String(pos[0][0]) + ':' +
  113. String(pos[0][1]) + '-' + String(pos[1][0]) + ':' +
  114. String(pos[1][1])]);
  115. }
  116. }
  117. cr();
  118. out(tag(tagname, attrs, selfClosing));
  119. if (container) {
  120. indentLevel += 1;
  121. } else if (!container && !selfClosing) {
  122. var lit = node.literal;
  123. if (lit) {
  124. out(unescapedContents ? lit : esc(lit));
  125. }
  126. out(tag('/' + tagname));
  127. }
  128. } else {
  129. indentLevel -= 1;
  130. cr();
  131. out(tag('/' + tagname));
  132. }
  133. }
  134. if (options.time) { console.timeEnd("rendering"); }
  135. buffer += '\n';
  136. return buffer;
  137. };
  138. // The XmlRenderer object.
  139. function XmlRenderer(options){
  140. return {
  141. // default options:
  142. softbreak: '\n', // by default, soft breaks are rendered as newlines in HTML
  143. // set to "<br />" to make them hard breaks
  144. // set to " " if you want to ignore line wrapping in source
  145. escape: escapeXml,
  146. options: options || {},
  147. render: renderNodes
  148. };
  149. }
  150. module.exports = XmlRenderer;