Git based wiki inspired by Gollum
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1152 lines
24KB

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