text: /**
   * @author Titus Wormer
   * @copyright 2015 Titus Wormer
   * @license MIT
   * @module remark:parse:tokenize:link
   * @fileoverview Tokenise a link.
   */
  
  'use strict';
  
  var has = require('has');
  var whitespace = require('is-whitespace-character');
  var locate = require('../locate/link');
  
  module.exports = link;
  link.locator = locate;
  
  var C_BACKSLASH = '\\';
  var C_BRACKET_OPEN = '[';
  var C_BRACKET_CLOSE = ']';
  var C_PAREN_OPEN = '(';
  var C_PAREN_CLOSE = ')';
  var C_LT = '<';
  var C_GT = '>';
  var C_TICK = '`';
  var C_DOUBLE_QUOTE = '"';
  var C_SINGLE_QUOTE = '\'';
  
  /* Map of characters, which can be used to mark link
   * and image titles. */
  var LINK_MARKERS = {};
  
  LINK_MARKERS[C_DOUBLE_QUOTE] = C_DOUBLE_QUOTE;
  LINK_MARKERS[C_SINGLE_QUOTE] = C_SINGLE_QUOTE;
  
  /* Map of characters, which can be used to mark link
   * and image titles in commonmark-mode. */
  var COMMONMARK_LINK_MARKERS = {};
  
  COMMONMARK_LINK_MARKERS[C_DOUBLE_QUOTE] = C_DOUBLE_QUOTE;
  COMMONMARK_LINK_MARKERS[C_SINGLE_QUOTE] = C_SINGLE_QUOTE;
  COMMONMARK_LINK_MARKERS[C_PAREN_OPEN] = C_PAREN_CLOSE;
  
  /* Tokenise a link. */
  function link(eat, value, silent) {
    var self = this;
    var subvalue = '';
    var index = 0;
    var character = value.charAt(0);
    var commonmark = self.options.commonmark;
    var gfm = self.options.gfm;
    var closed;
    var count;
    var opening;
    var beforeURL;
    var beforeTitle;
    var subqueue;
    var hasMarker;
    var markers;
    var isImage;
    var content;
    var marker;
    var length;
    var title;
    var depth;
    var queue;
    var url;
    var now;
    var exit;
    var node;
  
    /* Detect whether this is an image. */
    if (character === '!') {
      isImage = true;
      subvalue = character;
      character = value.charAt(++index);
    }
  
    /* Eat the opening. */
    if (character !== C_BRACKET_OPEN) {
      return;
    }
  
    /* Exit when this is a link and we’re already inside
     * a link. */
    if (!isImage && self.inLink) {
      return;
    }
  
    subvalue += character;
    queue = '';
    index++;
  
    /* Eat the content. */
    length = value.length;
    now = eat.now();
    depth = 0;
  
    now.column += index;
    now.offset += index;
  
    while (index < length) {
      character = value.charAt(index);
      subqueue = character;
  
      if (character === C_TICK) {
        /* Inline-code in link content. */
        count = 1;
  
        while (value.charAt(index + 1) === C_TICK) {
          subqueue += character;
          index++;
          count++;
        }
  
        if (!opening) {
          opening = count;
        } else if (count >= opening) {
          opening = 0;
        }
      } else if (character === C_BACKSLASH) {
        /* Allow brackets to be escaped. */
        index++;
        subqueue += value.charAt(index);
      /* In GFM mode, brackets in code still count.
       * In all other modes, they don’t.  This empty
       * block prevents the next statements are
       * entered. */
      } else if ((!opening || gfm) && character === C_BRACKET_OPEN) {
        depth++;
      } else if ((!opening || gfm) && character === C_BRACKET_CLOSE) {
        if (depth) {
          depth--;
        } else {
          /* Allow white-space between content and
           * url in GFM mode. */
          if (gfm) {
            while (index < length) {
              character = value.charAt(index + 1);
  
              if (!whitespace(character)) {
                break;
              }
  
              subqueue += character;
              index++;
            }
          }
  
          if (value.charAt(index + 1) !== C_PAREN_OPEN) {
            return;
          }
  
          subqueue += C_PAREN_OPEN;
          closed = true;
          index++;
  
          break;
        }
      }
  
      queue += subqueue;
      subqueue = '';
      index++;
    }
  
    /* Eat the content closing. */
    if (!closed) {
      return;
    }
  
    content = queue;
    subvalue += queue + subqueue;
    index++;
  
    /* Eat white-space. */
    while (index < length) {
      character = value.charAt(index);
  
      if (!whitespace(character)) {
        break;
      }
  
      subvalue += character;
      index++;
    }
  
    /* Eat the URL. */
    character = value.charAt(index);
    markers = commonmark ? COMMONMARK_LINK_MARKERS : LINK_MARKERS;
    queue = '';
    beforeURL = subvalue;
  
    if (character === C_LT) {
      index++;
      beforeURL += C_LT;
  
      while (index < length) {
        character = value.charAt(index);
  
        if (character === C_GT) {
          break;
        }
  
        if (commonmark && character === '\n') {
          return;
        }
  
        queue += character;
        index++;
      }
  
      if (value.charAt(index) !== C_GT) {
        return;
      }
  
      subvalue += C_LT + queue + C_GT;
      url = queue;
      index++;
    } else {
      character = null;
      subqueue = '';
  
      while (index < length) {
        character = value.charAt(index);
  
        if (subqueue && has(markers, character)) {
          break;
        }
  
        if (whitespace(character)) {
          if (commonmark) {
            break;
          }
  
          subqueue += character;
        } else {
          if (character === C_PAREN_OPEN) {
            depth++;
          } else if (character === C_PAREN_CLOSE) {
            if (depth === 0) {
              break;
            }
  
            depth--;
          }
  
          queue += subqueue;
          subqueue = '';
  
          if (character === C_BACKSLASH) {
            queue += C_BACKSLASH;
            character = value.charAt(++index);
          }
  
          queue += character;
        }
  
        index++;
      }
  
      subvalue += queue;
      url = queue;
      index = subvalue.length;
    }
  
    /* Eat white-space. */
    queue = '';
  
    while (index < length) {
      character = value.charAt(index);
  
      if (!whitespace(character)) {
        break;
      }
  
      queue += character;
      index++;
    }
  
    character = value.charAt(index);
    subvalue += queue;
  
    /* Eat the title. */
    if (queue && has(markers, character)) {
      index++;
      subvalue += character;
      queue = '';
      marker = markers[character];
      beforeTitle = subvalue;
  
      /* In commonmark-mode, things are pretty easy: the
       * marker cannot occur inside the title.
       *
       * Non-commonmark does, however, support nested
       * delimiters. */
      if (commonmark) {
        while (index < length) {
          character = value.charAt(index);
  
          if (character === marker) {
            break;
          }
  
          if (character === C_BACKSLASH) {
            queue += C_BACKSLASH;
            character = value.charAt(++index);
          }
  
          index++;
          queue += character;
        }
  
        character = value.charAt(index);
  
        if (character !== marker) {
          return;
        }
  
        title = queue;
        subvalue += queue + character;
        index++;
  
        while (index < length) {
          character = value.charAt(index);
  
          if (!whitespace(character)) {
            break;
          }
  
          subvalue += character;
          index++;
        }
      } else {
        subqueue = '';
  
        while (index < length) {
          character = value.charAt(index);
  
          if (character === marker) {
            if (hasMarker) {
              queue += marker + subqueue;
              subqueue = '';
            }
  
            hasMarker = true;
          } else if (!hasMarker) {
            queue += character;
          } else if (character === C_PAREN_CLOSE) {
            subvalue += queue + marker + subqueue;
            title = queue;
            break;
          } else if (whitespace(character)) {
            subqueue += character;
          } else {
            queue += marker + subqueue + character;
            subqueue = '';
            hasMarker = false;
          }
  
          index++;
        }
      }
    }
  
    if (value.charAt(index) !== C_PAREN_CLOSE) {
      return;
    }
  
    /* istanbul ignore if - never used (yet) */
    if (silent) {
      return true;
    }
  
    subvalue += C_PAREN_CLOSE;
  
    url = self.decode.raw(self.unescape(url), eat(beforeURL).test().end);
  
    if (title) {
      beforeTitle = eat(beforeTitle).test().end;
      title = self.decode.raw(self.unescape(title), beforeTitle);
    }
  
    node = {
      type: isImage ? 'image' : 'link',
      title: title || null,
      url: url
    };
  
    if (isImage) {
      node.alt = self.decode.raw(self.unescape(content), now) || null;
    } else {
      exit = self.enterLink();
      node.children = self.tokenizeInline(content, now);
      exit();
    }
  
    return eat(subvalue)(node);
  }
  