showdown.js 46 KB

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