// attacklab: hack around Konqueror 3.5.4 bug:
// "----------bug".replace(/^-/g,"") == "bug"
bq = bq.replace(/^[ \t]*>[ \t]?/gm, ''); // trim one level of quoting
// attacklab: clean up hack
bq = bq.replace(/¨0/g, '');
bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
bq = bq.replace(/(^|\n)/g, '$1 ');
// These leading spaces screw with <pre> content, so we need to fix that:
bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
// attacklab: hack around Konqueror 3.5.4 bug:
pre = pre.replace(/^ /mg, '¨0');
pre = pre.replace(/¨0/g, '');
return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
text = globals.converter._dispatch('blockQuotes.after', text, options, globals);
* Process Markdown `<pre><code>` blocks.
showdown.subParser('codeBlocks', function (text, options, globals) {
text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
// sentinel workarounds for lack of \A and \Z, safari\khtml bug
var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g;
text = text.replace(pattern, function (wholeMatch, m1, m2) {
codeblock = showdown.subParser('outdent')(codeblock, options, globals);
codeblock = showdown.subParser('encodeCode')(codeblock, options, globals);
codeblock = showdown.subParser('detab')(codeblock, options, globals);
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
if (options.omitExtraWLInCodeBlocks) {
codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
text = text.replace(/¨0/, '');
text = globals.converter._dispatch('codeBlocks.after', text, options, globals);
* * Backtick quotes are used for <code></code> spans.
* * You can use multiple backticks as the delimiters if you want to
* include literal backticks in the code span. So, this input:
* Just type ``foo `bar` baz`` at the prompt.
* <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
* There's no arbitrary limit to the number of backticks you
* can use as delimters. If you need three consecutive backticks
* in your code, use four for delimiters, etc.
* * You can use spaces to get literal backticks at the edges:
* ... type `` `bar` `` ...
* ... type <code>`bar`</code> ...
showdown.subParser('codeSpans', function (text, options, globals) {
text = globals.converter._dispatch('codeSpans.before', text, options, globals);
if (typeof text === 'undefined') {
text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
function (wholeMatch, m1, m2, m3) {
c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
c = showdown.subParser('encodeCode')(c, options, globals);
c = m1 + '<code>' + c + '</code>';
c = showdown.subParser('hashHTMLSpans')(c, options, globals);
text = globals.converter._dispatch('codeSpans.after', text, options, globals);
* Create a full HTML document from the processed markdown
showdown.subParser('completeHTMLDocument', function (text, options, globals) {
if (!options.completeHTMLDocument) {
text = globals.converter._dispatch('completeHTMLDocument.before', text, options, globals);
doctypeParsed = '<!DOCTYPE HTML>\n',
charset = '<meta charset="utf-8">\n',
if (typeof globals.metadata.parsed.doctype !== 'undefined') {
doctypeParsed = '<!DOCTYPE ' + globals.metadata.parsed.doctype + '>\n';
doctype = globals.metadata.parsed.doctype.toString().toLowerCase();
if (doctype === 'html' || doctype === 'html5') {
charset = '<meta charset="utf-8">';
for (var meta in globals.metadata.parsed) {
if (globals.metadata.parsed.hasOwnProperty(meta)) {
switch (meta.toLowerCase()) {
title = '<title>' + globals.metadata.parsed.title + '</title>\n';
if (doctype === 'html' || doctype === 'html5') {
charset = '<meta charset="' + globals.metadata.parsed.charset + '">\n';
charset = '<meta name="charset" content="' + globals.metadata.parsed.charset + '">\n';
lang = ' lang="' + globals.metadata.parsed[meta] + '"';
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
text = doctypeParsed + '<html' + lang + '>\n<head>\n' + title + charset + metadata + '</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
text = globals.converter._dispatch('completeHTMLDocument.after', text, options, globals);
* Convert all tabs to spaces
showdown.subParser('detab', function (text, options, globals) {
text = globals.converter._dispatch('detab.before', text, options, globals);
text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width
// replace the nth with two sentinels
text = text.replace(/\t/g, '¨A¨B');
// use the sentinel to anchor our regex so it doesn't explode
text = text.replace(/¨B(.+?)¨A/g, function (wholeMatch, m1) {
numSpaces = 4 - leadingText.length % 4; // g_tab_width
// there *must* be a better way to do this:
for (var i = 0; i < numSpaces; i++) {
text = text.replace(/¨A/g, ' '); // g_tab_width
text = text.replace(/¨B/g, '');
text = globals.converter._dispatch('detab.after', text, options, globals);
showdown.subParser('ellipsis', function (text, options, globals) {
text = globals.converter._dispatch('ellipsis.before', text, options, globals);
text = text.replace(/\.\.\./g, '…');
text = globals.converter._dispatch('ellipsis.after', text, options, globals);
* Turn emoji codes into emojis
* List of supported emojis: https://github.com/showdownjs/showdown/wiki/Emojis
showdown.subParser('emoji', function (text, options, globals) {
text = globals.converter._dispatch('emoji.before', text, options, globals);
var emojiRgx = /:([\S]+?):/g;
text = text.replace(emojiRgx, function (wm, emojiCode) {
if (showdown.helper.emojis.hasOwnProperty(emojiCode)) {
return showdown.helper.emojis[emojiCode];
text = globals.converter._dispatch('emoji.after', text, options, globals);
* Smart processing for ampersands and angle brackets that need to be encoded.
showdown.subParser('encodeAmpsAndAngles', function (text, options, globals) {
text = globals.converter._dispatch('encodeAmpsAndAngles.before', text, options, globals);
// Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
// http://bumppo.net/projects/amputator/
text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&');
text = text.replace(/<(?![a-z\/?$!])/gi, '<');
text = text.replace(/</g, '<');
text = text.replace(/>/g, '>');
text = globals.converter._dispatch('encodeAmpsAndAngles.after', text, options, globals);
* Returns the string, with after processing the following backslash escape sequences.
* attacklab: The polite way to do this is with the new escapeCharacters() function:
* text = escapeCharacters(text,"\\",true);
* text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
* ...but we're sidestepping its use of the (slow) RegExp constructor
* as an optimization for Firefox. This function gets called a LOT.
showdown.subParser('encodeBackslashEscapes', function (text, options, globals) {
text = globals.converter._dispatch('encodeBackslashEscapes.before', text, options, globals);
text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
text = text.replace(/\\([`*_{}\[\]()>#+.!~=|-])/g, showdown.helper.escapeCharactersCallback);
text = globals.converter._dispatch('encodeBackslashEscapes.after', text, options, globals);
* Encode/escape certain characters inside Markdown code runs.
* The point is that in code, these characters are literals,
* and lose their special Markdown meanings.
showdown.subParser('encodeCode', function (text, options, globals) {
text = globals.converter._dispatch('encodeCode.before', text, options, globals);
// Encode all ampersands; HTML entities are not
// entities within a Markdown code span.
// Do the angle bracket song and dance:
// Now, escape characters that are magic in Markdown:
.replace(/([*_{}\[\]\\=~-])/g, showdown.helper.escapeCharactersCallback);
text = globals.converter._dispatch('encodeCode.after', text, options, globals);
* Within tags -- meaning between < and > -- encode [\ ` * _ ~ =] so they
* don't conflict with their use in Markdown for code, italics and strong.
showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text, options, globals) {
text = globals.converter._dispatch('escapeSpecialCharsWithinTagAttributes.before', text, options, globals);
// Build a regex to find HTML tags.
var tags = /<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,
comments = /<!(--(?:(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>/gi;
text = text.replace(tags, function (wholeMatch) {
.replace(/(.)<\/?code>(?=.)/g, '$1`')
.replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback);
text = text.replace(comments, function (wholeMatch) {
.replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback);
text = globals.converter._dispatch('escapeSpecialCharsWithinTagAttributes.after', text, options, globals);
* Handle github codeblocks prior to running HashHTML so that
* HTML contained within the codeblock gets escaped properly
showdown.subParser('githubCodeBlocks', function (text, options, globals) {
// early exit if option is not enabled
if (!options.ghCodeBlocks) {
text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
text = text.replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g, function (wholeMatch, delim, language, codeblock) {
var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
// First parse the github code block
codeblock = showdown.subParser('encodeCode')(codeblock, options, globals);
codeblock = showdown.subParser('detab')(codeblock, options, globals);
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
// Since GHCodeblocks can be false positives, we need to
// store the primitive text and the parsed text in a global var,
// and then return a token
return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
// attacklab: strip sentinel
text = text.replace(/¨0/, '');
return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
showdown.subParser('hashBlock', function (text, options, globals) {
text = globals.converter._dispatch('hashBlock.before', text, options, globals);
text = text.replace(/(^\n+|\n+$)/g, '');
text = '\n\n¨K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
text = globals.converter._dispatch('hashBlock.after', text, options, globals);
* Hash and escape <code> elements that should not be parsed as markdown
showdown.subParser('hashCodeTags', function (text, options, globals) {
text = globals.converter._dispatch('hashCodeTags.before', text, options, globals);
var repFunc = function (wholeMatch, match, left, right) {
var codeblock = left + showdown.subParser('encodeCode')(match, options, globals) + right;
return '¨C' + (globals.gHtmlSpans.push(codeblock) - 1) + 'C';
text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '<code\\b[^>]*>', '</code>', 'gim');
text = globals.converter._dispatch('hashCodeTags.after', text, options, globals);
showdown.subParser('hashElement', function (text, options, globals) {
return function (wholeMatch, m1) {
blockText = blockText.replace(/\n\n/g, '\n');
blockText = blockText.replace(/^\n/, '');
// strip trailing blank lines
blockText = blockText.replace(/\n+$/g, '');
// Replace the element text with a marker ("¨KxK" where x is its key)
blockText = '\n\n¨K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n';
showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
text = globals.converter._dispatch('hashHTMLBlocks.before', text, options, globals);
repFunc = function (wholeMatch, match, left, right) {
// check if this html element is marked as markdown
// if so, it's contents should be parsed as markdown
if (left.search(/\bmarkdown\b/) !== -1) {
txt = left + globals.converter.makeHtml(match) + right;
return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
if (options.backslashEscapesHTMLTags) {
// encode backslash escaped HTML tags
text = text.replace(/\\<(\/?[^>]+?)>/g, function (wm, inside) {
return '<' + inside + '>';