showdown.js 44 KB

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