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