Git based wiki inspired by Gollum
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
5.1KB

  1. /*!
  2. * WMD - Wanton Markdown
  3. * Copyright (c) 2010 Caolan McMahon
  4. */
  5. /*
  6. * Fork of https://github.com/caolan/wmd
  7. */
  8. function escapeHtml(s) {
  9. s = ('' + s); /* Coerce to string */
  10. s = s.replace(/&/g, '&');
  11. s = s.replace(/</g, '&lt;');
  12. s = s.replace(/>/g, '&gt;');
  13. s = s.replace(/"/g, '&quot;');
  14. s = s.replace(/'/g, '&#39;');
  15. return s;
  16. }
  17. /**
  18. * Main function for converting markdown to HTML.
  19. *
  20. * @param {String} content
  21. * @param {Object} options
  22. * @return {String}
  23. * @api public
  24. */
  25. var WMD = {};
  26. WMD.convert = function(content, options) {
  27. var doc = {raw: content, markdown: content};
  28. var opt = WMD.readOptions(options);
  29. WMD.preprocess(doc, opt);
  30. doc.html = WMD.processor(doc.markdown);
  31. WMD.postprocess(doc, opt);
  32. doc.toString = function () {
  33. return doc.html;
  34. };
  35. return doc;
  36. };
  37. function gsub(str, re, fn, /*optional*/newstr) {
  38. newstr = newstr || '';
  39. var match = re.exec(str);
  40. if (match) {
  41. newstr += str.slice(0, match.index);
  42. newstr += fn.apply(null, match);
  43. remaining = str.slice(match.index + match[0].length);
  44. return gsub(remaining, re, fn, newstr);
  45. }
  46. return newstr + str;
  47. }
  48. WMD.processor = new Showdown.converter().makeHtml;
  49. WMD.preprocessors = {
  50. underscores: function (doc) {
  51. // prevent foo_bar_baz from ending up with an italic word in the middle
  52. doc.markdown = gsub(doc.markdown,
  53. /(^(?! {4}|\t)\w+_\w+_\w[\w_]*)/, function (match) {
  54. var count = 0;
  55. for (var i = 0; i < match.length; i++) {
  56. if (match[i] == '_') count++;
  57. }
  58. if (count === 2) {
  59. return match.replace(/_/g, '\\_');
  60. }
  61. return match;
  62. }
  63. );
  64. return doc;
  65. },
  66. metadata: function (doc) {
  67. var key;
  68. var lines = doc.markdown.split('\n');
  69. doc.metadata = {};
  70. while (lines.length) {
  71. var match = /^(\S+):\s+(.*)$/.exec(lines[0]);
  72. if (match) {
  73. var key = match[1];
  74. doc.metadata[key] = match[2];
  75. lines.shift();
  76. }
  77. else {
  78. var continued_value = /^\s+(.+)$/.exec(lines[0]);
  79. // strip empty lines
  80. if (/^\s*$/.exec(lines[0])) {
  81. lines.shift();
  82. }
  83. else if (continued_value && key) {
  84. doc.metadata[key] += '\n' + continued_value[1];
  85. lines.shift();
  86. }
  87. else break;
  88. }
  89. }
  90. doc.markdown = lines.join('\n');
  91. return doc;
  92. },
  93. fencedCodeBlocksHighlightJS: function (doc) {
  94. var re1 = /```([A-Za-z]+)\s*([\s\S]+?)```/; // with syntax highlighting
  95. var re2 = /```\s*([\s\S]+?)```/; // without syntax highlighting
  96. var block;
  97. while (block = re1.exec(doc.markdown) || re2.exec(doc.markdown)) {
  98. var pre;
  99. if (block.length === 3) {
  100. // we have a code format
  101. pre = '<pre style="padding:0;"><code class="' + escapeHtml(block[1]) + '">';
  102. if (block[1] in hljs.LANGUAGES) {
  103. pre += hljs.highlight(block[1], block[2]).value;
  104. }
  105. else {
  106. pre += escapeHtml(block[2]);
  107. }
  108. pre += '</code></pre>';
  109. }
  110. else {
  111. // no syntax highlighting
  112. pre = '<pre style="padding:0;"><code class="no-highlight">' +
  113. escapeHtml(block[1]) + '</code></pre>';
  114. }
  115. doc.markdown = doc.markdown.substr(0, block.index) +
  116. pre + doc.markdown.substr(block.index + block[0].length);
  117. }
  118. return doc;
  119. }
  120. };
  121. WMD.postprocessors = {};
  122. /**
  123. * Extends a default set of options with those passed to it.
  124. *
  125. * @param {Object} options
  126. * @return {Object}
  127. * @api public
  128. */
  129. WMD.readOptions = function (options) {
  130. var obj = {
  131. preprocessors: [
  132. WMD.preprocessors.metadata,
  133. WMD.preprocessors.underscores,
  134. WMD.preprocessors.fencedCodeBlocksHighlightJS
  135. ],
  136. postprocessors: [
  137. ]
  138. };
  139. for (var k in options) {
  140. obj[k] = options[k];
  141. }
  142. return obj;
  143. };
  144. /**
  145. * Runs all the preprocessors defined in options on the doc object.
  146. * This is executed before passing the doc's markdown property to the processor
  147. * function to be turned into HTML.
  148. *
  149. * @param {Object} doc
  150. * @param {Object} options
  151. * @return {String}
  152. * @api public
  153. */
  154. WMD.preprocess = function (doc, options) {
  155. return options.preprocessors.reduce(function (doc, fn) {
  156. return fn(doc);
  157. }, doc);
  158. };
  159. /**
  160. * Runs all the postprocessors defined in options on the doc object.
  161. * This is executed after passing the doc's markdown property to the processor
  162. * function to be turned into HTML.
  163. *
  164. * @param {Object} doc
  165. * @param {Object} options
  166. * @return {String}
  167. * @api public
  168. */
  169. WMD.postprocess = function (doc, options) {
  170. return options.postprocessors.reduce(function (doc, fn) {
  171. return fn(doc);
  172. }, doc);
  173. };