/*! * WMD - Wanton Markdown * Copyright (c) 2010 Caolan McMahon */ /* * Fork of https://github.com/caolan/wmd */ function escapeHtml(s) { s = ('' + s); /* Coerce to string */ s = s.replace(/&/g, '&'); s = s.replace(//g, '>'); s = s.replace(/"/g, '"'); s = s.replace(/'/g, '''); return s; } /** * Main function for converting markdown to HTML. * * @param {String} content * @param {Object} options * @return {String} * @api public */ var WMD = {}; WMD.convert = function(content, options) { var doc = {raw: content, markdown: content}; var opt = WMD.readOptions(options); WMD.preprocess(doc, opt); doc.html = WMD.processor(doc.markdown, true); WMD.postprocess(doc, opt); doc.toString = function () { return doc.html; }; return doc; }; function gsub(str, re, fn, /*optional*/newstr) { newstr = newstr || ''; var match = re.exec(str); if (match) { newstr += str.slice(0, match.index); newstr += fn.apply(null, match); remaining = str.slice(match.index + match[0].length); return gsub(remaining, re, fn, newstr); } return newstr + str; } WMD.showdown = new Showdown.converter({extensions: ['table']}); WMD.processor = WMD.showdown.makeHtml; WMD.preprocessors = { underscores: function (doc) { // prevent foo_bar_baz from ending up with an italic word in the middle doc.markdown = gsub(doc.markdown, /(^(?! {4}|\t)\w+_\w+_\w[\w_]*)/, function (match) { var count = 0; for (var i = 0; i < match.length; i++) { if (match[i] == '_') count++; } if (count === 2) { return match.replace(/_/g, '\\_'); } return match; } ); return doc; }, metadata: function (doc) { var key; var lines = doc.markdown.split('\n'); doc.metadata = {}; while (lines.length) { var match = /^(\S+):\s+(.*)$/.exec(lines[0]); if (match) { var key = match[1]; doc.metadata[key] = match[2]; lines.shift(); } else { var continued_value = /^\s+(.+)$/.exec(lines[0]); // strip empty lines if (/^\s*$/.exec(lines[0])) { lines.shift(); } else if (continued_value && key) { doc.metadata[key] += '\n' + continued_value[1]; lines.shift(); } else break; } } doc.markdown = lines.join('\n'); return doc; }, fencedCodeBlocksHighlightJS: function (doc) { var re1 = /```([A-Za-z]+)\s*([\s\S]+?)```/; // with syntax highlighting var re2 = /```\s*([\s\S]+?)```/; // without syntax highlighting var block; while (block = re1.exec(doc.markdown) || re2.exec(doc.markdown)) { var pre; if (block.length === 3) { // we have a code format pre = '
';
                if (block[1] in hljs.LANGUAGES) {
                    pre += hljs.highlight(block[1], block[2]).value;
                }
                else {
                    pre += escapeHtml(block[2]);
                }
                pre += '
'; } else { // no syntax highlighting pre = '
' +
                    escapeHtml(block[1]) + '
'; } doc.markdown = doc.markdown.substr(0, block.index) + pre + doc.markdown.substr(block.index + block[0].length); } return doc; } }; WMD.postprocessors = {}; /** * Extends a default set of options with those passed to it. * * @param {Object} options * @return {Object} * @api public */ WMD.readOptions = function (options) { var obj = { preprocessors: [ WMD.preprocessors.metadata, WMD.preprocessors.underscores, WMD.preprocessors.fencedCodeBlocksHighlightJS ], postprocessors: [ ] }; for (var k in options) { obj[k] = options[k]; } return obj; }; /** * Runs all the preprocessors defined in options on the doc object. * This is executed before passing the doc's markdown property to the processor * function to be turned into HTML. * * @param {Object} doc * @param {Object} options * @return {String} * @api public */ WMD.preprocess = function (doc, options) { return options.preprocessors.reduce(function (doc, fn) { return fn(doc); }, doc); }; /** * Runs all the postprocessors defined in options on the doc object. * This is executed after passing the doc's markdown property to the processor * function to be turned into HTML. * * @param {Object} doc * @param {Object} options * @return {String} * @api public */ WMD.postprocess = function (doc, options) { return options.postprocessors.reduce(function (doc, fn) { return fn(doc); }, doc); };