{"version":3,"sources":["../src/matcher/email-matcher.ts"],"names":[],"mappings":"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","file":"email-matcher.js","sourcesContent":["import { Matcher } from \"./matcher\";\nimport { alphaNumericAndMarksCharsStr, domainNameCharRegex } from \"../regex-lib\";\nimport { EmailMatch } from \"../match/email-match\";\nimport { Match } from \"../match/match\";\nimport { throwUnhandledCaseError } from '../utils';\nimport { tldRegex } from \"./tld-regex\";\n\n// For debugging: search for other \"For debugging\" lines\n// import CliTable from 'cli-table';\n\n// RegExp objects which are shared by all instances of EmailMatcher. These are\n// here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is\n// called multiple times, thus instantiating EmailMatcher and its RegExp \n// objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314). \n// See descriptions of the properties where they are used for details about them\nconst localPartCharRegex = new RegExp( `[${alphaNumericAndMarksCharsStr}!#$%&'*+/=?^_\\`{|}~-]` );\nconst strictTldRegex = new RegExp( `^${tldRegex.source}$` );\n\n/**\n * @class Autolinker.matcher.Email\n * @extends Autolinker.matcher.Matcher\n *\n * Matcher to find email matches in an input string.\n *\n * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more details.\n */\nexport class EmailMatcher extends Matcher {\n\n\t/**\n\t * Valid characters that can be used in the \"local\" part of an email address,\n\t * i.e. the \"name\" part of \"name@site.com\"\n\t */\n\tprotected localPartCharRegex = localPartCharRegex;\n\n\t/**\n\t * Stricter TLD regex which adds a beginning and end check to ensure\n\t * the string is a valid TLD\n\t */\n\tprotected strictTldRegex = strictTldRegex;\n\n\n\t/**\n\t * @inheritdoc\n\t */\n\tparseMatches( text: string ) {\n\t\tconst tagBuilder = this.tagBuilder,\n\t\t\t localPartCharRegex = this.localPartCharRegex,\n\t\t\t strictTldRegex = this.strictTldRegex,\n\t\t\t matches: Match[] = [],\n\t\t\t len = text.length,\n\t\t\t noCurrentEmailMatch = new CurrentEmailMatch();\n\n\t\t// for matching a 'mailto:' prefix\n\t\tconst mailtoTransitions = {\n\t\t\t'm': 'a',\n\t\t\t'a': 'i',\n\t\t\t'i': 'l',\n\t\t\t'l': 't',\n\t\t\t't': 'o',\n\t\t\t'o': ':',\n\t\t};\n\n\t\tlet charIdx = 0,\n\t\t\tstate = State.NonEmailMatch as State,\n\t\t\tcurrentEmailMatch = noCurrentEmailMatch;\n\n\t\t// For debugging: search for other \"For debugging\" lines\n\t\t// const table = new CliTable( {\n\t\t// \thead: [ 'charIdx', 'char', 'state', 'charIdx', 'currentEmailAddress.idx', 'hasDomainDot' ]\n\t\t// } );\n\n\t\twhile( charIdx < len ) {\n\t\t\tconst char = text.charAt( charIdx );\n\n\t\t\t// For debugging: search for other \"For debugging\" lines\n\t\t\t// table.push( \n\t\t\t// \t[ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ] \n\t\t\t// );\n\n\t\t\tswitch( state ) {\n\t\t\t\tcase State.NonEmailMatch: stateNonEmailAddress( char ); break;\n\n\t\t\t\tcase State.Mailto: \n\t\t\t\t\tstateMailTo( text.charAt( charIdx - 1 ) as MailtoChar, char ); \n\t\t\t\t\tbreak;\n\t\t\t\tcase State.LocalPart: stateLocalPart( char ); break;\n\t\t\t\tcase State.LocalPartDot: stateLocalPartDot( char ); break;\n\t\t\t\tcase State.AtSign: stateAtSign( char ); break;\n\t\t\t\tcase State.DomainChar: stateDomainChar( char ); break;\n\t\t\t\tcase State.DomainHyphen: stateDomainHyphen( char ); break;\n\t\t\t\tcase State.DomainDot: stateDomainDot( char ); break;\n\t\n\t\t\t\tdefault: \n\t\t\t\t\tthrowUnhandledCaseError( state );\n\t\t\t}\n\n\t\t\t// For debugging: search for other \"For debugging\" lines\n\t\t\t// table.push( \n\t\t\t// \t[ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ] \n\t\t\t// );\n\n\t\t\tcharIdx++;\n\t\t}\n\n\t\t// Capture any valid match at the end of the string\n\t\tcaptureMatchIfValidAndReset();\n\n\t\t// For debugging: search for other \"For debugging\" lines\n\t\t//console.log( '\\n' + table.toString() );\n\t\t\n\t\treturn matches;\n\n\n\t\t// Handles the state when we're not in an email address\n\t\tfunction stateNonEmailAddress( char: string ) {\n\t\t\tif( char === 'm' ) {\n\t\t\t\tbeginEmailMatch( State.Mailto );\n\n\t\t\t} else if( localPartCharRegex.test( char ) ) {\n\t\t\t\tbeginEmailMatch();\n\n\t\t\t} else {\n\t\t\t\t// not an email address character, continue\n\t\t\t}\n\t\t}\n\n\n\t\t// Handles if we're reading a 'mailto:' prefix on the string\n\t\tfunction stateMailTo( prevChar: MailtoChar, char: string ) {\n\t\t\tif( prevChar === ':' ) {\n\t\t\t\t// We've reached the end of the 'mailto:' prefix\n\t\t\t\tif( localPartCharRegex.test( char ) ) {\n\t\t\t\t\tstate = State.LocalPart;\n\t\t\t\t\tcurrentEmailMatch = new CurrentEmailMatch( { \n\t\t\t\t\t\t...currentEmailMatch, \n\t\t\t\t\t\thasMailtoPrefix: true \n\t\t\t\t\t} );\n\n\t\t\t\t} else {\n\t\t\t\t\t// we've matched 'mailto:' but didn't get anything meaningful\n\t\t\t\t\t// immediately afterwards (for example, we encountered a \n\t\t\t\t\t// space character, or an '@' character which formed 'mailto:@'\n\t\t\t\t\tresetToNonEmailMatchState();\n\t\t\t\t}\n\n\t\t\t} else if( mailtoTransitions[ prevChar ] === char ) {\n\t\t\t\t// We're currently reading the 'mailto:' prefix, stay in\n\t\t\t\t// Mailto state\n\t\t\t\t\n\t\t\t} else if( localPartCharRegex.test( char ) ) {\n\t\t\t\t// We we're reading a prefix of 'mailto:', but encountered a\n\t\t\t\t// different character that didn't continue the prefix\n\t\t\t\tstate = State.LocalPart;\n\n\t\t\t} else if( char === '.' ) {\n\t\t\t\t// We we're reading a prefix of 'mailto:', but encountered a\n\t\t\t\t// dot character\n\t\t\t\tstate = State.LocalPartDot;\n\n\t\t\t} else if( char === '@' ) {\n\t\t\t\t// We we're reading a prefix of 'mailto:', but encountered a\n\t\t\t\t// an @ character\n\t\t\t\tstate = State.AtSign;\n\n\t\t\t} else {\n\t\t\t\t// not an email address character, return to \"NonEmailAddress\" state\n\t\t\t\tresetToNonEmailMatchState();\n\t\t\t}\n\t\t}\n\n\n\t\t// Handles the state when we're currently in the \"local part\" of an \n\t\t// email address (as opposed to the \"domain part\")\n\t\tfunction stateLocalPart( char: string ) {\n\t\t\tif( char === '.' ) {\n\t\t\t\tstate = State.LocalPartDot;\n\n\t\t\t} else if( char === '@' ) {\n\t\t\t\tstate = State.AtSign;\n\n\t\t\t} else if( localPartCharRegex.test( char ) ) {\n\t\t\t\t// stay in the \"local part\" of the email address\n\n\t\t\t} else {\n\t\t\t\t// not an email address character, return to \"NonEmailAddress\" state\n\t\t\t\tresetToNonEmailMatchState();\n\t\t\t}\n\t\t}\n\n\n\t\t// Handles the state where we've read \n\t\tfunction stateLocalPartDot( char: string ) {\n\t\t\tif( char === '.' ) {\n\t\t\t\t// We read a second '.' in a row, not a valid email address \n\t\t\t\t// local part\n\t\t\t\tresetToNonEmailMatchState();\n\n\t\t\t} else if( char === '@' ) {\n\t\t\t\t// We read the '@' character immediately after a dot ('.'), not \n\t\t\t\t// an email address\n\t\t\t\tresetToNonEmailMatchState();\n\n\t\t\t} else if( localPartCharRegex.test( char ) ) {\n\t\t\t\tstate = State.LocalPart;\n\n\t\t\t} else {\n\t\t\t\t// Anything else, not an email address\n\t\t\t\tresetToNonEmailMatchState();\n\t\t\t}\n\t\t}\n\n\n\t\tfunction stateAtSign( char: string ) {\n\t\t\tif( domainNameCharRegex.test( char ) ) {\n\t\t\t\tstate = State.DomainChar;\n\n\t\t\t} else {\n\t\t\t\t// Anything else, not an email address\n\t\t\t\tresetToNonEmailMatchState();\n\t\t\t}\n\t\t}\n\n\t\tfunction stateDomainChar( char: string ) {\n\t\t\tif( char === '.' ) {\n\t\t\t\tstate = State.DomainDot;\n\n\t\t\t} else if( char === '-' ) {\n\t\t\t\tstate = State.DomainHyphen;\n\n\t\t\t} else if( domainNameCharRegex.test( char ) ) {\n\t\t\t\t// Stay in the DomainChar state\n\n\t\t\t} else {\n\t\t\t\t// Anything else, we potentially matched if the criteria has\n\t\t\t\t// been met\n\t\t\t\tcaptureMatchIfValidAndReset();\n\t\t\t}\n\t\t}\n\n\t\tfunction stateDomainHyphen( char: string ) {\n\t\t\tif( char === '-' || char === '.' ) {\n\t\t\t\t// Not valid to have two hyphens (\"--\") or hypen+dot (\"-.\")\n\t\t\t\tcaptureMatchIfValidAndReset();\n\n\t\t\t} else if( domainNameCharRegex.test( char ) ) {\n\t\t\t\tstate = State.DomainChar;\n\n\t\t\t} else {\n\t\t\t\t// Anything else\n\t\t\t\tcaptureMatchIfValidAndReset();\n\t\t\t}\n\t\t}\n\n\t\tfunction stateDomainDot( char: string ) {\n\t\t\tif( char === '.' || char === '-' ) {\n\t\t\t\t// not valid to have two dots (\"..\") or dot+hypen (\".-\")\n\t\t\t\tcaptureMatchIfValidAndReset();\n\n\t\t\t} else if( domainNameCharRegex.test( char ) ) {\n\t\t\t\tstate = State.DomainChar;\n\n\t\t\t\t// After having read a '.' and then a valid domain character,\n\t\t\t\t// we now know that the domain part of the email is valid, and\n\t\t\t\t// we have found at least a partial EmailMatch (however, the\n\t\t\t\t// email address may have additional characters from this point)\n\t\t\t\tcurrentEmailMatch = new CurrentEmailMatch( { \n\t\t\t\t\t...currentEmailMatch, \n\t\t\t\t\thasDomainDot: true \n\t\t\t\t} );\n\n\t\t\t} else {\n\t\t\t\t// Anything else\n\t\t\t\tcaptureMatchIfValidAndReset();\n\t\t\t}\n\t\t}\n\n\n\t\tfunction beginEmailMatch( newState = State.LocalPart ) {\n\t\t\tstate = newState;\n\t\t\tcurrentEmailMatch = new CurrentEmailMatch( { idx: charIdx } );\n\t\t}\n\n\t\tfunction resetToNonEmailMatchState() {\n\t\t\tstate = State.NonEmailMatch;\n\t\t\tcurrentEmailMatch = noCurrentEmailMatch;\n\t\t}\n\n\n\n\t\t/*\n\t\t * Captures the current email address as an EmailMatch if it's valid,\n\t\t * and resets the state to read another email address.\n\t\t */\n\t\tfunction captureMatchIfValidAndReset() {\n\t\t\tif( currentEmailMatch.hasDomainDot ) { // we need at least one dot in the domain to be considered a valid email address\n\t\t\t\tlet matchedText = text.slice( currentEmailMatch.idx, charIdx );\n\n\t\t\t\t// If we read a '.' or '-' char that ended the email address\n\t\t\t\t// (valid domain name characters, but only valid email address\n\t\t\t\t// characters if they are followed by something else), strip \n\t\t\t\t// it off now\n\t\t\t\tif( /[-.]$/.test( matchedText ) ){\n\t\t\t\t\tmatchedText = matchedText.slice( 0, -1 );\n\t\t\t\t}\n\n\t\t\t\tconst emailAddress = currentEmailMatch.hasMailtoPrefix \n\t\t\t\t\t? matchedText.slice( 'mailto:'.length ) \n\t\t\t\t\t: matchedText;\n\n\t\t\t\t// if the email address has a valid TLD, add it to the list of matches\n\t\t\t\tif ( doesEmailHaveValidTld( emailAddress ) ) {\n\t\t\t\t\tmatches.push( new EmailMatch( {\n\t\t\t\t\t\ttagBuilder : tagBuilder,\n\t\t\t\t\t\tmatchedText : matchedText,\n\t\t\t\t\t\toffset : currentEmailMatch.idx,\n\t\t\t\t\t\temail : emailAddress\n\t\t\t\t\t} ) );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresetToNonEmailMatchState();\n\t\t\n\n\t\t/**\n\t\t * Determines if the given email address has a valid TLD or not\n\t\t * @param {string} emailAddress - email address\n\t\t * @return {Boolean} - true is email have valid TLD, false otherwise\n\t\t */\n\t\tfunction doesEmailHaveValidTld( emailAddress: string ) {\n\t\t\tconst emailAddressTld : string = emailAddress.split( '.' ).pop() || '';\n\t\t\tconst emailAddressNormalized = emailAddressTld.toLowerCase();\n\t\t\tconst isValidTld = strictTldRegex.test( emailAddressNormalized );\n\n\t\t\treturn isValidTld;\n\t\t}}\n\t}\n\n}\n\ntype MailtoChar = 'm' | 'a' | 'i' | 'l' | 't' | 'o' | ':';\n\nconst enum State {\n\tNonEmailMatch = 0,\n\t\n\tMailto, // if matching a 'mailto:' prefix\n\tLocalPart,\n\tLocalPartDot,\n\tAtSign,\n\tDomainChar,\n\tDomainHyphen,\n\tDomainDot\n}\n\nclass CurrentEmailMatch {\n\treadonly idx: number; // the index of the first character in the email address\n\treadonly hasMailtoPrefix: boolean;\n\treadonly hasDomainDot: boolean;\n\n\tconstructor( cfg: Partial = {} ) {\n\t\tthis.idx = cfg.idx !== undefined ? cfg.idx : -1;\n\t\tthis.hasMailtoPrefix = !!cfg.hasMailtoPrefix;\n\t\tthis.hasDomainDot = !!cfg.hasDomainDot;\n\t}\n}"]}