marked.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614
  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: /^( *)(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)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
  28. + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=\\h*\\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+\.)/;
  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: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\n? *\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],
  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 (top && (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 (this.options.smartLists && i !== l - 1) {
  303. b = block.bullet.exec(cap[i + 1])[0];
  304. if (bull !== b && !(bull.length > 1 && b.length > 1)) {
  305. src = cap.slice(i + 1).join('\n') + src;
  306. i = l - 1;
  307. }
  308. }
  309. // Determine whether item is loose or not.
  310. // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
  311. // for discount behavior.
  312. loose = next || /\n\n(?!\s*$)/.test(item);
  313. if (i !== l - 1) {
  314. next = item.charAt(item.length - 1) === '\n';
  315. if (!loose) loose = next;
  316. }
  317. if (loose) {
  318. listStart.loose = true;
  319. }
  320. // Check for task list items
  321. istask = /^\[[ xX]\] /.test(item);
  322. ischecked = undefined;
  323. if (istask) {
  324. ischecked = item[1] !== ' ';
  325. item = item.replace(/^\[[ xX]\] +/, '');
  326. }
  327. t = {
  328. type: 'list_item_start',
  329. task: istask,
  330. checked: ischecked,
  331. loose: loose
  332. };
  333. listItems.push(t);
  334. this.tokens.push(t);
  335. // Recurse.
  336. this.token(item, false);
  337. this.tokens.push({
  338. type: 'list_item_end'
  339. });
  340. }
  341. if (listStart.loose) {
  342. l = listItems.length;
  343. i = 0;
  344. for (; i < l; i++) {
  345. listItems[i].loose = true;
  346. }
  347. }
  348. this.tokens.push({
  349. type: 'list_end'
  350. });
  351. continue;
  352. }
  353. // html
  354. if (cap = this.rules.html.exec(src)) {
  355. src = src.substring(cap[0].length);
  356. this.tokens.push({
  357. type: this.options.sanitize
  358. ? 'paragraph'
  359. : 'html',
  360. pre: !this.options.sanitizer
  361. && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
  362. text: cap[0]
  363. });
  364. continue;
  365. }
  366. // def
  367. if (top && (cap = this.rules.def.exec(src))) {
  368. src = src.substring(cap[0].length);
  369. if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
  370. tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
  371. if (!this.tokens.links[tag]) {
  372. this.tokens.links[tag] = {
  373. href: cap[2],
  374. title: cap[3]
  375. };
  376. }
  377. continue;
  378. }
  379. // table (gfm)
  380. if (top && (cap = this.rules.table.exec(src))) {
  381. item = {
  382. type: 'table',
  383. header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
  384. align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
  385. cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : []
  386. };
  387. if (item.header.length === item.align.length) {
  388. src = src.substring(cap[0].length);
  389. for (i = 0; i < item.align.length; i++) {
  390. if (/^ *-+: *$/.test(item.align[i])) {
  391. item.align[i] = 'right';
  392. } else if (/^ *:-+: *$/.test(item.align[i])) {
  393. item.align[i] = 'center';
  394. } else if (/^ *:-+ *$/.test(item.align[i])) {
  395. item.align[i] = 'left';
  396. } else {
  397. item.align[i] = null;
  398. }
  399. }
  400. for (i = 0; i < item.cells.length; i++) {
  401. item.cells[i] = splitCells(
  402. item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
  403. item.header.length);
  404. }
  405. this.tokens.push(item);
  406. continue;
  407. }
  408. }
  409. // lheading
  410. if (cap = this.rules.lheading.exec(src)) {
  411. src = src.substring(cap[0].length);
  412. this.tokens.push({
  413. type: 'heading',
  414. depth: cap[2] === '=' ? 1 : 2,
  415. text: cap[1]
  416. });
  417. continue;
  418. }
  419. // top-level paragraph
  420. if (top && (cap = this.rules.paragraph.exec(src))) {
  421. src = src.substring(cap[0].length);
  422. this.tokens.push({
  423. type: 'paragraph',
  424. text: cap[1].charAt(cap[1].length - 1) === '\n'
  425. ? cap[1].slice(0, -1)
  426. : cap[1]
  427. });
  428. continue;
  429. }
  430. // text
  431. if (cap = this.rules.text.exec(src)) {
  432. // Top-level should never reach here.
  433. src = src.substring(cap[0].length);
  434. this.tokens.push({
  435. type: 'text',
  436. text: cap[0]
  437. });
  438. continue;
  439. }
  440. if (src) {
  441. throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
  442. }
  443. }
  444. return this.tokens;
  445. };
  446. /**
  447. * Inline-Level Grammar
  448. */
  449. var inline = {
  450. escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
  451. autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
  452. url: noop,
  453. tag: '^comment'
  454. + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
  455. + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
  456. + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
  457. + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
  458. + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
  459. link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
  460. reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
  461. nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
  462. strong: /^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
  463. em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\s.])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\s.])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,
  464. code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
  465. br: /^( {2,}|\\)\n(?!\s*$)/,
  466. del: noop,
  467. text: /^(`+|[^`])[\s\S]*?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
  468. };
  469. inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
  470. inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
  471. 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])?)+(?![-_])/;
  472. inline.autolink = edit(inline.autolink)
  473. .replace('scheme', inline._scheme)
  474. .replace('email', inline._email)
  475. .getRegex();
  476. inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
  477. inline.tag = edit(inline.tag)
  478. .replace('comment', block._comment)
  479. .replace('attribute', inline._attribute)
  480. .getRegex();
  481. inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
  482. inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/;
  483. inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
  484. inline.link = edit(inline.link)
  485. .replace('label', inline._label)
  486. .replace('href', inline._href)
  487. .replace('title', inline._title)
  488. .getRegex();
  489. inline.reflink = edit(inline.reflink)
  490. .replace('label', inline._label)
  491. .getRegex();
  492. /**
  493. * Normal Inline Grammar
  494. */
  495. inline.normal = merge({}, inline);
  496. /**
  497. * Pedantic Inline Grammar
  498. */
  499. inline.pedantic = merge({}, inline.normal, {
  500. strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
  501. em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
  502. link: edit(/^!?\[(label)\]\((.*?)\)/)
  503. .replace('label', inline._label)
  504. .getRegex(),
  505. reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
  506. .replace('label', inline._label)
  507. .getRegex()
  508. });
  509. /**
  510. * GFM Inline Grammar
  511. */
  512. inline.gfm = merge({}, inline.normal, {
  513. escape: edit(inline.escape).replace('])', '~|])').getRegex(),
  514. _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
  515. url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
  516. _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
  517. del: /^~+(?=\S)([\s\S]*?\S)~+/,
  518. text: edit(inline.text)
  519. .replace(']|', '~]|')
  520. .replace('|$', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|$')
  521. .getRegex()
  522. });
  523. inline.gfm.url = edit(inline.gfm.url)
  524. .replace('email', inline.gfm._extended_email)
  525. .getRegex();
  526. /**
  527. * GFM + Line Breaks Inline Grammar
  528. */
  529. inline.breaks = merge({}, inline.gfm, {
  530. br: edit(inline.br).replace('{2,}', '*').getRegex(),
  531. text: edit(inline.gfm.text).replace('{2,}', '*').getRegex()
  532. });
  533. /**
  534. * Inline Lexer & Compiler
  535. */
  536. function InlineLexer(links, options) {
  537. this.options = options || marked.defaults;
  538. this.links = links;
  539. this.rules = inline.normal;
  540. this.renderer = this.options.renderer || new Renderer();
  541. this.renderer.options = this.options;
  542. if (!this.links) {
  543. throw new Error('Tokens array requires a `links` property.');
  544. }
  545. if (this.options.pedantic) {
  546. this.rules = inline.pedantic;
  547. } else if (this.options.gfm) {
  548. if (this.options.breaks) {
  549. this.rules = inline.breaks;
  550. } else {
  551. this.rules = inline.gfm;
  552. }
  553. }
  554. }
  555. /**
  556. * Expose Inline Rules
  557. */
  558. InlineLexer.rules = inline;
  559. /**
  560. * Static Lexing/Compiling Method
  561. */
  562. InlineLexer.output = function(src, links, options) {
  563. var inline = new InlineLexer(links, options);
  564. return inline.output(src);
  565. };
  566. /**
  567. * Lexing/Compiling
  568. */
  569. InlineLexer.prototype.output = function(src) {
  570. var out = '',
  571. link,
  572. text,
  573. href,
  574. title,
  575. cap,
  576. prevCapZero;
  577. while (src) {
  578. // escape
  579. if (cap = this.rules.escape.exec(src)) {
  580. src = src.substring(cap[0].length);
  581. out += cap[1];
  582. continue;
  583. }
  584. // autolink
  585. if (cap = this.rules.autolink.exec(src)) {
  586. src = src.substring(cap[0].length);
  587. if (cap[2] === '@') {
  588. text = escape(this.mangle(cap[1]));
  589. href = 'mailto:' + text;
  590. } else {
  591. text = escape(cap[1]);
  592. href = text;
  593. }
  594. out += this.renderer.link(href, null, text);
  595. continue;
  596. }
  597. // url (gfm)
  598. if (!this.inLink && (cap = this.rules.url.exec(src))) {
  599. if (cap[2] === '@') {
  600. text = escape(cap[0]);
  601. href = 'mailto:' + text;
  602. } else {
  603. // do extended autolink path validation
  604. do {
  605. prevCapZero = cap[0];
  606. cap[0] = this.rules._backpedal.exec(cap[0])[0];
  607. } while (prevCapZero !== cap[0]);
  608. text = escape(cap[0]);
  609. if (cap[1] === 'www.') {
  610. href = 'http://' + text;
  611. } else {
  612. href = text;
  613. }
  614. }
  615. src = src.substring(cap[0].length);
  616. out += this.renderer.link(href, null, text);
  617. continue;
  618. }
  619. // tag
  620. if (cap = this.rules.tag.exec(src)) {
  621. if (!this.inLink && /^<a /i.test(cap[0])) {
  622. this.inLink = true;
  623. } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
  624. this.inLink = false;
  625. }
  626. if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  627. this.inRawBlock = true;
  628. } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
  629. this.inRawBlock = false;
  630. }
  631. src = src.substring(cap[0].length);
  632. out += this.options.sanitize
  633. ? this.options.sanitizer
  634. ? this.options.sanitizer(cap[0])
  635. : escape(cap[0])
  636. : cap[0];
  637. continue;
  638. }
  639. // link
  640. if (cap = this.rules.link.exec(src)) {
  641. src = src.substring(cap[0].length);
  642. this.inLink = true;
  643. href = cap[2];
  644. if (this.options.pedantic) {
  645. link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
  646. if (link) {
  647. href = link[1];
  648. title = link[3];
  649. } else {
  650. title = '';
  651. }
  652. } else {
  653. title = cap[3] ? cap[3].slice(1, -1) : '';
  654. }
  655. href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
  656. out += this.outputLink(cap, {
  657. href: InlineLexer.escapes(href),
  658. title: InlineLexer.escapes(title)
  659. });
  660. this.inLink = false;
  661. continue;
  662. }
  663. // reflink, nolink
  664. if ((cap = this.rules.reflink.exec(src))
  665. || (cap = this.rules.nolink.exec(src))) {
  666. src = src.substring(cap[0].length);
  667. link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
  668. link = this.links[link.toLowerCase()];
  669. if (!link || !link.href) {
  670. out += cap[0].charAt(0);
  671. src = cap[0].substring(1) + src;
  672. continue;
  673. }
  674. this.inLink = true;
  675. out += this.outputLink(cap, link);
  676. this.inLink = false;
  677. continue;
  678. }
  679. // strong
  680. if (cap = this.rules.strong.exec(src)) {
  681. src = src.substring(cap[0].length);
  682. out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));
  683. continue;
  684. }
  685. // em
  686. if (cap = this.rules.em.exec(src)) {
  687. src = src.substring(cap[0].length);
  688. out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));
  689. continue;
  690. }
  691. // code
  692. if (cap = this.rules.code.exec(src)) {
  693. src = src.substring(cap[0].length);
  694. out += this.renderer.codespan(escape(cap[2].trim(), true));
  695. continue;
  696. }
  697. // br
  698. if (cap = this.rules.br.exec(src)) {
  699. src = src.substring(cap[0].length);
  700. out += this.renderer.br();
  701. continue;
  702. }
  703. // del (gfm)
  704. if (cap = this.rules.del.exec(src)) {
  705. src = src.substring(cap[0].length);
  706. out += this.renderer.del(this.output(cap[1]));
  707. continue;
  708. }
  709. // text
  710. if (cap = this.rules.text.exec(src)) {
  711. src = src.substring(cap[0].length);
  712. if (this.inRawBlock) {
  713. out += this.renderer.text(cap[0]);
  714. } else {
  715. out += this.renderer.text(escape(this.smartypants(cap[0])));
  716. }
  717. continue;
  718. }
  719. if (src) {
  720. throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
  721. }
  722. }
  723. return out;
  724. };
  725. InlineLexer.escapes = function(text) {
  726. return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
  727. };
  728. /**
  729. * Compile Link
  730. */
  731. InlineLexer.prototype.outputLink = function(cap, link) {
  732. var href = link.href,
  733. title = link.title ? escape(link.title) : null;
  734. return cap[0].charAt(0) !== '!'
  735. ? this.renderer.link(href, title, this.output(cap[1]))
  736. : this.renderer.image(href, title, escape(cap[1]));
  737. };
  738. /**
  739. * Smartypants Transformations
  740. */
  741. InlineLexer.prototype.smartypants = function(text) {
  742. if (!this.options.smartypants) return text;
  743. return text
  744. // em-dashes
  745. .replace(/---/g, '\u2014')
  746. // en-dashes
  747. .replace(/--/g, '\u2013')
  748. // opening singles
  749. .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
  750. // closing singles & apostrophes
  751. .replace(/'/g, '\u2019')
  752. // opening doubles
  753. .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
  754. // closing doubles
  755. .replace(/"/g, '\u201d')
  756. // ellipses
  757. .replace(/\.{3}/g, '\u2026');
  758. };
  759. /**
  760. * Mangle Links
  761. */
  762. InlineLexer.prototype.mangle = function(text) {
  763. if (!this.options.mangle) return text;
  764. var out = '',
  765. l = text.length,
  766. i = 0,
  767. ch;
  768. for (; i < l; i++) {
  769. ch = text.charCodeAt(i);
  770. if (Math.random() > 0.5) {
  771. ch = 'x' + ch.toString(16);
  772. }
  773. out += '&#' + ch + ';';
  774. }
  775. return out;
  776. };
  777. /**
  778. * Renderer
  779. */
  780. function Renderer(options) {
  781. this.options = options || marked.defaults;
  782. }
  783. Renderer.prototype.code = function(code, lang, escaped) {
  784. if (this.options.highlight) {
  785. var out = this.options.highlight(code, lang);
  786. if (out != null && out !== code) {
  787. escaped = true;
  788. code = out;
  789. }
  790. }
  791. if (!lang) {
  792. return '<pre><code>'
  793. + (escaped ? code : escape(code, true))
  794. + '</code></pre>';
  795. }
  796. return '<pre><code class="'
  797. + this.options.langPrefix
  798. + escape(lang, true)
  799. + '">'
  800. + (escaped ? code : escape(code, true))
  801. + '</code></pre>\n';
  802. };
  803. Renderer.prototype.blockquote = function(quote) {
  804. return '<blockquote>\n' + quote + '</blockquote>\n';
  805. };
  806. Renderer.prototype.html = function(html) {
  807. return html;
  808. };
  809. Renderer.prototype.heading = function(text, level, raw) {
  810. if (this.options.headerIds) {
  811. return '<h'
  812. + level
  813. + ' id="'
  814. + this.options.headerPrefix
  815. + raw.toLowerCase().replace(/[^\w]+/g, '-')
  816. + '">'
  817. + text
  818. + '</h'
  819. + level
  820. + '>\n';
  821. }
  822. // ignore IDs
  823. return '<h' + level + '>' + text + '</h' + level + '>\n';
  824. };
  825. Renderer.prototype.hr = function() {
  826. return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
  827. };
  828. Renderer.prototype.list = function(body, ordered, start) {
  829. var type = ordered ? 'ol' : 'ul',
  830. startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
  831. return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
  832. };
  833. Renderer.prototype.listitem = function(text) {
  834. return '<li>' + text + '</li>\n';
  835. };
  836. Renderer.prototype.checkbox = function(checked) {
  837. return '<input '
  838. + (checked ? 'checked="" ' : '')
  839. + 'disabled="" type="checkbox"'
  840. + (this.options.xhtml ? ' /' : '')
  841. + '> ';
  842. };
  843. Renderer.prototype.paragraph = function(text) {
  844. return '<p>' + text + '</p>\n';
  845. };
  846. Renderer.prototype.table = function(header, body) {
  847. if (body) body = '<tbody>' + body + '</tbody>';
  848. return '<table>\n'
  849. + '<thead>\n'
  850. + header
  851. + '</thead>\n'
  852. + body
  853. + '</table>\n';
  854. };
  855. Renderer.prototype.tablerow = function(content) {
  856. return '<tr>\n' + content + '</tr>\n';
  857. };
  858. Renderer.prototype.tablecell = function(content, flags) {
  859. var type = flags.header ? 'th' : 'td';
  860. var tag = flags.align
  861. ? '<' + type + ' align="' + flags.align + '">'
  862. : '<' + type + '>';
  863. return tag + content + '</' + type + '>\n';
  864. };
  865. // span level renderer
  866. Renderer.prototype.strong = function(text) {
  867. return '<strong>' + text + '</strong>';
  868. };
  869. Renderer.prototype.em = function(text) {
  870. return '<em>' + text + '</em>';
  871. };
  872. Renderer.prototype.codespan = function(text) {
  873. return '<code>' + text + '</code>';
  874. };
  875. Renderer.prototype.br = function() {
  876. return this.options.xhtml ? '<br/>' : '<br>';
  877. };
  878. Renderer.prototype.del = function(text) {
  879. return '<del>' + text + '</del>';
  880. };
  881. Renderer.prototype.link = function(href, title, text) {
  882. href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
  883. if (href === null) {
  884. return text;
  885. }
  886. var out = '<a href="' + escape(href) + '"';
  887. if (title) {
  888. out += ' title="' + title + '"';
  889. }
  890. out += '>' + text + '</a>';
  891. return out;
  892. };
  893. Renderer.prototype.image = function(href, title, text) {
  894. href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
  895. if (href === null) {
  896. return text;
  897. }
  898. var out = '<img src="' + href + '" alt="' + text + '"';
  899. if (title) {
  900. out += ' title="' + title + '"';
  901. }
  902. out += this.options.xhtml ? '/>' : '>';
  903. return out;
  904. };
  905. Renderer.prototype.text = function(text) {
  906. return text;
  907. };
  908. /**
  909. * TextRenderer
  910. * returns only the textual part of the token
  911. */
  912. function TextRenderer() {}
  913. // no need for block level renderers
  914. TextRenderer.prototype.strong =
  915. TextRenderer.prototype.em =
  916. TextRenderer.prototype.codespan =
  917. TextRenderer.prototype.del =
  918. TextRenderer.prototype.text = function (text) {
  919. return text;
  920. };
  921. TextRenderer.prototype.link =
  922. TextRenderer.prototype.image = function(href, title, text) {
  923. return '' + text;
  924. };
  925. TextRenderer.prototype.br = function() {
  926. return '';
  927. };
  928. /**
  929. * Parsing & Compiling
  930. */
  931. function Parser(options) {
  932. this.tokens = [];
  933. this.token = null;
  934. this.options = options || marked.defaults;
  935. this.options.renderer = this.options.renderer || new Renderer();
  936. this.renderer = this.options.renderer;
  937. this.renderer.options = this.options;
  938. }
  939. /**
  940. * Static Parse Method
  941. */
  942. Parser.parse = function(src, options) {
  943. var parser = new Parser(options);
  944. return parser.parse(src);
  945. };
  946. /**
  947. * Parse Loop
  948. */
  949. Parser.prototype.parse = function(src) {
  950. this.inline = new InlineLexer(src.links, this.options);
  951. // use an InlineLexer with a TextRenderer to extract pure text
  952. this.inlineText = new InlineLexer(
  953. src.links,
  954. merge({}, this.options, {renderer: new TextRenderer()})
  955. );
  956. this.tokens = src.reverse();
  957. var out = '';
  958. while (this.next()) {
  959. out += this.tok();
  960. }
  961. return out;
  962. };
  963. /**
  964. * Next Token
  965. */
  966. Parser.prototype.next = function() {
  967. return this.token = this.tokens.pop();
  968. };
  969. /**
  970. * Preview Next Token
  971. */
  972. Parser.prototype.peek = function() {
  973. return this.tokens[this.tokens.length - 1] || 0;
  974. };
  975. /**
  976. * Parse Text Tokens
  977. */
  978. Parser.prototype.parseText = function() {
  979. var body = this.token.text;
  980. while (this.peek().type === 'text') {
  981. body += '\n' + this.next().text;
  982. }
  983. return this.inline.output(body);
  984. };
  985. /**
  986. * Parse Current Token
  987. */
  988. Parser.prototype.tok = function() {
  989. switch (this.token.type) {
  990. case 'space': {
  991. return '';
  992. }
  993. case 'hr': {
  994. return this.renderer.hr();
  995. }
  996. case 'heading': {
  997. return this.renderer.heading(
  998. this.inline.output(this.token.text),
  999. this.token.depth,
  1000. unescape(this.inlineText.output(this.token.text)));
  1001. }
  1002. case 'code': {
  1003. return this.renderer.code(this.token.text,
  1004. this.token.lang,
  1005. this.token.escaped);
  1006. }
  1007. case 'table': {
  1008. var header = '',
  1009. body = '',
  1010. i,
  1011. row,
  1012. cell,
  1013. j;
  1014. // header
  1015. cell = '';
  1016. for (i = 0; i < this.token.header.length; i++) {
  1017. cell += this.renderer.tablecell(
  1018. this.inline.output(this.token.header[i]),
  1019. { header: true, align: this.token.align[i] }
  1020. );
  1021. }
  1022. header += this.renderer.tablerow(cell);
  1023. for (i = 0; i < this.token.cells.length; i++) {
  1024. row = this.token.cells[i];
  1025. cell = '';
  1026. for (j = 0; j < row.length; j++) {
  1027. cell += this.renderer.tablecell(
  1028. this.inline.output(row[j]),
  1029. { header: false, align: this.token.align[j] }
  1030. );
  1031. }
  1032. body += this.renderer.tablerow(cell);
  1033. }
  1034. return this.renderer.table(header, body);
  1035. }
  1036. case 'blockquote_start': {
  1037. body = '';
  1038. while (this.next().type !== 'blockquote_end') {
  1039. body += this.tok();
  1040. }
  1041. return this.renderer.blockquote(body);
  1042. }
  1043. case 'list_start': {
  1044. body = '';
  1045. var ordered = this.token.ordered,
  1046. start = this.token.start;
  1047. while (this.next().type !== 'list_end') {
  1048. body += this.tok();
  1049. }
  1050. return this.renderer.list(body, ordered, start);
  1051. }
  1052. case 'list_item_start': {
  1053. body = '';
  1054. var loose = this.token.loose;
  1055. if (this.token.task) {
  1056. body += this.renderer.checkbox(this.token.checked);
  1057. }
  1058. while (this.next().type !== 'list_item_end') {
  1059. body += !loose && this.token.type === 'text'
  1060. ? this.parseText()
  1061. : this.tok();
  1062. }
  1063. return this.renderer.listitem(body);
  1064. }
  1065. case 'html': {
  1066. // TODO parse inline content if parameter markdown=1
  1067. return this.renderer.html(this.token.text);
  1068. }
  1069. case 'paragraph': {
  1070. return this.renderer.paragraph(this.inline.output(this.token.text));
  1071. }
  1072. case 'text': {
  1073. return this.renderer.paragraph(this.parseText());
  1074. }
  1075. }
  1076. };
  1077. /**
  1078. * Helpers
  1079. */
  1080. function escape(html, encode) {
  1081. if (encode) {
  1082. if (escape.escapeTest.test(html)) {
  1083. return html.replace(escape.escapeReplace, function (ch) { return escape.replacements[ch]; });
  1084. }
  1085. } else {
  1086. if (escape.escapeTestNoEncode.test(html)) {
  1087. return html.replace(escape.escapeReplaceNoEncode, function (ch) { return escape.replacements[ch]; });
  1088. }
  1089. }
  1090. return html;
  1091. }
  1092. escape.escapeTest = /[&<>"']/;
  1093. escape.escapeReplace = /[&<>"']/g;
  1094. escape.replacements = {
  1095. '&': '&amp;',
  1096. '<': '&lt;',
  1097. '>': '&gt;',
  1098. '"': '&quot;',
  1099. "'": '&#39;'
  1100. };
  1101. escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
  1102. escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
  1103. function unescape(html) {
  1104. // explicitly match decimal, hex, and named HTML entities
  1105. return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
  1106. n = n.toLowerCase();
  1107. if (n === 'colon') return ':';
  1108. if (n.charAt(0) === '#') {
  1109. return n.charAt(1) === 'x'
  1110. ? String.fromCharCode(parseInt(n.substring(2), 16))
  1111. : String.fromCharCode(+n.substring(1));
  1112. }
  1113. return '';
  1114. });
  1115. }
  1116. function edit(regex, opt) {
  1117. regex = regex.source || regex;
  1118. opt = opt || '';
  1119. return {
  1120. replace: function(name, val) {
  1121. val = val.source || val;
  1122. val = val.replace(/(^|[^\[])\^/g, '$1');
  1123. regex = regex.replace(name, val);
  1124. return this;
  1125. },
  1126. getRegex: function() {
  1127. return new RegExp(regex, opt);
  1128. }
  1129. };
  1130. }
  1131. function cleanUrl(sanitize, base, href) {
  1132. if (sanitize) {
  1133. try {
  1134. var prot = decodeURIComponent(unescape(href))
  1135. .replace(/[^\w:]/g, '')
  1136. .toLowerCase();
  1137. } catch (e) {
  1138. return null;
  1139. }
  1140. if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
  1141. return null;
  1142. }
  1143. }
  1144. if (base && !originIndependentUrl.test(href)) {
  1145. href = resolveUrl(base, href);
  1146. }
  1147. try {
  1148. href = encodeURI(href).replace(/%25/g, '%');
  1149. } catch (e) {
  1150. return null;
  1151. }
  1152. return href;
  1153. }
  1154. function resolveUrl(base, href) {
  1155. if (!baseUrls[' ' + base]) {
  1156. // we can ignore everything in base after the last slash of its path component,
  1157. // but we might need to add _that_
  1158. // https://tools.ietf.org/html/rfc3986#section-3
  1159. if (/^[^:]+:\/*[^/]*$/.test(base)) {
  1160. baseUrls[' ' + base] = base + '/';
  1161. } else {
  1162. baseUrls[' ' + base] = rtrim(base, '/', true);
  1163. }
  1164. }
  1165. base = baseUrls[' ' + base];
  1166. if (href.slice(0, 2) === '//') {
  1167. return base.replace(/:[\s\S]*/, ':') + href;
  1168. } else if (href.charAt(0) === '/') {
  1169. return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href;
  1170. } else {
  1171. return base + href;
  1172. }
  1173. }
  1174. var baseUrls = {};
  1175. var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
  1176. function noop() {}
  1177. noop.exec = noop;
  1178. function merge(obj) {
  1179. var i = 1,
  1180. target,
  1181. key;
  1182. for (; i < arguments.length; i++) {
  1183. target = arguments[i];
  1184. for (key in target) {
  1185. if (Object.prototype.hasOwnProperty.call(target, key)) {
  1186. obj[key] = target[key];
  1187. }
  1188. }
  1189. }
  1190. return obj;
  1191. }
  1192. function splitCells(tableRow, count) {
  1193. // ensure that every cell-delimiting pipe has a space
  1194. // before it to distinguish it from an escaped pipe
  1195. var row = tableRow.replace(/\|/g, function (match, offset, str) {
  1196. var escaped = false,
  1197. curr = offset;
  1198. while (--curr >= 0 && str[curr] === '\\') escaped = !escaped;
  1199. if (escaped) {
  1200. // odd number of slashes means | is escaped
  1201. // so we leave it alone
  1202. return '|';
  1203. } else {
  1204. // add space before unescaped |
  1205. return ' |';
  1206. }
  1207. }),
  1208. cells = row.split(/ \|/),
  1209. i = 0;
  1210. if (cells.length > count) {
  1211. cells.splice(count);
  1212. } else {
  1213. while (cells.length < count) cells.push('');
  1214. }
  1215. for (; i < cells.length; i++) {
  1216. // leading or trailing whitespace is ignored per the gfm spec
  1217. cells[i] = cells[i].trim().replace(/\\\|/g, '|');
  1218. }
  1219. return cells;
  1220. }
  1221. // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
  1222. // /c*$/ is vulnerable to REDOS.
  1223. // invert: Remove suffix of non-c chars instead. Default falsey.
  1224. function rtrim(str, c, invert) {
  1225. if (str.length === 0) {
  1226. return '';
  1227. }
  1228. // Length of suffix matching the invert condition.
  1229. var suffLen = 0;
  1230. // Step left until we fail to match the invert condition.
  1231. while (suffLen < str.length) {
  1232. var currChar = str.charAt(str.length - suffLen - 1);
  1233. if (currChar === c && !invert) {
  1234. suffLen++;
  1235. } else if (currChar !== c && invert) {
  1236. suffLen++;
  1237. } else {
  1238. break;
  1239. }
  1240. }
  1241. return str.substr(0, str.length - suffLen);
  1242. }
  1243. /**
  1244. * Marked
  1245. */
  1246. function marked(src, opt, callback) {
  1247. // throw error in case of non string input
  1248. if (typeof src === 'undefined' || src === null) {
  1249. throw new Error('marked(): input parameter is undefined or null');
  1250. }
  1251. if (typeof src !== 'string') {
  1252. throw new Error('marked(): input parameter is of type '
  1253. + Object.prototype.toString.call(src) + ', string expected');
  1254. }
  1255. if (callback || typeof opt === 'function') {
  1256. if (!callback) {
  1257. callback = opt;
  1258. opt = null;
  1259. }
  1260. opt = merge({}, marked.defaults, opt || {});
  1261. var highlight = opt.highlight,
  1262. tokens,
  1263. pending,
  1264. i = 0;
  1265. try {
  1266. tokens = Lexer.lex(src, opt);
  1267. } catch (e) {
  1268. return callback(e);
  1269. }
  1270. pending = tokens.length;
  1271. var done = function(err) {
  1272. if (err) {
  1273. opt.highlight = highlight;
  1274. return callback(err);
  1275. }
  1276. var out;
  1277. try {
  1278. out = Parser.parse(tokens, opt);
  1279. } catch (e) {
  1280. err = e;
  1281. }
  1282. opt.highlight = highlight;
  1283. return err
  1284. ? callback(err)
  1285. : callback(null, out);
  1286. };
  1287. if (!highlight || highlight.length < 3) {
  1288. return done();
  1289. }
  1290. delete opt.highlight;
  1291. if (!pending) return done();
  1292. for (; i < tokens.length; i++) {
  1293. (function(token) {
  1294. if (token.type !== 'code') {
  1295. return --pending || done();
  1296. }
  1297. return highlight(token.text, token.lang, function(err, code) {
  1298. if (err) return done(err);
  1299. if (code == null || code === token.text) {
  1300. return --pending || done();
  1301. }
  1302. token.text = code;
  1303. token.escaped = true;
  1304. --pending || done();
  1305. });
  1306. })(tokens[i]);
  1307. }
  1308. return;
  1309. }
  1310. try {
  1311. if (opt) opt = merge({}, marked.defaults, opt);
  1312. return Parser.parse(Lexer.lex(src, opt), opt);
  1313. } catch (e) {
  1314. e.message += '\nPlease report this to https://github.com/markedjs/marked.';
  1315. if ((opt || marked.defaults).silent) {
  1316. return '<p>An error occurred:</p><pre>'
  1317. + escape(e.message + '', true)
  1318. + '</pre>';
  1319. }
  1320. throw e;
  1321. }
  1322. }
  1323. /**
  1324. * Options
  1325. */
  1326. marked.options =
  1327. marked.setOptions = function(opt) {
  1328. merge(marked.defaults, opt);
  1329. return marked;
  1330. };
  1331. marked.getDefaults = function () {
  1332. return {
  1333. baseUrl: null,
  1334. breaks: false,
  1335. gfm: true,
  1336. headerIds: true,
  1337. headerPrefix: '',
  1338. highlight: null,
  1339. langPrefix: 'language-',
  1340. mangle: true,
  1341. pedantic: false,
  1342. renderer: new Renderer(),
  1343. sanitize: false,
  1344. sanitizer: null,
  1345. silent: false,
  1346. smartLists: false,
  1347. smartypants: false,
  1348. tables: true,
  1349. xhtml: false
  1350. };
  1351. };
  1352. marked.defaults = marked.getDefaults();
  1353. /**
  1354. * Expose
  1355. */
  1356. marked.Parser = Parser;
  1357. marked.parser = Parser.parse;
  1358. marked.Renderer = Renderer;
  1359. marked.TextRenderer = TextRenderer;
  1360. marked.Lexer = Lexer;
  1361. marked.lexer = Lexer.lex;
  1362. marked.InlineLexer = InlineLexer;
  1363. marked.inlineLexer = InlineLexer.output;
  1364. marked.parse = marked;
  1365. if (typeof module !== 'undefined' && typeof exports === 'object') {
  1366. module.exports = marked;
  1367. } else if (typeof define === 'function' && define.amd) {
  1368. define(function() { return marked; });
  1369. } else {
  1370. root.marked = marked;
  1371. }
  1372. })(this || (typeof window !== 'undefined' ? window : global));