showdown.js 46 KB

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