text: /**
   * Ported from:
   *   https://github.com/chjj/marked/blob/49b7eaca/lib/marked.js
   * TODO:
   *   Use ES6 classes
   *   Add flow annotations
   */
  
  import {TextNode, ElementNode, FragmentNode, SELF_CLOSING} from 'synthetic-dom';
  
  const hasOwnProperty = Object.prototype.hasOwnProperty;
  
  const assign =
    Object.assign ||
    function(obj) {
      var i = 1;
      for (; i < arguments.length; i++) {
        var target = arguments[i];
        for (var key in target) {
          if (hasOwnProperty.call(target, key)) {
            obj[key] = target[key];
          }
        }
      }
      return obj;
    };
  
  const noop = function() {};
  noop.exec = noop;
  
  var defaults = {
    gfm: true,
    breaks: false,
    pedantic: false,
    smartLists: false,
    silent: false,
    langPrefix: 'lang-',
    renderer: new Renderer(),
    xhtml: false,
    atomicImages: false,
  };
  
  /**
   * Block-Level Grammar
   */
  
  var block = {
    newline: /^\n+/,
    code: /^( {4}[^\n]+\n*)+/,
    fences: noop,
    hr: /^( *[-*_]){3,} *(?:\n+|$)/,
    heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
    nptable: noop,
    lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
    blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
    list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
    def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
    paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|def))+)\n*/,
    text: /^[^\n]+/,
  };
  
  block.bullet = /(?:[*+-]|\d+\.)/;
  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.paragraph = replace(block.paragraph)('hr', block.hr)(
    'heading',
    block.heading,
  )('lheading', block.lheading)('blockquote', block.blockquote)(
    'def',
    block.def,
  )();
  
  /**
   * Normal Block Grammar
   */
  
  block.normal = assign({}, block);
  
  /**
   * GFM Block Grammar
   */
  
  block.gfm = assign({}, block.normal, {
    fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
    paragraph: /^/,
    heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/,
  });
  
  block.gfm.paragraph = replace(block.paragraph)(
    '(?!',
    '(?!' +
      block.gfm.fences.source.replace('\\1', '\\2') +
      '|' +
      block.list.source.replace('\\1', '\\3') +
      '|',
  )();
  
  /**
   * Block Lexer
   */
  
  function Lexer(options) {
    this.tokens = [];
    this.tokens.links = {};
    this.options = options ? assign({}, defaults, options) : assign({}, defaults);
    this.rules = block.normal;
  
    if (this.options.gfm) {
      this.rules = block.gfm;
    }
  }
  
  /**
   * Expose Block Rules
   */
  
  Lexer.rules = block;
  
  /**
   * Static Lex Method
   */
  
  Lexer.parse = function(src, options) {
    var lexer = new Lexer(options);
    return lexer.parse(src);
  };
  
  /**
   * Preprocessing
   */
  
  Lexer.prototype.parse = 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);
  };
  
  /**
   * Lexing
   */
  
  Lexer.prototype.token = function(src, top, bq) {
    var next;
    var loose;
    var cap;
    var bull;
    var b;
    var item;
    var space;
    var i;
    var l;
  
    src = src.replace(/^ +$/gm, '');
  
    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;
      }
  
      // lheading
      if ((cap = this.rules.lheading.exec(src))) {
        src = src.substring(cap[0].length);
        this.tokens.push({
          type: 'heading',
          depth: cap[2] === '=' ? 1 : 2,
          text: cap[1],
        });
        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];
  
        this.tokens.push({
          type: 'list_start',
          ordered: bull.length > 1,
        });
  
        // Get each top-level item.
        cap = cap[0].match(this.rules.item);
  
        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 (this.options.smartLists && i !== l - 1) {
            b = block.bullet.exec(cap[i + 1])[0];
            if (bull !== b && !(bull.length > 1 && b.length > 1)) {
              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;
            }
          }
  
          this.tokens.push({
            type: loose ? 'loose_item_start' : 'list_item_start',
          });
  
          // Recurse.
          this.token(item, false, bq);
  
          this.tokens.push({
            type: 'list_item_end',
          });
        }
  
        this.tokens.push({
          type: 'list_end',
        });
  
        continue;
      }
  
      // def
      if (!bq && top && (cap = this.rules.def.exec(src))) {
        src = src.substring(cap[0].length);
        this.tokens.links[cap[1].toLowerCase()] = {
          href: cap[2],
          title: cap[3],
        };
        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: /^\\([\\`*{}\[\]()#+\-.!_>])/,
    link: /^!?\[(inside)\]\(href\)/,
    reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
    nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
    strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
    em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
    code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
    br: /^ {2,}\n(?!\s*$)/,
    del: noop,
    ins: noop,
    text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/,
  };
  
  inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
  inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/;
  
  inline.link = replace(inline.link)('inside', inline._inside)(
    'href',
    inline._href,
  )();
  
  inline.reflink = replace(inline.reflink)('inside', inline._inside)();
  
  /**
   * Normal Inline Grammar
   */
  
  inline.normal = assign({}, inline);
  
  /**
   * Pedantic Inline Grammar
   */
  
  inline.pedantic = assign({}, 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 = assign({}, inline.normal, {
    escape: replace(inline.escape)('])', '~|])')(),
    del: /^~~(?=\S)([\s\S]*?\S)~~/,
    ins: /^\+\+(?=\S)([\s\S]*?\S)\+\+/,
    text: replace(inline.text)(']|', '~+]|')(),
  });
  
  /**
   * GFM + Line Breaks Inline Grammar
   */
  
  inline.breaks = assign({}, inline.gfm, {
    br: replace(inline.br)('{2,}', '*')(),
    text: replace(inline.gfm.text)('{2,}', '*')(),
  });
  
  /**
   * Inline Lexer & Compiler
   */
  
  function InlineLexer(links, options) {
    this.options = assign({}, options || 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.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.parse = function(src, links, options) {
    var inline = new InlineLexer(links, options);
    return inline.parse(src);
  };
  
  /**
   * Lexing/Compiling
   */
  
  InlineLexer.prototype.parse = function(src) {
    var out = new FragmentNode();
    var link;
    var cap;
  
    while (src) {
      // escape
      if ((cap = this.rules.escape.exec(src))) {
        src = src.substring(cap[0].length);
        out.appendChild(new TextNode(cap[1]));
        continue;
      }
  
      // link
      if ((cap = this.rules.link.exec(src))) {
        src = src.substring(cap[0].length);
        this.inLink = true;
        out.appendChild(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.appendChild(new TextNode(cap[0].charAt(0)));
          src = cap[0].substring(1) + src;
          continue;
        }
        this.inLink = true;
        out.appendChild(this.outputLink(cap, link));
        this.inLink = false;
        continue;
      }
  
      // strong
      if ((cap = this.rules.strong.exec(src))) {
        src = src.substring(cap[0].length);
        out.appendChild(this.renderer.strong(this.parse(cap[2] || cap[1])));
        continue;
      }
  
      // em
      if ((cap = this.rules.em.exec(src))) {
        src = src.substring(cap[0].length);
        out.appendChild(this.renderer.em(this.parse(cap[2] || cap[1])));
        continue;
      }
  
      // code
      if ((cap = this.rules.code.exec(src))) {
        src = src.substring(cap[0].length);
        out.appendChild(this.renderer.codespan(cap[2]));
        continue;
      }
  
      // br
      if ((cap = this.rules.br.exec(src))) {
        src = src.substring(cap[0].length);
        out.appendChild(this.renderer.br());
        continue;
      }
  
      // del (gfm)
      if ((cap = this.rules.del.exec(src))) {
        src = src.substring(cap[0].length);
        out.appendChild(this.renderer.del(this.parse(cap[1])));
        continue;
      }
  
      // ins (gfm extended)
      if ((cap = this.rules.ins.exec(src))) {
        src = src.substring(cap[0].length);
        out.appendChild(this.renderer.ins(this.parse(cap[1])));
        continue;
      }
  
      // text
      if ((cap = this.rules.text.exec(src))) {
        src = src.substring(cap[0].length);
        out.appendChild(this.renderer.text(new TextNode(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 = link.href;
    var title = link.title;
  
    return cap[0].charAt(0) !== '!'
      ? this.renderer.link(href, title, this.parse(cap[1]))
      : this.renderer.image(href, title, cap[1]);
  };
  
  /**
   * Renderer
   */
  
  function Renderer(options) {
    this.options = options || {};
  }
  
  Renderer.prototype.code = function(text, lang) {
    var codeAttrs = [];
    var preAttrs = [];
    if (lang) {
      codeAttrs.push({name: 'class', value: this.options.langPrefix + lang});
      preAttrs.push({name: 'data-language', value: lang});
    }
    var codeNode = new ElementNode('code', codeAttrs, [new TextNode(text)]);
    return new ElementNode('pre', preAttrs, [codeNode]);
  };
  
  Renderer.prototype.blockquote = function(childNode) {
    return new ElementNode('blockquote', [], [childNode]);
  };
  
  Renderer.prototype.heading = function(childNode, level) {
    return new ElementNode('h' + level, [], [childNode]);
  };
  
  Renderer.prototype.hr = function() {
    return new ElementNode('hr', [], SELF_CLOSING);
  };
  
  Renderer.prototype.list = function(childNode, isOrdered) {
    return new ElementNode(isOrdered ? 'ol' : 'ul', [], [childNode]);
  };
  
  Renderer.prototype.listitem = function(childNode) {
    return new ElementNode('li', [], [childNode]);
  };
  
  Renderer.prototype.paragraph = function(childNode) {
    return new ElementNode('p', [], [childNode]);
  };
  
  // span level renderer
  Renderer.prototype.strong = function(childNode) {
    return new ElementNode('strong', [], [childNode]);
  };
  
  Renderer.prototype.em = function(childNode) {
    return new ElementNode('em', [], [childNode]);
  };
  
  Renderer.prototype.codespan = function(text) {
    return new ElementNode('code', [], [new TextNode(text)]);
  };
  
  Renderer.prototype.br = function() {
    return new ElementNode('br', [], SELF_CLOSING);
  };
  
  Renderer.prototype.del = function(childNode) {
    return new ElementNode('del', [], [childNode]);
  };
  
  Renderer.prototype.ins = function(childNode) {
    return new ElementNode('ins', [], [childNode]);
  };
  
  Renderer.prototype.link = function(href, title, childNode) {
    var attributes = [{name: 'href', value: href}];
    if (title) {
      attributes.push({name: 'title', value: title});
    }
    return new ElementNode('a', attributes, [childNode]);
  };
  
  Renderer.prototype.image = function(href, title, alt) {
    var attributes = [{name: 'src', value: href}];
    if (title) {
      attributes.push({name: 'title', value: title});
    }
    if (alt) {
      attributes.push({name: 'alt', value: alt});
    }
    let img = new ElementNode('img', attributes, SELF_CLOSING);
    // Wrap the image in a <figure> if we want "atomic" images.
    if (this.options.atomicImages) {
      return new ElementNode('figure', [], [img]);
    } else {
      return img;
    }
  };
  
  Renderer.prototype.text = function(childNode) {
    return childNode;
  };
  
  /**
   * Parsing & Compiling
   */
  
  function Parser(options) {
    this.tokens = [];
    this.token = null;
    this.options = assign({}, options || 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);
    return parser.parse(src);
  };
  
  /**
   * Parse Loop
   */
  
  Parser.prototype.parse = function(src) {
    this.inline = new InlineLexer(src.links, this.options, this.renderer);
    this.tokens = src.slice().reverse();
  
    var out = new FragmentNode();
    while (this.next()) {
      out.appendChild(this.tok());
    }
  
    return out;
  };
  
  /**
   * 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.parse(body);
  };
  
  /**
   * Parse Current Token
   */
  
  Parser.prototype.tok = function() {
    switch (this.token.type) {
      case 'space': {
        return new TextNode('');
      }
      case 'hr': {
        return this.renderer.hr();
      }
      case 'heading': {
        return this.renderer.heading(
          this.inline.parse(this.token.text),
          this.token.depth,
        );
      }
      case 'code': {
        return this.renderer.code(this.token.text, this.token.lang);
      }
      case 'blockquote_start': {
        let body = new FragmentNode();
  
        while (this.next().type !== 'blockquote_end') {
          body.appendChild(this.tok());
        }
  
        return this.renderer.blockquote(body);
      }
      case 'list_start': {
        let body = new FragmentNode();
        var ordered = this.token.ordered;
  
        while (this.next().type !== 'list_end') {
          body.appendChild(this.tok());
        }
  
        return this.renderer.list(body, ordered);
      }
      case 'list_item_start': {
        let body = new FragmentNode();
  
        while (this.next().type !== 'list_item_end') {
          body.appendChild(
            this.token.type === 'text' ? this.parseText() : this.tok(),
          );
        }
  
        return this.renderer.listitem(body);
      }
      case 'loose_item_start': {
        let body = new FragmentNode();
  
        while (this.next().type !== 'list_item_end') {
          body.appendChild(this.tok());
        }
  
        return this.renderer.listitem(body);
      }
      case 'paragraph': {
        return this.renderer.paragraph(this.inline.parse(this.token.text));
      }
      case 'text': {
        return this.renderer.paragraph(this.parseText());
      }
    }
  };
  
  /**
   * Helpers
   */
  
  function replace(regex, options) {
    regex = regex.source;
    options = options || '';
    return function self(name, val) {
      if (!name) {
        return new RegExp(regex, options);
      }
      val = val.source || val;
      val = val.replace(/(^|[^\[])\^/g, '$1');
      regex = regex.replace(name, val);
      return self;
    };
  }
  
  const MarkdownParser = {
    parse(src, options) {
      options = assign({}, defaults, options);
      try {
        var fragment = Parser.parse(Lexer.parse(src, options), options);
      } catch (e) {
        if (options.silent) {
          fragment = new FragmentNode([
            new ElementNode('p', [], [new TextNode('An error occured:')]),
            new ElementNode('pre', [], [new TextNode(e.message)]),
          ]);
        } else {
          throw e;
        }
      }
      if (options.getAST) {
        return new ElementNode('body', [], [fragment]);
      } else {
        return fragment.toString(this.options.xhtml);
      }
    },
  };
  
  export default MarkdownParser;
  