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