marked.js 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286
  1. /**
  2. * marked - a markdown parser
  3. * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
  4. * https://github.com/chjj/marked
  5. */
  6. ;(function() {
  7. /**
  8. * Block-Level Grammar
  9. */
  10. var block = {
  11. newline: /^\n+/,
  12. code: /^( {4}[^\n]+\n*)+/,
  13. fences: noop,
  14. hr: /^( *[-*_]){3,} *(?:\n+|$)/,
  15. heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
  16. nptable: noop,
  17. lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
  18. blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
  19. list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
  20. html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
  21. def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
  22. table: noop,
  23. paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
  24. text: /^[^\n]+/
  25. };
  26. block.bullet = /(?:[*+-]|\d+\.)/;
  27. block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
  28. block.item = replace(block.item, 'gm')
  29. (/bull/g, block.bullet)
  30. ();
  31. block.list = replace(block.list)
  32. (/bull/g, block.bullet)
  33. ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
  34. ('def', '\\n+(?=' + block.def.source + ')')
  35. ();
  36. block.blockquote = replace(block.blockquote)
  37. ('def', block.def)
  38. ();
  39. block._tag = '(?!(?:'
  40. + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
  41. + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
  42. + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
  43. block.html = replace(block.html)
  44. ('comment', /<!--[\s\S]*?-->/)
  45. ('closed', /<(tag)[\s\S]+?<\/\1>/)
  46. ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
  47. (/tag/g, block._tag)
  48. ();
  49. block.paragraph = replace(block.paragraph)
  50. ('hr', block.hr)
  51. ('heading', block.heading)
  52. ('lheading', block.lheading)
  53. ('blockquote', block.blockquote)
  54. ('tag', '<' + block._tag)
  55. ('def', block.def)
  56. ();
  57. /**
  58. * Normal Block Grammar
  59. */
  60. block.normal = merge({}, block);
  61. /**
  62. * GFM Block Grammar
  63. */
  64. block.gfm = merge({}, block.normal, {
  65. fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
  66. paragraph: /^/,
  67. heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
  68. });
  69. block.gfm.paragraph = replace(block.paragraph)
  70. ('(?!', '(?!'
  71. + block.gfm.fences.source.replace('\\1', '\\2') + '|'
  72. + block.list.source.replace('\\1', '\\3') + '|')
  73. ();
  74. /**
  75. * GFM + Tables Block Grammar
  76. */
  77. block.tables = merge({}, block.gfm, {
  78. nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
  79. table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
  80. });
  81. /**
  82. * Block Lexer
  83. */
  84. function Lexer(options) {
  85. this.tokens = [];
  86. this.tokens.links = {};
  87. this.options = options || marked.defaults;
  88. this.rules = block.normal;
  89. if (this.options.gfm) {
  90. if (this.options.tables) {
  91. this.rules = block.tables;
  92. } else {
  93. this.rules = block.gfm;
  94. }
  95. }
  96. }
  97. /**
  98. * Expose Block Rules
  99. */
  100. Lexer.rules = block;
  101. /**
  102. * Static Lex Method
  103. */
  104. Lexer.lex = function(src, options) {
  105. var lexer = new Lexer(options);
  106. return lexer.lex(src);
  107. };
  108. /**
  109. * Preprocessing
  110. */
  111. Lexer.prototype.lex = function(src) {
  112. src = src
  113. .replace(/\r\n|\r/g, '\n')
  114. .replace(/\t/g, ' ')
  115. .replace(/\u00a0/g, ' ')
  116. .replace(/\u2424/g, '\n');
  117. return this.token(src, true);
  118. };
  119. /**
  120. * Lexing
  121. */
  122. Lexer.prototype.token = function(src, top, bq) {
  123. var src = src.replace(/^ +$/gm, '')
  124. , next
  125. , loose
  126. , cap
  127. , bull
  128. , b
  129. , item
  130. , space
  131. , i
  132. , l;
  133. while (src) {
  134. // newline
  135. if (cap = this.rules.newline.exec(src)) {
  136. src = src.substring(cap[0].length);
  137. if (cap[0].length > 1) {
  138. this.tokens.push({
  139. type: 'space'
  140. });
  141. }
  142. }
  143. // code
  144. if (cap = this.rules.code.exec(src)) {
  145. src = src.substring(cap[0].length);
  146. cap = cap[0].replace(/^ {4}/gm, '');
  147. this.tokens.push({
  148. type: 'code',
  149. text: !this.options.pedantic
  150. ? cap.replace(/\n+$/, '')
  151. : cap
  152. });
  153. continue;
  154. }
  155. // fences (gfm)
  156. if (cap = this.rules.fences.exec(src)) {
  157. src = src.substring(cap[0].length);
  158. this.tokens.push({
  159. type: 'code',
  160. lang: cap[2],
  161. text: cap[3] || ''
  162. });
  163. continue;
  164. }
  165. // heading
  166. if (cap = this.rules.heading.exec(src)) {
  167. src = src.substring(cap[0].length);
  168. this.tokens.push({
  169. type: 'heading',
  170. depth: cap[1].length,
  171. text: cap[2]
  172. });
  173. continue;
  174. }
  175. // table no leading pipe (gfm)
  176. if (top && (cap = this.rules.nptable.exec(src))) {
  177. src = src.substring(cap[0].length);
  178. item = {
  179. type: 'table',
  180. header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
  181. align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
  182. cells: cap[3].replace(/\n$/, '').split('\n')
  183. };
  184. for (i = 0; i < item.align.length; i++) {
  185. if (/^ *-+: *$/.test(item.align[i])) {
  186. item.align[i] = 'right';
  187. } else if (/^ *:-+: *$/.test(item.align[i])) {
  188. item.align[i] = 'center';
  189. } else if (/^ *:-+ *$/.test(item.align[i])) {
  190. item.align[i] = 'left';
  191. } else {
  192. item.align[i] = null;
  193. }
  194. }
  195. for (i = 0; i < item.cells.length; i++) {
  196. item.cells[i] = item.cells[i].split(/ *\| */);
  197. }
  198. this.tokens.push(item);
  199. continue;
  200. }
  201. // lheading
  202. if (cap = this.rules.lheading.exec(src)) {
  203. src = src.substring(cap[0].length);
  204. this.tokens.push({
  205. type: 'heading',
  206. depth: cap[2] === '=' ? 1 : 2,
  207. text: cap[1]
  208. });
  209. continue;
  210. }
  211. // hr
  212. if (cap = this.rules.hr.exec(src)) {
  213. src = src.substring(cap[0].length);
  214. this.tokens.push({
  215. type: 'hr'
  216. });
  217. continue;
  218. }
  219. // blockquote
  220. if (cap = this.rules.blockquote.exec(src)) {
  221. src = src.substring(cap[0].length);
  222. this.tokens.push({
  223. type: 'blockquote_start'
  224. });
  225. cap = cap[0].replace(/^ *> ?/gm, '');
  226. // Pass `top` to keep the current
  227. // "toplevel" state. This is exactly
  228. // how markdown.pl works.
  229. this.token(cap, top, true);
  230. this.tokens.push({
  231. type: 'blockquote_end'
  232. });
  233. continue;
  234. }
  235. // list
  236. if (cap = this.rules.list.exec(src)) {
  237. src = src.substring(cap[0].length);
  238. bull = cap[2];
  239. this.tokens.push({
  240. type: 'list_start',
  241. ordered: bull.length > 1
  242. });
  243. // Get each top-level item.
  244. cap = cap[0].match(this.rules.item);
  245. next = false;
  246. l = cap.length;
  247. i = 0;
  248. for (; i < l; i++) {
  249. item = cap[i];
  250. // Remove the list item's bullet
  251. // so it is seen as the next token.
  252. space = item.length;
  253. item = item.replace(/^ *([*+-]|\d+\.) +/, '');
  254. // Outdent whatever the
  255. // list item contains. Hacky.
  256. if (~item.indexOf('\n ')) {
  257. space -= item.length;
  258. item = !this.options.pedantic
  259. ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
  260. : item.replace(/^ {1,4}/gm, '');
  261. }
  262. // Determine whether the next list item belongs here.
  263. // Backpedal if it does not belong in this list.
  264. if (this.options.smartLists && i !== l - 1) {
  265. b = block.bullet.exec(cap[i + 1])[0];
  266. if (bull !== b && !(bull.length > 1 && b.length > 1)) {
  267. src = cap.slice(i + 1).join('\n') + src;
  268. i = l - 1;
  269. }
  270. }
  271. // Determine whether item is loose or not.
  272. // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
  273. // for discount behavior.
  274. loose = next || /\n\n(?!\s*$)/.test(item);
  275. if (i !== l - 1) {
  276. next = item.charAt(item.length - 1) === '\n';
  277. if (!loose) loose = next;
  278. }
  279. this.tokens.push({
  280. type: loose
  281. ? 'loose_item_start'
  282. : 'list_item_start'
  283. });
  284. // Recurse.
  285. this.token(item, false, bq);
  286. this.tokens.push({
  287. type: 'list_item_end'
  288. });
  289. }
  290. this.tokens.push({
  291. type: 'list_end'
  292. });
  293. continue;
  294. }
  295. // html
  296. if (cap = this.rules.html.exec(src)) {
  297. src = src.substring(cap[0].length);
  298. this.tokens.push({
  299. type: this.options.sanitize
  300. ? 'paragraph'
  301. : 'html',
  302. pre: !this.options.sanitizer
  303. && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
  304. text: cap[0]
  305. });
  306. continue;
  307. }
  308. // def
  309. if ((!bq && top) && (cap = this.rules.def.exec(src))) {
  310. src = src.substring(cap[0].length);
  311. this.tokens.links[cap[1].toLowerCase()] = {
  312. href: cap[2],
  313. title: cap[3]
  314. };
  315. continue;
  316. }
  317. // table (gfm)
  318. if (top && (cap = this.rules.table.exec(src))) {
  319. src = src.substring(cap[0].length);
  320. item = {
  321. type: 'table',
  322. header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
  323. align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
  324. cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
  325. };
  326. for (i = 0; i < item.align.length; i++) {
  327. if (/^ *-+: *$/.test(item.align[i])) {
  328. item.align[i] = 'right';
  329. } else if (/^ *:-+: *$/.test(item.align[i])) {
  330. item.align[i] = 'center';
  331. } else if (/^ *:-+ *$/.test(item.align[i])) {
  332. item.align[i] = 'left';
  333. } else {
  334. item.align[i] = null;
  335. }
  336. }
  337. for (i = 0; i < item.cells.length; i++) {
  338. item.cells[i] = item.cells[i]
  339. .replace(/^ *\| *| *\| *$/g, '')
  340. .split(/ *\| */);
  341. }
  342. this.tokens.push(item);
  343. continue;
  344. }
  345. // top-level paragraph
  346. if (top && (cap = this.rules.paragraph.exec(src))) {
  347. src = src.substring(cap[0].length);
  348. this.tokens.push({
  349. type: 'paragraph',
  350. text: cap[1].charAt(cap[1].length - 1) === '\n'
  351. ? cap[1].slice(0, -1)
  352. : cap[1]
  353. });
  354. continue;
  355. }
  356. // text
  357. if (cap = this.rules.text.exec(src)) {
  358. // Top-level should never reach here.
  359. src = src.substring(cap[0].length);
  360. this.tokens.push({
  361. type: 'text',
  362. text: cap[0]
  363. });
  364. continue;
  365. }
  366. if (src) {
  367. throw new
  368. Error('Infinite loop on byte: ' + src.charCodeAt(0));
  369. }
  370. }
  371. return this.tokens;
  372. };
  373. /**
  374. * Inline-Level Grammar
  375. */
  376. var inline = {
  377. escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
  378. autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
  379. url: noop,
  380. tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
  381. link: /^!?\[(inside)\]\(href\)/,
  382. reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
  383. nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
  384. strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
  385. em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
  386. code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
  387. br: /^ {2,}\n(?!\s*$)/,
  388. del: noop,
  389. text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
  390. };
  391. inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
  392. inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
  393. inline.link = replace(inline.link)
  394. ('inside', inline._inside)
  395. ('href', inline._href)
  396. ();
  397. inline.reflink = replace(inline.reflink)
  398. ('inside', inline._inside)
  399. ();
  400. /**
  401. * Normal Inline Grammar
  402. */
  403. inline.normal = merge({}, inline);
  404. /**
  405. * Pedantic Inline Grammar
  406. */
  407. inline.pedantic = merge({}, inline.normal, {
  408. strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
  409. em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
  410. });
  411. /**
  412. * GFM Inline Grammar
  413. */
  414. inline.gfm = merge({}, inline.normal, {
  415. escape: replace(inline.escape)('])', '~|])')(),
  416. url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
  417. del: /^~~(?=\S)([\s\S]*?\S)~~/,
  418. text: replace(inline.text)
  419. (']|', '~]|')
  420. ('|', '|https?://|')
  421. ()
  422. });
  423. /**
  424. * GFM + Line Breaks Inline Grammar
  425. */
  426. inline.breaks = merge({}, inline.gfm, {
  427. br: replace(inline.br)('{2,}', '*')(),
  428. text: replace(inline.gfm.text)('{2,}', '*')()
  429. });
  430. /**
  431. * Inline Lexer & Compiler
  432. */
  433. function InlineLexer(links, options) {
  434. this.options = options || marked.defaults;
  435. this.links = links;
  436. this.rules = inline.normal;
  437. this.renderer = this.options.renderer || new Renderer;
  438. this.renderer.options = this.options;
  439. if (!this.links) {
  440. throw new
  441. Error('Tokens array requires a `links` property.');
  442. }
  443. if (this.options.gfm) {
  444. if (this.options.breaks) {
  445. this.rules = inline.breaks;
  446. } else {
  447. this.rules = inline.gfm;
  448. }
  449. } else if (this.options.pedantic) {
  450. this.rules = inline.pedantic;
  451. }
  452. }
  453. /**
  454. * Expose Inline Rules
  455. */
  456. InlineLexer.rules = inline;
  457. /**
  458. * Static Lexing/Compiling Method
  459. */
  460. InlineLexer.output = function(src, links, options) {
  461. var inline = new InlineLexer(links, options);
  462. return inline.output(src);
  463. };
  464. /**
  465. * Lexing/Compiling
  466. */
  467. InlineLexer.prototype.output = function(src) {
  468. var out = ''
  469. , link
  470. , text
  471. , href
  472. , cap;
  473. while (src) {
  474. // escape
  475. if (cap = this.rules.escape.exec(src)) {
  476. src = src.substring(cap[0].length);
  477. out += cap[1];
  478. continue;
  479. }
  480. // autolink
  481. if (cap = this.rules.autolink.exec(src)) {
  482. src = src.substring(cap[0].length);
  483. if (cap[2] === '@') {
  484. text = cap[1].charAt(6) === ':'
  485. ? this.mangle(cap[1].substring(7))
  486. : this.mangle(cap[1]);
  487. href = this.mangle('mailto:') + text;
  488. } else {
  489. text = escape(cap[1]);
  490. href = text;
  491. }
  492. out += this.renderer.link(href, null, text);
  493. continue;
  494. }
  495. // url (gfm)
  496. if (!this.inLink && (cap = this.rules.url.exec(src))) {
  497. src = src.substring(cap[0].length);
  498. text = escape(cap[1]);
  499. href = text;
  500. out += this.renderer.link(href, null, text);
  501. continue;
  502. }
  503. // tag
  504. if (cap = this.rules.tag.exec(src)) {
  505. if (!this.inLink && /^<a /i.test(cap[0])) {
  506. this.inLink = true;
  507. } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
  508. this.inLink = false;
  509. }
  510. src = src.substring(cap[0].length);
  511. out += this.options.sanitize
  512. ? this.options.sanitizer
  513. ? this.options.sanitizer(cap[0])
  514. : escape(cap[0])
  515. : cap[0]
  516. continue;
  517. }
  518. // link
  519. if (cap = this.rules.link.exec(src)) {
  520. src = src.substring(cap[0].length);
  521. this.inLink = true;
  522. out += this.outputLink(cap, {
  523. href: cap[2],
  524. title: cap[3]
  525. });
  526. this.inLink = false;
  527. continue;
  528. }
  529. // reflink, nolink
  530. if ((cap = this.rules.reflink.exec(src))
  531. || (cap = this.rules.nolink.exec(src))) {
  532. src = src.substring(cap[0].length);
  533. link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
  534. link = this.links[link.toLowerCase()];
  535. if (!link || !link.href) {
  536. out += cap[0].charAt(0);
  537. src = cap[0].substring(1) + src;
  538. continue;
  539. }
  540. this.inLink = true;
  541. out += this.outputLink(cap, link);
  542. this.inLink = false;
  543. continue;
  544. }
  545. // strong
  546. if (cap = this.rules.strong.exec(src)) {
  547. src = src.substring(cap[0].length);
  548. out += this.renderer.strong(this.output(cap[2] || cap[1]));
  549. continue;
  550. }
  551. // em
  552. if (cap = this.rules.em.exec(src)) {
  553. src = src.substring(cap[0].length);
  554. out += this.renderer.em(this.output(cap[2] || cap[1]));
  555. continue;
  556. }
  557. // code
  558. if (cap = this.rules.code.exec(src)) {
  559. src = src.substring(cap[0].length);
  560. out += this.renderer.codespan(escape(cap[2], true));
  561. continue;
  562. }
  563. // br
  564. if (cap = this.rules.br.exec(src)) {
  565. src = src.substring(cap[0].length);
  566. out += this.renderer.br();
  567. continue;
  568. }
  569. // del (gfm)
  570. if (cap = this.rules.del.exec(src)) {
  571. src = src.substring(cap[0].length);
  572. out += this.renderer.del(this.output(cap[1]));
  573. continue;
  574. }
  575. // text
  576. if (cap = this.rules.text.exec(src)) {
  577. src = src.substring(cap[0].length);
  578. out += this.renderer.text(escape(this.smartypants(cap[0])));
  579. continue;
  580. }
  581. if (src) {
  582. throw new
  583. Error('Infinite loop on byte: ' + src.charCodeAt(0));
  584. }
  585. }
  586. return out;
  587. };
  588. /**
  589. * Compile Link
  590. */
  591. InlineLexer.prototype.outputLink = function(cap, link) {
  592. var href = escape(link.href)
  593. , title = link.title ? escape(link.title) : null;
  594. return cap[0].charAt(0) !== '!'
  595. ? this.renderer.link(href, title, this.output(cap[1]))
  596. : this.renderer.image(href, title, escape(cap[1]));
  597. };
  598. /**
  599. * Smartypants Transformations
  600. */
  601. InlineLexer.prototype.smartypants = function(text) {
  602. if (!this.options.smartypants) return text;
  603. return text
  604. // em-dashes
  605. .replace(/---/g, '\u2014')
  606. // en-dashes
  607. .replace(/--/g, '\u2013')
  608. // opening singles
  609. .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
  610. // closing singles & apostrophes
  611. .replace(/'/g, '\u2019')
  612. // opening doubles
  613. .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
  614. // closing doubles
  615. .replace(/"/g, '\u201d')
  616. // ellipses
  617. .replace(/\.{3}/g, '\u2026');
  618. };
  619. /**
  620. * Mangle Links
  621. */
  622. InlineLexer.prototype.mangle = function(text) {
  623. if (!this.options.mangle) return text;
  624. var out = ''
  625. , l = text.length
  626. , i = 0
  627. , ch;
  628. for (; i < l; i++) {
  629. ch = text.charCodeAt(i);
  630. if (Math.random() > 0.5) {
  631. ch = 'x' + ch.toString(16);
  632. }
  633. out += '&#' + ch + ';';
  634. }
  635. return out;
  636. };
  637. /**
  638. * Renderer
  639. */
  640. function Renderer(options) {
  641. this.options = options || {};
  642. }
  643. Renderer.prototype.code = function(code, lang, escaped) {
  644. if (this.options.highlight) {
  645. var out = this.options.highlight(code, lang);
  646. if (out != null && out !== code) {
  647. escaped = true;
  648. code = out;
  649. }
  650. }
  651. if (!lang) {
  652. return '<pre><code>'
  653. + (escaped ? code : escape(code, true))
  654. + '\n</code></pre>';
  655. }
  656. return '<pre><code class="'
  657. + this.options.langPrefix
  658. + escape(lang, true)
  659. + '">'
  660. + (escaped ? code : escape(code, true))
  661. + '\n</code></pre>\n';
  662. };
  663. Renderer.prototype.blockquote = function(quote) {
  664. return '<blockquote>\n' + quote + '</blockquote>\n';
  665. };
  666. Renderer.prototype.html = function(html) {
  667. return html;
  668. };
  669. Renderer.prototype.heading = function(text, level, raw) {
  670. return '<h'
  671. + level
  672. + ' id="'
  673. + this.options.headerPrefix
  674. + raw.toLowerCase().replace(/[^\w]+/g, '-')
  675. + '">'
  676. + text
  677. + '</h'
  678. + level
  679. + '>\n';
  680. };
  681. Renderer.prototype.hr = function() {
  682. return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
  683. };
  684. Renderer.prototype.list = function(body, ordered) {
  685. var type = ordered ? 'ol' : 'ul';
  686. return '<' + type + '>\n' + body + '</' + type + '>\n';
  687. };
  688. Renderer.prototype.listitem = function(text) {
  689. return '<li>' + text + '</li>\n';
  690. };
  691. Renderer.prototype.paragraph = function(text) {
  692. return '<p>' + text + '</p>\n';
  693. };
  694. Renderer.prototype.table = function(header, body) {
  695. return '<table>\n'
  696. + '<thead>\n'
  697. + header
  698. + '</thead>\n'
  699. + '<tbody>\n'
  700. + body
  701. + '</tbody>\n'
  702. + '</table>\n';
  703. };
  704. Renderer.prototype.tablerow = function(content) {
  705. return '<tr>\n' + content + '</tr>\n';
  706. };
  707. Renderer.prototype.tablecell = function(content, flags) {
  708. var type = flags.header ? 'th' : 'td';
  709. var tag = flags.align
  710. ? '<' + type + ' style="text-align:' + flags.align + '">'
  711. : '<' + type + '>';
  712. return tag + content + '</' + type + '>\n';
  713. };
  714. // span level renderer
  715. Renderer.prototype.strong = function(text) {
  716. return '<strong>' + text + '</strong>';
  717. };
  718. Renderer.prototype.em = function(text) {
  719. return '<em>' + text + '</em>';
  720. };
  721. Renderer.prototype.codespan = function(text) {
  722. return '<code>' + text + '</code>';
  723. };
  724. Renderer.prototype.br = function() {
  725. return this.options.xhtml ? '<br/>' : '<br>';
  726. };
  727. Renderer.prototype.del = function(text) {
  728. return '<del>' + text + '</del>';
  729. };
  730. Renderer.prototype.link = function(href, title, text) {
  731. if (this.options.sanitize) {
  732. try {
  733. var prot = decodeURIComponent(unescape(href))
  734. .replace(/[^\w:]/g, '')
  735. .toLowerCase();
  736. } catch (e) {
  737. return '';
  738. }
  739. if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
  740. return '';
  741. }
  742. }
  743. var out = '<a href="' + href + '"';
  744. if (title) {
  745. out += ' title="' + title + '"';
  746. }
  747. out += '>' + text + '</a>';
  748. return out;
  749. };
  750. Renderer.prototype.image = function(href, title, text) {
  751. var out = '<img src="' + href + '" alt="' + text + '"';
  752. if (title) {
  753. out += ' title="' + title + '"';
  754. }
  755. out += this.options.xhtml ? '/>' : '>';
  756. return out;
  757. };
  758. Renderer.prototype.text = function(text) {
  759. return text;
  760. };
  761. /**
  762. * Parsing & Compiling
  763. */
  764. function Parser(options) {
  765. this.tokens = [];
  766. this.token = null;
  767. this.options = options || marked.defaults;
  768. this.options.renderer = this.options.renderer || new Renderer;
  769. this.renderer = this.options.renderer;
  770. this.renderer.options = this.options;
  771. }
  772. /**
  773. * Static Parse Method
  774. */
  775. Parser.parse = function(src, options, renderer) {
  776. var parser = new Parser(options, renderer);
  777. return parser.parse(src);
  778. };
  779. /**
  780. * Parse Loop
  781. */
  782. Parser.prototype.parse = function(src) {
  783. this.inline = new InlineLexer(src.links, this.options, this.renderer);
  784. this.tokens = src.reverse();
  785. var out = '';
  786. while (this.next()) {
  787. out += this.tok();
  788. }
  789. return out;
  790. };
  791. /**
  792. * Next Token
  793. */
  794. Parser.prototype.next = function() {
  795. return this.token = this.tokens.pop();
  796. };
  797. /**
  798. * Preview Next Token
  799. */
  800. Parser.prototype.peek = function() {
  801. return this.tokens[this.tokens.length - 1] || 0;
  802. };
  803. /**
  804. * Parse Text Tokens
  805. */
  806. Parser.prototype.parseText = function() {
  807. var body = this.token.text;
  808. while (this.peek().type === 'text') {
  809. body += '\n' + this.next().text;
  810. }
  811. return this.inline.output(body);
  812. };
  813. /**
  814. * Parse Current Token
  815. */
  816. Parser.prototype.tok = function() {
  817. switch (this.token.type) {
  818. case 'space': {
  819. return '';
  820. }
  821. case 'hr': {
  822. return this.renderer.hr();
  823. }
  824. case 'heading': {
  825. return this.renderer.heading(
  826. this.inline.output(this.token.text),
  827. this.token.depth,
  828. this.token.text);
  829. }
  830. case 'code': {
  831. return this.renderer.code(this.token.text,
  832. this.token.lang,
  833. this.token.escaped);
  834. }
  835. case 'table': {
  836. var header = ''
  837. , body = ''
  838. , i
  839. , row
  840. , cell
  841. , flags
  842. , j;
  843. // header
  844. cell = '';
  845. for (i = 0; i < this.token.header.length; i++) {
  846. flags = { header: true, align: this.token.align[i] };
  847. cell += this.renderer.tablecell(
  848. this.inline.output(this.token.header[i]),
  849. { header: true, align: this.token.align[i] }
  850. );
  851. }
  852. header += this.renderer.tablerow(cell);
  853. for (i = 0; i < this.token.cells.length; i++) {
  854. row = this.token.cells[i];
  855. cell = '';
  856. for (j = 0; j < row.length; j++) {
  857. cell += this.renderer.tablecell(
  858. this.inline.output(row[j]),
  859. { header: false, align: this.token.align[j] }
  860. );
  861. }
  862. body += this.renderer.tablerow(cell);
  863. }
  864. return this.renderer.table(header, body);
  865. }
  866. case 'blockquote_start': {
  867. var body = '';
  868. while (this.next().type !== 'blockquote_end') {
  869. body += this.tok();
  870. }
  871. return this.renderer.blockquote(body);
  872. }
  873. case 'list_start': {
  874. var body = ''
  875. , ordered = this.token.ordered;
  876. while (this.next().type !== 'list_end') {
  877. body += this.tok();
  878. }
  879. return this.renderer.list(body, ordered);
  880. }
  881. case 'list_item_start': {
  882. var body = '';
  883. while (this.next().type !== 'list_item_end') {
  884. body += this.token.type === 'text'
  885. ? this.parseText()
  886. : this.tok();
  887. }
  888. return this.renderer.listitem(body);
  889. }
  890. case 'loose_item_start': {
  891. var body = '';
  892. while (this.next().type !== 'list_item_end') {
  893. body += this.tok();
  894. }
  895. return this.renderer.listitem(body);
  896. }
  897. case 'html': {
  898. var html = !this.token.pre && !this.options.pedantic
  899. ? this.inline.output(this.token.text)
  900. : this.token.text;
  901. return this.renderer.html(html);
  902. }
  903. case 'paragraph': {
  904. return this.renderer.paragraph(this.inline.output(this.token.text));
  905. }
  906. case 'text': {
  907. return this.renderer.paragraph(this.parseText());
  908. }
  909. }
  910. };
  911. /**
  912. * Helpers
  913. */
  914. function escape(html, encode) {
  915. return html
  916. .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
  917. .replace(/</g, '&lt;')
  918. .replace(/>/g, '&gt;')
  919. .replace(/"/g, '&quot;')
  920. .replace(/'/g, '&#39;');
  921. }
  922. function unescape(html) {
  923. // explicitly match decimal, hex, and named HTML entities
  924. return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
  925. n = n.toLowerCase();
  926. if (n === 'colon') return ':';
  927. if (n.charAt(0) === '#') {
  928. return n.charAt(1) === 'x'
  929. ? String.fromCharCode(parseInt(n.substring(2), 16))
  930. : String.fromCharCode(+n.substring(1));
  931. }
  932. return '';
  933. });
  934. }
  935. function replace(regex, opt) {
  936. regex = regex.source;
  937. opt = opt || '';
  938. return function self(name, val) {
  939. if (!name) return new RegExp(regex, opt);
  940. val = val.source || val;
  941. val = val.replace(/(^|[^\[])\^/g, '$1');
  942. regex = regex.replace(name, val);
  943. return self;
  944. };
  945. }
  946. function noop() {}
  947. noop.exec = noop;
  948. function merge(obj) {
  949. var i = 1
  950. , target
  951. , key;
  952. for (; i < arguments.length; i++) {
  953. target = arguments[i];
  954. for (key in target) {
  955. if (Object.prototype.hasOwnProperty.call(target, key)) {
  956. obj[key] = target[key];
  957. }
  958. }
  959. }
  960. return obj;
  961. }
  962. /**
  963. * Marked
  964. */
  965. function marked(src, opt, callback) {
  966. if (callback || typeof opt === 'function') {
  967. if (!callback) {
  968. callback = opt;
  969. opt = null;
  970. }
  971. opt = merge({}, marked.defaults, opt || {});
  972. var highlight = opt.highlight
  973. , tokens
  974. , pending
  975. , i = 0;
  976. try {
  977. tokens = Lexer.lex(src, opt)
  978. } catch (e) {
  979. return callback(e);
  980. }
  981. pending = tokens.length;
  982. var done = function(err) {
  983. if (err) {
  984. opt.highlight = highlight;
  985. return callback(err);
  986. }
  987. var out;
  988. try {
  989. out = Parser.parse(tokens, opt);
  990. } catch (e) {
  991. err = e;
  992. }
  993. opt.highlight = highlight;
  994. return err
  995. ? callback(err)
  996. : callback(null, out);
  997. };
  998. if (!highlight || highlight.length < 3) {
  999. return done();
  1000. }
  1001. delete opt.highlight;
  1002. if (!pending) return done();
  1003. for (; i < tokens.length; i++) {
  1004. (function(token) {
  1005. if (token.type !== 'code') {
  1006. return --pending || done();
  1007. }
  1008. return highlight(token.text, token.lang, function(err, code) {
  1009. if (err) return done(err);
  1010. if (code == null || code === token.text) {
  1011. return --pending || done();
  1012. }
  1013. token.text = code;
  1014. token.escaped = true;
  1015. --pending || done();
  1016. });
  1017. })(tokens[i]);
  1018. }
  1019. return;
  1020. }
  1021. try {
  1022. if (opt) opt = merge({}, marked.defaults, opt);
  1023. return Parser.parse(Lexer.lex(src, opt), opt);
  1024. } catch (e) {
  1025. e.message += '\nPlease report this to https://github.com/chjj/marked.';
  1026. if ((opt || marked.defaults).silent) {
  1027. return '<p>An error occured:</p><pre>'
  1028. + escape(e.message + '', true)
  1029. + '</pre>';
  1030. }
  1031. throw e;
  1032. }
  1033. }
  1034. /**
  1035. * Options
  1036. */
  1037. marked.options =
  1038. marked.setOptions = function(opt) {
  1039. merge(marked.defaults, opt);
  1040. return marked;
  1041. };
  1042. marked.defaults = {
  1043. gfm: true,
  1044. tables: true,
  1045. breaks: false,
  1046. pedantic: false,
  1047. sanitize: false,
  1048. sanitizer: null,
  1049. mangle: true,
  1050. smartLists: false,
  1051. silent: false,
  1052. highlight: null,
  1053. langPrefix: 'lang-',
  1054. smartypants: false,
  1055. headerPrefix: '',
  1056. renderer: new Renderer,
  1057. xhtml: false
  1058. };
  1059. /**
  1060. * Expose
  1061. */
  1062. marked.Parser = Parser;
  1063. marked.parser = Parser.parse;
  1064. marked.Renderer = Renderer;
  1065. marked.Lexer = Lexer;
  1066. marked.lexer = Lexer.lex;
  1067. marked.InlineLexer = InlineLexer;
  1068. marked.inlineLexer = InlineLexer.output;
  1069. marked.parse = marked;
  1070. if (typeof module !== 'undefined' && typeof exports === 'object') {
  1071. module.exports = marked;
  1072. } else if (typeof define === 'function' && define.amd) {
  1073. define(function() { return marked; });
  1074. } else {
  1075. this.marked = marked;
  1076. }
  1077. }).call(function() {
  1078. return this || (typeof window !== 'undefined' ? window : global);
  1079. }());