showdown.js 44 KB

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