showdown.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341
  1. //
  2. // showdown.js -- A javascript port of Markdown.
  3. //
  4. // Copyright (c) 2007 John Fraser.
  5. //
  6. // Original Markdown Copyright (c) 2004-2005 John Gruber
  7. // <http://daringfireball.net/projects/markdown/>
  8. //
  9. // Redistributable under a BSD-style open source license.
  10. // See license.txt for more information.
  11. //
  12. // The full source distribution is at:
  13. //
  14. // A A L
  15. // T C A
  16. // T K B
  17. //
  18. // <http://www.attacklab.net/>
  19. //
  20. //
  21. // Wherever possible, Showdown is a straight, line-by-line port
  22. // of the Perl version of Markdown.
  23. //
  24. // This is not a normal parser design; it's basically just a
  25. // series of string substitutions. It's hard to read and
  26. // maintain this way, but keeping Showdown close to the original
  27. // design makes it easier to port new features.
  28. //
  29. // More importantly, Showdown behaves like markdown.pl in most
  30. // edge cases. So web applications can do client-side preview
  31. // in Javascript, and then build identical HTML on the server.
  32. //
  33. // This port needs the new RegExp functionality of ECMA 262,
  34. // 3rd Edition (i.e. Javascript 1.5). Most modern web browsers
  35. // should do fine. Even with the new regular expression features,
  36. // We do a lot of work to emulate Perl's regex functionality.
  37. // The tricky changes in this file mostly have the "attacklab:"
  38. // label. Major or self-explanatory changes don't.
  39. //
  40. // Smart diff tools like Araxis Merge will be able to match up
  41. // this file with markdown.pl in a useful way. A little tweaking
  42. // helps: in a copy of markdown.pl, replace "#" with "//" and
  43. // replace "$text" with "text". Be sure to ignore whitespace
  44. // and line endings.
  45. //
  46. //
  47. // Showdown usage:
  48. //
  49. // var text = "Markdown *rocks*.";
  50. //
  51. // var converter = new Showdown.converter();
  52. // var html = converter.makeHtml(text);
  53. //
  54. // alert(html);
  55. //
  56. // Note: move the sample code to the bottom of this
  57. // file before uncommenting it.
  58. //
  59. //
  60. // Showdown namespace
  61. //
  62. var Showdown = {};
  63. //
  64. // converter
  65. //
  66. // Wraps all "globals" so that the only thing
  67. // exposed is makeHtml().
  68. //
  69. Showdown.converter = function() {
  70. //
  71. // Globals:
  72. //
  73. // Global hashes, used by various utility routines
  74. var g_urls;
  75. var g_titles;
  76. var g_html_blocks;
  77. // Used to track when we're inside an ordered or unordered list
  78. // (see _ProcessListItems() for details):
  79. var g_list_level = 0;
  80. this.makeHtml = function(text) {
  81. //
  82. // Main function. The order in which other subs are called here is
  83. // essential. Link and image substitutions need to happen before
  84. // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
  85. // and <img> tags get encoded.
  86. //
  87. // Clear the global hashes. If we don't clear these, you get conflicts
  88. // from other articles when generating a page which contains more than
  89. // one article (e.g. an index page that shows the N most recent
  90. // articles):
  91. g_urls = new Array();
  92. g_titles = new Array();
  93. g_html_blocks = new Array();
  94. // attacklab: Replace ~ with ~T
  95. // This lets us use tilde as an escape char to avoid md5 hashes
  96. // The choice of character is arbitray; anything that isn't
  97. // magic in Markdown will work.
  98. text = text.replace(/~/g,"~T");
  99. // attacklab: Replace $ with ~D
  100. // RegExp interprets $ as a special character
  101. // when it's in a replacement string
  102. text = text.replace(/\$/g,"~D");
  103. // Standardize line endings
  104. text = text.replace(/\r\n/g,"\n"); // DOS to Unix
  105. text = text.replace(/\r/g,"\n"); // Mac to Unix
  106. // Make sure text begins and ends with a couple of newlines:
  107. text = "\n\n" + text + "\n\n";
  108. // Convert all tabs to spaces.
  109. text = _Detab(text);
  110. // Strip any lines consisting only of spaces and tabs.
  111. // This makes subsequent regexen easier to write, because we can
  112. // match consecutive blank lines with /\n+/ instead of something
  113. // contorted like /[ \t]*\n+/ .
  114. text = text.replace(/^[ \t]+$/mg,"");
  115. // Handle github codeblocks prior to running HashHTML so that
  116. // HTML contained within the codeblock gets escaped propertly
  117. text = _DoGithubCodeBlocks(text);
  118. // Turn block-level HTML blocks into hash entries
  119. text = _HashHTMLBlocks(text);
  120. // Strip link definitions, store in hashes.
  121. text = _StripLinkDefinitions(text);
  122. text = _RunBlockGamut(text);
  123. text = _UnescapeSpecialChars(text);
  124. // attacklab: Restore dollar signs
  125. text = text.replace(/~D/g,"$$");
  126. // attacklab: Restore tildes
  127. text = text.replace(/~T/g,"~");
  128. return text;
  129. };
  130. var _StripLinkDefinitions = function(text) {
  131. //
  132. // Strips link definitions from text, stores the URLs and titles in
  133. // hash references.
  134. //
  135. // Link defs are in the form: ^[id]: url "optional title"
  136. /*
  137. var text = text.replace(/
  138. ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
  139. [ \t]*
  140. \n? // maybe *one* newline
  141. [ \t]*
  142. <?(\S+?)>? // url = $2
  143. [ \t]*
  144. \n? // maybe one newline
  145. [ \t]*
  146. (?:
  147. (\n*) // any lines skipped = $3 attacklab: lookbehind removed
  148. ["(]
  149. (.+?) // title = $4
  150. [")]
  151. [ \t]*
  152. )? // title is optional
  153. (?:\n+|$)
  154. /gm,
  155. function(){...});
  156. */
  157. var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,
  158. function (wholeMatch,m1,m2,m3,m4) {
  159. m1 = m1.toLowerCase();
  160. g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive
  161. if (m3) {
  162. // Oops, found blank lines, so it's not a title.
  163. // Put back the parenthetical statement we stole.
  164. return m3+m4;
  165. } else if (m4) {
  166. g_titles[m1] = m4.replace(/"/g,"&quot;");
  167. }
  168. // Completely remove the definition from the text
  169. return "";
  170. }
  171. );
  172. return text;
  173. }
  174. var _HashHTMLBlocks = function(text) {
  175. // attacklab: Double up blank lines to reduce lookaround
  176. text = text.replace(/\n/g,"\n\n");
  177. // Hashify HTML blocks:
  178. // We only want to do this for block-level HTML tags, such as headers,
  179. // lists, and tables. That's because we still want to wrap <p>s around
  180. // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
  181. // phrase emphasis, and spans. The list of tags we're looking for is
  182. // hard-coded:
  183. 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";
  184. 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";
  185. // First, look for nested blocks, e.g.:
  186. // <div>
  187. // <div>
  188. // tags for inner block must be indented.
  189. // </div>
  190. // </div>
  191. //
  192. // The outermost tags must start at the left margin for this to match, and
  193. // the inner nested divs must be indented.
  194. // We need to do this before the next, more liberal match, because the next
  195. // match will start at the first `<div>` and stop at the first `</div>`.
  196. // attacklab: This regex can be expensive when it fails.
  197. /*
  198. var text = text.replace(/
  199. ( // save in $1
  200. ^ // start of line (with /m)
  201. <($block_tags_a) // start tag = $2
  202. \b // word break
  203. // attacklab: hack around khtml/pcre bug...
  204. [^\r]*?\n // any number of lines, minimally matching
  205. </\2> // the matching end tag
  206. [ \t]* // trailing spaces/tabs
  207. (?=\n+) // followed by a newline
  208. ) // attacklab: there are sentinel newlines at end of document
  209. /gm,function(){...}};
  210. */
  211. 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);
  212. //
  213. // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
  214. //
  215. /*
  216. var text = text.replace(/
  217. ( // save in $1
  218. ^ // start of line (with /m)
  219. <($block_tags_b) // start tag = $2
  220. \b // word break
  221. // attacklab: hack around khtml/pcre bug...
  222. [^\r]*? // any number of lines, minimally matching
  223. .*</\2> // the matching end tag
  224. [ \t]* // trailing spaces/tabs
  225. (?=\n+) // followed by a newline
  226. ) // attacklab: there are sentinel newlines at end of document
  227. /gm,function(){...}};
  228. */
  229. 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);
  230. // Special case just for <hr />. It was easier to make a special case than
  231. // to make the other regex more complicated.
  232. /*
  233. text = text.replace(/
  234. ( // save in $1
  235. \n\n // Starting after a blank line
  236. [ ]{0,3}
  237. (<(hr) // start tag = $2
  238. \b // word break
  239. ([^<>])*? //
  240. \/?>) // the matching end tag
  241. [ \t]*
  242. (?=\n{2,}) // followed by a blank line
  243. )
  244. /g,hashElement);
  245. */
  246. text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
  247. // Special case for standalone HTML comments:
  248. /*
  249. text = text.replace(/
  250. ( // save in $1
  251. \n\n // Starting after a blank line
  252. [ ]{0,3} // attacklab: g_tab_width - 1
  253. <!
  254. (--[^\r]*?--\s*)+
  255. >
  256. [ \t]*
  257. (?=\n{2,}) // followed by a blank line
  258. )
  259. /g,hashElement);
  260. */
  261. text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
  262. // PHP and ASP-style processor instructions (<?...?> and <%...%>)
  263. /*
  264. text = text.replace(/
  265. (?:
  266. \n\n // Starting after a blank line
  267. )
  268. ( // save in $1
  269. [ ]{0,3} // attacklab: g_tab_width - 1
  270. (?:
  271. <([?%]) // $2
  272. [^\r]*?
  273. \2>
  274. )
  275. [ \t]*
  276. (?=\n{2,}) // followed by a blank line
  277. )
  278. /g,hashElement);
  279. */
  280. text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
  281. // attacklab: Undo double lines (see comment at top of this function)
  282. text = text.replace(/\n\n/g,"\n");
  283. return text;
  284. }
  285. var hashElement = function(wholeMatch,m1) {
  286. var blockText = m1;
  287. // Undo double lines
  288. blockText = blockText.replace(/\n\n/g,"\n");
  289. blockText = blockText.replace(/^\n/,"");
  290. // strip trailing blank lines
  291. blockText = blockText.replace(/\n+$/g,"");
  292. // Replace the element text with a marker ("~KxK" where x is its key)
  293. blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
  294. return blockText;
  295. };
  296. var _RunBlockGamut = function(text) {
  297. //
  298. // These are all the transformations that form block-level
  299. // tags like paragraphs, headers, and list items.
  300. //
  301. text = _DoHeaders(text);
  302. // Do Horizontal Rules:
  303. var key = hashBlock("<hr />");
  304. text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
  305. text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
  306. text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
  307. text = _DoLists(text);
  308. text = _DoCodeBlocks(text);
  309. text = _DoBlockQuotes(text);
  310. // We already ran _HashHTMLBlocks() before, in Markdown(), but that
  311. // was to escape raw HTML in the original Markdown source. This time,
  312. // we're escaping the markup we've just created, so that we don't wrap
  313. // <p> tags around block-level tags.
  314. text = _HashHTMLBlocks(text);
  315. text = _FormParagraphs(text);
  316. return text;
  317. };
  318. var _RunSpanGamut = function(text) {
  319. //
  320. // These are all the transformations that occur *within* block-level
  321. // tags like paragraphs, headers, and list items.
  322. //
  323. text = _DoCodeSpans(text);
  324. text = _EscapeSpecialCharsWithinTagAttributes(text);
  325. text = _EncodeBackslashEscapes(text);
  326. // Process anchor and image tags. Images must come first,
  327. // because ![foo][f] looks like an anchor.
  328. text = _DoImages(text);
  329. text = _DoAnchors(text);
  330. // Make links out of things like `<http://example.com/>`
  331. // Must come after _DoAnchors(), because you can use < and >
  332. // delimiters in inline links like [this](<url>).
  333. text = _DoAutoLinks(text);
  334. text = _EncodeAmpsAndAngles(text);
  335. text = _DoItalicsAndBold(text);
  336. // Do hard breaks:
  337. text = text.replace(/ +\n/g," <br />\n");
  338. return text;
  339. }
  340. var _EscapeSpecialCharsWithinTagAttributes = function(text) {
  341. //
  342. // Within tags -- meaning between < and > -- encode [\ ` * _] so they
  343. // don't conflict with their use in Markdown for code, italics and strong.
  344. //
  345. // Build a regex to find HTML tags and comments. See Friedl's
  346. // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
  347. var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
  348. text = text.replace(regex, function(wholeMatch) {
  349. var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
  350. tag = escapeCharacters(tag,"\\`*_");
  351. return tag;
  352. });
  353. return text;
  354. }
  355. var _DoAnchors = function(text) {
  356. //
  357. // Turn Markdown link shortcuts into XHTML <a> tags.
  358. //
  359. //
  360. // First, handle reference-style links: [link text] [id]
  361. //
  362. /*
  363. text = text.replace(/
  364. ( // wrap whole match in $1
  365. \[
  366. (
  367. (?:
  368. \[[^\]]*\] // allow brackets nested one level
  369. |
  370. [^\[] // or anything else
  371. )*
  372. )
  373. \]
  374. [ ]? // one optional space
  375. (?:\n[ ]*)? // one optional newline followed by spaces
  376. \[
  377. (.*?) // id = $3
  378. \]
  379. )()()()() // pad remaining backreferences
  380. /g,_DoAnchors_callback);
  381. */
  382. text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);
  383. //
  384. // Next, inline-style links: [link text](url "optional title")
  385. //
  386. /*
  387. text = text.replace(/
  388. ( // wrap whole match in $1
  389. \[
  390. (
  391. (?:
  392. \[[^\]]*\] // allow brackets nested one level
  393. |
  394. [^\[\]] // or anything else
  395. )
  396. )
  397. \]
  398. \( // literal paren
  399. [ \t]*
  400. () // no id, so leave $3 empty
  401. <?(.*?)>? // href = $4
  402. [ \t]*
  403. ( // $5
  404. (['"]) // quote char = $6
  405. (.*?) // Title = $7
  406. \6 // matching quote
  407. [ \t]* // ignore any spaces/tabs between closing quote and )
  408. )? // title is optional
  409. \)
  410. )
  411. /g,writeAnchorTag);
  412. */
  413. text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
  414. //
  415. // Last, handle reference-style shortcuts: [link text]
  416. // These must come last in case you've also got [link test][1]
  417. // or [link test](/foo)
  418. //
  419. /*
  420. text = text.replace(/
  421. ( // wrap whole match in $1
  422. \[
  423. ([^\[\]]+) // link text = $2; can't contain '[' or ']'
  424. \]
  425. )()()()()() // pad rest of backreferences
  426. /g, writeAnchorTag);
  427. */
  428. text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
  429. return text;
  430. }
  431. var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
  432. if (m7 == undefined) m7 = "";
  433. var whole_match = m1;
  434. var link_text = m2;
  435. var link_id = m3.toLowerCase();
  436. var url = m4;
  437. var title = m7;
  438. if (url == "") {
  439. if (link_id == "") {
  440. // lower-case and turn embedded newlines into spaces
  441. link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
  442. }
  443. url = "#"+link_id;
  444. if (g_urls[link_id] != undefined) {
  445. url = g_urls[link_id];
  446. if (g_titles[link_id] != undefined) {
  447. title = g_titles[link_id];
  448. }
  449. }
  450. else {
  451. if (whole_match.search(/\(\s*\)$/m)>-1) {
  452. // Special case for explicit empty url
  453. url = "";
  454. } else {
  455. return whole_match;
  456. }
  457. }
  458. }
  459. url = escapeCharacters(url,"*_");
  460. var result = "<a href=\"" + url + "\"";
  461. if (title != "") {
  462. title = title.replace(/"/g,"&quot;");
  463. title = escapeCharacters(title,"*_");
  464. result += " title=\"" + title + "\"";
  465. }
  466. result += ">" + link_text + "</a>";
  467. return result;
  468. }
  469. var _DoImages = function(text) {
  470. //
  471. // Turn Markdown image shortcuts into <img> tags.
  472. //
  473. //
  474. // First, handle reference-style labeled images: ![alt text][id]
  475. //
  476. /*
  477. text = text.replace(/
  478. ( // wrap whole match in $1
  479. !\[
  480. (.*?) // alt text = $2
  481. \]
  482. [ ]? // one optional space
  483. (?:\n[ ]*)? // one optional newline followed by spaces
  484. \[
  485. (.*?) // id = $3
  486. \]
  487. )()()()() // pad rest of backreferences
  488. /g,writeImageTag);
  489. */
  490. text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
  491. //
  492. // Next, handle inline images: ![alt text](url "optional title")
  493. // Don't forget: encode * and _
  494. /*
  495. text = text.replace(/
  496. ( // wrap whole match in $1
  497. !\[
  498. (.*?) // alt text = $2
  499. \]
  500. \s? // One optional whitespace character
  501. \( // literal paren
  502. [ \t]*
  503. () // no id, so leave $3 empty
  504. <?(\S+?)>? // src url = $4
  505. [ \t]*
  506. ( // $5
  507. (['"]) // quote char = $6
  508. (.*?) // title = $7
  509. \6 // matching quote
  510. [ \t]*
  511. )? // title is optional
  512. \)
  513. )
  514. /g,writeImageTag);
  515. */
  516. text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);
  517. return text;
  518. }
  519. var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
  520. var whole_match = m1;
  521. var alt_text = m2;
  522. var link_id = m3.toLowerCase();
  523. var url = m4;
  524. var title = m7;
  525. if (!title) title = "";
  526. if (url == "") {
  527. if (link_id == "") {
  528. // lower-case and turn embedded newlines into spaces
  529. link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
  530. }
  531. url = "#"+link_id;
  532. if (g_urls[link_id] != undefined) {
  533. url = g_urls[link_id];
  534. if (g_titles[link_id] != undefined) {
  535. title = g_titles[link_id];
  536. }
  537. }
  538. else {
  539. return whole_match;
  540. }
  541. }
  542. alt_text = alt_text.replace(/"/g,"&quot;");
  543. url = escapeCharacters(url,"*_");
  544. var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
  545. // attacklab: Markdown.pl adds empty title attributes to images.
  546. // Replicate this bug.
  547. //if (title != "") {
  548. title = title.replace(/"/g,"&quot;");
  549. title = escapeCharacters(title,"*_");
  550. result += " title=\"" + title + "\"";
  551. //}
  552. result += " />";
  553. return result;
  554. }
  555. var _DoHeaders = function(text) {
  556. // Setext-style headers:
  557. // Header 1
  558. // ========
  559. //
  560. // Header 2
  561. // --------
  562. //
  563. text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
  564. function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});
  565. text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
  566. function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});
  567. // atx-style headers:
  568. // # Header 1
  569. // ## Header 2
  570. // ## Header 2 with closing hashes ##
  571. // ...
  572. // ###### Header 6
  573. //
  574. /*
  575. text = text.replace(/
  576. ^(\#{1,6}) // $1 = string of #'s
  577. [ \t]*
  578. (.+?) // $2 = Header text
  579. [ \t]*
  580. \#* // optional closing #'s (not counted)
  581. \n+
  582. /gm, function() {...});
  583. */
  584. text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
  585. function(wholeMatch,m1,m2) {
  586. var h_level = m1.length;
  587. return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");
  588. });
  589. function headerId(m) {
  590. return m.replace(/[^\w]/g, '').toLowerCase();
  591. }
  592. return text;
  593. }
  594. // This declaration keeps Dojo compressor from outputting garbage:
  595. var _ProcessListItems;
  596. var _DoLists = function(text) {
  597. //
  598. // Form HTML ordered (numbered) and unordered (bulleted) lists.
  599. //
  600. // attacklab: add sentinel to hack around khtml/safari bug:
  601. // http://bugs.webkit.org/show_bug.cgi?id=11231
  602. text += "~0";
  603. // Re-usable pattern to match any entirel ul or ol list:
  604. /*
  605. var whole_list = /
  606. ( // $1 = whole list
  607. ( // $2
  608. [ ]{0,3} // attacklab: g_tab_width - 1
  609. ([*+-]|\d+[.]) // $3 = first list item marker
  610. [ \t]+
  611. )
  612. [^\r]+?
  613. ( // $4
  614. ~0 // sentinel for workaround; should be $
  615. |
  616. \n{2,}
  617. (?=\S)
  618. (?! // Negative lookahead for another list item marker
  619. [ \t]*
  620. (?:[*+-]|\d+[.])[ \t]+
  621. )
  622. )
  623. )/g
  624. */
  625. var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
  626. if (g_list_level) {
  627. text = text.replace(whole_list,function(wholeMatch,m1,m2) {
  628. var list = m1;
  629. var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
  630. // Turn double returns into triple returns, so that we can make a
  631. // paragraph for the last item in a list, if necessary:
  632. list = list.replace(/\n{2,}/g,"\n\n\n");;
  633. var result = _ProcessListItems(list);
  634. // Trim any trailing whitespace, to put the closing `</$list_type>`
  635. // up on the preceding line, to get it past the current stupid
  636. // HTML block parser. This is a hack to work around the terrible
  637. // hack that is the HTML block parser.
  638. result = result.replace(/\s+$/,"");
  639. result = "<"+list_type+">" + result + "</"+list_type+">\n";
  640. return result;
  641. });
  642. } else {
  643. whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
  644. text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
  645. var runup = m1;
  646. var list = m2;
  647. var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
  648. // Turn double returns into triple returns, so that we can make a
  649. // paragraph for the last item in a list, if necessary:
  650. var list = list.replace(/\n{2,}/g,"\n\n\n");;
  651. var result = _ProcessListItems(list);
  652. result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
  653. return result;
  654. });
  655. }
  656. // attacklab: strip sentinel
  657. text = text.replace(/~0/,"");
  658. return text;
  659. }
  660. _ProcessListItems = function(list_str) {
  661. //
  662. // Process the contents of a single ordered or unordered list, splitting it
  663. // into individual list items.
  664. //
  665. // The $g_list_level global keeps track of when we're inside a list.
  666. // Each time we enter a list, we increment it; when we leave a list,
  667. // we decrement. If it's zero, we're not in a list anymore.
  668. //
  669. // We do this because when we're not inside a list, we want to treat
  670. // something like this:
  671. //
  672. // I recommend upgrading to version
  673. // 8. Oops, now this line is treated
  674. // as a sub-list.
  675. //
  676. // As a single paragraph, despite the fact that the second line starts
  677. // with a digit-period-space sequence.
  678. //
  679. // Whereas when we're inside a list (or sub-list), that line will be
  680. // treated as the start of a sub-list. What a kludge, huh? This is
  681. // an aspect of Markdown's syntax that's hard to parse perfectly
  682. // without resorting to mind-reading. Perhaps the solution is to
  683. // change the syntax rules such that sub-lists must start with a
  684. // starting cardinal number; e.g. "1." or "a.".
  685. g_list_level++;
  686. // trim trailing blank lines:
  687. list_str = list_str.replace(/\n{2,}$/,"\n");
  688. // attacklab: add sentinel to emulate \z
  689. list_str += "~0";
  690. /*
  691. list_str = list_str.replace(/
  692. (\n)? // leading line = $1
  693. (^[ \t]*) // leading whitespace = $2
  694. ([*+-]|\d+[.]) [ \t]+ // list marker = $3
  695. ([^\r]+? // list item text = $4
  696. (\n{1,2}))
  697. (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
  698. /gm, function(){...});
  699. */
  700. list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
  701. function(wholeMatch,m1,m2,m3,m4){
  702. var item = m4;
  703. var leading_line = m1;
  704. var leading_space = m2;
  705. if (leading_line || (item.search(/\n{2,}/)>-1)) {
  706. item = _RunBlockGamut(_Outdent(item));
  707. }
  708. else {
  709. // Recursion for sub-lists:
  710. item = _DoLists(_Outdent(item));
  711. item = item.replace(/\n$/,""); // chomp(item)
  712. item = _RunSpanGamut(item);
  713. }
  714. return "<li>" + item + "</li>\n";
  715. }
  716. );
  717. // attacklab: strip sentinel
  718. list_str = list_str.replace(/~0/g,"");
  719. g_list_level--;
  720. return list_str;
  721. }
  722. var _DoCodeBlocks = function(text) {
  723. //
  724. // Process Markdown `<pre><code>` blocks.
  725. //
  726. /*
  727. text = text.replace(text,
  728. /(?:\n\n|^)
  729. ( // $1 = the code block -- one or more lines, starting with a space/tab
  730. (?:
  731. (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
  732. .*\n+
  733. )+
  734. )
  735. (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
  736. /g,function(){...});
  737. */
  738. // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
  739. text += "~0";
  740. text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
  741. function(wholeMatch,m1,m2) {
  742. var codeblock = m1;
  743. var nextChar = m2;
  744. codeblock = _EncodeCode( _Outdent(codeblock));
  745. codeblock = _Detab(codeblock);
  746. codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
  747. codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
  748. codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
  749. return hashBlock(codeblock) + nextChar;
  750. }
  751. );
  752. // attacklab: strip sentinel
  753. text = text.replace(/~0/,"");
  754. return text;
  755. };
  756. var _DoGithubCodeBlocks = function(text) {
  757. //
  758. // Process Github-style code blocks
  759. // Example:
  760. // ```ruby
  761. // def hello_world(x)
  762. // puts "Hello, #{x}"
  763. // end
  764. // ```
  765. //
  766. // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
  767. text += "~0";
  768. text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,
  769. function(wholeMatch,m1,m2) {
  770. var language = m1;
  771. var codeblock = m2;
  772. codeblock = _EncodeCode(codeblock);
  773. codeblock = _Detab(codeblock);
  774. codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
  775. codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
  776. codeblock = "<pre><code" + (language ? " class=\"" + language + '"' : "") + ">" + codeblock + "\n</code></pre>";
  777. return hashBlock(codeblock);
  778. }
  779. );
  780. // attacklab: strip sentinel
  781. text = text.replace(/~0/,"");
  782. return text;
  783. }
  784. var hashBlock = function(text) {
  785. text = text.replace(/(^\n+|\n+$)/g,"");
  786. return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
  787. }
  788. var _DoCodeSpans = function(text) {
  789. //
  790. // * Backtick quotes are used for <code></code> spans.
  791. //
  792. // * You can use multiple backticks as the delimiters if you want to
  793. // include literal backticks in the code span. So, this input:
  794. //
  795. // Just type ``foo `bar` baz`` at the prompt.
  796. //
  797. // Will translate to:
  798. //
  799. // <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
  800. //
  801. // There's no arbitrary limit to the number of backticks you
  802. // can use as delimters. If you need three consecutive backticks
  803. // in your code, use four for delimiters, etc.
  804. //
  805. // * You can use spaces to get literal backticks at the edges:
  806. //
  807. // ... type `` `bar` `` ...
  808. //
  809. // Turns to:
  810. //
  811. // ... type <code>`bar`</code> ...
  812. //
  813. /*
  814. text = text.replace(/
  815. (^|[^\\]) // Character before opening ` can't be a backslash
  816. (`+) // $2 = Opening run of `
  817. ( // $3 = The code block
  818. [^\r]*?
  819. [^`] // attacklab: work around lack of lookbehind
  820. )
  821. \2 // Matching closer
  822. (?!`)
  823. /gm, function(){...});
  824. */
  825. text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
  826. function(wholeMatch,m1,m2,m3,m4) {
  827. var c = m3;
  828. c = c.replace(/^([ \t]*)/g,""); // leading whitespace
  829. c = c.replace(/[ \t]*$/g,""); // trailing whitespace
  830. c = _EncodeCode(c);
  831. return m1+"<code>"+c+"</code>";
  832. });
  833. return text;
  834. }
  835. var _EncodeCode = function(text) {
  836. //
  837. // Encode/escape certain characters inside Markdown code runs.
  838. // The point is that in code, these characters are literals,
  839. // and lose their special Markdown meanings.
  840. //
  841. // Encode all ampersands; HTML entities are not
  842. // entities within a Markdown code span.
  843. text = text.replace(/&/g,"&amp;");
  844. // Do the angle bracket song and dance:
  845. text = text.replace(/</g,"&lt;");
  846. text = text.replace(/>/g,"&gt;");
  847. // Now, escape characters that are magic in Markdown:
  848. text = escapeCharacters(text,"\*_{}[]\\",false);
  849. // jj the line above breaks this:
  850. //---
  851. //* Item
  852. // 1. Subitem
  853. // special char: *
  854. //---
  855. return text;
  856. }
  857. var _DoItalicsAndBold = function(text) {
  858. // <strong> must go first:
  859. text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
  860. "<strong>$2</strong>");
  861. text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
  862. "<em>$2</em>");
  863. return text;
  864. }
  865. var _DoBlockQuotes = function(text) {
  866. /*
  867. text = text.replace(/
  868. ( // Wrap whole match in $1
  869. (
  870. ^[ \t]*>[ \t]? // '>' at the start of a line
  871. .+\n // rest of the first line
  872. (.+\n)* // subsequent consecutive lines
  873. \n* // blanks
  874. )+
  875. )
  876. /gm, function(){...});
  877. */
  878. text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
  879. function(wholeMatch,m1) {
  880. var bq = m1;
  881. // attacklab: hack around Konqueror 3.5.4 bug:
  882. // "----------bug".replace(/^-/g,"") == "bug"
  883. bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
  884. // attacklab: clean up hack
  885. bq = bq.replace(/~0/g,"");
  886. bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
  887. bq = _RunBlockGamut(bq); // recurse
  888. bq = bq.replace(/(^|\n)/g,"$1 ");
  889. // These leading spaces screw with <pre> content, so we need to fix that:
  890. bq = bq.replace(
  891. /(\s*<pre>[^\r]+?<\/pre>)/gm,
  892. function(wholeMatch,m1) {
  893. var pre = m1;
  894. // attacklab: hack around Konqueror 3.5.4 bug:
  895. pre = pre.replace(/^ /mg,"~0");
  896. pre = pre.replace(/~0/g,"");
  897. return pre;
  898. });
  899. return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
  900. });
  901. return text;
  902. }
  903. var _FormParagraphs = function(text) {
  904. //
  905. // Params:
  906. // $text - string to process with html <p> tags
  907. //
  908. // Strip leading and trailing lines:
  909. text = text.replace(/^\n+/g,"");
  910. text = text.replace(/\n+$/g,"");
  911. var grafs = text.split(/\n{2,}/g);
  912. var grafsOut = new Array();
  913. //
  914. // Wrap <p> tags.
  915. //
  916. var end = grafs.length;
  917. for (var i=0; i<end; i++) {
  918. var str = grafs[i];
  919. // if this is an HTML marker, copy it
  920. if (str.search(/~K(\d+)K/g) >= 0) {
  921. grafsOut.push(str);
  922. }
  923. else if (str.search(/\S/) >= 0) {
  924. str = _RunSpanGamut(str);
  925. str = str.replace(/^([ \t]*)/g,"<p>");
  926. str += "</p>"
  927. grafsOut.push(str);
  928. }
  929. }
  930. //
  931. // Unhashify HTML blocks
  932. //
  933. end = grafsOut.length;
  934. for (var i=0; i<end; i++) {
  935. // if this is a marker for an html block...
  936. while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
  937. var blockText = g_html_blocks[RegExp.$1];
  938. blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
  939. grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
  940. }
  941. }
  942. return grafsOut.join("\n\n");
  943. }
  944. var _EncodeAmpsAndAngles = function(text) {
  945. // Smart processing for ampersands and angle brackets that need to be encoded.
  946. // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
  947. // http://bumppo.net/projects/amputator/
  948. text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
  949. // Encode naked <'s
  950. text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
  951. return text;
  952. }
  953. var _EncodeBackslashEscapes = function(text) {
  954. //
  955. // Parameter: String.
  956. // Returns: The string, with after processing the following backslash
  957. // escape sequences.
  958. //
  959. // attacklab: The polite way to do this is with the new
  960. // escapeCharacters() function:
  961. //
  962. // text = escapeCharacters(text,"\\",true);
  963. // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
  964. //
  965. // ...but we're sidestepping its use of the (slow) RegExp constructor
  966. // as an optimization for Firefox. This function gets called a LOT.
  967. text = text.replace(/\\(\\)/g,escapeCharacters_callback);
  968. text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
  969. return text;
  970. }
  971. var _DoAutoLinks = function(text) {
  972. text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");
  973. // Email addresses: <address@domain.foo>
  974. /*
  975. text = text.replace(/
  976. <
  977. (?:mailto:)?
  978. (
  979. [-.\w]+
  980. \@
  981. [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
  982. )
  983. >
  984. /gi, _DoAutoLinks_callback());
  985. */
  986. text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
  987. function(wholeMatch,m1) {
  988. return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
  989. }
  990. );
  991. return text;
  992. }
  993. var _EncodeEmailAddress = function(addr) {
  994. //
  995. // Input: an email address, e.g. "foo@example.com"
  996. //
  997. // Output: the email address as a mailto link, with each character
  998. // of the address encoded as either a decimal or hex entity, in
  999. // the hopes of foiling most address harvesting spam bots. E.g.:
  1000. //
  1001. // <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
  1002. // x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
  1003. // &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
  1004. //
  1005. // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
  1006. // mailing list: <http://tinyurl.com/yu7ue>
  1007. //
  1008. // attacklab: why can't javascript speak hex?
  1009. function char2hex(ch) {
  1010. var hexDigits = '0123456789ABCDEF';
  1011. var dec = ch.charCodeAt(0);
  1012. return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
  1013. }
  1014. var encode = [
  1015. function(ch){return "&#"+ch.charCodeAt(0)+";";},
  1016. function(ch){return "&#x"+char2hex(ch)+";";},
  1017. function(ch){return ch;}
  1018. ];
  1019. addr = "mailto:" + addr;
  1020. addr = addr.replace(/./g, function(ch) {
  1021. if (ch == "@") {
  1022. // this *must* be encoded. I insist.
  1023. ch = encode[Math.floor(Math.random()*2)](ch);
  1024. } else if (ch !=":") {
  1025. // leave ':' alone (to spot mailto: later)
  1026. var r = Math.random();
  1027. // roughly 10% raw, 45% hex, 45% dec
  1028. ch = (
  1029. r > .9 ? encode[2](ch) :
  1030. r > .45 ? encode[1](ch) :
  1031. encode[0](ch)
  1032. );
  1033. }
  1034. return ch;
  1035. });
  1036. addr = "<a href=\"" + addr + "\">" + addr + "</a>";
  1037. addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
  1038. return addr;
  1039. }
  1040. var _UnescapeSpecialChars = function(text) {
  1041. //
  1042. // Swap back in all the special characters we've hidden.
  1043. //
  1044. text = text.replace(/~E(\d+)E/g,
  1045. function(wholeMatch,m1) {
  1046. var charCodeToReplace = parseInt(m1);
  1047. return String.fromCharCode(charCodeToReplace);
  1048. }
  1049. );
  1050. return text;
  1051. }
  1052. var _Outdent = function(text) {
  1053. //
  1054. // Remove one level of line-leading tabs or spaces
  1055. //
  1056. // attacklab: hack around Konqueror 3.5.4 bug:
  1057. // "----------bug".replace(/^-/g,"") == "bug"
  1058. text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
  1059. // attacklab: clean up hack
  1060. text = text.replace(/~0/g,"")
  1061. return text;
  1062. }
  1063. var _Detab = function(text) {
  1064. // attacklab: Detab's completely rewritten for speed.
  1065. // In perl we could fix it by anchoring the regexp with \G.
  1066. // In javascript we're less fortunate.
  1067. // expand first n-1 tabs
  1068. text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
  1069. // replace the nth with two sentinels
  1070. text = text.replace(/\t/g,"~A~B");
  1071. // use the sentinel to anchor our regex so it doesn't explode
  1072. text = text.replace(/~B(.+?)~A/g,
  1073. function(wholeMatch,m1,m2) {
  1074. var leadingText = m1;
  1075. var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
  1076. // there *must* be a better way to do this:
  1077. for (var i=0; i<numSpaces; i++) leadingText+=" ";
  1078. return leadingText;
  1079. }
  1080. );
  1081. // clean up sentinels
  1082. text = text.replace(/~A/g," "); // attacklab: g_tab_width
  1083. text = text.replace(/~B/g,"");
  1084. return text;
  1085. }
  1086. //
  1087. // attacklab: Utility functions
  1088. //
  1089. var escapeCharacters = function(text, charsToEscape, afterBackslash) {
  1090. // First we have to escape the escape characters so that
  1091. // we can build a character class out of them
  1092. var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";
  1093. if (afterBackslash) {
  1094. regexString = "\\\\" + regexString;
  1095. }
  1096. var regex = new RegExp(regexString,"g");
  1097. text = text.replace(regex,escapeCharacters_callback);
  1098. return text;
  1099. }
  1100. var escapeCharacters_callback = function(wholeMatch,m1) {
  1101. var charCodeToEscape = m1.charCodeAt(0);
  1102. return "~E"+charCodeToEscape+"E";
  1103. }
  1104. } // end of Showdown.converter
  1105. // export
  1106. if (typeof module !== 'undefined') module.exports = Showdown;