is-valid-email-address.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /**
  2. * isValidEmailAddress()
  3. *
  4. * Determine whether a string is a valid email address.
  5. *
  6. * This checks that:
  7. *
  8. * • The string starts with an alphanumeric
  9. * • The string contains an @ symbol
  10. * • The character preceding the @ symbol is an alphanumeric
  11. * • Has a string of alphanumeric characters following the @ symbol
  12. * • Has a '.' after those alphanumeric characters
  13. * • Has alphanumeric characters after the '.'
  14. * (More '.' characters are allowed, e.g. for '.co.uk', but must start and end with alphanumerics.)
  15. *
  16. * -----------------------------------------------------------------
  17. * @param {Ref} supposedEmailAddress
  18. * -----------------------------------------------------------------
  19. * @returns {Boolean}
  20. *
  21. */
  22. parasails.registerUtility('isValidEmailAddress', function isValidEmailAddress(supposedEmailAddress) {
  23. if(!_.isString(supposedEmailAddress)) {
  24. return false;
  25. }
  26. // Lowercase the string.
  27. supposedEmailAddress = supposedEmailAddress.toLowerCase();
  28. // Create a couple handy regular expressions for use below.
  29. // To validate whether a string starts with an alphanumeric character:
  30. var doesntStartWithAlphanumericRX = new RegExp(/^[^a-z0-9]/);
  31. // To validate whether a string ends with an alphanumeric character:
  32. var doesntEndWithAlphanumericRX = new RegExp(/[^a-z0-9]$/);
  33. // If the provided string doesn't start and end with an alphanumeric,
  34. // it has room for improvement as far as being an email address goes.
  35. if(supposedEmailAddress.match(doesntStartWithAlphanumericRX) || supposedEmailAddress.match(doesntEndWithAlphanumericRX)) {
  36. return false;
  37. }
  38. // Otherwise, the beginning and end are valid.
  39. // An email must contain an '@' symbol, so grab the chunks of the string on either side of that.
  40. var chunksOnEitherSideOfAtSymbol = supposedEmailAddress.split('@');
  41. // If there are not EXACTLY two chunks, that means the string has either no '@' symbols, or more than one.
  42. // In either case, the string fails validation because only one '@' is allowed.
  43. if(chunksOnEitherSideOfAtSymbol.length !== 2) {
  44. return false;
  45. }
  46. // Otherwise, it passes the '@' check.
  47. // Now, let's validate the first chunk (aka the part before the '@').
  48. // If it doesn't start and end with an alphanumeric, this isn't a valid email.
  49. if(chunksOnEitherSideOfAtSymbol[0].match(doesntStartWithAlphanumericRX) || chunksOnEitherSideOfAtSymbol[0].match(doesntEndWithAlphanumericRX)) {
  50. return false;
  51. }
  52. // Otherwise, make sure it doesn't contain any naughty special characters
  53. // (i.e. anything that isn't '.', '-', '_', or '+').
  54. var notAllowedInFirstPartOfEmailRX = new RegExp(/[^a-z0-9\.\-\_\+]/);
  55. if(chunksOnEitherSideOfAtSymbol[0].match(notAllowedInFirstPartOfEmailRX)) {
  56. return false;
  57. }
  58. // Otherwise, the first chunk is 100% valid.
  59. // Now, we'll validate the chunk after the '@'.
  60. // If it doesn't start and end with an alphanumeric, this isn't a valid email.
  61. if(chunksOnEitherSideOfAtSymbol[1].match(doesntStartWithAlphanumericRX) || chunksOnEitherSideOfAtSymbol[1].match(doesntEndWithAlphanumericRX)) {
  62. return false;
  63. }
  64. // Otherwise, validate that the chunk has an appropriate number of '.' characters.
  65. var chunksOnEitherSideOfPeriods = chunksOnEitherSideOfAtSymbol[1].split('.');
  66. // If there is one chunk, the '.' is missing, so the string isn't a valid email.
  67. if(chunksOnEitherSideOfPeriods.length === 1) {
  68. return false;
  69. }
  70. // We'll let there be more than one chunk, because of tlds with multiple dots like '.co.uk',
  71. // but if there are more than 3 dots (aka if there are 4+ chunks), we'll say this isn't legit.
  72. if(chunksOnEitherSideOfPeriods.length >= 4) {
  73. return false;
  74. }
  75. // Otherwise, we have a reasonable number of dots, so we'll evaluate the strings in between them.
  76. _.each(chunksOnEitherSideOfPeriods, (chunk)=>{
  77. // Validate that the characters at the beginning and end of this individual chunk are alphanumeric.
  78. if(chunk.match(doesntStartWithAlphanumericRX) || chunk.match(doesntEndWithAlphanumericRX)) {
  79. return false;
  80. }
  81. // Validate that the chunk does not contain any naughty characters.
  82. var notAllowedInChunksThatNeighborDotsRX = new RegExp(/[^a-z0-9\-]/);
  83. if(chunk.match(notAllowedInChunksThatNeighborDotsRX)) {
  84. return false;
  85. }
  86. });
  87. // Otherwise we've made it past all the checks. Overall, this string can really battle with the best of them.
  88. // Its best quality is its emaily-ness. Its stats are the best I've ever seen! No doubt about it!
  89. return true;
  90. });