marked.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  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',
  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]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
  377. em: /^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, // /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
  378. code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
  379. math: /^\$((?:[^\\]|\\\\|\\[^\\]+?)+?)\$|^\$\$((?:[^\\]|\\\\|\\[^\\]+?)+?)\$\$|^\\begin{[^}]+}((?:[^\\]|\\\\|\\[^\\]+?)+?)\\end{[^}]+}/,
  380. br: /^ {2,}\n(?!\s*$)/,
  381. del: noop,
  382. text: /^[\s\S]+?(?=[\\<!\[_*`\$]| {2,}\n|$)/
  383. };
  384. inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
  385. inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
  386. inline.link = replace(inline.link)
  387. ('inside', inline._inside)
  388. ('href', inline._href)
  389. ();
  390. inline.reflink = replace(inline.reflink)
  391. ('inside', inline._inside)
  392. ();
  393. /**
  394. * Normal Inline Grammar
  395. */
  396. inline.normal = merge({}, inline);
  397. /**
  398. * Pedantic Inline Grammar
  399. */
  400. inline.pedantic = merge({}, inline.normal, {
  401. strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
  402. em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
  403. });
  404. /**
  405. * GFM Inline Grammar
  406. */
  407. inline.gfm = merge({}, inline.normal, {
  408. escape: replace(inline.escape)('])', '~|])')(),
  409. url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
  410. del: /^~~(?=\S)([\s\S]*?\S)~~/,
  411. text: replace(inline.text)
  412. (']|', '~]|')
  413. ('|', '|https?://|')
  414. ()
  415. });
  416. /**
  417. * GFM + Line Breaks Inline Grammar
  418. */
  419. inline.breaks = merge({}, inline.gfm, {
  420. br: replace(inline.br)('{2,}', '*')(),
  421. text: replace(inline.gfm.text)('{2,}', '*')()
  422. });
  423. /**
  424. * Inline Lexer & Compiler
  425. */
  426. function InlineLexer(links, options) {
  427. this.options = options || marked.defaults;
  428. this.links = links;
  429. this.rules = inline.normal;
  430. if (!this.links) {
  431. throw new
  432. Error('Tokens array requires a `links` property.');
  433. }
  434. if (this.options.gfm) {
  435. if (this.options.breaks) {
  436. this.rules = inline.breaks;
  437. } else {
  438. this.rules = inline.gfm;
  439. }
  440. } else if (this.options.pedantic) {
  441. this.rules = inline.pedantic;
  442. }
  443. }
  444. /**
  445. * Expose Inline Rules
  446. */
  447. InlineLexer.rules = inline;
  448. /**
  449. * Static Lexing/Compiling Method
  450. */
  451. InlineLexer.output = function(src, links, options) {
  452. var inline = new InlineLexer(links, options);
  453. return inline.output(src);
  454. };
  455. /**
  456. * Lexing/Compiling
  457. */
  458. InlineLexer.prototype.output = function(src) {
  459. var out = ''
  460. , link
  461. , text
  462. , href
  463. , cap;
  464. while (src) {
  465. // escape
  466. if (cap = this.rules.escape.exec(src)) {
  467. src = src.substring(cap[0].length);
  468. out += cap[1];
  469. continue;
  470. }
  471. // math
  472. if (cap = this.rules.math.exec(src)) {
  473. src = src.substring(cap[0].length);
  474. out += cap[0];
  475. continue;
  476. }
  477. // autolink
  478. if (cap = this.rules.autolink.exec(src)) {
  479. src = src.substring(cap[0].length);
  480. if (cap[2] === '@') {
  481. text = cap[1][6] === ':'
  482. ? this.mangle(cap[1].substring(7))
  483. : this.mangle(cap[1]);
  484. href = this.mangle('mailto:') + text;
  485. } else {
  486. text = escape(cap[1]);
  487. href = text;
  488. }
  489. out += '<a href="'
  490. + href
  491. + '">'
  492. + text
  493. + '</a>';
  494. continue;
  495. }
  496. // url (gfm)
  497. if (cap = this.rules.url.exec(src)) {
  498. src = src.substring(cap[0].length);
  499. text = escape(cap[1]);
  500. href = text;
  501. out += '<a href="'
  502. + href
  503. + '">'
  504. + text
  505. + '</a>';
  506. continue;
  507. }
  508. // tag
  509. if (cap = this.rules.tag.exec(src)) {
  510. src = src.substring(cap[0].length);
  511. out += this.options.sanitize
  512. ? escape(cap[0])
  513. : cap[0];
  514. continue;
  515. }
  516. // link
  517. if (cap = this.rules.link.exec(src)) {
  518. src = src.substring(cap[0].length);
  519. out += this.outputLink(cap, {
  520. href: cap[2],
  521. title: cap[3]
  522. });
  523. continue;
  524. }
  525. // reflink, nolink
  526. if ((cap = this.rules.reflink.exec(src))
  527. || (cap = this.rules.nolink.exec(src))) {
  528. src = src.substring(cap[0].length);
  529. link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
  530. link = this.links[link.toLowerCase()];
  531. if (!link || !link.href) {
  532. out += cap[0][0];
  533. src = cap[0].substring(1) + src;
  534. continue;
  535. }
  536. out += this.outputLink(cap, link);
  537. continue;
  538. }
  539. // strong
  540. if (cap = this.rules.strong.exec(src)) {
  541. src = src.substring(cap[0].length);
  542. out += '<strong>'
  543. + this.output(cap[2] || cap[1])
  544. + '</strong>';
  545. continue;
  546. }
  547. // em
  548. if (cap = this.rules.em.exec(src)) {
  549. src = src.substring(cap[0].length);
  550. out += '<em>'
  551. + this.output(cap[2] || cap[1])
  552. + '</em>';
  553. continue;
  554. }
  555. // code
  556. if (cap = this.rules.code.exec(src)) {
  557. src = src.substring(cap[0].length);
  558. out += '<code>'
  559. + escape(cap[2], true)
  560. + '</code>';
  561. continue;
  562. }
  563. // br
  564. if (cap = this.rules.br.exec(src)) {
  565. src = src.substring(cap[0].length);
  566. out += '<br>';
  567. continue;
  568. }
  569. // del (gfm)
  570. if (cap = this.rules.del.exec(src)) {
  571. src = src.substring(cap[0].length);
  572. out += '<del>'
  573. + this.output(cap[1])
  574. + '</del>';
  575. continue;
  576. }
  577. // text
  578. if (cap = this.rules.text.exec(src)) {
  579. src = src.substring(cap[0].length);
  580. out += escape(cap[0]);
  581. continue;
  582. }
  583. if (src) {
  584. throw new
  585. Error('Infinite loop on byte: ' + src.charCodeAt(0));
  586. }
  587. }
  588. return out;
  589. };
  590. /**
  591. * Compile Link
  592. */
  593. InlineLexer.prototype.outputLink = function(cap, link) {
  594. if (cap[0][0] !== '!') {
  595. return '<a href="'
  596. + escape(link.href)
  597. + '"'
  598. + (link.title
  599. ? ' title="'
  600. + escape(link.title)
  601. + '"'
  602. : '')
  603. + '>'
  604. + this.output(cap[1])
  605. + '</a>';
  606. } else {
  607. return '<img src="'
  608. + escape(link.href)
  609. + '" alt="'
  610. + escape(cap[1])
  611. + '"'
  612. + (link.title
  613. ? ' title="'
  614. + escape(link.title)
  615. + '"'
  616. : '')
  617. + '>';
  618. }
  619. };
  620. /**
  621. * Mangle Links
  622. */
  623. InlineLexer.prototype.mangle = function(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. * Parsing & Compiling
  639. */
  640. function Parser(options) {
  641. this.tokens = [];
  642. this.token = null;
  643. this.options = options || marked.defaults;
  644. }
  645. /**
  646. * Static Parse Method
  647. */
  648. Parser.parse = function(src, options) {
  649. var parser = new Parser(options);
  650. return parser.parse(src);
  651. };
  652. /**
  653. * Parse Loop
  654. */
  655. Parser.prototype.parse = function(src) {
  656. this.inline = new InlineLexer(src.links, this.options);
  657. this.tokens = src.reverse();
  658. var out = '';
  659. while (this.next()) {
  660. out += this.tok();
  661. }
  662. return out;
  663. };
  664. /**
  665. * Next Token
  666. */
  667. Parser.prototype.next = function() {
  668. return this.token = this.tokens.pop();
  669. };
  670. /**
  671. * Preview Next Token
  672. */
  673. Parser.prototype.peek = function() {
  674. return this.tokens[this.tokens.length-1] || 0;
  675. };
  676. /**
  677. * Parse Text Tokens
  678. */
  679. Parser.prototype.parseText = function() {
  680. var body = this.token.text;
  681. while (this.peek().type === 'text') {
  682. body += '\n' + this.next().text;
  683. }
  684. return this.inline.output(body);
  685. };
  686. /**
  687. * Parse Current Token
  688. */
  689. Parser.prototype.tok = function() {
  690. var tok_class = this.options.getElementClass == null ? null : this.options.getElementClass(this.token);
  691. tok_class = tok_class == null ? '' : ' class="' + tok_class + '"';
  692. switch (this.token.type) {
  693. case 'space': {
  694. return '';
  695. }
  696. case 'hr': {
  697. return '<hr' + tok_class + '>\n';
  698. }
  699. case 'heading': {
  700. return '<h'
  701. + this.token.depth
  702. + tok_class
  703. + '>'
  704. + this.inline.output(this.token.text)
  705. + '</h'
  706. + this.token.depth
  707. + '>\n';
  708. }
  709. case 'code': {
  710. if (this.options.highlight) {
  711. var code = this.options.highlight(this.token.text, this.token.lang);
  712. if (code != null && code !== this.token.text) {
  713. this.token.escaped = true;
  714. this.token.text = code;
  715. }
  716. }
  717. if (!this.token.escaped) {
  718. this.token.text = escape(this.token.text, true);
  719. }
  720. return '<pre><code'
  721. + (this.options.getLangClass && this.token.lang != undefined
  722. ? ' class="'
  723. + escape(this.options.getLangClass(this.token.lang), true)
  724. + '"'
  725. : '')
  726. + '>'
  727. + this.token.text
  728. + '</code></pre>\n';
  729. }
  730. case 'table': {
  731. var body = ''
  732. , heading
  733. , i
  734. , row
  735. , cell
  736. , j;
  737. // header
  738. body += '<thead>\n<tr>\n';
  739. for (i = 0; i < this.token.header.length; i++) {
  740. heading = this.inline.output(this.token.header[i]);
  741. body += this.token.align[i]
  742. ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
  743. : '<th>' + heading + '</th>\n';
  744. }
  745. body += '</tr>\n</thead>\n';
  746. // body
  747. body += '<tbody>\n'
  748. for (i = 0; i < this.token.cells.length; i++) {
  749. row = this.token.cells[i];
  750. body += '<tr>\n';
  751. for (j = 0; j < row.length; j++) {
  752. cell = this.inline.output(row[j]);
  753. body += this.token.align[j]
  754. ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
  755. : '<td>' + cell + '</td>\n';
  756. }
  757. body += '</tr>\n';
  758. }
  759. body += '</tbody>\n';
  760. return '<table' + tok_class + '>\n'
  761. + body
  762. + '</table>\n';
  763. }
  764. case 'blockquote_start': {
  765. var body = '';
  766. while (this.next().type !== 'blockquote_end') {
  767. body += this.tok();
  768. }
  769. return '<blockquote' + tok_class + '>\n'
  770. + body
  771. + '</blockquote>\n';
  772. }
  773. case 'list_start': {
  774. var type = this.token.ordered ? 'ol' : 'ul'
  775. , body = '';
  776. while (this.next().type !== 'list_end') {
  777. body += this.tok();
  778. }
  779. return '<'
  780. + type
  781. + tok_class
  782. + '>\n'
  783. + body
  784. + '</'
  785. + type
  786. + '>\n';
  787. }
  788. case 'list_item_start': {
  789. var body = '';
  790. while (this.next().type !== 'list_item_end') {
  791. body += this.token.type === 'text'
  792. ? this.parseText()
  793. : this.tok();
  794. }
  795. return '<li' + tok_class + '>'
  796. + body
  797. + '</li>\n';
  798. }
  799. case 'loose_item_start': {
  800. var body = '';
  801. while (this.next().type !== 'list_item_end') {
  802. body += this.tok();
  803. }
  804. return '<li' + tok_class + '>'
  805. + body
  806. + '</li>\n';
  807. }
  808. case 'html': {
  809. return !this.token.pre && !this.options.pedantic
  810. ? this.inline.output(this.token.text)
  811. : this.token.text;
  812. }
  813. case 'paragraph': {
  814. return '<p' + tok_class + '>'
  815. + this.inline.output(this.token.text)
  816. + '</p>\n';
  817. }
  818. case 'text': {
  819. return '<p' + tok_class + '>'
  820. + this.parseText()
  821. + '</p>\n';
  822. }
  823. }
  824. };
  825. /**
  826. * Helpers
  827. */
  828. function escape(html, encode) {
  829. return html
  830. .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
  831. .replace(/</g, '&lt;')
  832. .replace(/>/g, '&gt;')
  833. .replace(/"/g, '&quot;')
  834. .replace(/'/g, '&#39;');
  835. }
  836. function replace(regex, opt) {
  837. regex = regex.source;
  838. opt = opt || '';
  839. return function self(name, val) {
  840. if (!name) return new RegExp(regex, opt);
  841. val = val.source || val;
  842. val = val.replace(/(^|[^\[])\^/g, '$1');
  843. regex = regex.replace(name, val);
  844. return self;
  845. };
  846. }
  847. function noop() {}
  848. noop.exec = noop;
  849. function merge(obj) {
  850. var i = 1
  851. , target
  852. , key;
  853. for (; i < arguments.length; i++) {
  854. target = arguments[i];
  855. for (key in target) {
  856. if (Object.prototype.hasOwnProperty.call(target, key)) {
  857. obj[key] = target[key];
  858. }
  859. }
  860. }
  861. return obj;
  862. }
  863. /**
  864. * Marked
  865. */
  866. function marked(src, opt) {
  867. try {
  868. if (opt) opt = merge({}, marked.defaults, opt);
  869. return Parser.parse(Lexer.lex(src, opt), opt);
  870. } catch (e) {
  871. e.message += '\nPlease report this to https://github.com/chjj/marked.';
  872. if ((opt || marked.defaults).silent) {
  873. return '<p>An error occured:</p><pre>'
  874. + escape(e.message + '', true)
  875. + '</pre>';
  876. }
  877. throw e;
  878. }
  879. }
  880. /**
  881. * Options
  882. */
  883. marked.options =
  884. marked.setOptions = function(opt) {
  885. merge(marked.defaults, opt);
  886. return marked;
  887. };
  888. marked.defaults = {
  889. gfm: true,
  890. tables: true,
  891. breaks: false,
  892. pedantic: false,
  893. sanitize: false,
  894. smartLists: false,
  895. silent: false,
  896. highlight: null,
  897. getLangClass: function(lang) {
  898. lang = lang.toLowerCase();
  899. switch (lang) {
  900. case 'c': return 'sh_c';
  901. case 'c++': return 'sh_cpp';
  902. case 'pascal': return 'sh_pascal';
  903. default: return 'sh_' + lang;
  904. }
  905. },
  906. getElementClass: null
  907. };
  908. /**
  909. * Expose
  910. */
  911. marked.Parser = Parser;
  912. marked.parser = Parser.parse;
  913. marked.Lexer = Lexer;
  914. marked.lexer = Lexer.lex;
  915. marked.InlineLexer = InlineLexer;
  916. marked.inlineLexer = InlineLexer.output;
  917. marked.parse = marked;
  918. if (typeof exports === 'object') {
  919. module.exports = marked;
  920. } else if (typeof define === 'function' && define.amd) {
  921. define(function() { return marked; });
  922. } else {
  923. this.marked = marked;
  924. }
  925. }).call(function() {
  926. return this || (typeof window !== 'undefined' ? window : global);
  927. }());