showdown.js 41 KB

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