--- /dev/null
+_ = require 'underscore'
+
+
+slice = [].slice
+hasOwn = {}.hasOwnProperty
+objToString = {}.toString
+
+toArray = _.toArray
+
+
+
+decorate = (fn) ->
+ if not fn.__decorated__
+ for name of _pet.FUNCTION_METHODS
+ m = _[name]
+ fn[name] = m.__methodized__ or methodize m
+ fn.__decorated__ = true
+ return fn
+
+methodize = (fn) ->
+ m = fn.__methodized__
+ return m if m
+
+ g = fn.__genericized__
+ return g.__wraps__ if g and g.__wraps__
+
+ m = fn.__methodized__ = (args...) ->
+ args.unshift this
+ return fn.apply this, args
+
+ m.__wraps__ = fn
+ return decorate m
+
+
+
+_pet = module.exports = \
+ function pet (o, start=0, end=undefined) ->
+ if _.isArguments o
+ o = _.toArray o, start, end
+
+ return decorate o if typeof o is 'function'
+ return _ o
+
+# function methods to be attached on call to _(fn)
+_pet.FUNCTION_METHODS = [
+ 'bind', 'bindAll', 'memoize',
+ 'delay', 'defer', 'throttle', 'debounce', 'once', 'after',
+ 'wrap', 'compose',
+ 'unwrap', 'partial', 'curry', 'flip', 'methodize', 'aritize', 'limit'
+]
+
+
+class2name = "Boolean Number String Function Array Date RegExp Object"
+ .split(" ")
+ .reduce ((class2name, name) ->
+ class2name[ "[object "+name+"]" ] = name
+ return class2name), {}
+
+
+## Objects
+_.mixin
+
+ has: (o, v) ->
+ vals = if _.isArray(o) then o else _.values(o)
+ return vals.indexOf(v) is not -1
+
+ remove: (o, vs...) ->
+ if _.isArray(o)
+ _.each vs, (v) ->
+ idx = o.indexOf v
+ if idx is not -1
+ o.splice idx, 1
+ else
+ _.each o, (v, k) ->
+ if vs.indexOf(v) != -1
+ delete o[k]
+ return o
+
+ set: (o, key, value, def) ->
+ if o and key? and (value? or def?)
+ o[key] = value ? def
+ return o
+
+ attr: (o, key, value, def) ->
+ return o if not o or key is undefined
+
+ if _.isPlainObject key
+ return _.extend o, key
+
+ if (value ? def) is not undefined
+ return _.set o, key, value, def
+
+ return o[key]
+
+
+
+## Types
+_.mixin
+
+ basicTypeName: (o) ->
+ return if o is null then "null" else (class2name[objToString.call(o)] || "Object")
+
+ isWindow: (o) ->
+ return o and typeof o is "object" and "setInterval" of o
+
+ isPlainObject: (o) ->
+ # Must be an Object.
+ # Because of IE, we also have to check the presence of the constructor property.
+ # Make sure that DOM nodes and window objects don't pass through, as well
+ if not o or basicTypeName(o) is not "Object" or o.nodeType or _.isWindow(o)
+ return false
+
+ # Not own constructor property? must be Object
+ C = o.constructor
+ if C and not hasOwn.call(o, "constructor") and not hasOwn.call(C.prototype, "isPrototypeOf")
+ return false
+
+ # Own properties are enumerated firstly, so to speed up,
+ # if last one is own, then all properties are own.
+ for key in o
+ ; # semicolon **on new line** is required by coffeescript to denote empty statement.
+
+ return key is undefined or hasOwn.call(o, key)
+
+
+## Arrays
+_.mixin
+
+ toArray: (iterable, start=0, end=undefined) ->
+ _.slice toArray(iterable), start, end
+
+ flatten: (A) ->
+ _.reduce do
+ slice.call(arguments)
+ (flat, v) ->
+ flat.concat( if _.isArray v then _.reduce(v, arguments.callee, []) else v )
+ []
+
+
+
+## Functions
+_ofArity = _.memoize(
+ (n, limit) ->
+ args = ( '$'+i for i from 0 til n ).join(',')
+ name = ( if limit then 'limited' else 'artized' )
+ apply_with = ( if limit then "[].slice.call(arguments, 0, #{n})" else 'arguments' )
+ return eval "
+ (function #{name}(fn){
+ var _fn = function(#{args}){ return fn.apply(this, #{apply_with}); };
+ _fn.__wraps__ = fn;
+ return _(_fn);
+ })"
+ )
+
+_.mixin
+ methodize: methodize
+
+ unwrap: (fn) ->
+ (fn and _.isFunction(fn) and _.unwrap(fn.__wraps__)) or fn
+
+
+ partial: (fn, args...) ->
+ partially = ->
+ fn.apply this, args.concat(slice.call(arguments))
+ partially.__wraps__ = fn
+ return _ partially
+
+
+ genericize: (fn) ->
+ g = fn.__genericized__
+ return g if g
+
+ m = fn.__methodized__
+ return m.__wraps__ if m and m.__wraps__
+
+ g = fn.__genericized__ = (args...) ->
+ fn.apply args.shift(), args
+
+ g.__wraps__ = fn
+ return _ g
+
+
+ curry: (fn, args...) ->
+ if not _.isFunction fn
+ return fn
+
+ if fn.__curried__
+ return fn.apply this, args
+
+ L = fn.length or _.unwrap(fn).length
+ if args.length >= L
+ return fn.apply this, args
+
+ curried = ->
+ _args = args.concat slice.call(arguments)
+ if _args.length >= L
+ return fn.apply this, _args
+ _args.unshift fn
+ return _.curry.apply this, _args
+
+ curried.__wraps__ = fn
+ curried.__curried__ = args
+ return _ curried
+
+
+ flip: (fn) ->
+ f = fn.__flipped__
+ return f if f
+
+ f = fn.__flipped__ = \
+ flipped = ->
+ args = arguments
+ hd = args[0]
+ args[0] = args[1]
+ args[1] = hd
+ return fn.apply this, args
+
+ f.__wraps__ = fn
+ return _ f
+
+
+ aritize: (fn, n) ->
+ return fn if fn.length is n
+
+ cache = fn.__aritized__
+ if not cache
+ cache = fn.__aritized__ = {}
+ else if cache[n]
+ return cache[n]
+
+ return ( cache[n] = _ofArity(n, false)(fn) )
+
+
+ limit: (fn, n) ->
+ cache = fn.__limited__
+ if not cache
+ cache = fn.__limited__ = {}
+ else if cache[n]
+ return cache[n]
+
+ return ( cache[n] = _ofArity(n, true)(fn) )
+
+
+
+
+
+
+_.extend _pet, _
--- /dev/null
+//\r
+// showdown.js -- A javascript port of Markdown.\r
+//\r
+// Copyright (c) 2007 John Fraser.\r
+//\r
+// Original Markdown Copyright (c) 2004-2005 John Gruber\r
+// <http://daringfireball.net/projects/markdown/>\r
+//\r
+// Redistributable under a BSD-style open source license.\r
+// See license.txt for more information.\r
+//\r
+// The full source distribution is at:\r
+//\r
+// A A L\r
+// T C A\r
+// T K B\r
+//\r
+// <http://www.attacklab.net/>\r
+//\r
+\r
+//\r
+// Wherever possible, Showdown is a straight, line-by-line port\r
+// of the Perl version of Markdown.\r
+//\r
+// This is not a normal parser design; it's basically just a\r
+// series of string substitutions. It's hard to read and\r
+// maintain this way, but keeping Showdown close to the original\r
+// design makes it easier to port new features.\r
+//\r
+// More importantly, Showdown behaves like markdown.pl in most\r
+// edge cases. So web applications can do client-side preview\r
+// in Javascript, and then build identical HTML on the server.\r
+//\r
+// This port needs the new RegExp functionality of ECMA 262,\r
+// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers\r
+// should do fine. Even with the new regular expression features,\r
+// We do a lot of work to emulate Perl's regex functionality.\r
+// The tricky changes in this file mostly have the "attacklab:"\r
+// label. Major or self-explanatory changes don't.\r
+//\r
+// Smart diff tools like Araxis Merge will be able to match up\r
+// this file with markdown.pl in a useful way. A little tweaking\r
+// helps: in a copy of markdown.pl, replace "#" with "//" and\r
+// replace "$text" with "text". Be sure to ignore whitespace\r
+// and line endings.\r
+//\r
+\r
+\r
+//\r
+// Showdown usage:\r
+//\r
+// var text = "Markdown *rocks*.";\r
+//\r
+// var converter = new Showdown.converter();\r
+// var html = converter.makeHtml(text);\r
+//\r
+// alert(html);\r
+//\r
+// Note: move the sample code to the bottom of this\r
+// file before uncommenting it.\r
+//\r
+\r
+\r
+//\r
+// Showdown namespace\r
+//\r
+var Showdown = {};\r
+\r
+//\r
+// converter\r
+//\r
+// Wraps all "globals" so that the only thing\r
+// exposed is makeHtml().\r
+//\r
+Showdown.converter = function() {\r
+\r
+//\r
+// Globals:\r
+//\r
+\r
+// Global hashes, used by various utility routines\r
+var g_urls;\r
+var g_titles;\r
+var g_html_blocks;\r
+\r
+// Used to track when we're inside an ordered or unordered list\r
+// (see _ProcessListItems() for details):\r
+var g_list_level = 0;\r
+\r
+\r
+this.makeHtml = function(text) {\r
+//\r
+// Main function. The order in which other subs are called here is\r
+// essential. Link and image substitutions need to happen before\r
+// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>\r
+// and <img> tags get encoded.\r
+//\r
+\r
+ // Clear the global hashes. If we don't clear these, you get conflicts\r
+ // from other articles when generating a page which contains more than\r
+ // one article (e.g. an index page that shows the N most recent\r
+ // articles):\r
+ g_urls = new Array();\r
+ g_titles = new Array();\r
+ g_html_blocks = new Array();\r
+\r
+ // attacklab: Replace ~ with ~T\r
+ // This lets us use tilde as an escape char to avoid md5 hashes\r
+ // The choice of character is arbitray; anything that isn't\r
+ // magic in Markdown will work.\r
+ text = text.replace(/~/g,"~T");\r
+\r
+ // attacklab: Replace $ with ~D\r
+ // RegExp interprets $ as a special character\r
+ // when it's in a replacement string\r
+ text = text.replace(/\$/g,"~D");\r
+\r
+ // Standardize line endings\r
+ text = text.replace(/\r\n/g,"\n"); // DOS to Unix\r
+ text = text.replace(/\r/g,"\n"); // Mac to Unix\r
+\r
+ // Make sure text begins and ends with a couple of newlines:\r
+ text = "\n\n" + text + "\n\n";\r
+\r
+ // Convert all tabs to spaces.\r
+ text = _Detab(text);\r
+\r
+ // Strip any lines consisting only of spaces and tabs.\r
+ // This makes subsequent regexen easier to write, because we can\r
+ // match consecutive blank lines with /\n+/ instead of something\r
+ // contorted like /[ \t]*\n+/ .\r
+ text = text.replace(/^[ \t]+$/mg,"");\r
+\r
+ // Turn block-level HTML blocks into hash entries\r
+ text = _HashHTMLBlocks(text);\r
+\r
+ // Strip link definitions, store in hashes.\r
+ text = _StripLinkDefinitions(text);\r
+\r
+ text = _RunBlockGamut(text);\r
+\r
+ text = _UnescapeSpecialChars(text);\r
+\r
+ // attacklab: Restore dollar signs\r
+ text = text.replace(/~D/g,"$$");\r
+\r
+ // attacklab: Restore tildes\r
+ text = text.replace(/~T/g,"~");\r
+\r
+ return text;\r
+}\r
+\r
+\r
+var _StripLinkDefinitions = function(text) {\r
+//\r
+// Strips link definitions from text, stores the URLs and titles in\r
+// hash references.\r
+//\r
+\r
+ // Link defs are in the form: ^[id]: url "optional title"\r
+\r
+ /*\r
+ var text = text.replace(/\r
+ ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1\r
+ [ \t]*\r
+ \n? // maybe *one* newline\r
+ [ \t]*\r
+ <?(\S+?)>? // url = $2\r
+ [ \t]*\r
+ \n? // maybe one newline\r
+ [ \t]*\r
+ (?:\r
+ (\n*) // any lines skipped = $3 attacklab: lookbehind removed\r
+ ["(]\r
+ (.+?) // title = $4\r
+ [")]\r
+ [ \t]*\r
+ )? // title is optional\r
+ (?:\n+|$)\r
+ /gm,\r
+ function(){...});\r
+ */\r
+ var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,\r
+ function (wholeMatch,m1,m2,m3,m4) {\r
+ m1 = m1.toLowerCase();\r
+ g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive\r
+ if (m3) {\r
+ // Oops, found blank lines, so it's not a title.\r
+ // Put back the parenthetical statement we stole.\r
+ return m3+m4;\r
+ } else if (m4) {\r
+ g_titles[m1] = m4.replace(/"/g,""");\r
+ }\r
+ \r
+ // Completely remove the definition from the text\r
+ return "";\r
+ }\r
+ );\r
+\r
+ return text;\r
+}\r
+\r
+\r
+var _HashHTMLBlocks = function(text) {\r
+ // attacklab: Double up blank lines to reduce lookaround\r
+ text = text.replace(/\n/g,"\n\n");\r
+\r
+ // Hashify HTML blocks:\r
+ // We only want to do this for block-level HTML tags, such as headers,\r
+ // lists, and tables. That's because we still want to wrap <p>s around\r
+ // "paragraphs" that are wrapped in non-block-level tags, such as anchors,\r
+ // phrase emphasis, and spans. The list of tags we're looking for is\r
+ // hard-coded:\r
+ var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"\r
+ var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"\r
+\r
+ // First, look for nested blocks, e.g.:\r
+ // <div>\r
+ // <div>\r
+ // tags for inner block must be indented.\r
+ // </div>\r
+ // </div>\r
+ //\r
+ // The outermost tags must start at the left margin for this to match, and\r
+ // the inner nested divs must be indented.\r
+ // We need to do this before the next, more liberal match, because the next\r
+ // match will start at the first `<div>` and stop at the first `</div>`.\r
+\r
+ // attacklab: This regex can be expensive when it fails.\r
+ /*\r
+ var text = text.replace(/\r
+ ( // save in $1\r
+ ^ // start of line (with /m)\r
+ <($block_tags_a) // start tag = $2\r
+ \b // word break\r
+ // attacklab: hack around khtml/pcre bug...\r
+ [^\r]*?\n // any number of lines, minimally matching\r
+ </\2> // the matching end tag\r
+ [ \t]* // trailing spaces/tabs\r
+ (?=\n+) // followed by a newline\r
+ ) // attacklab: there are sentinel newlines at end of document\r
+ /gm,function(){...}};\r
+ */\r
+ 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);\r
+\r
+ //\r
+ // Now match more liberally, simply from `\n<tag>` to `</tag>\n`\r
+ //\r
+\r
+ /*\r
+ var text = text.replace(/\r
+ ( // save in $1\r
+ ^ // start of line (with /m)\r
+ <($block_tags_b) // start tag = $2\r
+ \b // word break\r
+ // attacklab: hack around khtml/pcre bug...\r
+ [^\r]*? // any number of lines, minimally matching\r
+ .*</\2> // the matching end tag\r
+ [ \t]* // trailing spaces/tabs\r
+ (?=\n+) // followed by a newline\r
+ ) // attacklab: there are sentinel newlines at end of document\r
+ /gm,function(){...}};\r
+ */\r
+ text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);\r
+\r
+ // Special case just for <hr />. It was easier to make a special case than\r
+ // to make the other regex more complicated. \r
+\r
+ /*\r
+ text = text.replace(/\r
+ ( // save in $1\r
+ \n\n // Starting after a blank line\r
+ [ ]{0,3}\r
+ (<(hr) // start tag = $2\r
+ \b // word break\r
+ ([^<>])*? // \r
+ \/?>) // the matching end tag\r
+ [ \t]*\r
+ (?=\n{2,}) // followed by a blank line\r
+ )\r
+ /g,hashElement);\r
+ */\r
+ text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);\r
+\r
+ // Special case for standalone HTML comments:\r
+\r
+ /*\r
+ text = text.replace(/\r
+ ( // save in $1\r
+ \n\n // Starting after a blank line\r
+ [ ]{0,3} // attacklab: g_tab_width - 1\r
+ <!\r
+ (--[^\r]*?--\s*)+\r
+ >\r
+ [ \t]*\r
+ (?=\n{2,}) // followed by a blank line\r
+ )\r
+ /g,hashElement);\r
+ */\r
+ text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);\r
+\r
+ // PHP and ASP-style processor instructions (<?...?> and <%...%>)\r
+\r
+ /*\r
+ text = text.replace(/\r
+ (?:\r
+ \n\n // Starting after a blank line\r
+ )\r
+ ( // save in $1\r
+ [ ]{0,3} // attacklab: g_tab_width - 1\r
+ (?:\r
+ <([?%]) // $2\r
+ [^\r]*?\r
+ \2>\r
+ )\r
+ [ \t]*\r
+ (?=\n{2,}) // followed by a blank line\r
+ )\r
+ /g,hashElement);\r
+ */\r
+ text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);\r
+\r
+ // attacklab: Undo double lines (see comment at top of this function)\r
+ text = text.replace(/\n\n/g,"\n");\r
+ return text;\r
+}\r
+\r
+var hashElement = function(wholeMatch,m1) {\r
+ var blockText = m1;\r
+\r
+ // Undo double lines\r
+ blockText = blockText.replace(/\n\n/g,"\n");\r
+ blockText = blockText.replace(/^\n/,"");\r
+ \r
+ // strip trailing blank lines\r
+ blockText = blockText.replace(/\n+$/g,"");\r
+ \r
+ // Replace the element text with a marker ("~KxK" where x is its key)\r
+ blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";\r
+ \r
+ return blockText;\r
+};\r
+\r
+var _RunBlockGamut = function(text) {\r
+//\r
+// These are all the transformations that form block-level\r
+// tags like paragraphs, headers, and list items.\r
+//\r
+ text = _DoHeaders(text);\r
+\r
+ // Do Horizontal Rules:\r
+ var key = hashBlock("<hr />");\r
+ text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);\r
+ text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);\r
+ text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);\r
+\r
+ text = _DoLists(text);\r
+ text = _DoCodeBlocks(text);\r
+ text = _DoBlockQuotes(text);\r
+\r
+ // We already ran _HashHTMLBlocks() before, in Markdown(), but that\r
+ // was to escape raw HTML in the original Markdown source. This time,\r
+ // we're escaping the markup we've just created, so that we don't wrap\r
+ // <p> tags around block-level tags.\r
+ text = _HashHTMLBlocks(text);\r
+ text = _FormParagraphs(text);\r
+\r
+ return text;\r
+}\r
+\r
+\r
+var _RunSpanGamut = function(text) {\r
+//\r
+// These are all the transformations that occur *within* block-level\r
+// tags like paragraphs, headers, and list items.\r
+//\r
+\r
+ text = _DoCodeSpans(text);\r
+ text = _EscapeSpecialCharsWithinTagAttributes(text);\r
+ text = _EncodeBackslashEscapes(text);\r
+\r
+ // Process anchor and image tags. Images must come first,\r
+ // because ![foo][f] looks like an anchor.\r
+ text = _DoImages(text);\r
+ text = _DoAnchors(text);\r
+\r
+ // Make links out of things like `<http://example.com/>`\r
+ // Must come after _DoAnchors(), because you can use < and >\r
+ // delimiters in inline links like [this](<url>).\r
+ text = _DoAutoLinks(text);\r
+ text = _EncodeAmpsAndAngles(text);\r
+ text = _DoItalicsAndBold(text);\r
+\r
+ // Do hard breaks:\r
+ text = text.replace(/ +\n/g," <br />\n");\r
+\r
+ return text;\r
+}\r
+\r
+var _EscapeSpecialCharsWithinTagAttributes = function(text) {\r
+//\r
+// Within tags -- meaning between < and > -- encode [\ ` * _] so they\r
+// don't conflict with their use in Markdown for code, italics and strong.\r
+//\r
+\r
+ // Build a regex to find HTML tags and comments. See Friedl's \r
+ // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.\r
+ var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;\r
+\r
+ text = text.replace(regex, function(wholeMatch) {\r
+ var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");\r
+ tag = escapeCharacters(tag,"\\`*_");\r
+ return tag;\r
+ });\r
+\r
+ return text;\r
+}\r
+\r
+var _DoAnchors = function(text) {\r
+//\r
+// Turn Markdown link shortcuts into XHTML <a> tags.\r
+//\r
+ //\r
+ // First, handle reference-style links: [link text] [id]\r
+ //\r
+\r
+ /*\r
+ text = text.replace(/\r
+ ( // wrap whole match in $1\r
+ \[\r
+ (\r
+ (?:\r
+ \[[^\]]*\] // allow brackets nested one level\r
+ |\r
+ [^\[] // or anything else\r
+ )*\r
+ )\r
+ \]\r
+\r
+ [ ]? // one optional space\r
+ (?:\n[ ]*)? // one optional newline followed by spaces\r
+\r
+ \[\r
+ (.*?) // id = $3\r
+ \]\r
+ )()()()() // pad remaining backreferences\r
+ /g,_DoAnchors_callback);\r
+ */\r
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);\r
+\r
+ //\r
+ // Next, inline-style links: [link text](url "optional title")\r
+ //\r
+\r
+ /*\r
+ text = text.replace(/\r
+ ( // wrap whole match in $1\r
+ \[\r
+ (\r
+ (?:\r
+ \[[^\]]*\] // allow brackets nested one level\r
+ |\r
+ [^\[\]] // or anything else\r
+ )\r
+ )\r
+ \]\r
+ \( // literal paren\r
+ [ \t]*\r
+ () // no id, so leave $3 empty\r
+ <?(.*?)>? // href = $4\r
+ [ \t]*\r
+ ( // $5\r
+ (['"]) // quote char = $6\r
+ (.*?) // Title = $7\r
+ \6 // matching quote\r
+ [ \t]* // ignore any spaces/tabs between closing quote and )\r
+ )? // title is optional\r
+ \)\r
+ )\r
+ /g,writeAnchorTag);\r
+ */\r
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);\r
+\r
+ //\r
+ // Last, handle reference-style shortcuts: [link text]\r
+ // These must come last in case you've also got [link test][1]\r
+ // or [link test](/foo)\r
+ //\r
+\r
+ /*\r
+ text = text.replace(/\r
+ ( // wrap whole match in $1\r
+ \[\r
+ ([^\[\]]+) // link text = $2; can't contain '[' or ']'\r
+ \]\r
+ )()()()()() // pad rest of backreferences\r
+ /g, writeAnchorTag);\r
+ */\r
+ text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);\r
+\r
+ return text;\r
+}\r