showdown.js 45 KB

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