marked.js 42 KB

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