mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 14:03:30 +00:00 
			
		
		
		
	String.prototype.replace and String.prototype.replaceAll interpret certain sequences such as $$ within a string provided as the replacement argument. Avoid this interpretation by providing a function. Signed-off-by: Anders Kaseorg <anders@zulip.com>
		
			
				
	
	
		
			1557 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1557 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * marked - a markdown parser
 | 
						|
 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
 | 
						|
 * https://github.com/chjj/marked
 | 
						|
 */
 | 
						|
 | 
						|
;(function() {
 | 
						|
 | 
						|
/**
 | 
						|
 * Block-Level Grammar
 | 
						|
 */
 | 
						|
 | 
						|
var block = {
 | 
						|
  newline: /^\n+/,
 | 
						|
  code: /^( {4}[^\n]+\n*)+/,
 | 
						|
  fences: noop,
 | 
						|
  hr: /^( *[-*_]){3,} *(?:\n+|$)/,
 | 
						|
  heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
 | 
						|
  nptable: noop,
 | 
						|
  blockquote: /^(?!( *>\s*($|\n))*($|\n))( *>[^\n]*(\n(?!def)[^\n]+)*)+/,
 | 
						|
  list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
 | 
						|
  html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
 | 
						|
  def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
 | 
						|
  table: noop,
 | 
						|
  paragraph: /^((?:[^\n]+\n?(?!hr|heading|blockquote|tag|def))+)\n*/,
 | 
						|
  text: /^[^\n]+/
 | 
						|
};
 | 
						|
 | 
						|
block.bullet = /(?:[*+-]|\d{1,9}\.)/;
 | 
						|
block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
 | 
						|
block.item = replace(block.item, 'gm')
 | 
						|
  (/bull/g, block.bullet)
 | 
						|
  ();
 | 
						|
 | 
						|
block.list = replace(block.list)
 | 
						|
  (/bull/g, block.bullet)
 | 
						|
  ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
 | 
						|
  ('def', '\\n+(?=' + block.def.source + ')')
 | 
						|
  ();
 | 
						|
 | 
						|
block.blockquote = replace(block.blockquote)
 | 
						|
  ('def', block.def)
 | 
						|
  ();
 | 
						|
 | 
						|
block._tag = '(?!(?:'
 | 
						|
  + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
 | 
						|
  + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
 | 
						|
  + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
 | 
						|
 | 
						|
block.html = replace(block.html)
 | 
						|
  ('comment', /<!--[\s\S]*?-->/)
 | 
						|
  ('closed', /<(tag)[\s\S]+?<\/\1>/)
 | 
						|
  ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
 | 
						|
  (/tag/g, block._tag)
 | 
						|
  ();
 | 
						|
 | 
						|
block.paragraph = replace(block.paragraph)
 | 
						|
  ('hr', block.hr)
 | 
						|
  ('heading', ' {0,3}#{1,6} +')
 | 
						|
  ('blockquote', block.blockquote)
 | 
						|
  ('tag', '<' + block._tag)
 | 
						|
  ('def', block.def)
 | 
						|
  ();
 | 
						|
 | 
						|
/**
 | 
						|
 * Normal Block Grammar
 | 
						|
 */
 | 
						|
 | 
						|
block.normal = merge({}, block);
 | 
						|
 | 
						|
/**
 | 
						|
 * GFM Block Grammar
 | 
						|
 */
 | 
						|
 | 
						|
block.gfm = merge({}, block.normal, {
 | 
						|
  fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
 | 
						|
  paragraph: /^/,
 | 
						|
});
 | 
						|
 | 
						|
block.gfm.paragraph = replace(block.paragraph)
 | 
						|
  ('(?!', '(?!'
 | 
						|
    + block.gfm.fences.source.replace('\\1', '\\2') + '|'
 | 
						|
    + block.list.source.replace('\\1', '\\3') + '|')
 | 
						|
  ();
 | 
						|
 | 
						|
/**
 | 
						|
 * GFM + Tables Block Grammar
 | 
						|
 */
 | 
						|
 | 
						|
block.tables = merge({}, block.gfm, {
 | 
						|
  nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
 | 
						|
  table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
 | 
						|
});
 | 
						|
 | 
						|
/**
 | 
						|
 * Block Lexer
 | 
						|
 */
 | 
						|
 | 
						|
function Lexer(options) {
 | 
						|
  this.tokens = [];
 | 
						|
  this.tokens.links = {};
 | 
						|
  this.options = options || marked.defaults;
 | 
						|
  this.rules = block.normal;
 | 
						|
 | 
						|
  if (this.options.gfm) {
 | 
						|
    if (this.options.tables) {
 | 
						|
      this.rules = block.tables;
 | 
						|
    } else {
 | 
						|
      this.rules = block.gfm;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Expose Block Rules
 | 
						|
 */
 | 
						|
 | 
						|
Lexer.rules = block;
 | 
						|
 | 
						|
/**
 | 
						|
 * Static Lex Method
 | 
						|
 */
 | 
						|
 | 
						|
Lexer.lex = function(src, options) {
 | 
						|
  var lexer = new Lexer(options);
 | 
						|
  return lexer.lex(src);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Preprocessing
 | 
						|
 */
 | 
						|
 | 
						|
Lexer.prototype.lex = function(src) {
 | 
						|
  src = src
 | 
						|
    .replace(/\r\n|\r/g, '\n')
 | 
						|
    .replace(/\t/g, '    ')
 | 
						|
    .replace(/\u00a0/g, ' ')
 | 
						|
    .replace(/\u2424/g, '\n');
 | 
						|
 | 
						|
  return this.token(src, true);
 | 
						|
};
 | 
						|
 | 
						|
var htmlStashCounter = 0;
 | 
						|
var htmlStashTemplate = "klzzwxhzsd:";
 | 
						|
var htmlStashRegex = /\bklzzwxhzsd:[0-9]+\b/;
 | 
						|
var htmlStash = [];
 | 
						|
 | 
						|
function stashHtml(html, safe) {
 | 
						|
  var key = htmlStashTemplate + htmlStashCounter;
 | 
						|
  htmlStashCounter++;
 | 
						|
 | 
						|
  htmlStash.push([key, html, safe]);
 | 
						|
 | 
						|
  return key;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Lexing
 | 
						|
 */
 | 
						|
 | 
						|
Lexer.prototype.token = function(src, top, bq) {
 | 
						|
  var src = src.replace(/^ +$/gm, '')
 | 
						|
    , next
 | 
						|
    , loose
 | 
						|
    , cap
 | 
						|
    , bull
 | 
						|
    , b
 | 
						|
    , item
 | 
						|
    , listStart
 | 
						|
    , listItems
 | 
						|
    , space
 | 
						|
    , i
 | 
						|
    , l
 | 
						|
    , isordered;
 | 
						|
 | 
						|
  while (src) {
 | 
						|
    // newline
 | 
						|
    if (cap = this.rules.newline.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      if (cap[0].length > 1) {
 | 
						|
        this.tokens.push({
 | 
						|
          type: 'space'
 | 
						|
        });
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // code
 | 
						|
    if (cap = this.rules.code.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      cap = cap[0].replace(/^ {4}/gm, '');
 | 
						|
      this.tokens.push({
 | 
						|
        type: 'code',
 | 
						|
        text: !this.options.pedantic
 | 
						|
          ? cap.replace(/\n+$/, '')
 | 
						|
          : cap
 | 
						|
      });
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // fences (gfm)
 | 
						|
    if (cap = this.rules.fences.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      this.tokens.push({
 | 
						|
        type: 'code',
 | 
						|
        lang: cap[2],
 | 
						|
        text: cap[3] || ''
 | 
						|
      });
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // heading
 | 
						|
    if (cap = this.rules.heading.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      this.tokens.push({
 | 
						|
        type: 'heading',
 | 
						|
        depth: cap[1].length,
 | 
						|
        text: cap[2]
 | 
						|
      });
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // table no leading pipe (gfm)
 | 
						|
    if (top && (cap = this.rules.nptable.exec(src))) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
 | 
						|
      item = {
 | 
						|
        type: 'table',
 | 
						|
        header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
 | 
						|
        align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
 | 
						|
        cells: cap[3].replace(/\n$/, '').split('\n')
 | 
						|
      };
 | 
						|
 | 
						|
      for (i = 0; i < item.align.length; i++) {
 | 
						|
        if (/^ *-+: *$/.test(item.align[i])) {
 | 
						|
          item.align[i] = 'right';
 | 
						|
        } else if (/^ *:-+: *$/.test(item.align[i])) {
 | 
						|
          item.align[i] = 'center';
 | 
						|
        } else if (/^ *:-+ *$/.test(item.align[i])) {
 | 
						|
          item.align[i] = 'left';
 | 
						|
        } else {
 | 
						|
          item.align[i] = null;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      for (i = 0; i < item.cells.length; i++) {
 | 
						|
        item.cells[i] = item.cells[i].split(/ *\| */);
 | 
						|
      }
 | 
						|
 | 
						|
      this.tokens.push(item);
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // hr
 | 
						|
    if (cap = this.rules.hr.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      this.tokens.push({
 | 
						|
        type: 'hr'
 | 
						|
      });
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // blockquote
 | 
						|
    if (cap = this.rules.blockquote.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
 | 
						|
      this.tokens.push({
 | 
						|
        type: 'blockquote_start'
 | 
						|
      });
 | 
						|
 | 
						|
      cap = cap[0].replace(/^ *> ?/gm, '');
 | 
						|
 | 
						|
      // Pass `top` to keep the current
 | 
						|
      // "toplevel" state. This is exactly
 | 
						|
      // how markdown.pl works.
 | 
						|
      this.token(cap, top, true);
 | 
						|
 | 
						|
      this.tokens.push({
 | 
						|
        type: 'blockquote_end'
 | 
						|
      });
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // list
 | 
						|
    if (cap = this.rules.list.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      bull = cap[2];
 | 
						|
      isordered = bull.length > 1;
 | 
						|
 | 
						|
      listStart = {
 | 
						|
        type: 'list_start',
 | 
						|
        ordered: isordered,
 | 
						|
        start: isordered ? +bull : '',
 | 
						|
        loose: false
 | 
						|
      };
 | 
						|
 | 
						|
      this.tokens.push(listStart);
 | 
						|
 | 
						|
      // Get each top-level item.
 | 
						|
      cap = cap[0].match(this.rules.item);
 | 
						|
 | 
						|
      listItems = [];
 | 
						|
      next = false;
 | 
						|
      l = cap.length;
 | 
						|
      i = 0;
 | 
						|
 | 
						|
      for (; i < l; i++) {
 | 
						|
        item = cap[i];
 | 
						|
 | 
						|
        // Remove the list item's bullet
 | 
						|
        // so it is seen as the next token.
 | 
						|
        space = item.length;
 | 
						|
        item = item.replace(/^ *([*+-]|\d+\.) */, '');
 | 
						|
 | 
						|
        // Outdent whatever the
 | 
						|
        // list item contains. Hacky.
 | 
						|
        if (~item.indexOf('\n ')) {
 | 
						|
          space -= item.length;
 | 
						|
          item = !this.options.pedantic
 | 
						|
            ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
 | 
						|
            : item.replace(/^ {1,4}/gm, '');
 | 
						|
        }
 | 
						|
 | 
						|
        // Determine whether the next list item belongs here.
 | 
						|
        // Backpedal if it does not belong in this list.
 | 
						|
        if (i !== l - 1) {
 | 
						|
          b = block.bullet.exec(cap[i + 1])[0];
 | 
						|
          if (bull.length > 1 ? b.length === 1
 | 
						|
            : (b.length > 1 || (this.options.smartLists && b !== bull))) {
 | 
						|
            src = cap.slice(i + 1).join('\n') + src;
 | 
						|
            i = l - 1;
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        // Determine whether item is loose or not.
 | 
						|
        // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
 | 
						|
        // for discount behavior.
 | 
						|
        loose = next || /\n\n(?!\s*$)/.test(item);
 | 
						|
        if (i !== l - 1) {
 | 
						|
          next = item.charAt(item.length - 1) === '\n';
 | 
						|
          if (!loose) loose = next;
 | 
						|
        }
 | 
						|
 | 
						|
        if (loose) {
 | 
						|
          listStart.loose = true;
 | 
						|
        }
 | 
						|
 | 
						|
        var t = {
 | 
						|
          type: 'list_item_start',
 | 
						|
          loose: loose
 | 
						|
        };
 | 
						|
 | 
						|
        listItems.push(t);
 | 
						|
        this.tokens.push(t);
 | 
						|
 | 
						|
        // Recurse.
 | 
						|
        this.token(item, false);
 | 
						|
 | 
						|
        this.tokens.push({
 | 
						|
          type: 'list_item_end'
 | 
						|
        });
 | 
						|
      }
 | 
						|
 | 
						|
      if (listStart.loose) {
 | 
						|
        l = listItems.length;
 | 
						|
        i = 0;
 | 
						|
        for (; i < l; i++) {
 | 
						|
          listItems[i].loose = true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      this.tokens.push({
 | 
						|
        type: 'list_end'
 | 
						|
      });
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // html
 | 
						|
    if (cap = this.rules.html.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      this.tokens.push({
 | 
						|
        type: this.options.sanitize
 | 
						|
          ? 'paragraph'
 | 
						|
          : 'html',
 | 
						|
        pre: !this.options.sanitizer
 | 
						|
          && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
 | 
						|
        text: cap[0]
 | 
						|
      });
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // def
 | 
						|
    // We disable definition style links in Zulip.
 | 
						|
 | 
						|
    // table (gfm)
 | 
						|
    if (top && (cap = this.rules.table.exec(src))) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
 | 
						|
      item = {
 | 
						|
        type: 'table',
 | 
						|
        header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
 | 
						|
        align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
 | 
						|
        cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
 | 
						|
      };
 | 
						|
 | 
						|
      for (i = 0; i < item.align.length; i++) {
 | 
						|
        if (/^ *-+: *$/.test(item.align[i])) {
 | 
						|
          item.align[i] = 'right';
 | 
						|
        } else if (/^ *:-+: *$/.test(item.align[i])) {
 | 
						|
          item.align[i] = 'center';
 | 
						|
        } else if (/^ *:-+ *$/.test(item.align[i])) {
 | 
						|
          item.align[i] = 'left';
 | 
						|
        } else {
 | 
						|
          item.align[i] = null;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      for (i = 0; i < item.cells.length; i++) {
 | 
						|
        item.cells[i] = item.cells[i]
 | 
						|
          .replace(/^ *\| *| *\| *$/g, '')
 | 
						|
          .split(/ *\| */);
 | 
						|
      }
 | 
						|
 | 
						|
      this.tokens.push(item);
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // top-level paragraph
 | 
						|
    if (top && (cap = this.rules.paragraph.exec(src))) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      this.tokens.push({
 | 
						|
        type: 'paragraph',
 | 
						|
        text: cap[1].charAt(cap[1].length - 1) === '\n'
 | 
						|
          ? cap[1].slice(0, -1)
 | 
						|
          : cap[1]
 | 
						|
      });
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // text
 | 
						|
    if (cap = this.rules.text.exec(src)) {
 | 
						|
      // Top-level should never reach here.
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      this.tokens.push({
 | 
						|
        type: 'text',
 | 
						|
        text: cap[0]
 | 
						|
      });
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (src) {
 | 
						|
      throw new
 | 
						|
        Error('Infinite loop on byte: ' + src.charCodeAt(0));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return this.tokens;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Inline-Level Grammar
 | 
						|
 */
 | 
						|
 | 
						|
var inline = {
 | 
						|
  escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
 | 
						|
  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
 | 
						|
  url: noop,
 | 
						|
  tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
 | 
						|
  link: /^!?\[(inside)\]\(href\)/,
 | 
						|
  reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
 | 
						|
  nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
 | 
						|
  strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
 | 
						|
  em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
 | 
						|
  code: /^(`+)(\s*[\s\S]*?[^`]\s*)\1(?!`)/,
 | 
						|
  br: /^ {2,}\n(?!\s*$)/,
 | 
						|
  del: noop,
 | 
						|
  emoji: noop,
 | 
						|
  unicodeemoji: noop,
 | 
						|
  usermention: noop,
 | 
						|
  groupmention: noop,
 | 
						|
  stream: noop,
 | 
						|
  tex: noop,
 | 
						|
  timestamp: noop,
 | 
						|
  text: /^[\s\S]+?(?=[\\<!\[_*`$]| {2,}\n|$)/
 | 
						|
};
 | 
						|
 | 
						|
inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
 | 
						|
inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
 | 
						|
 | 
						|
inline.link = replace(inline.link)
 | 
						|
  ('inside', inline._inside)
 | 
						|
  ('href', inline._href)
 | 
						|
  ();
 | 
						|
 | 
						|
inline.reflink = replace(inline.reflink)
 | 
						|
  ('inside', inline._inside)
 | 
						|
  ();
 | 
						|
 | 
						|
/**
 | 
						|
 * Normal Inline Grammar
 | 
						|
 */
 | 
						|
 | 
						|
inline.normal = merge({}, inline);
 | 
						|
 | 
						|
/**
 | 
						|
 * Pedantic Inline Grammar
 | 
						|
 */
 | 
						|
 | 
						|
inline.pedantic = merge({}, inline.normal, {
 | 
						|
  strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
 | 
						|
  em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
 | 
						|
});
 | 
						|
 | 
						|
/**
 | 
						|
 * GFM Inline Grammar
 | 
						|
 */
 | 
						|
 | 
						|
inline.gfm = merge({}, inline.normal, {
 | 
						|
  escape: replace(inline.escape)('])', '~|])')(),
 | 
						|
  url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
 | 
						|
  del: /^~~(?=\S)([\s\S]*?\S)~~/,
 | 
						|
  text: replace(inline.text)
 | 
						|
    (']|', '~]|')
 | 
						|
    ('|', '|https?://|')
 | 
						|
    ()
 | 
						|
});
 | 
						|
 | 
						|
/**
 | 
						|
 * GFM + Line Breaks Inline Grammar
 | 
						|
 */
 | 
						|
 | 
						|
inline.breaks = merge({}, inline.gfm, {
 | 
						|
  br: replace(inline.br)('{2,}', '*')(),
 | 
						|
  text: replace(inline.gfm.text)('{2,}', '*')()
 | 
						|
});
 | 
						|
 | 
						|
// From https://unicode.org/reports/tr51/#EBNF_and_Regex. Keep this synced with POSSIBLE_EMOJI_RE.
 | 
						|
const possible_emoji_regex = /^(\p{RI}\p{RI}|\p{Emoji}(?:\p{Emoji_Modifier}|\u{FE0F}\u{20E3}?|[\u{E0020}-\u{E007E}]+\u{E007F})?(?:\u{200D}(?:\p{RI}\p{RI}|\p{Emoji}(?:\p{Emoji_Modifier}|\u{FE0F}\u{20E3}?|[\u{E0020}-\u{E007E}]+\u{E007F})?))*)/u;
 | 
						|
 | 
						|
inline.zulip = merge({}, inline.breaks, {
 | 
						|
  emoji: /^:([A-Za-z0-9_\-\+]+?):/,
 | 
						|
  unicodeemoji: possible_emoji_regex,
 | 
						|
  usermention: /^@(_?)(?:\*\*([^\*]+)\*\*)/, // Match potentially multi-word string between @** **
 | 
						|
  groupmention: /^@(_?)(?:\*([^\*]+)\*)/, // Match multi-word string between @* *
 | 
						|
  stream_topic: /^#\*\*([^\*>]+)>([^\*]+)\*\*/,
 | 
						|
  stream: /^#\*\*([^\*]+)\*\*/,
 | 
						|
  tex: /^(\$\$([^\n_$](\\\$|[^\n$])*)\$\$(?!\$))\B/,
 | 
						|
  timestamp: /^<time:([^>]+)>/,
 | 
						|
  text: replace(inline.breaks.text)
 | 
						|
    ('|', '|(\ud83c[\udd00-\udfff]|\ud83d[\udc00-\ude4f]|' +
 | 
						|
          '\ud83d[\ude80-\udeff]|\ud83e[\udd00-\uddff]|' +
 | 
						|
          '[\u2000-\u206F]|[\u2300-\u27BF]|[\u2B00-\u2BFF]|' +
 | 
						|
          '[\u3000-\u303F]|[\u3200-\u32FF])|')
 | 
						|
    (']|', '#@:]|')
 | 
						|
    ('^[', '^^\\${3,}|^^[')
 | 
						|
    ()
 | 
						|
});
 | 
						|
 | 
						|
/**
 | 
						|
 * Inline Lexer & Compiler
 | 
						|
 */
 | 
						|
 | 
						|
function InlineLexer(links, options) {
 | 
						|
  this.options = options || marked.defaults;
 | 
						|
  this.links = links;
 | 
						|
  this.rules = inline.normal;
 | 
						|
  this.renderer = this.options.renderer || new Renderer;
 | 
						|
  this.renderer.options = this.options;
 | 
						|
 | 
						|
  if (!this.links) {
 | 
						|
    throw new
 | 
						|
      Error('Tokens array requires a `links` property.');
 | 
						|
  }
 | 
						|
 | 
						|
  if (this.options.zulip) {
 | 
						|
    this.rules = inline.zulip;
 | 
						|
  } else {
 | 
						|
    if (this.options.gfm) {
 | 
						|
      if (this.options.breaks) {
 | 
						|
        this.rules = inline.breaks;
 | 
						|
      } else {
 | 
						|
        this.rules = inline.gfm;
 | 
						|
      }
 | 
						|
    } else if (this.options.pedantic) {
 | 
						|
      this.rules = inline.pedantic;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Expose Inline Rules
 | 
						|
 */
 | 
						|
 | 
						|
InlineLexer.rules = inline;
 | 
						|
 | 
						|
/**
 | 
						|
 * Static Lexing/Compiling Method
 | 
						|
 */
 | 
						|
 | 
						|
InlineLexer.output = function(src, links, options) {
 | 
						|
  var inline = new InlineLexer(links, options);
 | 
						|
  return inline.output(src);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Lexing/Compiling
 | 
						|
 */
 | 
						|
 | 
						|
InlineLexer.prototype.inlineReplacement = function(regex, src, replace_func) {
 | 
						|
  var cap, out = "";
 | 
						|
  regex.lastIndex = 0;
 | 
						|
  if (cap = regex.exec(src)) {
 | 
						|
    // Split before-match into its own segment and handle it separately
 | 
						|
    var match_idx = regex.lastIndex;
 | 
						|
    var before = src.substring(0, match_idx - cap[0].length);
 | 
						|
    before = this.output(before);
 | 
						|
    out += before;
 | 
						|
 | 
						|
    // Consume all of the matched text
 | 
						|
    src = src.substring(match_idx);
 | 
						|
 | 
						|
    out += replace_func(regex, cap.slice(1), cap[0]);
 | 
						|
  }
 | 
						|
 | 
						|
  return [src, out];
 | 
						|
};
 | 
						|
 | 
						|
InlineLexer.prototype.output = function(src) {
 | 
						|
  var out = ''
 | 
						|
    , link
 | 
						|
    , text
 | 
						|
    , href
 | 
						|
    , cap;
 | 
						|
 | 
						|
  while (src) {
 | 
						|
    // escape
 | 
						|
    if (cap = this.rules.escape.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += cap[1];
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // linkifier (Zulip)
 | 
						|
    var self = this;
 | 
						|
 | 
						|
    const regexes = this.options.get_linkifier_regexes ? this.options.get_linkifier_regexes() : [];
 | 
						|
    regexes.forEach(function (regex) {
 | 
						|
      var ret = self.inlineReplacement(regex, src, function(regex, groups, match) {
 | 
						|
        // Insert the created URL
 | 
						|
        href = self.linkifier(regex, groups, match);
 | 
						|
        if (href !== undefined) {
 | 
						|
          href = escape(href);
 | 
						|
          return self.renderer.link(href, href, match);
 | 
						|
        } else {
 | 
						|
          return match;
 | 
						|
        }
 | 
						|
      });
 | 
						|
 | 
						|
      src = ret[0];
 | 
						|
      out += ret[1];
 | 
						|
    });
 | 
						|
 | 
						|
    // autolink
 | 
						|
    if (cap = this.rules.autolink.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      if (cap[2] === '@') {
 | 
						|
        text = cap[1].charAt(6) === ':'
 | 
						|
          ? this.mangle(cap[1].substring(7))
 | 
						|
          : this.mangle(cap[1]);
 | 
						|
        href = this.mangle('mailto:') + text;
 | 
						|
      } else {
 | 
						|
        text = escape(cap[1]);
 | 
						|
        href = text;
 | 
						|
      }
 | 
						|
      out += this.renderer.link(href, null, text);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // url (gfm)
 | 
						|
    if (!this.inLink && (cap = this.rules.url.exec(src))) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      text = escape(cap[1]);
 | 
						|
      href = text;
 | 
						|
      out += this.renderer.link(href, null, text);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // timestamp
 | 
						|
    if (cap = this.rules.timestamp.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.timestamp(cap[1]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // tag
 | 
						|
    if (cap = this.rules.tag.exec(src)) {
 | 
						|
      if (!this.inLink && /^<a /i.test(cap[0])) {
 | 
						|
        this.inLink = true;
 | 
						|
      } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
 | 
						|
        this.inLink = false;
 | 
						|
      }
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.options.sanitize
 | 
						|
        ? this.options.sanitizer
 | 
						|
          ? this.options.sanitizer(cap[0])
 | 
						|
          : escape(cap[0])
 | 
						|
        : cap[0]
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // link
 | 
						|
    if (cap = this.rules.link.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      this.inLink = true;
 | 
						|
      out += this.outputLink(cap, {
 | 
						|
        href: cap[2],
 | 
						|
        title: cap[3]
 | 
						|
      });
 | 
						|
      this.inLink = false;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // reflink, nolink
 | 
						|
    if ((cap = this.rules.reflink.exec(src))
 | 
						|
        || (cap = this.rules.nolink.exec(src))) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
 | 
						|
      link = this.links[link.toLowerCase()];
 | 
						|
      if (!link || !link.href) {
 | 
						|
        out += cap[0].charAt(0);
 | 
						|
        src = cap[0].substring(1) + src;
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      this.inLink = true;
 | 
						|
      out += this.outputLink(cap, link);
 | 
						|
      this.inLink = false;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // usermention (Zulip)
 | 
						|
    if (cap = this.rules.usermention.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.usermention(unescape(cap[2]), cap[0], cap[1]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // groupmention (Zulip)
 | 
						|
    if (cap = this.rules.groupmention.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.groupmention(unescape(cap[2]), cap[0], cap[1]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // stream_topic (Zulip)
 | 
						|
    if (cap = this.rules.stream_topic.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.stream_topic(unescape(cap[1]), unescape(cap[2]), cap[0]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // stream (Zulip)
 | 
						|
    if (cap = this.rules.stream.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.stream(unescape(cap[1]), cap[0]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // strong
 | 
						|
    if (cap = this.rules.strong.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.renderer.strong(this.output(cap[2] || cap[1]));
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // em
 | 
						|
    if (cap = this.rules.em.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.renderer.em(this.output(cap[1] + cap[2]));
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // code
 | 
						|
    if (cap = this.rules.code.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.renderer.codespan(escape(cap[2], true));
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // br
 | 
						|
    if (cap = this.rules.br.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.renderer.br();
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // del (gfm)
 | 
						|
    if (cap = this.rules.del.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.renderer.del(this.output(cap[1]));
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // emoji (gfm)
 | 
						|
    if (cap = this.rules.emoji.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.emoji(cap[1]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // Unicode emoji
 | 
						|
    if (cap = this.rules.unicodeemoji.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.unicodeEmoji(cap[1]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // timestamp
 | 
						|
    if (cap = this.rules.timestamp.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.timestamp(cap[1]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // tex
 | 
						|
    if (cap = this.rules.tex.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.tex(cap[2], cap[0]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // WARNING: Do not place any parsing logic below this comment.
 | 
						|
    // Any parsing logic place after the `text` block below will most
 | 
						|
    // likely be silently never executed.
 | 
						|
 | 
						|
    // text
 | 
						|
    if (cap = this.rules.text.exec(src)) {
 | 
						|
      src = src.substring(cap[0].length);
 | 
						|
      out += this.renderer.text(escape(this.smartypants(cap[0])));
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (src) {
 | 
						|
      throw new
 | 
						|
        Error('Infinite loop on byte: ' + src.charCodeAt(0));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return out;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Compile Link
 | 
						|
 */
 | 
						|
 | 
						|
InlineLexer.prototype.outputLink = function(cap, link) {
 | 
						|
  var href = escape(link.href)
 | 
						|
    , title = link.title ? escape(link.title) : null;
 | 
						|
 | 
						|
  return cap[0].charAt(0) !== '!'
 | 
						|
    ? this.renderer.link(href, title, this.output(cap[1]))
 | 
						|
    : this.renderer.image(href, title, escape(cap[1]));
 | 
						|
};
 | 
						|
InlineLexer.prototype.emoji = function (name) {
 | 
						|
  name = escape(name)
 | 
						|
  if (typeof this.options.emojiHandler !== 'function')
 | 
						|
    return ':' + name + ':';
 | 
						|
 | 
						|
  return this.options.emojiHandler(name);
 | 
						|
};
 | 
						|
 | 
						|
InlineLexer.prototype.unicodeEmoji = function (name) {
 | 
						|
  name = escape(name)
 | 
						|
  if (typeof this.options.unicodeEmojiHandler !== 'function')
 | 
						|
    return name;
 | 
						|
  return this.options.unicodeEmojiHandler(name);
 | 
						|
};
 | 
						|
 | 
						|
InlineLexer.prototype.tex = function (tex, fullmatch) {
 | 
						|
  if (typeof this.options.texHandler !== 'function')
 | 
						|
    return fullmatch;
 | 
						|
  return this.options.texHandler(tex, fullmatch);
 | 
						|
};
 | 
						|
 | 
						|
InlineLexer.prototype.timestamp = function (time) {
 | 
						|
  if (typeof this.options.timestampHandler !== 'function')
 | 
						|
    return '<time:' + time + '>';
 | 
						|
  return this.options.timestampHandler(time);
 | 
						|
};
 | 
						|
 | 
						|
InlineLexer.prototype.linkifier = function (linkifier, matches, orig) {
 | 
						|
  if (typeof this.options.linkifierHandler !== 'function')
 | 
						|
    return;
 | 
						|
 | 
						|
  return this.options.linkifierHandler(linkifier, matches);
 | 
						|
};
 | 
						|
 | 
						|
InlineLexer.prototype.usermention = function (username, orig, silent) {
 | 
						|
  orig = escape(orig);
 | 
						|
  if (typeof this.options.userMentionHandler !== 'function')
 | 
						|
  {
 | 
						|
    return orig;
 | 
						|
  }
 | 
						|
  var handled = this.options.userMentionHandler(username, silent === '_');
 | 
						|
  if (handled !== undefined) {
 | 
						|
    return handled;
 | 
						|
  }
 | 
						|
 | 
						|
  return orig;
 | 
						|
};
 | 
						|
 | 
						|
InlineLexer.prototype.groupmention = function (groupname, orig, silent) {
 | 
						|
  orig = escape(orig);
 | 
						|
  if (typeof this.options.groupMentionHandler !== 'function')
 | 
						|
  {
 | 
						|
    return orig;
 | 
						|
  }
 | 
						|
 | 
						|
  var handled = this.options.groupMentionHandler(groupname, silent === '_');
 | 
						|
  if (handled !== undefined) {
 | 
						|
    return handled;
 | 
						|
  }
 | 
						|
 | 
						|
  return orig;
 | 
						|
};
 | 
						|
 | 
						|
InlineLexer.prototype.stream = function (streamName, orig) {
 | 
						|
  orig = escape(orig);
 | 
						|
  if (typeof this.options.streamHandler !== 'function')
 | 
						|
    return orig;
 | 
						|
 | 
						|
  var handled = this.options.streamHandler(streamName);
 | 
						|
  if (handled !== undefined) {
 | 
						|
    return handled;
 | 
						|
  }
 | 
						|
  return orig;
 | 
						|
};
 | 
						|
 | 
						|
InlineLexer.prototype.stream_topic = function (streamName, topic, orig) {
 | 
						|
  orig = escape(orig);
 | 
						|
  if (typeof this.options.streamHandler !== 'function')
 | 
						|
    return orig;
 | 
						|
 | 
						|
  var handled = this.options.streamTopicHandler(streamName, topic);
 | 
						|
  if (handled !== undefined) {
 | 
						|
    return handled;
 | 
						|
  }
 | 
						|
  return orig;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Smartypants Transformations
 | 
						|
 */
 | 
						|
 | 
						|
InlineLexer.prototype.smartypants = function(text) {
 | 
						|
  if (!this.options.smartypants) return text;
 | 
						|
  return text
 | 
						|
    // em-dashes
 | 
						|
    .replace(/---/g, '\u2014')
 | 
						|
    // en-dashes
 | 
						|
    .replace(/--/g, '\u2013')
 | 
						|
    // opening singles
 | 
						|
    .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
 | 
						|
    // closing singles & apostrophes
 | 
						|
    .replace(/'/g, '\u2019')
 | 
						|
    // opening doubles
 | 
						|
    .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
 | 
						|
    // closing doubles
 | 
						|
    .replace(/"/g, '\u201d')
 | 
						|
    // ellipses
 | 
						|
    .replace(/\.{3}/g, '\u2026');
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Mangle Links
 | 
						|
 */
 | 
						|
 | 
						|
InlineLexer.prototype.mangle = function(text) {
 | 
						|
  if (!this.options.mangle) return text;
 | 
						|
  var out = ''
 | 
						|
    , l = text.length
 | 
						|
    , i = 0
 | 
						|
    , ch;
 | 
						|
 | 
						|
  for (; i < l; i++) {
 | 
						|
    ch = text.charCodeAt(i);
 | 
						|
    if (Math.random() > 0.5) {
 | 
						|
      ch = 'x' + ch.toString(16);
 | 
						|
    }
 | 
						|
    out += '&#' + ch + ';';
 | 
						|
  }
 | 
						|
 | 
						|
  return out;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Renderer
 | 
						|
 */
 | 
						|
 | 
						|
function Renderer(options) {
 | 
						|
  this.options = options || {};
 | 
						|
}
 | 
						|
 | 
						|
Renderer.prototype.code = function(code, lang, escaped) {
 | 
						|
  if (this.options.highlight) {
 | 
						|
    var out = this.options.highlight(code, lang);
 | 
						|
    if (out != null && out !== code) {
 | 
						|
      escaped = true;
 | 
						|
      code = out;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!lang) {
 | 
						|
    return '<pre><code>'
 | 
						|
      + (escaped ? code : escape(code, true))
 | 
						|
      + '\n</code></pre>';
 | 
						|
  }
 | 
						|
 | 
						|
  return '<pre><code class="'
 | 
						|
    + this.options.langPrefix
 | 
						|
    + escape(lang, true)
 | 
						|
    + '">'
 | 
						|
    + (escaped ? code : escape(code, true))
 | 
						|
    + '\n</code></pre>\n';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.blockquote = function(quote) {
 | 
						|
  quote = this.options.silencedMentionHandler(quote);
 | 
						|
  return '<blockquote>\n' + quote + '</blockquote>\n';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.html = function(html) {
 | 
						|
  return html;
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.heading = function(text, level, raw, slugger) {
 | 
						|
  // ignore IDs
 | 
						|
  return '<h' + level + '>' + text + '</h' + level + '>\n';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.hr = function() {
 | 
						|
  return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.list = function(body, ordered, start) {
 | 
						|
  var type = ordered ? 'ol' : 'ul',
 | 
						|
      startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
 | 
						|
  return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.listitem = function(text) {
 | 
						|
  return '<li>' + text + '</li>\n';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.paragraph = function(text) {
 | 
						|
  return '<p>' + text + '</p>\n';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.table = function(header, body) {
 | 
						|
  return '<table>\n'
 | 
						|
    + '<thead>\n'
 | 
						|
    + header
 | 
						|
    + '</thead>\n'
 | 
						|
    + '<tbody>\n'
 | 
						|
    + body
 | 
						|
    + '</tbody>\n'
 | 
						|
    + '</table>\n';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.tablerow = function(content) {
 | 
						|
  return '<tr>\n' + content + '</tr>\n';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.tablecell = function(content, flags) {
 | 
						|
  var type = flags.header ? 'th' : 'td';
 | 
						|
  var tag = flags.align
 | 
						|
    ? '<' + type + ' style="text-align:' + flags.align + '">'
 | 
						|
    : '<' + type + '>';
 | 
						|
  return tag + content + '</' + type + '>\n';
 | 
						|
};
 | 
						|
 | 
						|
// span level renderer
 | 
						|
Renderer.prototype.strong = function(text) {
 | 
						|
  return '<strong>' + text + '</strong>';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.em = function(text) {
 | 
						|
  return '<em>' + text + '</em>';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.codespan = function(text) {
 | 
						|
  return '<code>' + text + '</code>';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.br = function() {
 | 
						|
  return this.options.xhtml ? '<br/>' : '<br>';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.del = function(text) {
 | 
						|
  return '<del>' + text + '</del>';
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.link = function(href, title, text) {
 | 
						|
  if (this.options.sanitize) {
 | 
						|
    try {
 | 
						|
      var prot = decodeURIComponent(unescape(href))
 | 
						|
        .replace(/[^\w:]/g, '')
 | 
						|
        .toLowerCase();
 | 
						|
    } catch (e) {
 | 
						|
      return '';
 | 
						|
    }
 | 
						|
    if (prot.startsWith('javascript:') || prot.startsWith('vbscript:')) {
 | 
						|
      return '';
 | 
						|
    }
 | 
						|
  }
 | 
						|
  var out = '<a href="' + href + '"';
 | 
						|
  if (title) {
 | 
						|
    out += ' title="' + title + '"';
 | 
						|
  }
 | 
						|
  out += '>' + text + '</a>';
 | 
						|
  return out;
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.image = function(href, title, text) {
 | 
						|
  var out = '<img src="' + href + '" alt="' + text + '"';
 | 
						|
  if (title) {
 | 
						|
    out += ' title="' + title + '"';
 | 
						|
  }
 | 
						|
  out += this.options.xhtml ? '/>' : '>';
 | 
						|
  return out;
 | 
						|
};
 | 
						|
 | 
						|
Renderer.prototype.text = function(text) {
 | 
						|
  return text;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Parsing & Compiling
 | 
						|
 */
 | 
						|
 | 
						|
function Parser(options) {
 | 
						|
  this.tokens = [];
 | 
						|
  this.token = null;
 | 
						|
  this.options = options || marked.defaults;
 | 
						|
  this.options.renderer = this.options.renderer || new Renderer;
 | 
						|
  this.renderer = this.options.renderer;
 | 
						|
  this.renderer.options = this.options;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Static Parse Method
 | 
						|
 */
 | 
						|
 | 
						|
Parser.parse = function(src, options, renderer) {
 | 
						|
  var parser = new Parser(options, renderer);
 | 
						|
  var out = parser.parse(src);
 | 
						|
  out = parser.postprocess(out);
 | 
						|
  return out;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Parse Loop
 | 
						|
 */
 | 
						|
 | 
						|
Parser.prototype.parse = function(src) {
 | 
						|
  this.inline = new InlineLexer(src.links, this.options, this.renderer);
 | 
						|
  this.tokens = src.reverse();
 | 
						|
 | 
						|
  var out = '';
 | 
						|
  while (this.next()) {
 | 
						|
    out += this.tok();
 | 
						|
  }
 | 
						|
 | 
						|
  return out;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Post Processing - replace stashed HTML
 | 
						|
 **/
 | 
						|
 Parser.prototype.postprocess = function(output) {
 | 
						|
  for (var i = 0; i < htmlStash.length; i++) {
 | 
						|
    var stash = htmlStash[i];
 | 
						|
    var key = stash[0],
 | 
						|
        html = stash[1],
 | 
						|
        safe = stash[2];
 | 
						|
    if (!safe) {
 | 
						|
      html = escape(html);
 | 
						|
    }
 | 
						|
    output = output.replace('<p>' + key + '</p>', () => html)
 | 
						|
  }
 | 
						|
  return output;
 | 
						|
 };
 | 
						|
 | 
						|
/**
 | 
						|
 * Next Token
 | 
						|
 */
 | 
						|
 | 
						|
Parser.prototype.next = function() {
 | 
						|
  return this.token = this.tokens.pop();
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Preview Next Token
 | 
						|
 */
 | 
						|
 | 
						|
Parser.prototype.peek = function() {
 | 
						|
  return this.tokens[this.tokens.length - 1] || 0;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Parse Text Tokens
 | 
						|
 */
 | 
						|
 | 
						|
Parser.prototype.parseText = function() {
 | 
						|
  var body = this.token.text;
 | 
						|
 | 
						|
  while (this.peek().type === 'text') {
 | 
						|
    body += '\n' + this.next().text;
 | 
						|
  }
 | 
						|
 | 
						|
  return this.inline.output(body);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Parse Current Token
 | 
						|
 */
 | 
						|
 | 
						|
Parser.prototype.tok = function() {
 | 
						|
  switch (this.token.type) {
 | 
						|
    case 'space': {
 | 
						|
      return '';
 | 
						|
    }
 | 
						|
    case 'heading': {
 | 
						|
      return this.renderer.heading(
 | 
						|
        this.inline.output(this.token.text),
 | 
						|
        this.token.depth,
 | 
						|
        unescape(this.token.text),
 | 
						|
        this.slugger);
 | 
						|
    }
 | 
						|
    case 'hr': {
 | 
						|
      return this.renderer.hr();
 | 
						|
    }
 | 
						|
    case 'code': {
 | 
						|
      return this.renderer.code(this.token.text,
 | 
						|
        this.token.lang,
 | 
						|
        this.token.escaped);
 | 
						|
    }
 | 
						|
    case 'table': {
 | 
						|
      var header = ''
 | 
						|
        , body = ''
 | 
						|
        , i
 | 
						|
        , row
 | 
						|
        , cell
 | 
						|
        , flags
 | 
						|
        , j;
 | 
						|
 | 
						|
      // header
 | 
						|
      cell = '';
 | 
						|
      for (i = 0; i < this.token.header.length; i++) {
 | 
						|
        flags = { header: true, align: this.token.align[i] };
 | 
						|
        cell += this.renderer.tablecell(
 | 
						|
          this.inline.output(this.token.header[i]),
 | 
						|
          { header: true, align: this.token.align[i] }
 | 
						|
        );
 | 
						|
      }
 | 
						|
      header += this.renderer.tablerow(cell);
 | 
						|
 | 
						|
      for (i = 0; i < this.token.cells.length; i++) {
 | 
						|
        row = this.token.cells[i];
 | 
						|
 | 
						|
        cell = '';
 | 
						|
        for (j = 0; j < row.length; j++) {
 | 
						|
          cell += this.renderer.tablecell(
 | 
						|
            this.inline.output(row[j]),
 | 
						|
            { header: false, align: this.token.align[j] }
 | 
						|
          );
 | 
						|
        }
 | 
						|
 | 
						|
        body += this.renderer.tablerow(cell);
 | 
						|
      }
 | 
						|
      return this.renderer.table(header, body);
 | 
						|
    }
 | 
						|
    case 'blockquote_start': {
 | 
						|
      var body = '';
 | 
						|
 | 
						|
      while (this.next().type !== 'blockquote_end') {
 | 
						|
        body += this.tok();
 | 
						|
      }
 | 
						|
 | 
						|
      return this.renderer.blockquote(body);
 | 
						|
    }
 | 
						|
    case 'list_start': {
 | 
						|
      var body = ''
 | 
						|
        , ordered = this.token.ordered
 | 
						|
        , start = this.token.start;
 | 
						|
 | 
						|
      while (this.next().type !== 'list_end') {
 | 
						|
        body += this.tok();
 | 
						|
      }
 | 
						|
 | 
						|
      return this.renderer.list(body, ordered, start);
 | 
						|
    }
 | 
						|
    case 'list_item_start': {
 | 
						|
      var body = ''
 | 
						|
        , loose = this.token.loose;
 | 
						|
 | 
						|
      while (this.next().type !== 'list_item_end') {
 | 
						|
        body += !loose && this.token.type === 'text'
 | 
						|
          ? this.parseText()
 | 
						|
          : this.tok();
 | 
						|
      }
 | 
						|
 | 
						|
      return this.renderer.listitem(body);
 | 
						|
    }
 | 
						|
    case 'html': {
 | 
						|
      var html = !this.token.pre && !this.options.pedantic
 | 
						|
        ? this.inline.output(this.token.text)
 | 
						|
        : this.token.text;
 | 
						|
      return this.renderer.html(html);
 | 
						|
    }
 | 
						|
    case 'paragraph': {
 | 
						|
      return this.renderer.paragraph(this.inline.output(this.token.text));
 | 
						|
    }
 | 
						|
    case 'text': {
 | 
						|
      return this.renderer.paragraph(this.parseText());
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Helpers
 | 
						|
 */
 | 
						|
 | 
						|
function escape(html, encode) {
 | 
						|
  return html
 | 
						|
    .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
 | 
						|
    .replace(/</g, '<')
 | 
						|
    .replace(/>/g, '>')
 | 
						|
    .replace(/"/g, '"')
 | 
						|
    .replace(/'/g, ''');
 | 
						|
}
 | 
						|
 | 
						|
var unescapeReplacements = {
 | 
						|
  amp: '&',
 | 
						|
  lt: '<',
 | 
						|
  gt: '>',
 | 
						|
  quot: '"',
 | 
						|
  colon: ':',
 | 
						|
};
 | 
						|
 | 
						|
function unescape(html) {
 | 
						|
  // explicitly match decimal, hex, and named HTML entities
 | 
						|
  return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:[0-9A-Za-z]+));?/g, function(_, n) {
 | 
						|
    n = n.toLowerCase();
 | 
						|
    if (n.charAt(0) === '#') {
 | 
						|
      return n.charAt(1) === 'x'
 | 
						|
        ? String.fromCharCode(parseInt(n.substring(2), 16))
 | 
						|
        : String.fromCharCode(+n.substring(1));
 | 
						|
    }
 | 
						|
    return unescapeReplacements[n] || '';
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function replace(regex, opt) {
 | 
						|
  regex = regex.source;
 | 
						|
  opt = opt || '';
 | 
						|
  return function self(name, val) {
 | 
						|
    if (!name) return new RegExp(regex, opt);
 | 
						|
    val = val.source || val;
 | 
						|
    val = val.replace(/(^|[^\[])\^/g, '$1');
 | 
						|
    regex = regex.replace(name, val);
 | 
						|
    return self;
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
function noop() {}
 | 
						|
noop.exec = noop;
 | 
						|
 | 
						|
function merge(obj) {
 | 
						|
  var i = 1
 | 
						|
    , target
 | 
						|
    , key;
 | 
						|
 | 
						|
  for (; i < arguments.length; i++) {
 | 
						|
    target = arguments[i];
 | 
						|
    for (key in target) {
 | 
						|
      if (Object.prototype.hasOwnProperty.call(target, key)) {
 | 
						|
        obj[key] = target[key];
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return obj;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Marked
 | 
						|
 */
 | 
						|
 | 
						|
function marked(src, opt, callback) {
 | 
						|
  if (callback || typeof opt === 'function') {
 | 
						|
    if (!callback) {
 | 
						|
      callback = opt;
 | 
						|
      opt = null;
 | 
						|
    }
 | 
						|
 | 
						|
    opt = merge({}, marked.defaults, opt || {});
 | 
						|
 | 
						|
    var highlight = opt.highlight
 | 
						|
      , tokens
 | 
						|
      , pending
 | 
						|
      , i = 0;
 | 
						|
 | 
						|
    htmlStashCounter = 0;
 | 
						|
    htmlStash = [];
 | 
						|
 | 
						|
    try {
 | 
						|
      tokens = Lexer.lex(src, opt)
 | 
						|
    } catch (e) {
 | 
						|
      return callback(e);
 | 
						|
    }
 | 
						|
 | 
						|
    pending = tokens.length;
 | 
						|
 | 
						|
    var done = function(err) {
 | 
						|
      if (err) {
 | 
						|
        opt.highlight = highlight;
 | 
						|
        return callback(err);
 | 
						|
      }
 | 
						|
 | 
						|
      var out;
 | 
						|
 | 
						|
      try {
 | 
						|
        out = Parser.parse(tokens, opt);
 | 
						|
      } catch (e) {
 | 
						|
        err = e;
 | 
						|
      }
 | 
						|
 | 
						|
      opt.highlight = highlight;
 | 
						|
 | 
						|
      return err
 | 
						|
        ? callback(err)
 | 
						|
        : callback(null, out);
 | 
						|
    };
 | 
						|
 | 
						|
    if (!highlight || highlight.length < 3) {
 | 
						|
      return done();
 | 
						|
    }
 | 
						|
 | 
						|
    delete opt.highlight;
 | 
						|
 | 
						|
    if (!pending) return done();
 | 
						|
 | 
						|
    for (; i < tokens.length; i++) {
 | 
						|
      (function(token) {
 | 
						|
        if (token.type !== 'code') {
 | 
						|
          return --pending || done();
 | 
						|
        }
 | 
						|
        return highlight(token.text, token.lang, function(err, code) {
 | 
						|
          if (err) return done(err);
 | 
						|
          if (code == null || code === token.text) {
 | 
						|
            return --pending || done();
 | 
						|
          }
 | 
						|
          token.text = code;
 | 
						|
          token.escaped = true;
 | 
						|
          --pending || done();
 | 
						|
        });
 | 
						|
      })(tokens[i]);
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  try {
 | 
						|
    if (opt) opt = merge({}, marked.defaults, opt);
 | 
						|
    htmlStashCounter = 0;
 | 
						|
    htmlStash = [];
 | 
						|
    for (var i = 0; i < opt.preprocessors.length; i++) {
 | 
						|
      src = opt.preprocessors[i](src);
 | 
						|
    }
 | 
						|
    return Parser.parse(Lexer.lex(src, opt), opt);
 | 
						|
  } catch (e) {
 | 
						|
    e.message += "\nPlease report this to https://zulip.com/development-community/";
 | 
						|
    if ((opt || marked.defaults).silent) {
 | 
						|
      return '<p>An error occurred:</p><pre>'
 | 
						|
        + escape(e.message + '', true)
 | 
						|
        + '</pre>';
 | 
						|
    }
 | 
						|
    throw e;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Options
 | 
						|
 */
 | 
						|
 | 
						|
marked.options =
 | 
						|
marked.setOptions = function(opt) {
 | 
						|
  merge(marked.defaults, opt);
 | 
						|
  return marked;
 | 
						|
};
 | 
						|
 | 
						|
marked.defaults = {
 | 
						|
  gfm: true,
 | 
						|
  emoji: false,
 | 
						|
  unicodeemoji: false,
 | 
						|
  timestamp: true,
 | 
						|
  tables: true,
 | 
						|
  breaks: false,
 | 
						|
  pedantic: false,
 | 
						|
  sanitize: false,
 | 
						|
  sanitizer: null,
 | 
						|
  mangle: true,
 | 
						|
  smartLists: false,
 | 
						|
  silent: false,
 | 
						|
  highlight: null,
 | 
						|
  langPrefix: 'lang-',
 | 
						|
  smartypants: false,
 | 
						|
  headerPrefix: '',
 | 
						|
  renderer: new Renderer,
 | 
						|
  preprocessors: [],
 | 
						|
  xhtml: false
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Expose
 | 
						|
 */
 | 
						|
 | 
						|
marked.Parser = Parser;
 | 
						|
marked.parser = Parser.parse;
 | 
						|
 | 
						|
marked.Renderer = Renderer;
 | 
						|
 | 
						|
marked.Lexer = Lexer;
 | 
						|
marked.lexer = Lexer.lex;
 | 
						|
 | 
						|
marked.InlineLexer = InlineLexer;
 | 
						|
marked.inlineLexer = InlineLexer.output;
 | 
						|
 | 
						|
marked.parse = marked;
 | 
						|
 | 
						|
marked.stashHtml = stashHtml;
 | 
						|
 | 
						|
if (typeof module !== 'undefined' && typeof exports === 'object') {
 | 
						|
  module.exports = marked;
 | 
						|
} else if (typeof define === 'function' && define.amd) {
 | 
						|
  define(function() { return marked; });
 | 
						|
} else {
 | 
						|
  this.marked = marked;
 | 
						|
}
 | 
						|
 | 
						|
}).call(function() {
 | 
						|
  return this || (typeof window !== 'undefined' ? window : global);
 | 
						|
}());
 |