showdown.js 50 KB

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