diff --git a/admin/js/markdown.js b/admin/js/markdown.js index e5162b07..fbe343d2 100644 --- a/admin/js/markdown.js +++ b/admin/js/markdown.js @@ -11,9 +11,9 @@ // // The full source distribution is at: // -// A A L -// T C A -// T K B +// A A L +// T C A +// T K B // // // @@ -70,21 +70,21 @@ var Showdown = { extensions: {} }; // forEach // var forEach = Showdown.forEach = function(obj, callback) { - if (typeof obj.forEach === 'function') { - obj.forEach(callback); - } else { - var i, len = obj.length; - for (i = 0; i < len; i++) { - callback(obj[i], i, obj); - } - } + if (typeof obj.forEach === 'function') { + obj.forEach(callback); + } else { + var i, len = obj.length; + for (i = 0; i < len; i++) { + callback(obj[i], i, obj); + } + } }; // // Standard extension naming // var stdExtName = function(s) { - return s.replace(/[_-]||\s/g, '').toLowerCase(); + return s.replace(/[_-]||\s/g, '').toLowerCase(); }; // @@ -118,21 +118,21 @@ var g_output_modifiers = []; // if (typeof module !== 'undefind' && typeof exports !== 'undefined' && typeof require !== 'undefind') { - var fs = require('fs'); + var fs = require('fs'); - if (fs) { - // Search extensions folder - var extensions = fs.readdirSync((__dirname || '.')+'/extensions').filter(function(file){ - return ~file.indexOf('.js'); - }).map(function(file){ - return file.replace(/\.js$/, ''); - }); - // Load extensions into Showdown namespace - Showdown.forEach(extensions, function(ext){ - var name = stdExtName(ext); - Showdown.extensions[name] = require('./extensions/' + ext); - }); - } + if (fs) { + // Search extensions folder + var extensions = fs.readdirSync((__dirname || '.')+'/extensions').filter(function(file){ + return ~file.indexOf('.js'); + }).map(function(file){ + return file.replace(/\.js$/, ''); + }); + // Load extensions into Showdown namespace + Showdown.forEach(extensions, function(ext){ + var name = stdExtName(ext); + Showdown.extensions[name] = require('./extensions/' + ext); + }); + } } this.makeHtml = function(text) { @@ -143,70 +143,70 @@ this.makeHtml = function(text) { // and tags get encoded. // - // Clear the global hashes. If we don't clear these, you get conflicts - // from other articles when generating a page which contains more than - // one article (e.g. an index page that shows the N most recent - // articles): - g_urls = {}; - g_titles = {}; - g_html_blocks = []; + // Clear the global hashes. If we don't clear these, you get conflicts + // from other articles when generating a page which contains more than + // one article (e.g. an index page that shows the N most recent + // articles): + g_urls = {}; + g_titles = {}; + g_html_blocks = []; - // attacklab: Replace ~ with ~T - // This lets us use tilde as an escape char to avoid md5 hashes - // The choice of character is arbitray; anything that isn't - // magic in Markdown will work. - text = text.replace(/~/g,"~T"); + // attacklab: Replace ~ with ~T + // This lets us use tilde as an escape char to avoid md5 hashes + // The choice of character is arbitray; anything that isn't + // magic in Markdown will work. + text = text.replace(/~/g,"~T"); - // attacklab: Replace $ with ~D - // RegExp interprets $ as a special character - // when it's in a replacement string - text = text.replace(/\$/g,"~D"); + // attacklab: Replace $ with ~D + // RegExp interprets $ as a special character + // when it's in a replacement string + text = text.replace(/\$/g,"~D"); - // Standardize line endings - text = text.replace(/\r\n/g,"\n"); // DOS to Unix - text = text.replace(/\r/g,"\n"); // Mac to Unix + // Standardize line endings + text = text.replace(/\r\n/g,"\n"); // DOS to Unix + text = text.replace(/\r/g,"\n"); // Mac to Unix - // Make sure text begins and ends with a couple of newlines: - text = "\n\n" + text + "\n\n"; + // Make sure text begins and ends with a couple of newlines: + text = "\n\n" + text + "\n\n"; - // Convert all tabs to spaces. - text = _Detab(text); + // Convert all tabs to spaces. + text = _Detab(text); - // Strip any lines consisting only of spaces and tabs. - // This makes subsequent regexen easier to write, because we can - // match consecutive blank lines with /\n+/ instead of something - // contorted like /[ \t]*\n+/ . - text = text.replace(/^[ \t]+$/mg,""); + // Strip any lines consisting only of spaces and tabs. + // This makes subsequent regexen easier to write, because we can + // match consecutive blank lines with /\n+/ instead of something + // contorted like /[ \t]*\n+/ . + text = text.replace(/^[ \t]+$/mg,""); - // Run language extensions - Showdown.forEach(g_lang_extensions, function(x){ - text = _ExecuteExtension(x, text); - }); + // Run language extensions + Showdown.forEach(g_lang_extensions, function(x){ + text = _ExecuteExtension(x, text); + }); - // Handle github codeblocks prior to running HashHTML so that - // HTML contained within the codeblock gets escaped propertly - text = _DoGithubCodeBlocks(text); + // Handle github codeblocks prior to running HashHTML so that + // HTML contained within the codeblock gets escaped propertly + text = _DoGithubCodeBlocks(text); - // Turn block-level HTML blocks into hash entries - text = _HashHTMLBlocks(text); + // Turn block-level HTML blocks into hash entries + text = _HashHTMLBlocks(text); - // Strip link definitions, store in hashes. - text = _StripLinkDefinitions(text); + // Strip link definitions, store in hashes. + text = _StripLinkDefinitions(text); - text = _RunBlockGamut(text); + text = _RunBlockGamut(text); - text = _UnescapeSpecialChars(text); + text = _UnescapeSpecialChars(text); - // attacklab: Restore dollar signs - text = text.replace(/~D/g,"$$"); + // attacklab: Restore dollar signs + text = text.replace(/~D/g,"$$"); - // attacklab: Restore tildes - text = text.replace(/~T/g,"~"); + // attacklab: Restore tildes + text = text.replace(/~T/g,"~"); - // Run output modifiers - Showdown.forEach(g_output_modifiers, function(x){ - text = _ExecuteExtension(x, text); - }); + // Run output modifiers + Showdown.forEach(g_output_modifiers, function(x){ + text = _ExecuteExtension(x, text); + }); text = text.replace(/<\/?(\!doctype|html|head|body|link|title|input|select|button|textarea|style|script|noscript|iframe|object|embed)[^>]*>/ig, function (all) { return all.replace(/&/g, '&') @@ -217,7 +217,7 @@ this.makeHtml = function(text) { return all; }); - return text; + return text; }; // // Options: @@ -228,43 +228,43 @@ if (converter_options && converter_options.extensions) { var self = this; - // Iterate over each plugin - Showdown.forEach(converter_options.extensions, function(plugin){ + // Iterate over each plugin + Showdown.forEach(converter_options.extensions, function(plugin){ - // Assume it's a bundled plugin if a string is given - if (typeof plugin === 'string') { - plugin = Showdown.extensions[stdExtName(plugin)]; - } + // Assume it's a bundled plugin if a string is given + if (typeof plugin === 'string') { + plugin = Showdown.extensions[stdExtName(plugin)]; + } - if (typeof plugin === 'function') { - // Iterate over each extension within that plugin - Showdown.forEach(plugin(self), function(ext){ - // Sort extensions by type - if (ext.type) { - if (ext.type === 'language' || ext.type === 'lang') { - g_lang_extensions.push(ext); - } else if (ext.type === 'output' || ext.type === 'html') { - g_output_modifiers.push(ext); - } - } else { - // Assume language extension - g_output_modifiers.push(ext); - } - }); - } else { - throw "Extension '" + plugin + "' could not be loaded. It was either not found or is not a valid extension."; - } - }); + if (typeof plugin === 'function') { + // Iterate over each extension within that plugin + Showdown.forEach(plugin(self), function(ext){ + // Sort extensions by type + if (ext.type) { + if (ext.type === 'language' || ext.type === 'lang') { + g_lang_extensions.push(ext); + } else if (ext.type === 'output' || ext.type === 'html') { + g_output_modifiers.push(ext); + } + } else { + // Assume language extension + g_output_modifiers.push(ext); + } + }); + } else { + throw "Extension '" + plugin + "' could not be loaded. It was either not found or is not a valid extension."; + } + }); } var _ExecuteExtension = function(ext, text) { - if (ext.regex) { - var re = new RegExp(ext.regex, 'g'); - return text.replace(re, ext.replace); - } else if (ext.filter) { - return ext.filter(text); - } + if (ext.regex) { + var re = new RegExp(ext.regex, 'g'); + return text.replace(re, ext.replace); + } else if (ext.filter) { + return ext.filter(text); + } }; var _StripLinkDefinitions = function(text) { @@ -273,195 +273,195 @@ var _StripLinkDefinitions = function(text) { // hash references. // - // Link defs are in the form: ^[id]: url "optional title" + // Link defs are in the form: ^[id]: url "optional title" - /* - var text = text.replace(/ - ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 - [ \t]* - \n? // maybe *one* newline - [ \t]* - ? // url = $2 - [ \t]* - \n? // maybe one newline - [ \t]* - (?: - (\n*) // any lines skipped = $3 attacklab: lookbehind removed - ["(] - (.+?) // title = $4 - [")] - [ \t]* - )? // title is optional - (?:\n+|$) - /gm, - function(){...}); - */ + /* + var text = text.replace(/ + ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 + [ \t]* + \n? // maybe *one* newline + [ \t]* + ? // url = $2 + [ \t]* + \n? // maybe one newline + [ \t]* + (?: + (\n*) // any lines skipped = $3 attacklab: lookbehind removed + ["(] + (.+?) // title = $4 + [")] + [ \t]* + )? // title is optional + (?:\n+|$) + /gm, + function(){...}); + */ - // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug - text += "~0"; + // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug + text += "~0"; - text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|(?=~0))/gm, - function (wholeMatch,m1,m2,m3,m4) { - m1 = m1.toLowerCase(); - g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive - if (m3) { - // Oops, found blank lines, so it's not a title. - // Put back the parenthetical statement we stole. - return m3+m4; - } else if (m4) { - g_titles[m1] = m4.replace(/"/g,"""); - } + text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|(?=~0))/gm, + function (wholeMatch,m1,m2,m3,m4) { + m1 = m1.toLowerCase(); + g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive + if (m3) { + // Oops, found blank lines, so it's not a title. + // Put back the parenthetical statement we stole. + return m3+m4; + } else if (m4) { + g_titles[m1] = m4.replace(/"/g,"""); + } - // Completely remove the definition from the text - return ""; - } - ); + // Completely remove the definition from the text + return ""; + } + ); - // attacklab: strip sentinel - text = text.replace(/~0/,""); + // attacklab: strip sentinel + text = text.replace(/~0/,""); - return text; + return text; } var _HashHTMLBlocks = function(text) { - // attacklab: Double up blank lines to reduce lookaround - text = text.replace(/\n/g,"\n\n"); + // attacklab: Double up blank lines to reduce lookaround + text = text.replace(/\n/g,"\n\n"); - // Hashify HTML blocks: - // We only want to do this for block-level HTML tags, such as headers, - // lists, and tables. That's because we still want to wrap

s around - // "paragraphs" that are wrapped in non-block-level tags, such as anchors, - // phrase emphasis, and spans. The list of tags we're looking for is - // hard-coded: - var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside"; - var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside"; + // Hashify HTML blocks: + // We only want to do this for block-level HTML tags, such as headers, + // lists, and tables. That's because we still want to wrap

s around + // "paragraphs" that are wrapped in non-block-level tags, such as anchors, + // phrase emphasis, and spans. The list of tags we're looking for is + // hard-coded: + var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside"; + var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside"; - // First, look for nested blocks, e.g.: - //

- //
- // tags for inner block must be indented. - //
- //
- // - // The outermost tags must start at the left margin for this to match, and - // the inner nested divs must be indented. - // We need to do this before the next, more liberal match, because the next - // match will start at the first `
` and stop at the first `
`. + // First, look for nested blocks, e.g.: + //
+ //
+ // tags for inner block must be indented. + //
+ //
+ // + // The outermost tags must start at the left margin for this to match, and + // the inner nested divs must be indented. + // We need to do this before the next, more liberal match, because the next + // match will start at the first `
` and stop at the first `
`. - // attacklab: This regex can be expensive when it fails. - /* - var text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_a) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*?\n // any number of lines, minimally matching - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement); + // attacklab: This regex can be expensive when it fails. + /* + var text = text.replace(/ + ( // save in $1 + ^ // start of line (with /m) + <($block_tags_a) // start tag = $2 + \b // word break + // attacklab: hack around khtml/pcre bug... + [^\r]*?\n // any number of lines, minimally matching + // the matching end tag + [ \t]* // trailing spaces/tabs + (?=\n+) // followed by a newline + ) // attacklab: there are sentinel newlines at end of document + /gm,function(){...}}; + */ + text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement); - // - // Now match more liberally, simply from `\n` to `\n` - // + // + // Now match more liberally, simply from `\n` to `\n` + // - /* - var text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_b) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*? // any number of lines, minimally matching - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement); + /* + var text = text.replace(/ + ( // save in $1 + ^ // start of line (with /m) + <($block_tags_b) // start tag = $2 + \b // word break + // attacklab: hack around khtml/pcre bug... + [^\r]*? // any number of lines, minimally matching + // the matching end tag + [ \t]* // trailing spaces/tabs + (?=\n+) // followed by a newline + ) // attacklab: there are sentinel newlines at end of document + /gm,function(){...}}; + */ + text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement); - // Special case just for
. It was easier to make a special case than - // to make the other regex more complicated. + // Special case just for
. It was easier to make a special case than + // to make the other regex more complicated. - /* - text = text.replace(/ - ( // save in $1 - \n\n // Starting after a blank line - [ ]{0,3} - (<(hr) // start tag = $2 - \b // word break - ([^<>])*? // - \/?>) // the matching end tag - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement); + /* + text = text.replace(/ + ( // save in $1 + \n\n // Starting after a blank line + [ ]{0,3} + (<(hr) // start tag = $2 + \b // word break + ([^<>])*? // + \/?>) // the matching end tag + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,hashElement); + */ + text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement); - // Special case for standalone HTML comments: + // Special case for standalone HTML comments: - /* - text = text.replace(/ - ( // save in $1 - \n\n // Starting after a blank line - [ ]{0,3} // attacklab: g_tab_width - 1 - - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g,hashElement); + /* + text = text.replace(/ + ( // save in $1 + \n\n // Starting after a blank line + [ ]{0,3} // attacklab: g_tab_width - 1 + + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,hashElement); + */ + text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g,hashElement); - // PHP and ASP-style processor instructions ( and <%...%>) + // PHP and ASP-style processor instructions ( and <%...%>) - /* - text = text.replace(/ - (?: - \n\n // Starting after a blank line - ) - ( // save in $1 - [ ]{0,3} // attacklab: g_tab_width - 1 - (?: - <([?%]) // $2 - [^\r]*? - \2> - ) - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,hashElement); - */ - text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement); + /* + text = text.replace(/ + (?: + \n\n // Starting after a blank line + ) + ( // save in $1 + [ ]{0,3} // attacklab: g_tab_width - 1 + (?: + <([?%]) // $2 + [^\r]*? + \2> + ) + [ \t]* + (?=\n{2,}) // followed by a blank line + ) + /g,hashElement); + */ + text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement); - // attacklab: Undo double lines (see comment at top of this function) - text = text.replace(/\n\n/g,"\n"); - return text; + // attacklab: Undo double lines (see comment at top of this function) + text = text.replace(/\n\n/g,"\n"); + return text; } var hashElement = function(wholeMatch,m1) { - var blockText = m1; + var blockText = m1; - // Undo double lines - blockText = blockText.replace(/\n\n/g,"\n"); - blockText = blockText.replace(/^\n/,""); + // Undo double lines + blockText = blockText.replace(/\n\n/g,"\n"); + blockText = blockText.replace(/^\n/,""); - // strip trailing blank lines - blockText = blockText.replace(/\n+$/g,""); + // 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" + (g_html_blocks.push(blockText)-1) + "K\n\n"; + // Replace the element text with a marker ("~KxK" where x is its key) + blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n"; - return blockText; + return blockText; }; var _RunBlockGamut = function(text) { @@ -469,26 +469,26 @@ var _RunBlockGamut = function(text) { // These are all the transformations that form block-level // tags like paragraphs, headers, and list items. // - text = _DoHeaders(text); + text = _DoHeaders(text); - // Do Horizontal Rules: - var key = hashBlock("
"); - text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key); - text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key); - text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key); + // Do Horizontal Rules: + var key = hashBlock("
"); + text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key); + text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key); + text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key); - text = _DoLists(text); - text = _DoCodeBlocks(text); - text = _DoBlockQuotes(text); + text = _DoLists(text); + text = _DoCodeBlocks(text); + text = _DoBlockQuotes(text); - // We already ran _HashHTMLBlocks() before, in Markdown(), but that - // was to escape raw HTML in the original Markdown source. This time, - // we're escaping the markup we've just created, so that we don't wrap - //

tags around block-level tags. - text = _HashHTMLBlocks(text); - text = _FormParagraphs(text); + // We already ran _HashHTMLBlocks() before, in Markdown(), but that + // was to escape raw HTML in the original Markdown source. This time, + // we're escaping the markup we've just created, so that we don't wrap + //

tags around block-level tags. + text = _HashHTMLBlocks(text); + text = _FormParagraphs(text); - return text; + return text; }; @@ -498,26 +498,26 @@ var _RunSpanGamut = function(text) { // tags like paragraphs, headers, and list items. // - text = _DoCodeSpans(text); - text = _EscapeSpecialCharsWithinTagAttributes(text); - text = _EncodeBackslashEscapes(text); + text = _DoCodeSpans(text); + text = _EscapeSpecialCharsWithinTagAttributes(text); + text = _EncodeBackslashEscapes(text); - // Process anchor and image tags. Images must come first, - // because ![foo][f] looks like an anchor. - text = _DoImages(text); - text = _DoAnchors(text); + // Process anchor and image tags. Images must come first, + // because ![foo][f] looks like an anchor. + text = _DoImages(text); + text = _DoAnchors(text); - // Make links out of things like `` - // Must come after _DoAnchors(), because you can use < and > - // delimiters in inline links like [this](). - text = _DoAutoLinks(text); - text = _EncodeAmpsAndAngles(text); - text = _DoItalicsAndBold(text); + // Make links out of things like `` + // Must come after _DoAnchors(), because you can use < and > + // delimiters in inline links like [this](). + text = _DoAutoLinks(text); + text = _EncodeAmpsAndAngles(text); + text = _DoItalicsAndBold(text); - // Do hard breaks: - text = text.replace(/ +\n/g,"
\n"); + // Do hard breaks: + text = text.replace(/ +\n/g,"
\n"); - return text; + return text; } var _EscapeSpecialCharsWithinTagAttributes = function(text) { @@ -526,147 +526,147 @@ var _EscapeSpecialCharsWithinTagAttributes = function(text) { // don't conflict with their use in Markdown for code, italics and strong. // - // Build a regex to find HTML tags and comments. See Friedl's - // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. - var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; + // Build a regex to find HTML tags and comments. See Friedl's + // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. + var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; - text = text.replace(regex, function(wholeMatch) { - var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`"); - tag = escapeCharacters(tag,"\\`*_"); - return tag; - }); + text = text.replace(regex, function(wholeMatch) { + var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`"); + tag = escapeCharacters(tag,"\\`*_"); + return tag; + }); - return text; + return text; } var _DoAnchors = function(text) { // // Turn Markdown link shortcuts into XHTML tags. // - // - // First, handle reference-style links: [link text] [id] - // + // + // First, handle reference-style links: [link text] [id] + // - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[] // or anything else - )* - ) - \] + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ( + (?: + \[[^\]]*\] // allow brackets nested one level + | + [^\[] // or anything else + )* + ) + \] - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces + [ ]? // one optional space + (?:\n[ ]*)? // one optional newline followed by spaces - \[ - (.*?) // id = $3 - \] - )()()()() // pad remaining backreferences - /g,_DoAnchors_callback); - */ - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag); + \[ + (.*?) // id = $3 + \] + )()()()() // pad remaining backreferences + /g,_DoAnchors_callback); + */ + text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag); - // - // Next, inline-style links: [link text](url "optional title") - // + // + // Next, inline-style links: [link text](url "optional title") + // - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[\]] // or anything else - ) - ) - \] - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? // href = $4 - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // Title = $7 - \6 // matching quote - [ \t]* // ignore any spaces/tabs between closing quote and ) - )? // title is optional - \) - ) - /g,writeAnchorTag); - */ - text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag); + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ( + (?: + \[[^\]]*\] // allow brackets nested one level + | + [^\[\]] // or anything else + ) + ) + \] + \( // literal paren + [ \t]* + () // no id, so leave $3 empty + ? // href = $4 + [ \t]* + ( // $5 + (['"]) // quote char = $6 + (.*?) // Title = $7 + \6 // matching quote + [ \t]* // ignore any spaces/tabs between closing quote and ) + )? // title is optional + \) + ) + /g,writeAnchorTag); + */ + text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag); - // - // Last, handle reference-style shortcuts: [link text] - // These must come last in case you've also got [link test][1] - // or [link test](/foo) - // + // + // Last, handle reference-style shortcuts: [link text] + // These must come last in case you've also got [link test][1] + // or [link test](/foo) + // - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ([^\[\]]+) // link text = $2; can't contain '[' or ']' - \] - )()()()()() // pad rest of backreferences - /g, writeAnchorTag); - */ - text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); + /* + text = text.replace(/ + ( // wrap whole match in $1 + \[ + ([^\[\]]+) // link text = $2; can't contain '[' or ']' + \] + )()()()()() // pad rest of backreferences + /g, writeAnchorTag); + */ + text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); - return text; + return text; } var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { - if (m7 == undefined) m7 = ""; - var whole_match = m1; - var link_text = m2; - var link_id = m3.toLowerCase(); - var url = m4; - var title = m7; + if (m7 == undefined) m7 = ""; + var whole_match = m1; + var link_text = m2; + var link_id = m3.toLowerCase(); + var url = m4; + var title = m7; - if (url == "") { - if (link_id == "") { - // lower-case and turn embedded newlines into spaces - link_id = link_text.toLowerCase().replace(/ ?\n/g," "); - } - url = "#"+link_id; + if (url == "") { + if (link_id == "") { + // lower-case and turn embedded newlines into spaces + link_id = link_text.toLowerCase().replace(/ ?\n/g," "); + } + url = "#"+link_id; - if (g_urls[link_id] != undefined) { - url = g_urls[link_id]; - if (g_titles[link_id] != undefined) { - title = g_titles[link_id]; - } - } - else { - if (whole_match.search(/\(\s*\)$/m)>-1) { - // Special case for explicit empty url - url = ""; - } else { - return whole_match; - } - } - } + if (g_urls[link_id] != undefined) { + url = g_urls[link_id]; + if (g_titles[link_id] != undefined) { + title = g_titles[link_id]; + } + } + else { + if (whole_match.search(/\(\s*\)$/m)>-1) { + // Special case for explicit empty url + url = ""; + } else { + return whole_match; + } + } + } - url = escapeCharacters(url,"*_"); - var result = ""; + result += ">" + link_text + ""; - return result; + return result; } @@ -675,149 +675,149 @@ var _DoImages = function(text) { // Turn Markdown image shortcuts into tags. // - // - // First, handle reference-style labeled images: ![alt text][id] - // + // + // First, handle reference-style labeled images: ![alt text][id] + // - /* - text = text.replace(/ - ( // wrap whole match in $1 - !\[ - (.*?) // alt text = $2 - \] + /* + text = text.replace(/ + ( // wrap whole match in $1 + !\[ + (.*?) // alt text = $2 + \] - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces + [ ]? // one optional space + (?:\n[ ]*)? // one optional newline followed by spaces - \[ - (.*?) // id = $3 - \] - )()()()() // pad rest of backreferences - /g,writeImageTag); - */ - text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag); + \[ + (.*?) // id = $3 + \] + )()()()() // pad rest of backreferences + /g,writeImageTag); + */ + text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag); - // - // Next, handle inline images: ![alt text](url "optional title") - // Don't forget: encode * and _ + // + // Next, handle inline images: ![alt text](url "optional title") + // Don't forget: encode * and _ - /* - text = text.replace(/ - ( // wrap whole match in $1 - !\[ - (.*?) // alt text = $2 - \] - \s? // One optional whitespace character - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? // src url = $4 - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // title = $7 - \6 // matching quote - [ \t]* - )? // title is optional - \) - ) - /g,writeImageTag); - */ - text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag); + /* + text = text.replace(/ + ( // wrap whole match in $1 + !\[ + (.*?) // alt text = $2 + \] + \s? // One optional whitespace character + \( // literal paren + [ \t]* + () // no id, so leave $3 empty + ? // src url = $4 + [ \t]* + ( // $5 + (['"]) // quote char = $6 + (.*?) // title = $7 + \6 // matching quote + [ \t]* + )? // title is optional + \) + ) + /g,writeImageTag); + */ + text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag); - return text; + return text; } var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { - var whole_match = m1; - var alt_text = m2; - var link_id = m3.toLowerCase(); - var url = m4; - var title = m7; + var whole_match = m1; + var alt_text = m2; + var link_id = m3.toLowerCase(); + var url = m4; + var title = m7; - if (!title) title = ""; + if (!title) title = ""; - if (url == "") { - if (link_id == "") { - // lower-case and turn embedded newlines into spaces - link_id = alt_text.toLowerCase().replace(/ ?\n/g," "); - } - url = "#"+link_id; + if (url == "") { + if (link_id == "") { + // lower-case and turn embedded newlines into spaces + link_id = alt_text.toLowerCase().replace(/ ?\n/g," "); + } + url = "#"+link_id; - if (g_urls[link_id] != undefined) { - url = g_urls[link_id]; - if (g_titles[link_id] != undefined) { - title = g_titles[link_id]; - } - } - else { - return whole_match; - } - } + if (g_urls[link_id] != undefined) { + url = g_urls[link_id]; + if (g_titles[link_id] != undefined) { + title = g_titles[link_id]; + } + } + else { + return whole_match; + } + } - alt_text = alt_text.replace(/"/g,"""); - url = escapeCharacters(url,"*_"); - var result = "\""' + _RunSpanGamut(m1) + "");}); + // Setext-style headers: + // Header 1 + // ======== + // + // Header 2 + // -------- + // + text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm, + function(wholeMatch,m1){return hashBlock('

' + _RunSpanGamut(m1) + "

");}); - text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, - function(matchFound,m1){return hashBlock('

' + _RunSpanGamut(m1) + "

");}); + text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, + function(matchFound,m1){return hashBlock('

' + _RunSpanGamut(m1) + "

");}); - // atx-style headers: - // # Header 1 - // ## Header 2 - // ## Header 2 with closing hashes ## - // ... - // ###### Header 6 - // + // atx-style headers: + // # Header 1 + // ## Header 2 + // ## Header 2 with closing hashes ## + // ... + // ###### Header 6 + // - /* - text = text.replace(/ - ^(\#{1,6}) // $1 = string of #'s - [ \t]* - (.+?) // $2 = Header text - [ \t]* - \#* // optional closing #'s (not counted) - \n+ - /gm, function() {...}); - */ + /* + text = text.replace(/ + ^(\#{1,6}) // $1 = string of #'s + [ \t]* + (.+?) // $2 = Header text + [ \t]* + \#* // optional closing #'s (not counted) + \n+ + /gm, function() {...}); + */ - text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, - function(wholeMatch,m1,m2) { - var h_level = m1.length; - return hashBlock("' + _RunSpanGamut(m2) + ""); - }); + text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, + function(wholeMatch,m1,m2) { + var h_level = m1.length; + return hashBlock("' + _RunSpanGamut(m2) + ""); + }); - function headerId(m) { - return m.replace(/[^\w]/g, '').toLowerCase(); - } - return text; + function headerId(m) { + return m.replace(/[^\w]/g, '').toLowerCase(); + } + return text; } // This declaration keeps Dojo compressor from outputting garbage: @@ -828,73 +828,73 @@ var _DoLists = function(text) { // Form HTML ordered (numbered) and unordered (bulleted) lists. // - // attacklab: add sentinel to hack around khtml/safari bug: - // http://bugs.webkit.org/show_bug.cgi?id=11231 - text += "~0"; + // attacklab: add sentinel to hack around khtml/safari bug: + // http://bugs.webkit.org/show_bug.cgi?id=11231 + text += "~0"; - // Re-usable pattern to match any entirel ul or ol list: + // Re-usable pattern to match any entirel ul or ol list: - /* - var whole_list = / - ( // $1 = whole list - ( // $2 - [ ]{0,3} // attacklab: g_tab_width - 1 - ([*+-]|\d+[.]) // $3 = first list item marker - [ \t]+ - ) - [^\r]+? - ( // $4 - ~0 // sentinel for workaround; should be $ - | - \n{2,} - (?=\S) - (?! // Negative lookahead for another list item marker - [ \t]* - (?:[*+-]|\d+[.])[ \t]+ - ) - ) - )/g - */ - var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; + /* + var whole_list = / + ( // $1 = whole list + ( // $2 + [ ]{0,3} // attacklab: g_tab_width - 1 + ([*+-]|\d+[.]) // $3 = first list item marker + [ \t]+ + ) + [^\r]+? + ( // $4 + ~0 // sentinel for workaround; should be $ + | + \n{2,} + (?=\S) + (?! // Negative lookahead for another list item marker + [ \t]* + (?:[*+-]|\d+[.])[ \t]+ + ) + ) + )/g + */ + var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; - if (g_list_level) { - text = text.replace(whole_list,function(wholeMatch,m1,m2) { - var list = m1; - var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol"; + if (g_list_level) { + text = text.replace(whole_list,function(wholeMatch,m1,m2) { + var list = m1; + var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol"; - // Turn double returns into triple returns, so that we can make a - // paragraph for the last item in a list, if necessary: - list = list.replace(/\n{2,}/g,"\n\n\n");; - var result = _ProcessListItems(list); + // Turn double returns into triple returns, so that we can make a + // paragraph for the last item in a list, if necessary: + list = list.replace(/\n{2,}/g,"\n\n\n");; + var result = _ProcessListItems(list); - // Trim any trailing whitespace, to put the closing `` - // up on the preceding line, to get it past the current stupid - // HTML block parser. This is a hack to work around the terrible - // hack that is the HTML block parser. - result = result.replace(/\s+$/,""); - result = "<"+list_type+">" + result + "\n"; - return result; - }); - } else { - whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; - text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) { - var runup = m1; - var list = m2; + // Trim any trailing whitespace, to put the closing `` + // up on the preceding line, to get it past the current stupid + // HTML block parser. This is a hack to work around the terrible + // hack that is the HTML block parser. + result = result.replace(/\s+$/,""); + result = "<"+list_type+">" + result + "\n"; + return result; + }); + } else { + whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; + text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) { + var runup = m1; + var list = m2; - var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol"; - // Turn double returns into triple returns, so that we can make a - // paragraph for the last item in a list, if necessary: - var list = list.replace(/\n{2,}/g,"\n\n\n");; - var result = _ProcessListItems(list); - result = runup + "<"+list_type+">\n" + result + "\n"; - return result; - }); - } + var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol"; + // Turn double returns into triple returns, so that we can make a + // paragraph for the last item in a list, if necessary: + var list = list.replace(/\n{2,}/g,"\n\n\n");; + var result = _ProcessListItems(list); + result = runup + "<"+list_type+">\n" + result + "\n"; + return result; + }); + } - // attacklab: strip sentinel - text = text.replace(/~0/,""); + // attacklab: strip sentinel + text = text.replace(/~0/,""); - return text; + return text; } _ProcessListItems = function(list_str) { @@ -902,70 +902,70 @@ _ProcessListItems = function(list_str) { // Process the contents of a single ordered or unordered list, splitting it // into individual list items. // - // The $g_list_level global keeps track of when we're inside a list. - // Each time we enter a list, we increment it; when we leave a list, - // we decrement. If it's zero, we're not in a list anymore. - // - // We do this because when we're not inside a list, we want to treat - // something like this: - // - // I recommend upgrading to version - // 8. Oops, now this line is treated - // as a sub-list. - // - // As a single paragraph, despite the fact that the second line starts - // with a digit-period-space sequence. - // - // Whereas when we're inside a list (or sub-list), that line will be - // treated as the start of a sub-list. What a kludge, huh? This is - // an aspect of Markdown's syntax that's hard to parse perfectly - // without resorting to mind-reading. Perhaps the solution is to - // change the syntax rules such that sub-lists must start with a - // starting cardinal number; e.g. "1." or "a.". + // The $g_list_level global keeps track of when we're inside a list. + // Each time we enter a list, we increment it; when we leave a list, + // we decrement. If it's zero, we're not in a list anymore. + // + // We do this because when we're not inside a list, we want to treat + // something like this: + // + // I recommend upgrading to version + // 8. Oops, now this line is treated + // as a sub-list. + // + // As a single paragraph, despite the fact that the second line starts + // with a digit-period-space sequence. + // + // Whereas when we're inside a list (or sub-list), that line will be + // treated as the start of a sub-list. What a kludge, huh? This is + // an aspect of Markdown's syntax that's hard to parse perfectly + // without resorting to mind-reading. Perhaps the solution is to + // change the syntax rules such that sub-lists must start with a + // starting cardinal number; e.g. "1." or "a.". - g_list_level++; + g_list_level++; - // trim trailing blank lines: - list_str = list_str.replace(/\n{2,}$/,"\n"); + // trim trailing blank lines: + list_str = list_str.replace(/\n{2,}$/,"\n"); - // attacklab: add sentinel to emulate \z - list_str += "~0"; + // attacklab: add sentinel to emulate \z + list_str += "~0"; - /* - list_str = list_str.replace(/ - (\n)? // leading line = $1 - (^[ \t]*) // leading whitespace = $2 - ([*+-]|\d+[.]) [ \t]+ // list marker = $3 - ([^\r]+? // list item text = $4 - (\n{1,2})) - (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+)) - /gm, function(){...}); - */ - list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, - function(wholeMatch,m1,m2,m3,m4){ - var item = m4; - var leading_line = m1; - var leading_space = m2; + /* + list_str = list_str.replace(/ + (\n)? // leading line = $1 + (^[ \t]*) // leading whitespace = $2 + ([*+-]|\d+[.]) [ \t]+ // list marker = $3 + ([^\r]+? // list item text = $4 + (\n{1,2})) + (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+)) + /gm, function(){...}); + */ + list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, + function(wholeMatch,m1,m2,m3,m4){ + var item = m4; + var leading_line = m1; + var leading_space = m2; - if (leading_line || (item.search(/\n{2,}/)>-1)) { - item = _RunBlockGamut(_Outdent(item)); - } - else { - // Recursion for sub-lists: - item = _DoLists(_Outdent(item)); - item = item.replace(/\n$/,""); // chomp(item) - item = _RunSpanGamut(item); - } + if (leading_line || (item.search(/\n{2,}/)>-1)) { + item = _RunBlockGamut(_Outdent(item)); + } + else { + // Recursion for sub-lists: + item = _DoLists(_Outdent(item)); + item = item.replace(/\n$/,""); // chomp(item) + item = _RunSpanGamut(item); + } - return "
  • " + item + "
  • \n"; - } - ); + return "
  • " + item + "
  • \n"; + } + ); - // attacklab: strip sentinel - list_str = list_str.replace(/~0/g,""); + // attacklab: strip sentinel + list_str = list_str.replace(/~0/g,""); - g_list_level--; - return list_str; + g_list_level--; + return list_str; } @@ -974,42 +974,42 @@ var _DoCodeBlocks = function(text) { // Process Markdown `
    ` blocks.
     //
     
    -	/*
    -		text = text.replace(text,
    -			/(?:\n\n|^)
    -			(								// $1 = the code block -- one or more lines, starting with a space/tab
    -				(?:
    -					(?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
    -					.*\n+
    -				)+
    -			)
    -			(\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
    -		/g,function(){...});
    -	*/
    +    /*
    +        text = text.replace(text,
    +            /(?:\n\n|^)
    +            (                               // $1 = the code block -- one or more lines, starting with a space/tab
    +                (?:
    +                    (?:[ ]{4}|\t)           // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
    +                    .*\n+
    +                )+
    +            )
    +            (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width
    +        /g,function(){...});
    +    */
     
    -	// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    -	text += "~0";
    +    // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    +    text += "~0";
     
    -	text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
    -		function(wholeMatch,m1,m2) {
    -			var codeblock = m1;
    -			var nextChar = m2;
    +    text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
    +        function(wholeMatch,m1,m2) {
    +            var codeblock = m1;
    +            var nextChar = m2;
     
    -			codeblock = _EncodeCode( _Outdent(codeblock));
    -			codeblock = _Detab(codeblock);
    -			codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
    -			codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
    +            codeblock = _EncodeCode( _Outdent(codeblock));
    +            codeblock = _Detab(codeblock);
    +            codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
    +            codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
     
    -			codeblock = "
    " + codeblock + "\n
    "; + codeblock = "
    " + codeblock + "\n
    "; - return hashBlock(codeblock) + nextChar; - } - ); + return hashBlock(codeblock) + nextChar; + } + ); - // attacklab: strip sentinel - text = text.replace(/~0/,""); + // attacklab: strip sentinel + text = text.replace(/~0/,""); - return text; + return text; }; var _DoGithubCodeBlocks = function(text) { @@ -1024,34 +1024,34 @@ var _DoGithubCodeBlocks = function(text) { // - // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug - text += "~0"; + // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug + text += "~0"; - text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, - function(wholeMatch,m1,m2) { - var language = m1; - var codeblock = m2; + text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, + function(wholeMatch,m1,m2) { + var language = m1; + var codeblock = m2; - codeblock = _EncodeCode(codeblock); - codeblock = _Detab(codeblock); - codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines - codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace + codeblock = _EncodeCode(codeblock); + codeblock = _Detab(codeblock); + codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines + codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace - codeblock = "
    " + codeblock + "\n
    "; + codeblock = "
    " + codeblock + "\n
    "; - return hashBlock(codeblock); - } - ); + return hashBlock(codeblock); + } + ); - // attacklab: strip sentinel - text = text.replace(/~0/,""); + // attacklab: strip sentinel + text = text.replace(/~0/,""); - return text; + return text; } var hashBlock = function(text) { - text = text.replace(/(^\n+|\n+$)/g,""); - return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n"; + text = text.replace(/(^\n+|\n+$)/g,""); + return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n"; } var _DoCodeSpans = function(text) { @@ -1059,50 +1059,50 @@ var _DoCodeSpans = function(text) { // * Backtick quotes are used for spans. // // * You can use multiple backticks as the delimiters if you want to -// include literal backticks in the code span. So, this input: +// include literal backticks in the code span. So, this input: // -// Just type ``foo `bar` baz`` at the prompt. +// Just type ``foo `bar` baz`` at the prompt. // -// Will translate to: +// Will translate to: // -//

    Just type foo `bar` baz at the prompt.

    +//

    Just type foo `bar` baz at the prompt.

    // -// 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. +// 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 `` `bar` `` ... // -// Turns to: +// Turns to: // -// ... type `bar` ... +// ... type `bar` ... // - /* - text = text.replace(/ - (^|[^\\]) // Character before opening ` can't be a backslash - (`+) // $2 = Opening run of ` - ( // $3 = The code block - [^\r]*? - [^`] // attacklab: work around lack of lookbehind - ) - \2 // Matching closer - (?!`) - /gm, function(){...}); - */ + /* + text = text.replace(/ + (^|[^\\]) // Character before opening ` can't be a backslash + (`+) // $2 = Opening run of ` + ( // $3 = The code block + [^\r]*? + [^`] // attacklab: work around lack of lookbehind + ) + \2 // Matching closer + (?!`) + /gm, function(){...}); + */ - text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, - function(wholeMatch,m1,m2,m3,m4) { - var c = m3; - c = c.replace(/^([ \t]*)/g,""); // leading whitespace - c = c.replace(/[ \t]*$/g,""); // trailing whitespace - c = _EncodeCode(c); - return m1+""+c+""; - }); + text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, + function(wholeMatch,m1,m2,m3,m4) { + var c = m3; + c = c.replace(/^([ \t]*)/g,""); // leading whitespace + c = c.replace(/[ \t]*$/g,""); // trailing whitespace + c = _EncodeCode(c); + return m1+""+c+""; + }); - return text; + return text; } var _EncodeCode = function(text) { @@ -1111,16 +1111,16 @@ var _EncodeCode = function(text) { // The point is that in code, these characters are literals, // and lose their special Markdown meanings. // - // Encode all ampersands; HTML entities are not - // entities within a Markdown code span. - text = text.replace(/&/g,"&"); + // Encode all ampersands; HTML entities are not + // entities within a Markdown code span. + text = text.replace(/&/g,"&"); - // Do the angle bracket song and dance: - text = text.replace(//g,">"); + // Do the angle bracket song and dance: + text = text.replace(//g,">"); - // Now, escape characters that are magic in Markdown: - text = escapeCharacters(text,"\*_{}[]\\",false); + // Now, escape characters that are magic in Markdown: + text = escapeCharacters(text,"\*_{}[]\\",false); // jj the line above breaks this: //--- @@ -1132,68 +1132,68 @@ var _EncodeCode = function(text) { // special char: * //--- - return text; + return text; } var _DoItalicsAndBold = function(text) { - // must go first: - text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, - "$2"); + // must go first: + text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, + "$2"); - text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, - "$2"); + text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, + "$2"); - return text; + return text; } var _DoBlockQuotes = function(text) { - /* - text = text.replace(/ - ( // Wrap whole match in $1 - ( - ^[ \t]*>[ \t]? // '>' at the start of a line - .+\n // rest of the first line - (.+\n)* // subsequent consecutive lines - \n* // blanks - )+ - ) - /gm, function(){...}); - */ + /* + text = text.replace(/ + ( // Wrap whole match in $1 + ( + ^[ \t]*>[ \t]? // '>' at the start of a line + .+\n // rest of the first line + (.+\n)* // subsequent consecutive lines + \n* // blanks + )+ + ) + /gm, function(){...}); + */ - text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, - function(wholeMatch,m1) { - var bq = m1; + text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, + function(wholeMatch,m1) { + var bq = m1; - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" - bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting + bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting - // attacklab: clean up hack - bq = bq.replace(/~0/g,""); + // attacklab: clean up hack + bq = bq.replace(/~0/g,""); - bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines - bq = _RunBlockGamut(bq); // recurse + bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines + bq = _RunBlockGamut(bq); // recurse - bq = bq.replace(/(^|\n)/g,"$1 "); - // These leading spaces screw with
     content, so we need to fix that:
    -			bq = bq.replace(
    -					/(\s*
    [^\r]+?<\/pre>)/gm,
    -				function(wholeMatch,m1) {
    -					var pre = m1;
    -					// attacklab: hack around Konqueror 3.5.4 bug:
    -					pre = pre.replace(/^  /mg,"~0");
    -					pre = pre.replace(/~0/g,"");
    -					return pre;
    -				});
    +            bq = bq.replace(/(^|\n)/g,"$1  ");
    +            // These leading spaces screw with 
     content, so we need to fix that:
    +            bq = bq.replace(
    +                    /(\s*
    [^\r]+?<\/pre>)/gm,
    +                function(wholeMatch,m1) {
    +                    var pre = m1;
    +                    // attacklab: hack around Konqueror 3.5.4 bug:
    +                    pre = pre.replace(/^  /mg,"~0");
    +                    pre = pre.replace(/~0/g,"");
    +                    return pre;
    +                });
     
    -			return hashBlock("
    \n" + bq + "\n
    "); - }); - return text; + return hashBlock("
    \n" + bq + "\n
    "); + }); + return text; } @@ -1203,83 +1203,83 @@ var _FormParagraphs = function(text) { // $text - string to process with html

    tags // - // Strip leading and trailing lines: - text = text.replace(/^\n+/g,""); - text = text.replace(/\n+$/g,""); + // Strip leading and trailing lines: + text = text.replace(/^\n+/g,""); + text = text.replace(/\n+$/g,""); - var grafs = text.split(/\n{2,}/g); - var grafsOut = []; + var grafs = text.split(/\n{2,}/g); + var grafsOut = []; - // - // Wrap

    tags. - // - var end = grafs.length; - for (var i=0; i tags. + // + var end = grafs.length; + for (var i=0; i= 0) { - grafsOut.push(str); - } - else if (str.search(/\S/) >= 0) { - str = _RunSpanGamut(str); - str = str.replace(/^([ \t]*)/g,"

    "); - str += "

    " - grafsOut.push(str); - } + // if this is an HTML marker, copy it + if (str.search(/~K(\d+)K/g) >= 0) { + grafsOut.push(str); + } + else if (str.search(/\S/) >= 0) { + str = _RunSpanGamut(str); + str = str.replace(/^([ \t]*)/g,"

    "); + str += "

    " + grafsOut.push(str); + } - } + } - // - // Unhashify HTML blocks - // - end = grafsOut.length; - for (var i=0; i= 0) { - var blockText = g_html_blocks[RegExp.$1]; - blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs - grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText); - } - } + // + // Unhashify HTML blocks + // + end = grafsOut.length; + for (var i=0; i= 0) { + var blockText = g_html_blocks[RegExp.$1]; + blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs + grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText); + } + } - return grafsOut.join("\n\n"); + return grafsOut.join("\n\n"); } var _EncodeAmpsAndAngles = function(text) { // Smart processing for ampersands and angle brackets that need to be encoded. - // 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,"&"); + // 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,"&"); - // Encode naked <'s - text = text.replace(/<(?![a-z\/?\$!])/gi,"<"); + // Encode naked <'s + text = text.replace(/<(?![a-z\/?\$!])/gi,"<"); - return text; + return text; } var _EncodeBackslashEscapes = function(text) { // // Parameter: String. -// Returns: The string, with after processing the following backslash -// escape sequences. +// 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. + // 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. - text = text.replace(/\\(\\)/g,escapeCharacters_callback); - text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback); - return text; + text = text.replace(/\\(\\)/g,escapeCharacters_callback); + text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback); + return text; } @@ -1317,32 +1317,32 @@ var _handleTrailingParens = function(wholeMatch, lookbehind, protocol, link) { var _DoAutoLinks = function(text) { - // text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"$1"); + // text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"$1"); text = text.replace(/(="|<)?\b(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\])])(?=$|\W)/gi, _handleTrailingParens); var replacer = function (wholematch, m1) { return "" + m1 + ""; } text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer); - // Email addresses: + // Email addresses: - /* - text = text.replace(/ - < - (?:mailto:)? - ( - [-.\w]+ - \@ - [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ - ) - > - /gi, _DoAutoLinks_callback()); - */ - text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, - function(wholeMatch,m1) { - return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); - } - ); + /* + text = text.replace(/ + < + (?:mailto:)? + ( + [-.\w]+ + \@ + [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ + ) + > + /gi, _DoAutoLinks_callback()); + */ + text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, + function(wholeMatch,m1) { + return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); + } + ); - return text; + return text; } @@ -1351,46 +1351,46 @@ var _EncodeEmailAddress = function(addr) { // Input: an email address, e.g. "foo@example.com" // // Output: the email address as a mailto link, with each character -// of the address encoded as either a decimal or hex entity, in -// the hopes of foiling most address harvesting spam bots. E.g.: +// of the address encoded as either a decimal or hex entity, in +// the hopes of foiling most address harvesting spam bots. E.g.: // -// foo -// @example.com +// foo +// @example.com // // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk // mailing list: // - var encode = [ - function(ch){return "&#"+ch.charCodeAt(0)+";";}, - function(ch){return "&#x"+ch.charCodeAt(0).toString(16)+";";}, - function(ch){return ch;} - ]; + var encode = [ + function(ch){return "&#"+ch.charCodeAt(0)+";";}, + function(ch){return "&#x"+ch.charCodeAt(0).toString(16)+";";}, + function(ch){return ch;} + ]; - addr = "mailto:" + addr; + addr = "mailto:" + addr; - addr = addr.replace(/./g, function(ch) { - if (ch == "@") { - // this *must* be encoded. I insist. - ch = encode[Math.floor(Math.random()*2)](ch); - } else if (ch !=":") { - // leave ':' alone (to spot mailto: later) - var r = Math.random(); - // roughly 10% raw, 45% hex, 45% dec - ch = ( - r > .9 ? encode[2](ch) : - r > .45 ? encode[1](ch) : - encode[0](ch) - ); - } - return ch; - }); + addr = addr.replace(/./g, function(ch) { + if (ch == "@") { + // this *must* be encoded. I insist. + ch = encode[Math.floor(Math.random()*2)](ch); + } else if (ch !=":") { + // leave ':' alone (to spot mailto: later) + var r = Math.random(); + // roughly 10% raw, 45% hex, 45% dec + ch = ( + r > .9 ? encode[2](ch) : + r > .45 ? encode[1](ch) : + encode[0](ch) + ); + } + return ch; + }); - addr = "" + addr + ""; - addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part + addr = "" + addr + ""; + addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part - return addr; + return addr; } @@ -1398,13 +1398,13 @@ var _UnescapeSpecialChars = function(text) { // // Swap back in all the special characters we've hidden. // - text = text.replace(/~E(\d+)E/g, - function(wholeMatch,m1) { - var charCodeToReplace = parseInt(m1); - return String.fromCharCode(charCodeToReplace); - } - ); - return text; + text = text.replace(/~E(\d+)E/g, + function(wholeMatch,m1) { + var charCodeToReplace = parseInt(m1); + return String.fromCharCode(charCodeToReplace); + } + ); + return text; } @@ -1413,15 +1413,15 @@ var _Outdent = function(text) { // Remove one level of line-leading tabs or spaces // - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" - text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width + text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width - // attacklab: clean up hack - text = text.replace(/~0/g,"") + // attacklab: clean up hack + text = text.replace(/~0/g,"") - return text; + return text; } var _Detab = function(text) { @@ -1429,30 +1429,30 @@ var _Detab = function(text) { // In perl we could fix it by anchoring the regexp with \G. // In javascript we're less fortunate. - // expand first n-1 tabs - text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width + // expand first n-1 tabs + text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width - // replace the nth with two sentinels - text = text.replace(/\t/g,"~A~B"); + // 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,m2) { - var leadingText = m1; - var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width + // use the sentinel to anchor our regex so it doesn't explode + text = text.replace(/~B(.+?)~A/g, + function(wholeMatch,m1,m2) { + var leadingText = m1; + var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width - // there *must* be a better way to do this: - for (var i=0; i