2
0

marked.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689
  1. /**
  2. * marked - a markdown parser
  3. * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT Licensed)
  4. * https://github.com/markedjs/marked
  5. */
  6. ;(function(root) {
  7. 'use strict';
  8. /**
  9. * Block-Level Grammar
  10. */
  11. var block = {
  12. newline: /^\n+/,
  13. code: /^( {4}[^\n]+\n*)+/,
  14. fences: noop,
  15. hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
  16. heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
  17. nptable: noop,
  18. blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
  19. list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
  20. html: '^ {0,3}(?:' // optional indentation
  21. + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
  22. + '|comment[^\\n]*(\\n+|$)' // (2)
  23. + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
  24. + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
  25. + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
  26. + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
  27. + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
  28. + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
  29. + ')',
  30. def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
  31. table: noop,
  32. lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
  33. paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/,
  34. text: /^[^\n]+/
  35. };
  36. block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
  37. block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
  38. block.def = edit(block.def)
  39. .replace('label', block._label)
  40. .replace('title', block._title)
  41. .getRegex();
  42. block.bullet = /(?:[*+-]|\d{1,9}\.)/;
  43. block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
  44. block.item = edit(block.item, 'gm')
  45. .replace(/bull/g, block.bullet)
  46. .getRegex();
  47. block.list = edit(block.list)
  48. .replace(/bull/g, block.bullet)
  49. .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
  50. .replace('def', '\\n+(?=' + block.def.source + ')')
  51. .getRegex();
  52. block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
  53. + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
  54. + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
  55. + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
  56. + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
  57. + '|track|ul';
  58. block._comment = /<!--(?!-?>)[\s\S]*?-->/;
  59. block.html = edit(block.html, 'i')
  60. .replace('comment', block._comment)
  61. .replace('tag', block._tag)
  62. .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
  63. .getRegex();
  64. block.paragraph = edit(block.paragraph)
  65. .replace('hr', block.hr)
  66. .replace('heading', block.heading)
  67. .replace('lheading', block.lheading)
  68. .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
  69. .getRegex();
  70. block.blockquote = edit(block.blockquote)
  71. .replace('paragraph', block.paragraph)
  72. .getRegex();
  73. /**
  74. * Normal Block Grammar
  75. */
  76. block.normal = merge({}, block);
  77. /**
  78. * GFM Block Grammar
  79. */
  80. block.gfm = merge({}, block.normal, {
  81. fences: /^ {0,3}(`{3,}|~{3,})([^`\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
  82. paragraph: /^/,
  83. heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
  84. });
  85. block.gfm.paragraph = edit(block.paragraph)
  86. .replace('(?!', '(?!'
  87. + block.gfm.fences.source.replace('\\1', '\\2') + '|'
  88. + block.list.source.replace('\\1', '\\3') + '|')
  89. .getRegex();
  90. /**
  91. * GFM + Tables Block Grammar
  92. */
  93. block.tables = merge({}, block.gfm, {
  94. nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,
  95. table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/
  96. });
  97. /**
  98. * Pedantic grammar
  99. */
  100. block.pedantic = merge({}, block.normal, {
  101. html: edit(
  102. '^ *(?:comment *(?:\\n|\\s*$)'
  103. + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
  104. + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
  105. .replace('comment', block._comment)
  106. .replace(/tag/g, '(?!(?:'
  107. + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
  108. + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
  109. + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
  110. .getRegex(),
  111. def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/
  112. });
  113. /**
  114. * Block Lexer
  115. */
  116. function Lexer(options) {
  117. this.tokens = [];
  118. this.tokens.links = Object.create(null);
  119. this.options = options || marked.defaults;
  120. this.rules = block.normal;
  121. if (this.options.pedantic) {
  122. this.rules = block.pedantic;
  123. } else if (this.options.gfm) {
  124. if (this.options.tables) {
  125. this.rules = block.tables;
  126. } else {
  127. this.rules = block.gfm;
  128. }
  129. }
  130. }
  131. /**
  132. * Expose Block Rules
  133. */
  134. Lexer.rules = block;
  135. /**
  136. * Static Lex Method
  137. */
  138. Lexer.lex = function(src, options) {
  139. var lexer = new Lexer(options);
  140. return lexer.lex(src);
  141. };
  142. /**
  143. * Preprocessing
  144. */
  145. Lexer.prototype.lex = function(src) {
  146. src = src
  147. .replace(/\r\n|\r/g, '\n')
  148. .replace(/\t/g, ' ')
  149. .replace(/\u00a0/g, ' ')
  150. .replace(/\u2424/g, '\n');
  151. return this.token(src, true);
  152. };
  153. /**
  154. * Lexing
  155. */
  156. Lexer.prototype.token = function(src, top) {
  157. src = src.replace(/^ +$/gm, '');
  158. var next,
  159. loose,
  160. cap,
  161. bull,
  162. b,
  163. item,
  164. listStart,
  165. listItems,
  166. t,
  167. space,
  168. i,
  169. tag,
  170. l,
  171. isordered,
  172. istask,
  173. ischecked;
  174. while (src) {
  175. // newline
  176. if (cap = this.rules.newline.exec(src)) {
  177. src = src.substring(cap[0].length);
  178. if (cap[0].length > 1) {
  179. this.tokens.push({
  180. type: 'space'
  181. });
  182. }
  183. }
  184. // code
  185. if (cap = this.rules.code.exec(src)) {
  186. src = src.substring(cap[0].length);
  187. cap = cap[0].replace(/^ {4}/gm, '');
  188. this.tokens.push({
  189. type: 'code',
  190. text: !this.options.pedantic
  191. ? rtrim(cap, '\n')
  192. : cap
  193. });
  194. continue;
  195. }
  196. // fences (gfm)
  197. if (cap = this.rules.fences.exec(src)) {
  198. src = src.substring(cap[0].length);
  199. this.tokens.push({
  200. type: 'code',
  201. lang: cap[2] ? cap[2].trim() : cap[2],
  202. text: cap[3] || ''
  203. });
  204. continue;
  205. }
  206. // heading
  207. if (cap = this.rules.heading.exec(src)) {
  208. src = src.substring(cap[0].length);
  209. this.tokens.push({
  210. type: 'heading',
  211. depth: cap[1].length,
  212. text: cap[2]
  213. });
  214. continue;
  215. }
  216. // table no leading pipe (gfm)
  217. if (cap = this.rules.nptable.exec(src)) {
  218. item = {
  219. type: 'table',
  220. header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
  221. align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
  222. cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
  223. };
  224. if (item.header.length === item.align.length) {
  225. src = src.substring(cap[0].length);
  226. for (i = 0; i < item.align.length; i++) {
  227. if (/^ *-+: *$/.test(item.align[i])) {
  228. item.align[i] = 'right';
  229. } else if (/^ *:-+: *$/.test(item.align[i])) {
  230. item.align[i] = 'center';
  231. } else if (/^ *:-+ *$/.test(item.align[i])) {
  232. item.align[i] = 'left';
  233. } else {
  234. item.align[i] = null;
  235. }
  236. }
  237. for (i = 0; i < item.cells.length; i++) {
  238. item.cells[i] = splitCells(item.cells[i], item.header.length);
  239. }
  240. this.tokens.push(item);
  241. continue;
  242. }
  243. }
  244. // hr
  245. if (cap = this.rules.hr.exec(src)) {
  246. src = src.substring(cap[0].length);
  247. this.tokens.push({
  248. type: 'hr'
  249. });
  250. continue;
  251. }
  252. // blockquote
  253. if (cap = this.rules.blockquote.exec(src)) {
  254. src = src.substring(cap[0].length);
  255. this.tokens.push({
  256. type: 'blockquote_start'
  257. });
  258. cap = cap[0].replace(/^ *> ?/gm, '');
  259. // Pass `top` to keep the current
  260. // "toplevel" state. This is exactly
  261. // how markdown.pl works.
  262. this.token(cap, top);
  263. this.tokens.push({
  264. type: 'blockquote_end'
  265. });
  266. continue;
  267. }
  268. // list
  269. if (cap = this.rules.list.exec(src)) {
  270. src = src.substring(cap[0].length);
  271. bull = cap[2];
  272. isordered = bull.length > 1;
  273. listStart = {
  274. type: 'list_start',
  275. ordered: isordered,
  276. start: isordered ? +bull : '',
  277. loose: false
  278. };
  279. this.tokens.push(listStart);
  280. // Get each top-level item.
  281. cap = cap[0].match(this.rules.item);
  282. listItems = [];
  283. next = false;
  284. l = cap.length;
  285. i = 0;
  286. for (; i < l; i++) {
  287. item = cap[i];
  288. // Remove the list item's bullet
  289. // so it is seen as the next token.
  290. space = item.length;
  291. item = item.replace(/^ *([*+-]|\d+\.) */, '');
  292. // Outdent whatever the
  293. // list item contains. Hacky.
  294. if (~item.indexOf('\n ')) {
  295. space -= item.length;
  296. item = !this.options.pedantic
  297. ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
  298. : item.replace(/^ {1,4}/gm, '');
  299. }
  300. // Determine whether the next list item belongs here.
  301. // Backpedal if it does not belong in this list.
  302. if (i !== l - 1) {
  303. b = block.bullet.exec(cap[i + 1])[0];
  304. if (bull.length > 1 ? b.length === 1
  305. : (b.length > 1 || (this.options.smartLists && b !== bull))) {
  306. src = cap.slice(i + 1).join('\n') + src;
  307. i = l - 1;
  308. }
  309. }
  310. // Determine whether item is loose or not.
  311. // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
  312. // for discount behavior.
  313. loose = next || /\n\n(?!\s*$)/.test(item);
  314. if (i !== l - 1) {
  315. next = item.charAt(item.length - 1) === '\n';
  316. if (!loose) loose = next;
  317. }
  318. if (loose) {
  319. listStart.loose = true;
  320. }
  321. // Check for task list items
  322. istask = /^\[[ xX]\] /.test(item);
  323. ischecked = undefined;
  324. if (istask) {
  325. ischecked = item[1] !== ' ';
  326. item = item.replace(/^\[[ xX]\] +/, '');
  327. }
  328. t = {
  329. type: 'list_item_start',
  330. task: istask,
  331. checked: ischecked,
  332. loose: loose
  333. };
  334. listItems.push(t);
  335. this.tokens.push(t);
  336. // Recurse.
  337. this.token(item, false);
  338. this.tokens.push({
  339. type: 'list_item_end'
  340. });
  341. }
  342. if (listStart.loose) {
  343. l = listItems.length;
  344. i = 0;
  345. for (; i < l; i++) {
  346. listItems[i].loose = true;
  347. }
  348. }
  349. this.tokens.push({
  350. type: 'list_end'
  351. });
  352. continue;
  353. }
  354. // html
  355. if (cap = this.rules.html.exec(src)) {
  356. src = src.substring(cap[0].length);
  357. this.tokens.push({
  358. type: this.options.sanitize
  359. ? 'paragraph'
  360. : 'html',
  361. pre: !this.options.sanitizer
  362. && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
  363. text: cap[0]
  364. });
  365. continue;
  366. }
  367. // def
  368. if (top && (cap = this.rules.def.exec(src))) {
  369. src = src.substring(cap[0].length);
  370. if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
  371. tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
  372. if (!this.tokens.links[tag]) {
  373. this.tokens.links[tag] = {
  374. href: cap[2],
  375. title: cap[3]
  376. };
  377. }
  378. continue;
  379. }
  380. // table (gfm)
  381. if (cap = this.rules.table.exec(src)) {
  382. item = {
  383. type: 'table',
  384. header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
  385. align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
  386. cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
  387. };
  388. if (item.header.length === item.align.length) {
  389. src = src.substring(cap[0].length);
  390. for (i = 0; i < item.align.length; i++) {
  391. if (/^ *-+: *$/.test(item.align[i])) {
  392. item.align[i] = 'right';
  393. } else if (/^ *:-+: *$/.test(item.align[i])) {
  394. item.align[i] = 'center';
  395. } else if (/^ *:-+ *$/.test(item.align[i])) {
  396. item.align[i] = 'left';
  397. } else {
  398. item.align[i] = null;
  399. }
  400. }
  401. for (i = 0; i < item.cells.length; i++) {
  402. item.cells[i] = splitCells(
  403. item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
  404. item.header.length);
  405. }
  406. this.tokens.push(item);
  407. continue;
  408. }
  409. }
  410. // lheading
  411. if (cap = this.rules.lheading.exec(src)) {
  412. src = src.substring(cap[0].length);
  413. this.tokens.push({
  414. type: 'heading',
  415. depth: cap[2] === '=' ? 1 : 2,
  416. text: cap[1]
  417. });
  418. continue;
  419. }
  420. // top-level paragraph
  421. if (top && (cap = this.rules.paragraph.exec(src))) {
  422. src = src.substring(cap[0].length);
  423. this.tokens.push({
  424. type: 'paragraph',
  425. text: cap[1].charAt(cap[1].length - 1) === '\n'
  426. ? cap[1].slice(0, -1)
  427. : cap[1]
  428. });
  429. continue;
  430. }
  431. // text
  432. if (cap = this.rules.text.exec(src)) {
  433. // Top-level should never reach here.
  434. src = src.substring(cap[0].length);
  435. this.tokens.push({
  436. type: 'text',
  437. text: cap[0]
  438. });
  439. continue;
  440. }
  441. if (src) {
  442. throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
  443. }
  444. }
  445. return this.tokens;
  446. };
  447. /**
  448. * Inline-Level Grammar
  449. */
  450. var inline = {
  451. escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
  452. autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
  453. url: noop,
  454. tag: '^comment'
  455. + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
  456. + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
  457. + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
  458. + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
  459. + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
  460. link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
  461. reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
  462. nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
  463. strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
  464. em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,
  465. code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
  466. br: /^( {2,}|\\)\n(?!\s*$)/,
  467. del: noop,
  468. text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n))|(?= {2,}\n))/
  469. };
  470. // list of punctuation marks from common mark spec
  471. // without ` and ] to workaround Rule 17 (inline code blocks/links)
  472. inline._punctuation = '!"#$%&\'()*+,\\-./:;<=>?@\\[^_{|}~';
  473. inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
  474. inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
  475. inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
  476. inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
  477. inline.autolink = edit(inline.autolink)
  478. .replace('scheme', inline._scheme)
  479. .replace('email', inline._email)
  480. .getRegex();
  481. inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
  482. inline.tag = edit(inline.tag)
  483. .replace('comment', block._comment)
  484. .replace('attribute', inline._attribute)
  485. .getRegex();
  486. inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|`(?!`)|[^\[\]\\`])*?/;
  487. inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*)/;
  488. inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
  489. inline.link = edit(inline.link)
  490. .replace('label', inline._label)
  491. .replace('href', inline._href)
  492. .replace('title', inline._title)
  493. .getRegex();
  494. inline.reflink = edit(inline.reflink)
  495. .replace('label', inline._label)
  496. .getRegex();
  497. /**
  498. * Normal Inline Grammar
  499. */
  500. inline.normal = merge({}, inline);
  501. /**
  502. * Pedantic Inline Grammar
  503. */
  504. inline.pedantic = merge({}, inline.normal, {
  505. strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
  506. em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
  507. link: edit(/^!?\[(label)\]\((.*?)\)/)
  508. .replace('label', inline._label)
  509. .getRegex(),
  510. reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
  511. .replace('label', inline._label)
  512. .getRegex()
  513. });
  514. /**
  515. * GFM Inline Grammar
  516. */
  517. inline.gfm = merge({}, inline.normal, {
  518. escape: edit(inline.escape).replace('])', '~|])').getRegex(),
  519. _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
  520. url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
  521. _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
  522. del: /^~+(?=\S)([\s\S]*?\S)~+/,
  523. text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?= {2,}\n|[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
  524. });
  525. inline.gfm.url = edit(inline.gfm.url, 'i')
  526. .replace('email', inline.gfm._extended_email)
  527. .getRegex();
  528. /**
  529. * GFM + Line Breaks Inline Grammar
  530. */
  531. inline.breaks = merge({}, inline.gfm, {
  532. br: edit(inline.br).replace('{2,}', '*').getRegex(),
  533. text: edit(inline.gfm.text).replace(/\{2,\}/g, '*').getRegex()
  534. });
  535. /**
  536. * Inline Lexer & Compiler
  537. */
  538. function InlineLexer(links, options) {
  539. this.options = options || marked.defaults;
  540. this.links = links;
  541. this.rules = inline.normal;
  542. this.renderer = this.options.renderer || new Renderer();
  543. this.renderer.options = this.options;
  544. if (!this.links) {
  545. throw new Error('Tokens array requires a `links` property.');
  546. }
  547. if (this.options.pedantic) {
  548. this.rules = inline.pedantic;
  549. } else if (this.options.gfm) {
  550. if (this.options.breaks) {
  551. this.rules = inline.breaks;
  552. } else {
  553. this.rules = inline.gfm;
  554. }
  555. }
  556. }
  557. /**
  558. * Expose Inline Rules
  559. */
  560. InlineLexer.rules = inline;
  561. /**
  562. * Static Lexing/Compiling Method
  563. */
  564. InlineLexer.output = function(src, links, options) {
  565. var inline = new InlineLexer(links, options);
  566. return inline.output(src);
  567. };
  568. /**
  569. * Lexing/Compiling
  570. */
  571. InlineLexer.prototype.output = function(src) {
  572. var out = '',
  573. link,
  574. text,
  575. href,
  576. title,
  577. cap,
  578. prevCapZero;
  579. while (src) {
  580. // escape
  581. if (cap = this.rules.escape.exec(src)) {
  582. src = src.substring(cap[0].length);
  583. out += escape(cap[1]);
  584. continue;
  585. }
  586. // tag
  587. if (cap = this.rules.tag.exec(src)) {
  588. if (!this.inLink && /^<a /i.test(cap[0])) {
  589. this.inLink = true;
  590. } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
  591. this.inLink = false;
  592. }
  593. if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  594. this.inRawBlock = true;
  595. } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  596. this.inRawBlock = false;
  597. }
  598. src = src.substring(cap[0].length);
  599. out += this.options.sanitize
  600. ? this.options.sanitizer
  601. ? this.options.sanitizer(cap[0])
  602. : escape(cap[0])
  603. : cap[0];
  604. continue;
  605. }
  606. // link
  607. if (cap = this.rules.link.exec(src)) {
  608. var lastParenIndex = findClosingBracket(cap[2], '()');
  609. if (lastParenIndex > -1) {
  610. var linkLen = cap[0].length - (cap[2].length - lastParenIndex) - (cap[3] || '').length;
  611. cap[2] = cap[2].substring(0, lastParenIndex);
  612. cap[0] = cap[0].substring(0, linkLen).trim();
  613. cap[3] = '';
  614. }
  615. src = src.substring(cap[0].length);
  616. this.inLink = true;
  617. href = cap[2];
  618. if (this.options.pedantic) {
  619. link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
  620. if (link) {
  621. href = link[1];
  622. title = link[3];
  623. } else {
  624. title = '';
  625. }
  626. } else {
  627. title = cap[3] ? cap[3].slice(1, -1) : '';
  628. }
  629. href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
  630. out += this.outputLink(cap, {
  631. href: InlineLexer.escapes(href),
  632. title: InlineLexer.escapes(title)
  633. });
  634. this.inLink = false;
  635. continue;
  636. }
  637. // reflink, nolink
  638. if ((cap = this.rules.reflink.exec(src))
  639. || (cap = this.rules.nolink.exec(src))) {
  640. src = src.substring(cap[0].length);
  641. link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
  642. link = this.links[link.toLowerCase()];
  643. if (!link || !link.href) {
  644. out += cap[0].charAt(0);
  645. src = cap[0].substring(1) + src;
  646. continue;
  647. }
  648. this.inLink = true;
  649. out += this.outputLink(cap, link);
  650. this.inLink = false;
  651. continue;
  652. }
  653. // strong
  654. if (cap = this.rules.strong.exec(src)) {
  655. src = src.substring(cap[0].length);
  656. out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));
  657. continue;
  658. }
  659. // em
  660. if (cap = this.rules.em.exec(src)) {
  661. src = src.substring(cap[0].length);
  662. out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));
  663. continue;
  664. }
  665. // code
  666. if (cap = this.rules.code.exec(src)) {
  667. src = src.substring(cap[0].length);
  668. out += this.renderer.codespan(escape(cap[2].trim(), true));
  669. continue;
  670. }
  671. // br
  672. if (cap = this.rules.br.exec(src)) {
  673. src = src.substring(cap[0].length);
  674. out += this.renderer.br();
  675. continue;
  676. }
  677. // del (gfm)
  678. if (cap = this.rules.del.exec(src)) {
  679. src = src.substring(cap[0].length);
  680. out += this.renderer.del(this.output(cap[1]));
  681. continue;
  682. }
  683. // autolink
  684. if (cap = this.rules.autolink.exec(src)) {
  685. src = src.substring(cap[0].length);
  686. if (cap[2] === '@') {
  687. text = escape(this.mangle(cap[1]));
  688. href = 'mailto:' + text;
  689. } else {
  690. text = escape(cap[1]);
  691. href = text;
  692. }
  693. out += this.renderer.link(href, null, text);
  694. continue;
  695. }
  696. // url (gfm)
  697. if (!this.inLink && (cap = this.rules.url.exec(src))) {
  698. if (cap[2] === '@') {
  699. text = escape(cap[0]);
  700. href = 'mailto:' + text;
  701. } else {
  702. // do extended autolink path validation
  703. do {
  704. prevCapZero = cap[0];
  705. cap[0] = this.rules._backpedal.exec(cap[0])[0];
  706. } while (prevCapZero !== cap[0]);
  707. text = escape(cap[0]);
  708. if (cap[1] === 'www.') {
  709. href = 'http://' + text;
  710. } else {
  711. href = text;
  712. }
  713. }
  714. src = src.substring(cap[0].length);
  715. out += this.renderer.link(href, null, text);
  716. continue;
  717. }
  718. // text
  719. if (cap = this.rules.text.exec(src)) {
  720. src = src.substring(cap[0].length);
  721. if (this.inRawBlock) {
  722. out += this.renderer.text(cap[0]);
  723. } else {
  724. out += this.renderer.text(escape(this.smartypants(cap[0])));
  725. }
  726. continue;
  727. }
  728. if (src) {
  729. throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
  730. }
  731. }
  732. return out;
  733. };
  734. InlineLexer.escapes = function(text) {
  735. return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
  736. };
  737. /**
  738. * Compile Link
  739. */
  740. InlineLexer.prototype.outputLink = function(cap, link) {
  741. var href = link.href,
  742. title = link.title ? escape(link.title) : null;
  743. return cap[0].charAt(0) !== '!'
  744. ? this.renderer.link(href, title, this.output(cap[1]))
  745. : this.renderer.image(href, title, escape(cap[1]));
  746. };
  747. /**
  748. * Smartypants Transformations
  749. */
  750. InlineLexer.prototype.smartypants = function(text) {
  751. if (!this.options.smartypants) return text;
  752. return text
  753. // em-dashes
  754. .replace(/---/g, '\u2014')
  755. // en-dashes
  756. .replace(/--/g, '\u2013')
  757. // opening singles
  758. .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
  759. // closing singles & apostrophes
  760. .replace(/'/g, '\u2019')
  761. // opening doubles
  762. .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
  763. // closing doubles
  764. .replace(/"/g, '\u201d')
  765. // ellipses
  766. .replace(/\.{3}/g, '\u2026');
  767. };
  768. /**
  769. * Mangle Links
  770. */
  771. InlineLexer.prototype.mangle = function(text) {
  772. if (!this.options.mangle) return text;
  773. var out = '',
  774. l = text.length,
  775. i = 0,
  776. ch;
  777. for (; i < l; i++) {
  778. ch = text.charCodeAt(i);
  779. if (Math.random() > 0.5) {
  780. ch = 'x' + ch.toString(16);
  781. }
  782. out += '&#' + ch + ';';
  783. }
  784. return out;
  785. };
  786. /**
  787. * Renderer
  788. */
  789. function Renderer(options) {
  790. this.options = options || marked.defaults;
  791. }
  792. Renderer.prototype.code = function(code, infostring, escaped) {
  793. var lang = (infostring || '').match(/\S*/)[0];
  794. if (this.options.highlight) {
  795. var out = this.options.highlight(code, lang);
  796. if (out != null && out !== code) {
  797. escaped = true;
  798. code = out;
  799. }
  800. }
  801. if (!lang) {
  802. return '<pre><code>'
  803. + (escaped ? code : escape(code, true))
  804. + '</code></pre>';
  805. }
  806. return '<pre><code class="'
  807. + this.options.langPrefix
  808. + escape(lang, true)
  809. + '">'
  810. + (escaped ? code : escape(code, true))
  811. + '</code></pre>\n';
  812. };
  813. Renderer.prototype.blockquote = function(quote) {
  814. return '<blockquote>\n' + quote + '</blockquote>\n';
  815. };
  816. Renderer.prototype.html = function(html) {
  817. return html;
  818. };
  819. Renderer.prototype.heading = function(text, level, raw, slugger) {
  820. if (this.options.headerIds) {
  821. return '<h'
  822. + level
  823. + ' id="'
  824. + this.options.headerPrefix
  825. + slugger.slug(raw)
  826. + '">'
  827. + text
  828. + '</h'
  829. + level
  830. + '>\n';
  831. }
  832. // ignore IDs
  833. return '<h' + level + '>' + text + '</h' + level + '>\n';
  834. };
  835. Renderer.prototype.hr = function() {
  836. return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
  837. };
  838. Renderer.prototype.list = function(body, ordered, start) {
  839. var type = ordered ? 'ol' : 'ul',
  840. startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
  841. return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
  842. };
  843. Renderer.prototype.listitem = function(text) {
  844. return '<li>' + text + '</li>\n';
  845. };
  846. Renderer.prototype.checkbox = function(checked) {
  847. return '<input '
  848. + (checked ? 'checked="" ' : '')
  849. + 'disabled="" type="checkbox"'
  850. + (this.options.xhtml ? ' /' : '')
  851. + '> ';
  852. };
  853. Renderer.prototype.paragraph = function(text) {
  854. return '<p>' + text + '</p>\n';
  855. };
  856. Renderer.prototype.table = function(header, body) {
  857. if (body) body = '<tbody>' + body + '</tbody>';
  858. return '<table>\n'
  859. + '<thead>\n'
  860. + header
  861. + '</thead>\n'
  862. + body
  863. + '</table>\n';
  864. };
  865. Renderer.prototype.tablerow = function(content) {
  866. return '<tr>\n' + content + '</tr>\n';
  867. };
  868. Renderer.prototype.tablecell = function(content, flags) {
  869. var type = flags.header ? 'th' : 'td';
  870. var tag = flags.align
  871. ? '<' + type + ' align="' + flags.align + '">'
  872. : '<' + type + '>';
  873. return tag + content + '</' + type + '>\n';
  874. };
  875. // span level renderer
  876. Renderer.prototype.strong = function(text) {
  877. return '<strong>' + text + '</strong>';
  878. };
  879. Renderer.prototype.em = function(text) {
  880. return '<em>' + text + '</em>';
  881. };
  882. Renderer.prototype.codespan = function(text) {
  883. return '<code>' + text + '</code>';
  884. };
  885. Renderer.prototype.br = function() {
  886. return this.options.xhtml ? '<br/>' : '<br>';
  887. };
  888. Renderer.prototype.del = function(text) {
  889. return '<del>' + text + '</del>';
  890. };
  891. Renderer.prototype.link = function(href, title, text) {
  892. href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
  893. if (href === null) {
  894. return text;
  895. }
  896. var out = '<a href="' + escape(href) + '"';
  897. if (title) {
  898. out += ' title="' + title + '"';
  899. }
  900. out += '>' + text + '</a>';
  901. return out;
  902. };
  903. Renderer.prototype.image = function(href, title, text) {
  904. href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
  905. if (href === null) {
  906. return text;
  907. }
  908. var out = '<img src="' + href + '" alt="' + text + '"';
  909. if (title) {
  910. out += ' title="' + title + '"';
  911. }
  912. out += this.options.xhtml ? '/>' : '>';
  913. return out;
  914. };
  915. Renderer.prototype.text = function(text) {
  916. return text;
  917. };
  918. /**
  919. * TextRenderer
  920. * returns only the textual part of the token
  921. */
  922. function TextRenderer() {}
  923. // no need for block level renderers
  924. TextRenderer.prototype.strong =
  925. TextRenderer.prototype.em =
  926. TextRenderer.prototype.codespan =
  927. TextRenderer.prototype.del =
  928. TextRenderer.prototype.text = function (text) {
  929. return text;
  930. };
  931. TextRenderer.prototype.link =
  932. TextRenderer.prototype.image = function(href, title, text) {
  933. return '' + text;
  934. };
  935. TextRenderer.prototype.br = function() {
  936. return '';
  937. };
  938. /**
  939. * Parsing & Compiling
  940. */
  941. function Parser(options) {
  942. this.tokens = [];
  943. this.token = null;
  944. this.options = options || marked.defaults;
  945. this.options.renderer = this.options.renderer || new Renderer();
  946. this.renderer = this.options.renderer;
  947. this.renderer.options = this.options;
  948. this.slugger = new Slugger();
  949. }
  950. /**
  951. * Static Parse Method
  952. */
  953. Parser.parse = function(src, options) {
  954. var parser = new Parser(options);
  955. return parser.parse(src);
  956. };
  957. /**
  958. * Parse Loop
  959. */
  960. Parser.prototype.parse = function(src) {
  961. this.inline = new InlineLexer(src.links, this.options);
  962. // use an InlineLexer with a TextRenderer to extract pure text
  963. this.inlineText = new InlineLexer(
  964. src.links,
  965. merge({}, this.options, {renderer: new TextRenderer()})
  966. );
  967. this.tokens = src.reverse();
  968. var out = '';
  969. while (this.next()) {
  970. out += this.tok();
  971. }
  972. return out;
  973. };
  974. /**
  975. * Next Token
  976. */
  977. Parser.prototype.next = function() {
  978. return this.token = this.tokens.pop();
  979. };
  980. /**
  981. * Preview Next Token
  982. */
  983. Parser.prototype.peek = function() {
  984. return this.tokens[this.tokens.length - 1] || 0;
  985. };
  986. /**
  987. * Parse Text Tokens
  988. */
  989. Parser.prototype.parseText = function() {
  990. var body = this.token.text;
  991. while (this.peek().type === 'text') {
  992. body += '\n' + this.next().text;
  993. }
  994. return this.inline.output(body);
  995. };
  996. /**
  997. * Parse Current Token
  998. */
  999. Parser.prototype.tok = function() {
  1000. switch (this.token.type) {
  1001. case 'space': {
  1002. return '';
  1003. }
  1004. case 'hr': {
  1005. return this.renderer.hr();
  1006. }
  1007. case 'heading': {
  1008. return this.renderer.heading(
  1009. this.inline.output(this.token.text),
  1010. this.token.depth,
  1011. unescape(this.inlineText.output(this.token.text)),
  1012. this.slugger);
  1013. }
  1014. case 'code': {
  1015. return this.renderer.code(this.token.text,
  1016. this.token.lang,
  1017. this.token.escaped);
  1018. }
  1019. case 'table': {
  1020. var header = '',
  1021. body = '',
  1022. i,
  1023. row,
  1024. cell,
  1025. j;
  1026. // header
  1027. cell = '';
  1028. for (i = 0; i < this.token.header.length; i++) {
  1029. cell += this.renderer.tablecell(
  1030. this.inline.output(this.token.header[i]),
  1031. { header: true, align: this.token.align[i] }
  1032. );
  1033. }
  1034. header += this.renderer.tablerow(cell);
  1035. for (i = 0; i < this.token.cells.length; i++) {
  1036. row = this.token.cells[i];
  1037. cell = '';
  1038. for (j = 0; j < row.length; j++) {
  1039. cell += this.renderer.tablecell(
  1040. this.inline.output(row[j]),
  1041. { header: false, align: this.token.align[j] }
  1042. );
  1043. }
  1044. body += this.renderer.tablerow(cell);
  1045. }
  1046. return this.renderer.table(header, body);
  1047. }
  1048. case 'blockquote_start': {
  1049. body = '';
  1050. while (this.next().type !== 'blockquote_end') {
  1051. body += this.tok();
  1052. }
  1053. return this.renderer.blockquote(body);
  1054. }
  1055. case 'list_start': {
  1056. body = '';
  1057. var ordered = this.token.ordered,
  1058. start = this.token.start;
  1059. while (this.next().type !== 'list_end') {
  1060. body += this.tok();
  1061. }
  1062. return this.renderer.list(body, ordered, start);
  1063. }
  1064. case 'list_item_start': {
  1065. body = '';
  1066. var loose = this.token.loose;
  1067. var checked = this.token.checked;
  1068. var task = this.token.task;
  1069. if (this.token.task) {
  1070. body += this.renderer.checkbox(checked);
  1071. }
  1072. while (this.next().type !== 'list_item_end') {
  1073. body += !loose && this.token.type === 'text'
  1074. ? this.parseText()
  1075. : this.tok();
  1076. }
  1077. return this.renderer.listitem(body, task, checked);
  1078. }
  1079. case 'html': {
  1080. // TODO parse inline content if parameter markdown=1
  1081. return this.renderer.html(this.token.text);
  1082. }
  1083. case 'paragraph': {
  1084. return this.renderer.paragraph(this.inline.output(this.token.text));
  1085. }
  1086. case 'text': {
  1087. return this.renderer.paragraph(this.parseText());
  1088. }
  1089. default: {
  1090. var errMsg = 'Token with "' + this.token.type + '" type was not found.';
  1091. if (this.options.silent) {
  1092. console.log(errMsg);
  1093. } else {
  1094. throw new Error(errMsg);
  1095. }
  1096. }
  1097. }
  1098. };
  1099. /**
  1100. * Slugger generates header id
  1101. */
  1102. function Slugger () {
  1103. this.seen = {};
  1104. }
  1105. /**
  1106. * Convert string to unique id
  1107. */
  1108. Slugger.prototype.slug = function (value) {
  1109. var slug = value
  1110. .toLowerCase()
  1111. .trim()
  1112. .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
  1113. .replace(/\s/g, '-');
  1114. if (this.seen.hasOwnProperty(slug)) {
  1115. var originalSlug = slug;
  1116. do {
  1117. this.seen[originalSlug]++;
  1118. slug = originalSlug + '-' + this.seen[originalSlug];
  1119. } while (this.seen.hasOwnProperty(slug));
  1120. }
  1121. this.seen[slug] = 0;
  1122. return slug;
  1123. };
  1124. /**
  1125. * Helpers
  1126. */
  1127. function escape(html, encode) {
  1128. if (encode) {
  1129. if (escape.escapeTest.test(html)) {
  1130. return html.replace(escape.escapeReplace, function (ch) { return escape.replacements[ch]; });
  1131. }
  1132. } else {
  1133. if (escape.escapeTestNoEncode.test(html)) {
  1134. return html.replace(escape.escapeReplaceNoEncode, function (ch) { return escape.replacements[ch]; });
  1135. }
  1136. }
  1137. return html;
  1138. }
  1139. escape.escapeTest = /[&<>"']/;
  1140. escape.escapeReplace = /[&<>"']/g;
  1141. escape.replacements = {
  1142. '&': '&amp;',
  1143. '<': '&lt;',
  1144. '>': '&gt;',
  1145. '"': '&quot;',
  1146. "'": '&#39;'
  1147. };
  1148. escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
  1149. escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
  1150. function unescape(html) {
  1151. // explicitly match decimal, hex, and named HTML entities
  1152. return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
  1153. n = n.toLowerCase();
  1154. if (n === 'colon') return ':';
  1155. if (n.charAt(0) === '#') {
  1156. return n.charAt(1) === 'x'
  1157. ? String.fromCharCode(parseInt(n.substring(2), 16))
  1158. : String.fromCharCode(+n.substring(1));
  1159. }
  1160. return '';
  1161. });
  1162. }
  1163. function edit(regex, opt) {
  1164. regex = regex.source || regex;
  1165. opt = opt || '';
  1166. return {
  1167. replace: function(name, val) {
  1168. val = val.source || val;
  1169. val = val.replace(/(^|[^\[])\^/g, '$1');
  1170. regex = regex.replace(name, val);
  1171. return this;
  1172. },
  1173. getRegex: function() {
  1174. return new RegExp(regex, opt);
  1175. }
  1176. };
  1177. }
  1178. function cleanUrl(sanitize, base, href) {
  1179. if (sanitize) {
  1180. try {
  1181. var prot = decodeURIComponent(unescape(href))
  1182. .replace(/[^\w:]/g, '')
  1183. .toLowerCase();
  1184. } catch (e) {
  1185. return null;
  1186. }
  1187. if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
  1188. return null;
  1189. }
  1190. }
  1191. if (base && !originIndependentUrl.test(href)) {
  1192. href = resolveUrl(base, href);
  1193. }
  1194. try {
  1195. href = encodeURI(href).replace(/%25/g, '%');
  1196. } catch (e) {
  1197. return null;
  1198. }
  1199. return href;
  1200. }
  1201. function resolveUrl(base, href) {
  1202. if (!baseUrls[' ' + base]) {
  1203. // we can ignore everything in base after the last slash of its path component,
  1204. // but we might need to add _that_
  1205. // https://tools.ietf.org/html/rfc3986#section-3
  1206. if (/^[^:]+:\/*[^/]*$/.test(base)) {
  1207. baseUrls[' ' + base] = base + '/';
  1208. } else {
  1209. baseUrls[' ' + base] = rtrim(base, '/', true);
  1210. }
  1211. }
  1212. base = baseUrls[' ' + base];
  1213. if (href.slice(0, 2) === '//') {
  1214. return base.replace(/:[\s\S]*/, ':') + href;
  1215. } else if (href.charAt(0) === '/') {
  1216. return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href;
  1217. } else {
  1218. return base + href;
  1219. }
  1220. }
  1221. var baseUrls = {};
  1222. var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
  1223. function noop() {}
  1224. noop.exec = noop;
  1225. function merge(obj) {
  1226. var i = 1,
  1227. target,
  1228. key;
  1229. for (; i < arguments.length; i++) {
  1230. target = arguments[i];
  1231. for (key in target) {
  1232. if (Object.prototype.hasOwnProperty.call(target, key)) {
  1233. obj[key] = target[key];
  1234. }
  1235. }
  1236. }
  1237. return obj;
  1238. }
  1239. function splitCells(tableRow, count) {
  1240. // ensure that every cell-delimiting pipe has a space
  1241. // before it to distinguish it from an escaped pipe
  1242. var row = tableRow.replace(/\|/g, function (match, offset, str) {
  1243. var escaped = false,
  1244. curr = offset;
  1245. while (--curr >= 0 && str[curr] === '\\') escaped = !escaped;
  1246. if (escaped) {
  1247. // odd number of slashes means | is escaped
  1248. // so we leave it alone
  1249. return '|';
  1250. } else {
  1251. // add space before unescaped |
  1252. return ' |';
  1253. }
  1254. }),
  1255. cells = row.split(/ \|/),
  1256. i = 0;
  1257. if (cells.length > count) {
  1258. cells.splice(count);
  1259. } else {
  1260. while (cells.length < count) cells.push('');
  1261. }
  1262. for (; i < cells.length; i++) {
  1263. // leading or trailing whitespace is ignored per the gfm spec
  1264. cells[i] = cells[i].trim().replace(/\\\|/g, '|');
  1265. }
  1266. return cells;
  1267. }
  1268. // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
  1269. // /c*$/ is vulnerable to REDOS.
  1270. // invert: Remove suffix of non-c chars instead. Default falsey.
  1271. function rtrim(str, c, invert) {
  1272. if (str.length === 0) {
  1273. return '';
  1274. }
  1275. // Length of suffix matching the invert condition.
  1276. var suffLen = 0;
  1277. // Step left until we fail to match the invert condition.
  1278. while (suffLen < str.length) {
  1279. var currChar = str.charAt(str.length - suffLen - 1);
  1280. if (currChar === c && !invert) {
  1281. suffLen++;
  1282. } else if (currChar !== c && invert) {
  1283. suffLen++;
  1284. } else {
  1285. break;
  1286. }
  1287. }
  1288. return str.substr(0, str.length - suffLen);
  1289. }
  1290. function findClosingBracket(str, b) {
  1291. if (str.indexOf(b[1]) === -1) {
  1292. return -1;
  1293. }
  1294. var level = 0;
  1295. for (var i = 0; i < str.length; i++) {
  1296. if (str[i] === '\\') {
  1297. i++;
  1298. } else if (str[i] === b[0]) {
  1299. level++;
  1300. } else if (str[i] === b[1]) {
  1301. level--;
  1302. if (level < 0) {
  1303. return i;
  1304. }
  1305. }
  1306. }
  1307. return -1;
  1308. }
  1309. /**
  1310. * Marked
  1311. */
  1312. function marked(src, opt, callback) {
  1313. // throw error in case of non string input
  1314. if (typeof src === 'undefined' || src === null) {
  1315. throw new Error('marked(): input parameter is undefined or null');
  1316. }
  1317. if (typeof src !== 'string') {
  1318. throw new Error('marked(): input parameter is of type '
  1319. + Object.prototype.toString.call(src) + ', string expected');
  1320. }
  1321. if (callback || typeof opt === 'function') {
  1322. if (!callback) {
  1323. callback = opt;
  1324. opt = null;
  1325. }
  1326. opt = merge({}, marked.defaults, opt || {});
  1327. var highlight = opt.highlight,
  1328. tokens,
  1329. pending,
  1330. i = 0;
  1331. try {
  1332. tokens = Lexer.lex(src, opt);
  1333. } catch (e) {
  1334. return callback(e);
  1335. }
  1336. pending = tokens.length;
  1337. var done = function(err) {
  1338. if (err) {
  1339. opt.highlight = highlight;
  1340. return callback(err);
  1341. }
  1342. var out;
  1343. try {
  1344. out = Parser.parse(tokens, opt);
  1345. } catch (e) {
  1346. err = e;
  1347. }
  1348. opt.highlight = highlight;
  1349. return err
  1350. ? callback(err)
  1351. : callback(null, out);
  1352. };
  1353. if (!highlight || highlight.length < 3) {
  1354. return done();
  1355. }
  1356. delete opt.highlight;
  1357. if (!pending) return done();
  1358. for (; i < tokens.length; i++) {
  1359. (function(token) {
  1360. if (token.type !== 'code') {
  1361. return --pending || done();
  1362. }
  1363. return highlight(token.text, token.lang, function(err, code) {
  1364. if (err) return done(err);
  1365. if (code == null || code === token.text) {
  1366. return --pending || done();
  1367. }
  1368. token.text = code;
  1369. token.escaped = true;
  1370. --pending || done();
  1371. });
  1372. })(tokens[i]);
  1373. }
  1374. return;
  1375. }
  1376. try {
  1377. if (opt) opt = merge({}, marked.defaults, opt);
  1378. return Parser.parse(Lexer.lex(src, opt), opt);
  1379. } catch (e) {
  1380. e.message += '\nPlease report this to https://github.com/markedjs/marked.';
  1381. if ((opt || marked.defaults).silent) {
  1382. return '<p>An error occurred:</p><pre>'
  1383. + escape(e.message + '', true)
  1384. + '</pre>';
  1385. }
  1386. throw e;
  1387. }
  1388. }
  1389. /**
  1390. * Options
  1391. */
  1392. marked.options =
  1393. marked.setOptions = function(opt) {
  1394. merge(marked.defaults, opt);
  1395. return marked;
  1396. };
  1397. marked.getDefaults = function () {
  1398. return {
  1399. baseUrl: null,
  1400. breaks: false,
  1401. gfm: true,
  1402. headerIds: true,
  1403. headerPrefix: '',
  1404. highlight: null,
  1405. langPrefix: 'language-',
  1406. mangle: true,
  1407. pedantic: false,
  1408. renderer: new Renderer(),
  1409. sanitize: false,
  1410. sanitizer: null,
  1411. silent: false,
  1412. smartLists: false,
  1413. smartypants: false,
  1414. tables: true,
  1415. xhtml: false
  1416. };
  1417. };
  1418. marked.defaults = marked.getDefaults();
  1419. /**
  1420. * Expose
  1421. */
  1422. marked.Parser = Parser;
  1423. marked.parser = Parser.parse;
  1424. marked.Renderer = Renderer;
  1425. marked.TextRenderer = TextRenderer;
  1426. marked.Lexer = Lexer;
  1427. marked.lexer = Lexer.lex;
  1428. marked.InlineLexer = InlineLexer;
  1429. marked.inlineLexer = InlineLexer.output;
  1430. marked.Slugger = Slugger;
  1431. marked.parse = marked;
  1432. if (typeof module !== 'undefined' && typeof exports === 'object') {
  1433. module.exports = marked;
  1434. } else if (typeof define === 'function' && define.amd) {
  1435. define(function() { return marked; });
  1436. } else {
  1437. root.marked = marked;
  1438. }
  1439. })(this || (typeof window !== 'undefined' ? window : global));