text: 'use strict';
  
  exports.__esModule = true;
  exports.start = exports.run = exports.TOKENS = exports.State = undefined;
  
  var _state = require('./state');
  
  var _multi = require('./tokens/multi');
  
  var MULTI_TOKENS = _interopRequireWildcard(_multi);
  
  var _text = require('./tokens/text');
  
  function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
  
  /**
  	Not exactly parser, more like the second-stage scanner (although we can
  	theoretically hotswap the code here with a real parser in the future... but
  	for a little URL-finding utility abstract syntax trees may be a little
  	overkill).
  
  	URL format: http://en.wikipedia.org/wiki/URI_scheme
  	Email format: http://en.wikipedia.org/wiki/Email_address (links to RFC in
  	reference)
  
  	@module linkify
  	@submodule parser
  	@main parser
  */
  
  var makeState = function makeState(tokenClass) {
  	return new _state.TokenState(tokenClass);
  };
  
  // The universal starting state.
  var S_START = makeState();
  
  // Intermediate states for URLs. Note that domains that begin with a protocol
  // are treated slighly differently from those that don't.
  var S_PROTOCOL = makeState(); // e.g., 'http:'
  var S_MAILTO = makeState(); // 'mailto:'
  var S_PROTOCOL_SLASH = makeState(); // e.g., '/', 'http:/''
  var S_PROTOCOL_SLASH_SLASH = makeState(); // e.g., '//', 'http://'
  var S_DOMAIN = makeState(); // parsed string ends with a potential domain name (A)
  var S_DOMAIN_DOT = makeState(); // (A) domain followed by DOT
  var S_TLD = makeState(_multi.URL); // (A) Simplest possible URL with no query string
  var S_TLD_COLON = makeState(); // (A) URL followed by colon (potential port number here)
  var S_TLD_PORT = makeState(_multi.URL); // TLD followed by a port number
  var S_URL = makeState(_multi.URL); // Long URL with optional port and maybe query string
  var S_URL_NON_ACCEPTING = makeState(); // URL followed by some symbols (will not be part of the final URL)
  var S_URL_OPENBRACE = makeState(); // URL followed by {
  var S_URL_OPENBRACKET = makeState(); // URL followed by [
  var S_URL_OPENANGLEBRACKET = makeState(); // URL followed by <
  var S_URL_OPENPAREN = makeState(); // URL followed by (
  var S_URL_OPENBRACE_Q = makeState(_multi.URL); // URL followed by { and some symbols that the URL can end it
  var S_URL_OPENBRACKET_Q = makeState(_multi.URL); // URL followed by [ and some symbols that the URL can end it
  var S_URL_OPENANGLEBRACKET_Q = makeState(_multi.URL); // URL followed by < and some symbols that the URL can end it
  var S_URL_OPENPAREN_Q = makeState(_multi.URL); // URL followed by ( and some symbols that the URL can end it
  var S_URL_OPENBRACE_SYMS = makeState(); // S_URL_OPENBRACE_Q followed by some symbols it cannot end it
  var S_URL_OPENBRACKET_SYMS = makeState(); // S_URL_OPENBRACKET_Q followed by some symbols it cannot end it
  var S_URL_OPENANGLEBRACKET_SYMS = makeState(); // S_URL_OPENANGLEBRACKET_Q followed by some symbols it cannot end it
  var S_URL_OPENPAREN_SYMS = makeState(); // S_URL_OPENPAREN_Q followed by some symbols it cannot end it
  var S_EMAIL_DOMAIN = makeState(); // parsed string starts with local email info + @ with a potential domain name (C)
  var S_EMAIL_DOMAIN_DOT = makeState(); // (C) domain followed by DOT
  var S_EMAIL = makeState(_multi.EMAIL); // (C) Possible email address (could have more tlds)
  var S_EMAIL_COLON = makeState(); // (C) URL followed by colon (potential port number here)
  var S_EMAIL_PORT = makeState(_multi.EMAIL); // (C) Email address with a port
  var S_MAILTO_EMAIL = makeState(_multi.MAILTOEMAIL); // Email that begins with the mailto prefix (D)
  var S_MAILTO_EMAIL_NON_ACCEPTING = makeState(); // (D) Followed by some non-query string chars
  var S_LOCALPART = makeState(); // Local part of the email address
  var S_LOCALPART_AT = makeState(); // Local part of the email address plus @
  var S_LOCALPART_DOT = makeState(); // Local part of the email address plus '.' (localpart cannot end in .)
  var S_NL = makeState(_multi.NL); // single new line
  
  // Make path from start to protocol (with '//')
  S_START.on(_text.NL, S_NL).on(_text.PROTOCOL, S_PROTOCOL).on(_text.MAILTO, S_MAILTO).on(_text.SLASH, S_PROTOCOL_SLASH);
  
  S_PROTOCOL.on(_text.SLASH, S_PROTOCOL_SLASH);
  S_PROTOCOL_SLASH.on(_text.SLASH, S_PROTOCOL_SLASH_SLASH);
  
  // The very first potential domain name
  S_START.on(_text.TLD, S_DOMAIN).on(_text.DOMAIN, S_DOMAIN).on(_text.LOCALHOST, S_TLD).on(_text.NUM, S_DOMAIN);
  
  // Force URL for protocol followed by anything sane
  S_PROTOCOL_SLASH_SLASH.on(_text.TLD, S_URL).on(_text.DOMAIN, S_URL).on(_text.NUM, S_URL).on(_text.LOCALHOST, S_URL);
  
  // Account for dots and hyphens
  // hyphens are usually parts of domain names
  S_DOMAIN.on(_text.DOT, S_DOMAIN_DOT);
  S_EMAIL_DOMAIN.on(_text.DOT, S_EMAIL_DOMAIN_DOT);
  
  // Hyphen can jump back to a domain name
  
  // After the first domain and a dot, we can find either a URL or another domain
  S_DOMAIN_DOT.on(_text.TLD, S_TLD).on(_text.DOMAIN, S_DOMAIN).on(_text.NUM, S_DOMAIN).on(_text.LOCALHOST, S_DOMAIN);
  
  S_EMAIL_DOMAIN_DOT.on(_text.TLD, S_EMAIL).on(_text.DOMAIN, S_EMAIL_DOMAIN).on(_text.NUM, S_EMAIL_DOMAIN).on(_text.LOCALHOST, S_EMAIL_DOMAIN);
  
  // S_TLD accepts! But the URL could be longer, try to find a match greedily
  // The `run` function should be able to "rollback" to the accepting state
  S_TLD.on(_text.DOT, S_DOMAIN_DOT);
  S_EMAIL.on(_text.DOT, S_EMAIL_DOMAIN_DOT);
  
  // Become real URLs after `SLASH` or `COLON NUM SLASH`
  // Here PSS and non-PSS converge
  S_TLD.on(_text.COLON, S_TLD_COLON).on(_text.SLASH, S_URL);
  S_TLD_COLON.on(_text.NUM, S_TLD_PORT);
  S_TLD_PORT.on(_text.SLASH, S_URL);
  S_EMAIL.on(_text.COLON, S_EMAIL_COLON);
  S_EMAIL_COLON.on(_text.NUM, S_EMAIL_PORT);
  
  // Types of characters the URL can definitely end in
  var qsAccepting = [_text.DOMAIN, _text.AT, _text.LOCALHOST, _text.NUM, _text.PLUS, _text.POUND, _text.PROTOCOL, _text.SLASH, _text.TLD, _text.UNDERSCORE, _text.SYM, _text.AMPERSAND];
  
  // Types of tokens that can follow a URL and be part of the query string
  // but cannot be the very last characters
  // Characters that cannot appear in the URL at all should be excluded
  var qsNonAccepting = [_text.COLON, _text.DOT, _text.QUERY, _text.PUNCTUATION, _text.CLOSEBRACE, _text.CLOSEBRACKET, _text.CLOSEANGLEBRACKET, _text.CLOSEPAREN, _text.OPENBRACE, _text.OPENBRACKET, _text.OPENANGLEBRACKET, _text.OPENPAREN];
  
  // These states are responsible primarily for determining whether or not to
  // include the final round bracket.
  
  // URL, followed by an opening bracket
  S_URL.on(_text.OPENBRACE, S_URL_OPENBRACE).on(_text.OPENBRACKET, S_URL_OPENBRACKET).on(_text.OPENANGLEBRACKET, S_URL_OPENANGLEBRACKET).on(_text.OPENPAREN, S_URL_OPENPAREN);
  
  // URL with extra symbols at the end, followed by an opening bracket
  S_URL_NON_ACCEPTING.on(_text.OPENBRACE, S_URL_OPENBRACE).on(_text.OPENBRACKET, S_URL_OPENBRACKET).on(_text.OPENANGLEBRACKET, S_URL_OPENANGLEBRACKET).on(_text.OPENPAREN, S_URL_OPENPAREN);
  
  // Closing bracket component. This character WILL be included in the URL
  S_URL_OPENBRACE.on(_text.CLOSEBRACE, S_URL);
  S_URL_OPENBRACKET.on(_text.CLOSEBRACKET, S_URL);
  S_URL_OPENANGLEBRACKET.on(_text.CLOSEANGLEBRACKET, S_URL);
  S_URL_OPENPAREN.on(_text.CLOSEPAREN, S_URL);
  S_URL_OPENBRACE_Q.on(_text.CLOSEBRACE, S_URL);
  S_URL_OPENBRACKET_Q.on(_text.CLOSEBRACKET, S_URL);
  S_URL_OPENANGLEBRACKET_Q.on(_text.CLOSEANGLEBRACKET, S_URL);
  S_URL_OPENPAREN_Q.on(_text.CLOSEPAREN, S_URL);
  S_URL_OPENBRACE_SYMS.on(_text.CLOSEBRACE, S_URL);
  S_URL_OPENBRACKET_SYMS.on(_text.CLOSEBRACKET, S_URL);
  S_URL_OPENANGLEBRACKET_SYMS.on(_text.CLOSEANGLEBRACKET, S_URL);
  S_URL_OPENPAREN_SYMS.on(_text.CLOSEPAREN, S_URL);
  
  // URL that beings with an opening bracket, followed by a symbols.
  // Note that the final state can still be `S_URL_OPENBRACE_Q` (if the URL only
  // has a single opening bracket for some reason).
  S_URL_OPENBRACE.on(qsAccepting, S_URL_OPENBRACE_Q);
  S_URL_OPENBRACKET.on(qsAccepting, S_URL_OPENBRACKET_Q);
  S_URL_OPENANGLEBRACKET.on(qsAccepting, S_URL_OPENANGLEBRACKET_Q);
  S_URL_OPENPAREN.on(qsAccepting, S_URL_OPENPAREN_Q);
  S_URL_OPENBRACE.on(qsNonAccepting, S_URL_OPENBRACE_SYMS);
  S_URL_OPENBRACKET.on(qsNonAccepting, S_URL_OPENBRACKET_SYMS);
  S_URL_OPENANGLEBRACKET.on(qsNonAccepting, S_URL_OPENANGLEBRACKET_SYMS);
  S_URL_OPENPAREN.on(qsNonAccepting, S_URL_OPENPAREN_SYMS);
  
  // URL that begins with an opening bracket, followed by some symbols
  S_URL_OPENBRACE_Q.on(qsAccepting, S_URL_OPENBRACE_Q);
  S_URL_OPENBRACKET_Q.on(qsAccepting, S_URL_OPENBRACKET_Q);
  S_URL_OPENANGLEBRACKET_Q.on(qsAccepting, S_URL_OPENANGLEBRACKET_Q);
  S_URL_OPENPAREN_Q.on(qsAccepting, S_URL_OPENPAREN_Q);
  S_URL_OPENBRACE_Q.on(qsNonAccepting, S_URL_OPENBRACE_Q);
  S_URL_OPENBRACKET_Q.on(qsNonAccepting, S_URL_OPENBRACKET_Q);
  S_URL_OPENANGLEBRACKET_Q.on(qsNonAccepting, S_URL_OPENANGLEBRACKET_Q);
  S_URL_OPENPAREN_Q.on(qsNonAccepting, S_URL_OPENPAREN_Q);
  
  S_URL_OPENBRACE_SYMS.on(qsAccepting, S_URL_OPENBRACE_Q);
  S_URL_OPENBRACKET_SYMS.on(qsAccepting, S_URL_OPENBRACKET_Q);
  S_URL_OPENANGLEBRACKET_SYMS.on(qsAccepting, S_URL_OPENANGLEBRACKET_Q);
  S_URL_OPENPAREN_SYMS.on(qsAccepting, S_URL_OPENPAREN_Q);
  S_URL_OPENBRACE_SYMS.on(qsNonAccepting, S_URL_OPENBRACE_SYMS);
  S_URL_OPENBRACKET_SYMS.on(qsNonAccepting, S_URL_OPENBRACKET_SYMS);
  S_URL_OPENANGLEBRACKET_SYMS.on(qsNonAccepting, S_URL_OPENANGLEBRACKET_SYMS);
  S_URL_OPENPAREN_SYMS.on(qsNonAccepting, S_URL_OPENPAREN_SYMS);
  
  // Account for the query string
  S_URL.on(qsAccepting, S_URL);
  S_URL_NON_ACCEPTING.on(qsAccepting, S_URL);
  
  S_URL.on(qsNonAccepting, S_URL_NON_ACCEPTING);
  S_URL_NON_ACCEPTING.on(qsNonAccepting, S_URL_NON_ACCEPTING);
  
  // Email address-specific state definitions
  // Note: We are not allowing '/' in email addresses since this would interfere
  // with real URLs
  
  // For addresses with the mailto prefix
  // 'mailto:' followed by anything sane is a valid email
  S_MAILTO.on(_text.TLD, S_MAILTO_EMAIL).on(_text.DOMAIN, S_MAILTO_EMAIL).on(_text.NUM, S_MAILTO_EMAIL).on(_text.LOCALHOST, S_MAILTO_EMAIL);
  
  // Greedily get more potential valid email values
  S_MAILTO_EMAIL.on(qsAccepting, S_MAILTO_EMAIL).on(qsNonAccepting, S_MAILTO_EMAIL_NON_ACCEPTING);
  S_MAILTO_EMAIL_NON_ACCEPTING.on(qsAccepting, S_MAILTO_EMAIL).on(qsNonAccepting, S_MAILTO_EMAIL_NON_ACCEPTING);
  
  // For addresses without the mailto prefix
  // Tokens allowed in the localpart of the email
  var localpartAccepting = [_text.DOMAIN, _text.NUM, _text.PLUS, _text.POUND, _text.QUERY, _text.UNDERSCORE, _text.SYM, _text.AMPERSAND, _text.TLD];
  
  // Some of the tokens in `localpartAccepting` are already accounted for here and
  // will not be overwritten (don't worry)
  S_DOMAIN.on(localpartAccepting, S_LOCALPART).on(_text.AT, S_LOCALPART_AT);
  S_TLD.on(localpartAccepting, S_LOCALPART).on(_text.AT, S_LOCALPART_AT);
  S_DOMAIN_DOT.on(localpartAccepting, S_LOCALPART);
  
  // Okay we're on a localpart. Now what?
  // TODO: IP addresses and what if the email starts with numbers?
  S_LOCALPART.on(localpartAccepting, S_LOCALPART).on(_text.AT, S_LOCALPART_AT) // close to an email address now
  .on(_text.DOT, S_LOCALPART_DOT);
  S_LOCALPART_DOT.on(localpartAccepting, S_LOCALPART);
  S_LOCALPART_AT.on(_text.TLD, S_EMAIL_DOMAIN).on(_text.DOMAIN, S_EMAIL_DOMAIN).on(_text.LOCALHOST, S_EMAIL);
  // States following `@` defined above
  
  var run = function run(tokens) {
  	var len = tokens.length;
  	var cursor = 0;
  	var multis = [];
  	var textTokens = [];
  
  	while (cursor < len) {
  		var state = S_START;
  		var secondState = null;
  		var nextState = null;
  		var multiLength = 0;
  		var latestAccepting = null;
  		var sinceAccepts = -1;
  
  		while (cursor < len && !(secondState = state.next(tokens[cursor]))) {
  			// Starting tokens with nowhere to jump to.
  			// Consider these to be just plain text
  			textTokens.push(tokens[cursor++]);
  		}
  
  		while (cursor < len && (nextState = secondState || state.next(tokens[cursor]))) {
  
  			// Get the next state
  			secondState = null;
  			state = nextState;
  
  			// Keep track of the latest accepting state
  			if (state.accepts()) {
  				sinceAccepts = 0;
  				latestAccepting = state;
  			} else if (sinceAccepts >= 0) {
  				sinceAccepts++;
  			}
  
  			cursor++;
  			multiLength++;
  		}
  
  		if (sinceAccepts < 0) {
  
  			// No accepting state was found, part of a regular text token
  			// Add all the tokens we looked at to the text tokens array
  			for (var i = cursor - multiLength; i < cursor; i++) {
  				textTokens.push(tokens[i]);
  			}
  		} else {
  
  			// Accepting state!
  
  			// First close off the textTokens (if available)
  			if (textTokens.length > 0) {
  				multis.push(new _multi.TEXT(textTokens));
  				textTokens = [];
  			}
  
  			// Roll back to the latest accepting state
  			cursor -= sinceAccepts;
  			multiLength -= sinceAccepts;
  
  			// Create a new multitoken
  			var MULTI = latestAccepting.emit();
  			multis.push(new MULTI(tokens.slice(cursor - multiLength, cursor)));
  		}
  	}
  
  	// Finally close off the textTokens (if available)
  	if (textTokens.length > 0) {
  		multis.push(new _multi.TEXT(textTokens));
  	}
  
  	return multis;
  };
  
  exports.State = _state.TokenState;
  exports.TOKENS = MULTI_TOKENS;
  exports.run = run;
  exports.start = S_START;