showdown.js 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435
  1. function getDefaultOpts(simple) {
  2. var defaultOptions = {
  3. omitExtraWLInCodeBlocks: {
  4. defaultValue: false,
  5. describe: 'Omit the default extra whiteline added to code blocks',
  6. type: 'boolean'
  7. },
  8. noHeaderId: {
  9. defaultValue: false,
  10. describe: 'Turn on/off generated header id',
  11. type: 'boolean'
  12. },
  13. prefixHeaderId: {
  14. defaultValue: false,
  15. describe: 'Specify a prefix to generated header ids',
  16. type: 'string'
  17. },
  18. headerLevelStart: {
  19. defaultValue: false,
  20. describe: 'The header blocks level start',
  21. type: 'integer'
  22. },
  23. parseImgDimensions: {
  24. defaultValue: false,
  25. describe: 'Turn on/off image dimension parsing',
  26. type: 'boolean'
  27. },
  28. simplifiedAutoLink: {
  29. defaultValue: false,
  30. describe: 'Turn on/off GFM autolink style',
  31. type: 'boolean'
  32. },
  33. literalMidWordUnderscores: {
  34. defaultValue: false,
  35. describe: 'Parse midword underscores as literal underscores',
  36. type: 'boolean'
  37. },
  38. strikethrough: {
  39. defaultValue: false,
  40. describe: 'Turn on/off strikethrough support',
  41. type: 'boolean'
  42. },
  43. tables: {
  44. defaultValue: false,
  45. describe: 'Turn on/off tables support',
  46. type: 'boolean'
  47. },
  48. tablesHeaderId: {
  49. defaultValue: false,
  50. describe: 'Add an id to table headers',
  51. type: 'boolean'
  52. },
  53. ghCodeBlocks: {
  54. defaultValue: true,
  55. describe: 'Turn on/off GFM fenced code blocks support',
  56. type: 'boolean'
  57. },
  58. tasklists: {
  59. defaultValue: false,
  60. describe: 'Turn on/off GFM tasklist support',
  61. type: 'boolean'
  62. },
  63. smoothLivePreview: {
  64. defaultValue: false,
  65. describe: 'Prevents weird effects in live previews due to incomplete input',
  66. type: 'boolean'
  67. },
  68. smartIndentationFix: {
  69. defaultValue: false,
  70. description: 'Tries to smartly fix identation in es6 strings',
  71. type: 'boolean'
  72. }
  73. };
  74. if (simple === false) {
  75. return JSON.parse(JSON.stringify(defaultOptions));
  76. }
  77. var ret = {};
  78. for (var opt in defaultOptions) {
  79. if (defaultOptions.hasOwnProperty(opt)) {
  80. ret[opt] = defaultOptions[opt].defaultValue;
  81. }
  82. }
  83. return ret;
  84. }
  85. var showdown = {};
  86. var parsers = {};
  87. var extensions = {};
  88. var globalOptions = getDefaultOpts(true);
  89. var flavor = {
  90. github: {
  91. omitExtraWLInCodeBlocks: true,
  92. prefixHeaderId: 'user-content-',
  93. simplifiedAutoLink: true,
  94. literalMidWordUnderscores: true,
  95. strikethrough: true,
  96. tables: true,
  97. tablesHeaderId: true,
  98. ghCodeBlocks: true,
  99. tasklists: true
  100. },
  101. vanilla: getDefaultOpts(true)
  102. };
  103. showdown.helper = {};
  104. showdown.extensions = {};
  105. showdown.setOption = function (key, value) {
  106. globalOptions[key] = value;
  107. return this;
  108. };
  109. showdown.getOption = function (key) {
  110. return globalOptions[key];
  111. };
  112. showdown.getOptions = function () {
  113. return globalOptions;
  114. };
  115. showdown.resetOptions = function () {
  116. globalOptions = getDefaultOpts(true);
  117. };
  118. showdown.setFlavor = function (name) {
  119. if (flavor.hasOwnProperty(name)) {
  120. var preset = flavor[name];
  121. for (var option in preset) {
  122. if (preset.hasOwnProperty(option)) {
  123. globalOptions[option] = preset[option];
  124. }
  125. }
  126. }
  127. };
  128. showdown.getDefaultOptions = function (simple) {
  129. return getDefaultOpts(simple);
  130. };
  131. showdown.subParser = function (name, func) {
  132. if (showdown.helper.isString(name)) {
  133. if (typeof func !== 'undefined') {
  134. parsers[name] = func;
  135. } else {
  136. if (parsers.hasOwnProperty(name)) {
  137. return parsers[name];
  138. } else {
  139. throw Error('SubParser named ' + name + ' not registered!');
  140. }
  141. }
  142. }
  143. };
  144. showdown.extension = function (name, ext) {
  145. if (!showdown.helper.isString(name)) {
  146. throw Error("Extension 'name' must be a string");
  147. }
  148. name = showdown.helper.stdExtName(name);
  149. if (showdown.helper.isUndefined(ext)) {
  150. if (!extensions.hasOwnProperty(name)) {
  151. throw Error('Extension named ' + name + ' is not registered!');
  152. }
  153. return extensions[name];
  154. } else {
  155. if (typeof ext === 'function') {
  156. ext = ext();
  157. }
  158. if (!showdown.helper.isArray(ext)) {
  159. ext = [ext];
  160. }
  161. var validExtension = validate(ext, name);
  162. if (validExtension.valid) {
  163. extensions[name] = ext;
  164. } else {
  165. throw Error(validExtension.error);
  166. }
  167. }
  168. };
  169. showdown.getAllExtensions = function () {
  170. return extensions;
  171. };
  172. showdown.removeExtension = function (name) {
  173. delete extensions[name];
  174. };
  175. showdown.resetExtensions = function () {
  176. extensions = {};
  177. };
  178. function validate(extension, name) {
  179. var errMsg = name ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension';
  180. var ret = {
  181. valid: true,
  182. error: ''
  183. };
  184. if (!showdown.helper.isArray(extension)) {
  185. extension = [extension];
  186. }
  187. for (var i = 0; i < extension.length; ++i) {
  188. var baseMsg = errMsg + ' sub-extension ' + i + ': ';
  189. var ext = extension[i];
  190. if (typeof ext !== 'object') {
  191. ret.valid = false;
  192. ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
  193. return ret;
  194. }
  195. if (!showdown.helper.isString(ext.type)) {
  196. ret.valid = false;
  197. ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
  198. return ret;
  199. }
  200. var type = (ext.type = ext.type.toLowerCase());
  201. if (type === 'language') {
  202. type = ext.type = 'lang';
  203. }
  204. if (type === 'html') {
  205. type = ext.type = 'output';
  206. }
  207. if (type !== 'lang' && type !== 'output' && type !== 'listener') {
  208. ret.valid = false;
  209. ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"';
  210. return ret;
  211. }
  212. if (type === 'listener') {
  213. if (showdown.helper.isUndefined(ext.listeners)) {
  214. ret.valid = false;
  215. ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"';
  216. return ret;
  217. }
  218. } else {
  219. if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
  220. ret.valid = false;
  221. ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method';
  222. return ret;
  223. }
  224. }
  225. if (ext.listeners) {
  226. if (typeof ext.listeners !== 'object') {
  227. ret.valid = false;
  228. ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given';
  229. return ret;
  230. }
  231. for (var ln in ext.listeners) {
  232. if (ext.listeners.hasOwnProperty(ln)) {
  233. if (typeof ext.listeners[ln] !== 'function') {
  234. ret.valid = false;
  235. ret.error =
  236. baseMsg +
  237. '"listeners" property must be an hash of [event name]: [callback]. listeners.' +
  238. ln +
  239. ' must be a function but ' +
  240. typeof ext.listeners[ln] +
  241. ' given';
  242. return ret;
  243. }
  244. }
  245. }
  246. }
  247. if (ext.filter) {
  248. if (typeof ext.filter !== 'function') {
  249. ret.valid = false;
  250. ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
  251. return ret;
  252. }
  253. } else {
  254. if (ext.regex) {
  255. if (showdown.helper.isString(ext.regex)) {
  256. ext.regex = new RegExp(ext.regex, 'g');
  257. }
  258. if (!ext.regex instanceof RegExp) {
  259. ret.valid = false;
  260. ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given';
  261. return ret;
  262. }
  263. if (showdown.helper.isUndefined(ext.replace)) {
  264. ret.valid = false;
  265. ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
  266. return ret;
  267. }
  268. }
  269. }
  270. }
  271. return ret;
  272. }
  273. showdown.validateExtension = function (ext) {
  274. var validateExtension = validate(ext, null);
  275. if (!validateExtension.valid) {
  276. console.warn(validateExtension.error);
  277. return false;
  278. }
  279. return true;
  280. };
  281. if (!showdown.hasOwnProperty('helper')) {
  282. showdown.helper = {};
  283. }
  284. showdown.helper.isString = function isString(a) {
  285. return typeof a === 'string' || a instanceof String;
  286. };
  287. showdown.helper.isFunction = function isFunction(a) {
  288. var getType = {};
  289. return a && getType.toString.call(a) === '[object Function]';
  290. };
  291. showdown.helper.forEach = function forEach(obj, callback) {
  292. if (typeof obj.forEach === 'function') {
  293. obj.forEach(callback);
  294. } else {
  295. for (var i = 0; i < obj.length; i++) {
  296. callback(obj[i], i, obj);
  297. }
  298. }
  299. };
  300. showdown.helper.isArray = function isArray(a) {
  301. return a.constructor === Array;
  302. };
  303. showdown.helper.isUndefined = function isUndefined(value) {
  304. return typeof value === 'undefined';
  305. };
  306. showdown.helper.stdExtName = function (s) {
  307. return s.replace(/[_-]||\s/g, '').toLowerCase();
  308. };
  309. function escapeCharactersCallback(wholeMatch, m1) {
  310. var charCodeToEscape = m1.charCodeAt(0);
  311. return '~E' + charCodeToEscape + 'E';
  312. }
  313. showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
  314. showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) {
  315. var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
  316. if (afterBackslash) {
  317. regexString = '\\\\' + regexString;
  318. }
  319. var regex = new RegExp(regexString, 'g');
  320. text = text.replace(regex, escapeCharactersCallback);
  321. return text;
  322. };
  323. var rgxFindMatchPos = function (str, left, right, flags) {
  324. var f = flags || '';
  325. var g = f.indexOf('g') > -1;
  326. var x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, ''));
  327. var l = new RegExp(left, f.replace(/g/g, ''));
  328. var pos = [];
  329. var t;
  330. var s;
  331. var m;
  332. var start;
  333. var end;
  334. do {
  335. t = 0;
  336. while ((m = x.exec(str))) {
  337. if (l.test(m[0])) {
  338. if (!t++) {
  339. s = x.lastIndex;
  340. start = s - m[0].length;
  341. }
  342. } else {
  343. if (t) {
  344. if (!--t) {
  345. end = m.index + m[0].length;
  346. var obj = {
  347. left: {
  348. start: start,
  349. end: s
  350. },
  351. match: {
  352. start: s,
  353. end: m.index
  354. },
  355. right: {
  356. start: m.index,
  357. end: end
  358. },
  359. wholeMatch: {
  360. start: start,
  361. end: end
  362. }
  363. };
  364. pos.push(obj);
  365. if (!g) {
  366. return pos;
  367. }
  368. }
  369. }
  370. }
  371. }
  372. } while (t && (x.lastIndex = s));
  373. return pos;
  374. };
  375. showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
  376. var matchPos = rgxFindMatchPos(str, left, right, flags);
  377. var results = [];
  378. for (var i = 0; i < matchPos.length; ++i) {
  379. results.push([
  380. str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
  381. str.slice(matchPos[i].match.start, matchPos[i].match.end),
  382. str.slice(matchPos[i].left.start, matchPos[i].left.end),
  383. str.slice(matchPos[i].right.start, matchPos[i].right.end)
  384. ]);
  385. }
  386. return results;
  387. };
  388. showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) {
  389. if (!showdown.helper.isFunction(replacement)) {
  390. var repStr = replacement;
  391. replacement = function () {
  392. return repStr;
  393. };
  394. }
  395. var matchPos = rgxFindMatchPos(str, left, right, flags);
  396. var finalStr = str;
  397. var lng = matchPos.length;
  398. if (lng > 0) {
  399. var bits = [];
  400. if (matchPos[0].wholeMatch.start !== 0) {
  401. bits.push(str.slice(0, matchPos[0].wholeMatch.start));
  402. }
  403. for (var i = 0; i < lng; ++i) {
  404. bits.push(
  405. replacement(
  406. str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
  407. str.slice(matchPos[i].match.start, matchPos[i].match.end),
  408. str.slice(matchPos[i].left.start, matchPos[i].left.end),
  409. str.slice(matchPos[i].right.start, matchPos[i].right.end)
  410. )
  411. );
  412. if (i < lng - 1) {
  413. bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start));
  414. }
  415. }
  416. if (matchPos[lng - 1].wholeMatch.end < str.length) {
  417. bits.push(str.slice(matchPos[lng - 1].wholeMatch.end));
  418. }
  419. finalStr = bits.join('');
  420. }
  421. return finalStr;
  422. };
  423. if (showdown.helper.isUndefined(console)) {
  424. console = {
  425. warn: function (msg) {
  426. alert(msg);
  427. },
  428. log: function (msg) {
  429. alert(msg);
  430. },
  431. error: function (msg) {
  432. throw msg;
  433. }
  434. };
  435. }
  436. showdown.Converter = function (converterOptions) {
  437. var options = {};
  438. var langExtensions = [];
  439. var outputModifiers = [];
  440. var listeners = {};
  441. _constructor();
  442. function _constructor() {
  443. converterOptions = converterOptions || {};
  444. for (var gOpt in globalOptions) {
  445. if (globalOptions.hasOwnProperty(gOpt)) {
  446. options[gOpt] = globalOptions[gOpt];
  447. }
  448. }
  449. if (typeof converterOptions === 'object') {
  450. for (var opt in converterOptions) {
  451. if (converterOptions.hasOwnProperty(opt)) {
  452. options[opt] = converterOptions[opt];
  453. }
  454. }
  455. } else {
  456. throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions + ' was passed instead.');
  457. }
  458. if (options.extensions) {
  459. showdown.helper.forEach(options.extensions, _parseExtension);
  460. }
  461. }
  462. function _parseExtension(ext, name) {
  463. name = name || null;
  464. if (showdown.helper.isString(ext)) {
  465. ext = showdown.helper.stdExtName(ext);
  466. name = ext;
  467. if (showdown.extensions[ext]) {
  468. console.warn(
  469. 'DEPRECATION WARNING: ' +
  470. ext +
  471. ' is an old extension that uses a deprecated loading method.' +
  472. 'Please inform the developer that the extension should be updated!'
  473. );
  474. legacyExtensionLoading(showdown.extensions[ext], ext);
  475. return;
  476. } else {
  477. if (!showdown.helper.isUndefined(extensions[ext])) {
  478. ext = extensions[ext];
  479. } else {
  480. throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
  481. }
  482. }
  483. }
  484. if (typeof ext === 'function') {
  485. ext = ext();
  486. }
  487. if (!showdown.helper.isArray(ext)) {
  488. ext = [ext];
  489. }
  490. var validExt = validate(ext, name);
  491. if (!validExt.valid) {
  492. throw Error(validExt.error);
  493. }
  494. for (var i = 0; i < ext.length; ++i) {
  495. switch (ext[i].type) {
  496. case 'lang':
  497. langExtensions.push(ext[i]);
  498. break;
  499. case 'output':
  500. outputModifiers.push(ext[i]);
  501. break;
  502. }
  503. if (ext[i].hasOwnProperty(listeners)) {
  504. for (var ln in ext[i].listeners) {
  505. if (ext[i].listeners.hasOwnProperty(ln)) {
  506. listen(ln, ext[i].listeners[ln]);
  507. }
  508. }
  509. }
  510. }
  511. }
  512. function legacyExtensionLoading(ext, name) {
  513. if (typeof ext === 'function') {
  514. ext = ext(new showdown.Converter());
  515. }
  516. if (!showdown.helper.isArray(ext)) {
  517. ext = [ext];
  518. }
  519. var valid = validate(ext, name);
  520. if (!valid.valid) {
  521. throw Error(valid.error);
  522. }
  523. for (var i = 0; i < ext.length; ++i) {
  524. switch (ext[i].type) {
  525. case 'lang':
  526. langExtensions.push(ext[i]);
  527. break;
  528. case 'output':
  529. outputModifiers.push(ext[i]);
  530. break;
  531. default:
  532. throw Error('Extension loader error: Type unrecognized!!!');
  533. }
  534. }
  535. }
  536. function listen(name, callback) {
  537. if (!showdown.helper.isString(name)) {
  538. throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given');
  539. }
  540. if (typeof callback !== 'function') {
  541. throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given');
  542. }
  543. if (!listeners.hasOwnProperty(name)) {
  544. listeners[name] = [];
  545. }
  546. listeners[name].push(callback);
  547. }
  548. function rTrimInputText(text) {
  549. var rsp = text.match(/^\s*/)[0].length;
  550. var rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm');
  551. return text.replace(rgx, '');
  552. }
  553. this._dispatch = function dispatch(evtName, text, options, globals) {
  554. if (listeners.hasOwnProperty(evtName)) {
  555. for (var ei = 0; ei < listeners[evtName].length; ++ei) {
  556. var nText = listeners[evtName][ei](evtName, text, this, options, globals);
  557. if (nText && typeof nText !== 'undefined') {
  558. text = nText;
  559. }
  560. }
  561. }
  562. return text;
  563. };
  564. this.listen = function (name, callback) {
  565. listen(name, callback);
  566. return this;
  567. };
  568. this.makeHtml = function (text) {
  569. if (!text) {
  570. return text;
  571. }
  572. var globals = {
  573. gHtmlBlocks: [],
  574. gHtmlMdBlocks: [],
  575. gHtmlSpans: [],
  576. gUrls: {},
  577. gTitles: {},
  578. gDimensions: {},
  579. gListLevel: 0,
  580. hashLinkCounts: {},
  581. langExtensions: langExtensions,
  582. outputModifiers: outputModifiers,
  583. converter: this,
  584. ghCodeBlocks: []
  585. };
  586. text = text.replace(/~/g, '~T');
  587. text = text.replace(/\$/g, '~D');
  588. text = text.replace(/\r\n/g, '\n');
  589. text = text.replace(/\r/g, '\n');
  590. if (options.smartIndentationFix) {
  591. text = rTrimInputText(text);
  592. }
  593. text = text;
  594. text = showdown.subParser('detab')(text, options, globals);
  595. text = showdown.subParser('stripBlankLines')(text, options, globals);
  596. showdown.helper.forEach(langExtensions, function (ext) {
  597. text = showdown.subParser('runExtension')(ext, text, options, globals);
  598. });
  599. text = showdown.subParser('hashPreCodeTags')(text, options, globals);
  600. text = showdown.subParser('githubCodeBlocks')(text, options, globals);
  601. text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
  602. text = showdown.subParser('hashHTMLSpans')(text, options, globals);
  603. text = showdown.subParser('stripLinkDefinitions')(text, options, globals);
  604. text = showdown.subParser('blockGamut')(text, options, globals);
  605. text = showdown.subParser('unhashHTMLSpans')(text, options, globals);
  606. text = showdown.subParser('unescapeSpecialChars')(text, options, globals);
  607. text = text.replace(/~D/g, '$$');
  608. text = text.replace(/~T/g, '~');
  609. showdown.helper.forEach(outputModifiers, function (ext) {
  610. text = showdown.subParser('runExtension')(ext, text, options, globals);
  611. });
  612. return text;
  613. };
  614. this.setOption = function (key, value) {
  615. options[key] = value;
  616. };
  617. this.getOption = function (key) {
  618. return options[key];
  619. };
  620. this.getOptions = function () {
  621. return options;
  622. };
  623. this.addExtension = function (extension, name) {
  624. name = name || null;
  625. _parseExtension(extension, name);
  626. };
  627. this.useExtension = function (extensionName) {
  628. _parseExtension(extensionName);
  629. };
  630. this.setFlavor = function (name) {
  631. if (flavor.hasOwnProperty(name)) {
  632. var preset = flavor[name];
  633. for (var option in preset) {
  634. if (preset.hasOwnProperty(option)) {
  635. options[option] = preset[option];
  636. }
  637. }
  638. }
  639. };
  640. this.removeExtension = function (extension) {
  641. if (!showdown.helper.isArray(extension)) {
  642. extension = [extension];
  643. }
  644. for (var a = 0; a < extension.length; ++a) {
  645. var ext = extension[a];
  646. for (var i = 0; i < langExtensions.length; ++i) {
  647. if (langExtensions[i] === ext) {
  648. langExtensions[i].splice(i, 1);
  649. }
  650. }
  651. for (var ii = 0; ii < outputModifiers.length; ++i) {
  652. if (outputModifiers[ii] === ext) {
  653. outputModifiers[ii].splice(i, 1);
  654. }
  655. }
  656. }
  657. };
  658. this.getAllExtensions = function () {
  659. return {
  660. language: langExtensions,
  661. output: outputModifiers
  662. };
  663. };
  664. };
  665. showdown.subParser('anchors', function (text, options, globals) {
  666. text = globals.converter._dispatch('anchors.before', text, options, globals);
  667. var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
  668. if (showdown.helper.isUndefined(m7)) {
  669. m7 = '';
  670. }
  671. wholeMatch = m1;
  672. var linkText = m2;
  673. var linkId = m3.toLowerCase();
  674. var url = m4;
  675. var title = m7;
  676. if (!url) {
  677. if (!linkId) {
  678. linkId = linkText.toLowerCase().replace(/ ?\n/g, ' ');
  679. }
  680. url = '#' + linkId;
  681. if (!showdown.helper.isUndefined(globals.gUrls[linkId])) {
  682. url = globals.gUrls[linkId];
  683. if (!showdown.helper.isUndefined(globals.gTitles[linkId])) {
  684. title = globals.gTitles[linkId];
  685. }
  686. } else {
  687. if (wholeMatch.search(/\(\s*\)$/m) > -1) {
  688. url = '';
  689. } else {
  690. return wholeMatch;
  691. }
  692. }
  693. }
  694. url = showdown.helper.escapeCharacters(url, '*_', false);
  695. var result = '<a href="' + url + '"';
  696. if (title !== '' && title !== null) {
  697. title = title.replace(/"/g, '&quot;');
  698. title = showdown.helper.escapeCharacters(title, '*_', false);
  699. result += ' title="' + title + '"';
  700. }
  701. result += '>' + linkText + '</a>';
  702. return result;
  703. };
  704. text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g, writeAnchorTag);
  705. text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeAnchorTag);
  706. text = text.replace(/(\[([^\[\]]+)])()()()()()/g, writeAnchorTag);
  707. text = globals.converter._dispatch('anchors.after', text, options, globals);
  708. return text;
  709. });
  710. showdown.subParser('autoLinks', function (text, options, globals) {
  711. text = globals.converter._dispatch('autoLinks.before', text, options, globals);
  712. var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi;
  713. var delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi;
  714. var simpleMailRegex = /(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi;
  715. var delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
  716. text = text.replace(delimUrlRegex, replaceLink);
  717. text = text.replace(delimMailRegex, replaceMail);
  718. if (options.simplifiedAutoLink) {
  719. text = text.replace(simpleURLRegex, replaceLink);
  720. text = text.replace(simpleMailRegex, replaceMail);
  721. }
  722. function replaceLink(wm, link) {
  723. var lnkTxt = link;
  724. if (/^www\./i.test(link)) {
  725. link = link.replace(/^www\./i, 'http://www.');
  726. }
  727. return '<a href="' + link + '">' + lnkTxt + '</a>';
  728. }
  729. function replaceMail(wholeMatch, m1) {
  730. var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1);
  731. return showdown.subParser('encodeEmailAddress')(unescapedStr);
  732. }
  733. text = globals.converter._dispatch('autoLinks.after', text, options, globals);
  734. return text;
  735. });
  736. showdown.subParser('blockGamut', function (text, options, globals) {
  737. text = globals.converter._dispatch('blockGamut.before', text, options, globals);
  738. text = showdown.subParser('blockQuotes')(text, options, globals);
  739. text = showdown.subParser('headers')(text, options, globals);
  740. var key = showdown.subParser('hashBlock')('<hr />', options, globals);
  741. text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key);
  742. text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key);
  743. text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key);
  744. text = showdown.subParser('lists')(text, options, globals);
  745. text = showdown.subParser('codeBlocks')(text, options, globals);
  746. text = showdown.subParser('tables')(text, options, globals);
  747. text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
  748. text = showdown.subParser('paragraphs')(text, options, globals);
  749. text = globals.converter._dispatch('blockGamut.after', text, options, globals);
  750. return text;
  751. });
  752. showdown.subParser('blockQuotes', function (text, options, globals) {
  753. text = globals.converter._dispatch('blockQuotes.before', text, options, globals);
  754. text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
  755. var bq = m1;
  756. bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0');
  757. bq = bq.replace(/~0/g, '');
  758. bq = bq.replace(/^[ \t]+$/gm, '');
  759. bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
  760. bq = showdown.subParser('blockGamut')(bq, options, globals);
  761. bq = bq.replace(/(^|\n)/g, '$1 ');
  762. bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
  763. var pre = m1;
  764. pre = pre.replace(/^ /gm, '~0');
  765. pre = pre.replace(/~0/g, '');
  766. return pre;
  767. });
  768. return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
  769. });
  770. text = globals.converter._dispatch('blockQuotes.after', text, options, globals);
  771. return text;
  772. });
  773. showdown.subParser('codeBlocks', function (text, options, globals) {
  774. text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
  775. text += '~0';
  776. var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
  777. text = text.replace(pattern, function (wholeMatch, m1, m2) {
  778. var codeblock = m1;
  779. var nextChar = m2;
  780. var end = '\n';
  781. codeblock = showdown.subParser('outdent')(codeblock);
  782. codeblock = showdown.subParser('encodeCode')(codeblock);
  783. codeblock = showdown.subParser('detab')(codeblock);
  784. codeblock = codeblock.replace(/^\n+/g, '');
  785. codeblock = codeblock.replace(/\n+$/g, '');
  786. if (options.omitExtraWLInCodeBlocks) {
  787. end = '';
  788. }
  789. codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
  790. return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
  791. });
  792. text = text.replace(/~0/, '');
  793. text = globals.converter._dispatch('codeBlocks.after', text, options, globals);
  794. return text;
  795. });
  796. showdown.subParser('codeSpans', function (text, options, globals) {
  797. text = globals.converter._dispatch('codeSpans.before', text, options, globals);
  798. if (typeof text === 'undefined') {
  799. text = '';
  800. }
  801. text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, function (wholeMatch, m1, m2, m3) {
  802. var c = m3;
  803. c = c.replace(/^([ \t]*)/g, '');
  804. c = c.replace(/[ \t]*$/g, '');
  805. c = showdown.subParser('encodeCode')(c);
  806. return m1 + '<code>' + c + '</code>';
  807. });
  808. text = globals.converter._dispatch('codeSpans.after', text, options, globals);
  809. return text;
  810. });
  811. showdown.subParser('detab', function (text) {
  812. text = text.replace(/\t(?=\t)/g, ' ');
  813. text = text.replace(/\t/g, '~A~B');
  814. text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) {
  815. var leadingText = m1;
  816. var numSpaces = 4 - (leadingText.length % 4);
  817. for (var i = 0; i < numSpaces; i++) {
  818. leadingText += ' ';
  819. }
  820. return leadingText;
  821. });
  822. text = text.replace(/~A/g, ' ');
  823. text = text.replace(/~B/g, '');
  824. return text;
  825. });
  826. showdown.subParser('encodeAmpsAndAngles', function (text) {
  827. text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&amp;');
  828. text = text.replace(/<(?![a-z\/?\$!])/gi, '&lt;');
  829. return text;
  830. });
  831. showdown.subParser('encodeBackslashEscapes', function (text) {
  832. text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
  833. text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback);
  834. return text;
  835. });
  836. showdown.subParser('encodeCode', function (text) {
  837. text = text.replace(/&/g, '&amp;');
  838. text = text.replace(/</g, '&lt;');
  839. text = text.replace(/>/g, '&gt;');
  840. text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false);
  841. return text;
  842. });
  843. showdown.subParser('encodeEmailAddress', function (addr) {
  844. var encode = [
  845. function (ch) {
  846. return '&#' + ch.charCodeAt(0) + ';';
  847. },
  848. function (ch) {
  849. return '&#x' + ch.charCodeAt(0).toString(16) + ';';
  850. },
  851. function (ch) {
  852. return ch;
  853. }
  854. ];
  855. addr = 'mailto:' + addr;
  856. addr = addr.replace(/./g, function (ch) {
  857. if (ch === '@') {
  858. ch = encode[Math.floor(Math.random() * 2)](ch);
  859. } else {
  860. if (ch !== ':') {
  861. var r = Math.random();
  862. ch = r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch);
  863. }
  864. }
  865. return ch;
  866. });
  867. addr = '<a href="' + addr + '">' + addr + '</a>';
  868. addr = addr.replace(/">.+:/g, '">');
  869. return addr;
  870. });
  871. showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) {
  872. var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
  873. text = text.replace(regex, function (wholeMatch) {
  874. var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`');
  875. tag = showdown.helper.escapeCharacters(tag, '\\`*_', false);
  876. return tag;
  877. });
  878. return text;
  879. });
  880. showdown.subParser('githubCodeBlocks', function (text, options, globals) {
  881. if (!options.ghCodeBlocks) {
  882. return text;
  883. }
  884. text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
  885. text += '~0';
  886. text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) {
  887. var end = options.omitExtraWLInCodeBlocks ? '' : '\n';
  888. codeblock = showdown.subParser('encodeCode')(codeblock);
  889. codeblock = showdown.subParser('detab')(codeblock);
  890. codeblock = codeblock.replace(/^\n+/g, '');
  891. codeblock = codeblock.replace(/\n+$/g, '');
  892. codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
  893. codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
  894. return (
  895. '\n\n~G' +
  896. (globals.ghCodeBlocks.push({
  897. text: wholeMatch,
  898. codeblock: codeblock
  899. }) -
  900. 1) +
  901. 'G\n\n'
  902. );
  903. });
  904. text = text.replace(/~0/, '');
  905. return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
  906. });
  907. showdown.subParser('hashBlock', function (text, options, globals) {
  908. text = text.replace(/(^\n+|\n+$)/g, '');
  909. return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
  910. });
  911. showdown.subParser('hashElement', function (text, options, globals) {
  912. return function (wholeMatch, m1) {
  913. var blockText = m1;
  914. blockText = blockText.replace(/\n\n/g, '\n');
  915. blockText = blockText.replace(/^\n/, '');
  916. blockText = blockText.replace(/\n+$/g, '');
  917. blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n';
  918. return blockText;
  919. };
  920. });
  921. showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
  922. var blockTags = [
  923. 'pre',
  924. 'div',
  925. 'h1',
  926. 'h2',
  927. 'h3',
  928. 'h4',
  929. 'h5',
  930. 'h6',
  931. 'blockquote',
  932. 'table',
  933. 'dl',
  934. 'ol',
  935. 'ul',
  936. 'script',
  937. 'noscript',
  938. 'form',
  939. 'fieldset',
  940. 'iframe',
  941. 'math',
  942. 'style',
  943. 'section',
  944. 'header',
  945. 'footer',
  946. 'nav',
  947. 'article',
  948. 'aside',
  949. 'address',
  950. 'audio',
  951. 'canvas',
  952. 'figure',
  953. 'hgroup',
  954. 'output',
  955. 'video',
  956. 'p'
  957. ];
  958. var repFunc = function (wholeMatch, match, left, right) {
  959. var txt = wholeMatch;
  960. if (left.search(/\bmarkdown\b/) !== -1) {
  961. txt = left + globals.converter.makeHtml(match) + right;
  962. }
  963. return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
  964. };
  965. for (var i = 0; i < blockTags.length; ++i) {
  966. text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<' + blockTags[i] + '\\b[^>]*>', '</' + blockTags[i] + '>', 'gim');
  967. }
  968. text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, showdown.subParser('hashElement')(text, options, globals));
  969. text = text.replace(/(<!--[\s\S]*?-->)/g, showdown.subParser('hashElement')(text, options, globals));
  970. text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, showdown.subParser('hashElement')(text, options, globals));
  971. return text;
  972. });
  973. showdown.subParser('hashHTMLSpans', function (text, config, globals) {
  974. var matches = showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');
  975. for (var i = 0; i < matches.length; ++i) {
  976. text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L');
  977. }
  978. return text;
  979. });
  980. showdown.subParser('unhashHTMLSpans', function (text, config, globals) {
  981. for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
  982. text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]);
  983. }
  984. return text;
  985. });
  986. showdown.subParser('hashPreCodeTags', function (text, config, globals) {
  987. var repFunc = function (wholeMatch, match, left, right) {
  988. var codeblock = left + showdown.subParser('encodeCode')(match) + right;
  989. return (
  990. '\n\n~G' +
  991. (globals.ghCodeBlocks.push({
  992. text: wholeMatch,
  993. codeblock: codeblock
  994. }) -
  995. 1) +
  996. 'G\n\n'
  997. );
  998. };
  999. text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^(?: |\\t){0,3}</code>\\s*</pre>', 'gim');
  1000. return text;
  1001. });
  1002. showdown.subParser('headers', function (text, options, globals) {
  1003. text = globals.converter._dispatch('headers.before', text, options, globals);
  1004. var prefixHeader = options.prefixHeaderId;
  1005. var headerLevelStart = isNaN(parseInt(options.headerLevelStart)) ? 1 : parseInt(options.headerLevelStart);
  1006. var setextRegexH1 = options.smoothLivePreview ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm;
  1007. var setextRegexH2 = options.smoothLivePreview ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
  1008. text = text.replace(setextRegexH1, function (wholeMatch, m1) {
  1009. var spanGamut = showdown.subParser('spanGamut')(m1, options, globals);
  1010. var hID = options.noHeaderId ? '' : ' id="' + headerId(m1) + '"';
  1011. var hLevel = headerLevelStart;
  1012. var hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
  1013. return showdown.subParser('hashBlock')(hashBlock, options, globals);
  1014. });
  1015. text = text.replace(setextRegexH2, function (matchFound, m1) {
  1016. var spanGamut = showdown.subParser('spanGamut')(m1, options, globals);
  1017. var hID = options.noHeaderId ? '' : ' id="' + headerId(m1) + '"';
  1018. var hLevel = headerLevelStart + 1;
  1019. var hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
  1020. return showdown.subParser('hashBlock')(hashBlock, options, globals);
  1021. });
  1022. text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) {
  1023. var span = showdown.subParser('spanGamut')(m2, options, globals);
  1024. var hID = options.noHeaderId ? '' : ' id="' + headerId(m2) + '"';
  1025. var hLevel = headerLevelStart - 1 + m1.length;
  1026. var header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
  1027. return showdown.subParser('hashBlock')(header, options, globals);
  1028. });
  1029. function headerId(m) {
  1030. var title;
  1031. var escapedId = m.replace(/[^\w]/g, '').toLowerCase();
  1032. if (globals.hashLinkCounts[escapedId]) {
  1033. title = escapedId + '-' + globals.hashLinkCounts[escapedId]++;
  1034. } else {
  1035. title = escapedId;
  1036. globals.hashLinkCounts[escapedId] = 1;
  1037. }
  1038. if (prefixHeader === true) {
  1039. prefixHeader = 'section';
  1040. }
  1041. if (showdown.helper.isString(prefixHeader)) {
  1042. return prefixHeader + title;
  1043. }
  1044. return title;
  1045. }
  1046. text = globals.converter._dispatch('headers.after', text, options, globals);
  1047. return text;
  1048. });
  1049. showdown.subParser('images', function (text, options, globals) {
  1050. text = globals.converter._dispatch('images.before', text, options, globals);
  1051. var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g;
  1052. var referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g;
  1053. function writeImageTag(wholeMatch, altText, linkId, url, width, height, m5, title) {
  1054. var gUrls = globals.gUrls;
  1055. var gTitles = globals.gTitles;
  1056. var gDims = globals.gDimensions;
  1057. linkId = linkId.toLowerCase();
  1058. if (!title) {
  1059. title = '';
  1060. }
  1061. if (url === '' || url === null) {
  1062. if (linkId === '' || linkId === null) {
  1063. linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
  1064. }
  1065. url = '#' + linkId;
  1066. if (!showdown.helper.isUndefined(gUrls[linkId])) {
  1067. url = gUrls[linkId];
  1068. if (!showdown.helper.isUndefined(gTitles[linkId])) {
  1069. title = gTitles[linkId];
  1070. }
  1071. if (!showdown.helper.isUndefined(gDims[linkId])) {
  1072. width = gDims[linkId].width;
  1073. height = gDims[linkId].height;
  1074. }
  1075. } else {
  1076. return wholeMatch;
  1077. }
  1078. }
  1079. altText = altText.replace(/"/g, '&quot;');
  1080. altText = showdown.helper.escapeCharacters(altText, '*_', false);
  1081. url = showdown.helper.escapeCharacters(url, '*_', false);
  1082. var result = '<img src="' + url + '" alt="' + altText + '"';
  1083. if (title) {
  1084. title = title.replace(/"/g, '&quot;');
  1085. title = showdown.helper.escapeCharacters(title, '*_', false);
  1086. result += ' title="' + title + '"';
  1087. }
  1088. if (width && height) {
  1089. width = width === '*' ? 'auto' : width;
  1090. height = height === '*' ? 'auto' : height;
  1091. result += ' width="' + width + '"';
  1092. result += ' height="' + height + '"';
  1093. }
  1094. result += ' />';
  1095. return result;
  1096. }
  1097. text = text.replace(referenceRegExp, writeImageTag);
  1098. text = text.replace(inlineRegExp, writeImageTag);
  1099. text = globals.converter._dispatch('images.after', text, options, globals);
  1100. return text;
  1101. });
  1102. showdown.subParser('italicsAndBold', function (text, options, globals) {
  1103. text = globals.converter._dispatch('italicsAndBold.before', text, options, globals);
  1104. if (options.literalMidWordUnderscores) {
  1105. text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>');
  1106. text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>');
  1107. text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '<strong>$2</strong>');
  1108. text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
  1109. } else {
  1110. text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '<strong>$2</strong>');
  1111. text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
  1112. }
  1113. text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
  1114. return text;
  1115. });
  1116. showdown.subParser('lists', function (text, options, globals) {
  1117. text = globals.converter._dispatch('lists.before', text, options, globals);
  1118. function processListItems(listStr, trimTrailing) {
  1119. globals.gListLevel++;
  1120. listStr = listStr.replace(/\n{2,}$/, '\n');
  1121. listStr += '~0';
  1122. var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm;
  1123. var isParagraphed = /\n[ \t]*\n(?!~0)/.test(listStr);
  1124. listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
  1125. checked = checked && checked.trim() !== '';
  1126. var item = showdown.subParser('outdent')(m4, options, globals);
  1127. var bulletStyle = '';
  1128. if (taskbtn && options.tasklists) {
  1129. bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
  1130. item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () {
  1131. var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
  1132. if (checked) {
  1133. otp += ' checked';
  1134. }
  1135. otp += '>';
  1136. return otp;
  1137. });
  1138. }
  1139. if (m1 || item.search(/\n{2,}/) > -1) {
  1140. item = showdown.subParser('githubCodeBlocks')(item, options, globals);
  1141. item = showdown.subParser('blockGamut')(item, options, globals);
  1142. } else {
  1143. item = showdown.subParser('lists')(item, options, globals);
  1144. item = item.replace(/\n$/, '');
  1145. if (isParagraphed) {
  1146. item = showdown.subParser('paragraphs')(item, options, globals);
  1147. } else {
  1148. item = showdown.subParser('spanGamut')(item, options, globals);
  1149. }
  1150. }
  1151. item = '\n<li' + bulletStyle + '>' + item + '</li>\n';
  1152. return item;
  1153. });
  1154. listStr = listStr.replace(/~0/g, '');
  1155. globals.gListLevel--;
  1156. if (trimTrailing) {
  1157. listStr = listStr.replace(/\s+$/, '');
  1158. }
  1159. return listStr;
  1160. }
  1161. function parseConsecutiveLists(list, listType, trimTrailing) {
  1162. var counterRxg = listType === 'ul' ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
  1163. var subLists = [];
  1164. var result = '';
  1165. if (list.search(counterRxg) !== -1) {
  1166. (function parseCL(txt) {
  1167. var pos = txt.search(counterRxg);
  1168. if (pos !== -1) {
  1169. result += '\n\n<' + listType + '>' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n\n';
  1170. listType = listType === 'ul' ? 'ol' : 'ul';
  1171. counterRxg = listType === 'ul' ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
  1172. parseCL(txt.slice(pos));
  1173. } else {
  1174. result += '\n\n<' + listType + '>' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n\n';
  1175. }
  1176. })(list);
  1177. for (var i = 0; i < subLists.length; ++i) {}
  1178. } else {
  1179. result = '\n\n<' + listType + '>' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n\n';
  1180. }
  1181. return result;
  1182. }
  1183. text += '~0';
  1184. var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
  1185. if (globals.gListLevel) {
  1186. text = text.replace(wholeList, function (wholeMatch, list, m2) {
  1187. var listType = m2.search(/[*+-]/g) > -1 ? 'ul' : 'ol';
  1188. return parseConsecutiveLists(list, listType, true);
  1189. });
  1190. } else {
  1191. wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
  1192. text = text.replace(wholeList, function (wholeMatch, m1, list, m3) {
  1193. var listType = m3.search(/[*+-]/g) > -1 ? 'ul' : 'ol';
  1194. return parseConsecutiveLists(list, listType);
  1195. });
  1196. }
  1197. text = text.replace(/~0/, '');
  1198. text = globals.converter._dispatch('lists.after', text, options, globals);
  1199. return text;
  1200. });
  1201. showdown.subParser('outdent', function (text) {
  1202. text = text.replace(/^(\t|[ ]{1,4})/gm, '~0');
  1203. text = text.replace(/~0/g, '');
  1204. return text;
  1205. });
  1206. showdown.subParser('paragraphs', function (text, options, globals) {
  1207. text = globals.converter._dispatch('paragraphs.before', text, options, globals);
  1208. text = text.replace(/^\n+/g, '');
  1209. text = text.replace(/\n+$/g, '');
  1210. var grafs = text.split(/\n{2,}/g);
  1211. var grafsOut = [];
  1212. var end = grafs.length;
  1213. for (var i = 0; i < end; i++) {
  1214. var str = grafs[i];
  1215. if (str.search(/~(K|G)(\d+)\1/g) >= 0) {
  1216. grafsOut.push(str);
  1217. } else {
  1218. str = showdown.subParser('spanGamut')(str, options, globals);
  1219. str = str.replace(/^([ \t]*)/g, '<p>');
  1220. str += '</p>';
  1221. grafsOut.push(str);
  1222. }
  1223. }
  1224. end = grafsOut.length;
  1225. for (i = 0; i < end; i++) {
  1226. var blockText = '';
  1227. var grafsOutIt = grafsOut[i];
  1228. var codeFlag = false;
  1229. while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) {
  1230. var delim = RegExp.$1;
  1231. var num = RegExp.$2;
  1232. if (delim === 'K') {
  1233. blockText = globals.gHtmlBlocks[num];
  1234. } else {
  1235. if (codeFlag) {
  1236. blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text);
  1237. } else {
  1238. blockText = globals.ghCodeBlocks[num].codeblock;
  1239. }
  1240. }
  1241. blockText = blockText.replace(/\$/g, '$$$$');
  1242. grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText);
  1243. if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
  1244. codeFlag = true;
  1245. }
  1246. }
  1247. grafsOut[i] = grafsOutIt;
  1248. }
  1249. text = grafsOut.join('\n\n');
  1250. text = text.replace(/^\n+/g, '');
  1251. text = text.replace(/\n+$/g, '');
  1252. return globals.converter._dispatch('paragraphs.after', text, options, globals);
  1253. });
  1254. showdown.subParser('runExtension', function (ext, text, options, globals) {
  1255. if (ext.filter) {
  1256. text = ext.filter(text, globals.converter, options);
  1257. } else {
  1258. if (ext.regex) {
  1259. var re = ext.regex;
  1260. if (!re instanceof RegExp) {
  1261. re = new RegExp(re, 'g');
  1262. }
  1263. text = text.replace(re, ext.replace);
  1264. }
  1265. }
  1266. return text;
  1267. });
  1268. showdown.subParser('spanGamut', function (text, options, globals) {
  1269. text = globals.converter._dispatch('spanGamut.before', text, options, globals);
  1270. text = showdown.subParser('codeSpans')(text, options, globals);
  1271. text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
  1272. text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
  1273. text = showdown.subParser('images')(text, options, globals);
  1274. text = showdown.subParser('anchors')(text, options, globals);
  1275. text = showdown.subParser('autoLinks')(text, options, globals);
  1276. text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
  1277. text = showdown.subParser('italicsAndBold')(text, options, globals);
  1278. text = showdown.subParser('strikethrough')(text, options, globals);
  1279. text = text.replace(/ +\n/g, ' <br />\n');
  1280. text = globals.converter._dispatch('spanGamut.after', text, options, globals);
  1281. return text;
  1282. });
  1283. showdown.subParser('strikethrough', function (text, options, globals) {
  1284. if (options.strikethrough) {
  1285. text = globals.converter._dispatch('strikethrough.before', text, options, globals);
  1286. text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '<del>$1</del>');
  1287. text = globals.converter._dispatch('strikethrough.after', text, options, globals);
  1288. }
  1289. return text;
  1290. });
  1291. showdown.subParser('stripBlankLines', function (text) {
  1292. return text.replace(/^[ \t]+$/gm, '');
  1293. });
  1294. showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
  1295. var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm;
  1296. text += '~0';
  1297. text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
  1298. linkId = linkId.toLowerCase();
  1299. globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url);
  1300. if (blankLines) {
  1301. return blankLines + title;
  1302. } else {
  1303. if (title) {
  1304. globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
  1305. }
  1306. if (options.parseImgDimensions && width && height) {
  1307. globals.gDimensions[linkId] = {
  1308. width: width,
  1309. height: height
  1310. };
  1311. }
  1312. }
  1313. return '';
  1314. });
  1315. text = text.replace(/~0/, '');
  1316. return text;
  1317. });
  1318. showdown.subParser('tables', function (text, options, globals) {
  1319. if (!options.tables) {
  1320. return text;
  1321. }
  1322. var tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm;
  1323. function parseStyles(sLine) {
  1324. if (/^:[ \t]*--*$/.test(sLine)) {
  1325. return ' style="text-align:left;"';
  1326. } else {
  1327. if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
  1328. return ' style="text-align:right;"';
  1329. } else {
  1330. if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
  1331. return ' style="text-align:center;"';
  1332. } else {
  1333. return '';
  1334. }
  1335. }
  1336. }
  1337. }
  1338. function parseHeaders(header, style) {
  1339. var id = '';
  1340. header = header.trim();
  1341. if (options.tableHeaderId) {
  1342. id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
  1343. }
  1344. header = showdown.subParser('spanGamut')(header, options, globals);
  1345. return '<th' + id + style + '>' + header + '</th>\n';
  1346. }
  1347. function parseCells(cell, style) {
  1348. var subText = showdown.subParser('spanGamut')(cell, options, globals);
  1349. return '<td' + style + '>' + subText + '</td>\n';
  1350. }
  1351. function buildTable(headers, cells) {
  1352. var tb = '<table>\n<thead>\n<tr>\n';
  1353. var tblLgn = headers.length;
  1354. for (var i = 0; i < tblLgn; ++i) {
  1355. tb += headers[i];
  1356. }
  1357. tb += '</tr>\n</thead>\n<tbody>\n';
  1358. for (i = 0; i < cells.length; ++i) {
  1359. tb += '<tr>\n';
  1360. for (var ii = 0; ii < tblLgn; ++ii) {
  1361. tb += cells[i][ii];
  1362. }
  1363. tb += '</tr>\n';
  1364. }
  1365. tb += '</tbody>\n</table>\n';
  1366. return tb;
  1367. }
  1368. text = globals.converter._dispatch('tables.before', text, options, globals);
  1369. text = text.replace(tableRgx, function (rawTable) {
  1370. var i;
  1371. var tableLines = rawTable.split('\n');
  1372. for (i = 0; i < tableLines.length; ++i) {
  1373. if (/^[ \t]{0,3}\|/.test(tableLines[i])) {
  1374. tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, '');
  1375. }
  1376. if (/\|[ \t]*$/.test(tableLines[i])) {
  1377. tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
  1378. }
  1379. }
  1380. var rawHeaders = tableLines[0].split('|').map(function (s) {
  1381. return s.trim();
  1382. });
  1383. var rawStyles = tableLines[1].split('|').map(function (s) {
  1384. return s.trim();
  1385. });
  1386. var rawCells = [];
  1387. var headers = [];
  1388. var styles = [];
  1389. var cells = [];
  1390. tableLines.shift();
  1391. tableLines.shift();
  1392. for (i = 0; i < tableLines.length; ++i) {
  1393. if (tableLines[i].trim() === '') {
  1394. continue;
  1395. }
  1396. rawCells.push(
  1397. tableLines[i].split('|').map(function (s) {
  1398. return s.trim();
  1399. })
  1400. );
  1401. }
  1402. if (rawHeaders.length < rawStyles.length) {
  1403. return rawTable;
  1404. }
  1405. for (i = 0; i < rawStyles.length; ++i) {
  1406. styles.push(parseStyles(rawStyles[i]));
  1407. }
  1408. for (i = 0; i < rawHeaders.length; ++i) {
  1409. if (showdown.helper.isUndefined(styles[i])) {
  1410. styles[i] = '';
  1411. }
  1412. headers.push(parseHeaders(rawHeaders[i], styles[i]));
  1413. }
  1414. for (i = 0; i < rawCells.length; ++i) {
  1415. var row = [];
  1416. for (var ii = 0; ii < headers.length; ++ii) {
  1417. if (showdown.helper.isUndefined(rawCells[i][ii])) {
  1418. }
  1419. row.push(parseCells(rawCells[i][ii], styles[ii]));
  1420. }
  1421. cells.push(row);
  1422. }
  1423. return buildTable(headers, cells);
  1424. });
  1425. text = globals.converter._dispatch('tables.after', text, options, globals);
  1426. return text;
  1427. });
  1428. showdown.subParser('unescapeSpecialChars', function (text) {
  1429. text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) {
  1430. var charCodeToReplace = parseInt(m1);
  1431. return String.fromCharCode(charCodeToReplace);
  1432. });
  1433. return text;
  1434. });
  1435. module.exports = showdown;