showdown.js 46 KB

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