marked.js 24 KB

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