500 lines
14 KiB
JavaScript
500 lines
14 KiB
JavaScript
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview Provides functions to parse and manipulate email addresses.
|
|
*
|
|
*/
|
|
|
|
goog.provide('goog.format.EmailAddress');
|
|
|
|
goog.require('goog.string');
|
|
|
|
|
|
|
|
/**
|
|
* Formats an email address string for display, and allows for extraction of
|
|
* the individual components of the address.
|
|
* @param {string=} opt_address The email address.
|
|
* @param {string=} opt_name The name associated with the email address.
|
|
* @constructor
|
|
*/
|
|
goog.format.EmailAddress = function(opt_address, opt_name) {
|
|
/**
|
|
* The name or personal string associated with the address.
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
this.name_ = opt_name || '';
|
|
|
|
/**
|
|
* The email address.
|
|
* @type {string}
|
|
* @protected
|
|
*/
|
|
this.address = opt_address || '';
|
|
};
|
|
|
|
|
|
/**
|
|
* Match string for opening tokens.
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.OPENERS_ = '"<([';
|
|
|
|
|
|
/**
|
|
* Match string for closing tokens.
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.CLOSERS_ = '">)]';
|
|
|
|
|
|
/**
|
|
* Match string for characters that require display names to be quoted and are
|
|
* not address separators.
|
|
* @type {string}
|
|
* @const
|
|
* @package
|
|
*/
|
|
goog.format.EmailAddress.SPECIAL_CHARS = '()<>@:\\\".[]';
|
|
|
|
|
|
/**
|
|
* Match string for address separators.
|
|
* @type {string}
|
|
* @const
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.ADDRESS_SEPARATORS_ = ',;';
|
|
|
|
|
|
/**
|
|
* Match string for characters that, when in a display name, require it to be
|
|
* quoted.
|
|
* @type {string}
|
|
* @const
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.CHARS_REQUIRE_QUOTES_ =
|
|
goog.format.EmailAddress.SPECIAL_CHARS +
|
|
goog.format.EmailAddress.ADDRESS_SEPARATORS_;
|
|
|
|
|
|
/**
|
|
* A RegExp to match all double quotes. Used in cleanAddress().
|
|
* @type {RegExp}
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.ALL_DOUBLE_QUOTES_ = /\"/g;
|
|
|
|
|
|
/**
|
|
* A RegExp to match escaped double quotes. Used in parse().
|
|
* @type {RegExp}
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.ESCAPED_DOUBLE_QUOTES_ = /\\\"/g;
|
|
|
|
|
|
/**
|
|
* A RegExp to match all backslashes. Used in cleanAddress().
|
|
* @type {RegExp}
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.ALL_BACKSLASHES_ = /\\/g;
|
|
|
|
|
|
/**
|
|
* A RegExp to match escaped backslashes. Used in parse().
|
|
* @type {RegExp}
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.ESCAPED_BACKSLASHES_ = /\\\\/g;
|
|
|
|
|
|
/**
|
|
* A string representing the RegExp for the local part of an email address.
|
|
* @private {string}
|
|
*/
|
|
goog.format.EmailAddress.LOCAL_PART_REGEXP_STR_ =
|
|
'[+a-zA-Z0-9_.!#$%&\'*\\/=?^`{|}~-]+';
|
|
|
|
|
|
/**
|
|
* A string representing the RegExp for the domain part of an email address.
|
|
* @private {string}
|
|
*/
|
|
goog.format.EmailAddress.DOMAIN_PART_REGEXP_STR_ =
|
|
'([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9]{2,63}';
|
|
|
|
|
|
/**
|
|
* A RegExp to match the local part of an email address.
|
|
* @private {!RegExp}
|
|
*/
|
|
goog.format.EmailAddress.LOCAL_PART_ =
|
|
new RegExp('^' + goog.format.EmailAddress.LOCAL_PART_REGEXP_STR_ + '$');
|
|
|
|
|
|
/**
|
|
* A RegExp to match the domain part of an email address.
|
|
* @private {!RegExp}
|
|
*/
|
|
goog.format.EmailAddress.DOMAIN_PART_ =
|
|
new RegExp('^' + goog.format.EmailAddress.DOMAIN_PART_REGEXP_STR_ + '$');
|
|
|
|
|
|
/**
|
|
* A RegExp to match an email address.
|
|
* @private {!RegExp}
|
|
*/
|
|
goog.format.EmailAddress.EMAIL_ADDRESS_ =
|
|
new RegExp('^' + goog.format.EmailAddress.LOCAL_PART_REGEXP_STR_ + '@' +
|
|
goog.format.EmailAddress.DOMAIN_PART_REGEXP_STR_ + '$');
|
|
|
|
|
|
/**
|
|
* Get the name associated with the email address.
|
|
* @return {string} The name or personal portion of the address.
|
|
* @final
|
|
*/
|
|
goog.format.EmailAddress.prototype.getName = function() {
|
|
return this.name_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the email address.
|
|
* @return {string} The email address.
|
|
* @final
|
|
*/
|
|
goog.format.EmailAddress.prototype.getAddress = function() {
|
|
return this.address;
|
|
};
|
|
|
|
|
|
/**
|
|
* Set the name associated with the email address.
|
|
* @param {string} name The name to associate.
|
|
* @final
|
|
*/
|
|
goog.format.EmailAddress.prototype.setName = function(name) {
|
|
this.name_ = name;
|
|
};
|
|
|
|
|
|
/**
|
|
* Set the email address.
|
|
* @param {string} address The email address.
|
|
* @final
|
|
*/
|
|
goog.format.EmailAddress.prototype.setAddress = function(address) {
|
|
this.address = address;
|
|
};
|
|
|
|
|
|
/**
|
|
* Return the address in a standard format:
|
|
* - remove extra spaces.
|
|
* - Surround name with quotes if it contains special characters.
|
|
* @return {string} The cleaned address.
|
|
* @override
|
|
*/
|
|
goog.format.EmailAddress.prototype.toString = function() {
|
|
return this.toStringInternal(
|
|
goog.format.EmailAddress.CHARS_REQUIRE_QUOTES_);
|
|
};
|
|
|
|
|
|
/**
|
|
* Check if a display name requires quoting.
|
|
* @param {string} name The display name
|
|
* @param {string} specialChars String that contains the characters that require
|
|
* the display name to be quoted. This may change based in whereas we are
|
|
* in EAI context or not.
|
|
* @return {boolean}
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.isQuoteNeeded_ = function(name, specialChars) {
|
|
for (var i = 0; i < specialChars.length; i++) {
|
|
var specialChar = specialChars[i];
|
|
if (goog.string.contains(name, specialChar)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
|
|
/**
|
|
* Return the address in a standard format:
|
|
* - remove extra spaces.
|
|
* - Surround name with quotes if it contains special characters.
|
|
* @param {string} specialChars String that contains the characters that require
|
|
* the display name to be quoted.
|
|
* @return {string} The cleaned address.
|
|
* @protected
|
|
*/
|
|
goog.format.EmailAddress.prototype.toStringInternal = function(specialChars) {
|
|
var name = this.getName();
|
|
|
|
// We intentionally remove double quotes in the name because escaping
|
|
// them to \" looks ugly.
|
|
name = name.replace(goog.format.EmailAddress.ALL_DOUBLE_QUOTES_, '');
|
|
|
|
// If the name has special characters, we need to quote it and escape \'s.
|
|
if (goog.format.EmailAddress.isQuoteNeeded_(name, specialChars)) {
|
|
name = '"' +
|
|
name.replace(goog.format.EmailAddress.ALL_BACKSLASHES_, '\\\\') + '"';
|
|
}
|
|
|
|
if (name == '') {
|
|
return this.address;
|
|
}
|
|
if (this.address == '') {
|
|
return name;
|
|
}
|
|
return name + ' <' + this.address + '>';
|
|
};
|
|
|
|
|
|
/**
|
|
* Determines is the current object is a valid email address.
|
|
* @return {boolean} Whether the email address is valid.
|
|
*/
|
|
goog.format.EmailAddress.prototype.isValid = function() {
|
|
return goog.format.EmailAddress.isValidAddrSpec(this.address);
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks if the provided string is a valid email address. Supports both
|
|
* simple email addresses (address specs) and addresses that contain display
|
|
* names.
|
|
* @param {string} str The email address to check.
|
|
* @return {boolean} Whether the provided string is a valid address.
|
|
*/
|
|
goog.format.EmailAddress.isValidAddress = function(str) {
|
|
return goog.format.EmailAddress.parse(str).isValid();
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks if the provided string is a valid address spec (local@domain.com).
|
|
* @param {string} str The email address to check.
|
|
* @return {boolean} Whether the provided string is a valid address spec.
|
|
*/
|
|
goog.format.EmailAddress.isValidAddrSpec = function(str) {
|
|
// This is a fairly naive implementation, but it covers 99% of use cases.
|
|
// For more details, see http://en.wikipedia.org/wiki/Email_address#Syntax
|
|
return goog.format.EmailAddress.EMAIL_ADDRESS_.test(str);
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks if the provided string is a valid local part (part before the '@') of
|
|
* an email address.
|
|
* @param {string} str The local part to check.
|
|
* @return {boolean} Whether the provided string is a valid local part.
|
|
*/
|
|
goog.format.EmailAddress.isValidLocalPartSpec = function(str) {
|
|
return goog.format.EmailAddress.LOCAL_PART_.test(str);
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks if the provided string is a valid domain part (part after the '@') of
|
|
* an email address.
|
|
* @param {string} str The domain part to check.
|
|
* @return {boolean} Whether the provided string is a valid domain part.
|
|
*/
|
|
goog.format.EmailAddress.isValidDomainPartSpec = function(str) {
|
|
return goog.format.EmailAddress.DOMAIN_PART_.test(str);
|
|
};
|
|
|
|
|
|
/**
|
|
* Parses an email address of the form "name" <address> ("name" is
|
|
* optional) into an email address.
|
|
* @param {string} addr The address string.
|
|
* @param {function(new: goog.format.EmailAddress, string=,string=)} ctor
|
|
* EmailAddress constructor to instantiate the output address.
|
|
* @return {!goog.format.EmailAddress} The parsed address.
|
|
* @protected
|
|
*/
|
|
goog.format.EmailAddress.parseInternal = function(addr, ctor) {
|
|
// TODO(ecattell): Strip bidi markers.
|
|
var name = '';
|
|
var address = '';
|
|
for (var i = 0; i < addr.length;) {
|
|
var token = goog.format.EmailAddress.getToken_(addr, i);
|
|
if (token.charAt(0) == '<' && token.indexOf('>') != -1) {
|
|
var end = token.indexOf('>');
|
|
address = token.substring(1, end);
|
|
} else if (address == '') {
|
|
name += token;
|
|
}
|
|
i += token.length;
|
|
}
|
|
|
|
// Check if it's a simple email address of the form "jlim@google.com".
|
|
if (address == '' && name.indexOf('@') != -1) {
|
|
address = name;
|
|
name = '';
|
|
}
|
|
|
|
name = goog.string.collapseWhitespace(name);
|
|
name = goog.string.stripQuotes(name, '\'');
|
|
name = goog.string.stripQuotes(name, '"');
|
|
// Replace escaped quotes and slashes.
|
|
name = name.replace(goog.format.EmailAddress.ESCAPED_DOUBLE_QUOTES_, '"');
|
|
name = name.replace(goog.format.EmailAddress.ESCAPED_BACKSLASHES_, '\\');
|
|
address = goog.string.collapseWhitespace(address);
|
|
return new ctor(address, name);
|
|
};
|
|
|
|
|
|
/**
|
|
* Parses an email address of the form "name" <address> into
|
|
* an email address.
|
|
* @param {string} addr The address string.
|
|
* @return {!goog.format.EmailAddress} The parsed address.
|
|
*/
|
|
goog.format.EmailAddress.parse = function(addr) {
|
|
return goog.format.EmailAddress.parseInternal(
|
|
addr, goog.format.EmailAddress);
|
|
};
|
|
|
|
|
|
/**
|
|
* Parse a string containing email addresses of the form
|
|
* "name" <address> into an array of email addresses.
|
|
* @param {string} str The address list.
|
|
* @param {function(string)} parser The parser to employ.
|
|
* @param {function(string):boolean} separatorChecker Accepts a character and
|
|
* returns whether it should be considered an address separator.
|
|
* @return {!Array<!goog.format.EmailAddress>} The parsed emails.
|
|
* @protected
|
|
*/
|
|
goog.format.EmailAddress.parseListInternal = function(
|
|
str, parser, separatorChecker) {
|
|
var result = [];
|
|
var email = '';
|
|
var token;
|
|
|
|
// Remove non-UNIX-style newlines that would otherwise cause getToken_ to
|
|
// choke. Remove multiple consecutive whitespace characters for the same
|
|
// reason.
|
|
str = goog.string.collapseWhitespace(str);
|
|
|
|
for (var i = 0; i < str.length; ) {
|
|
token = goog.format.EmailAddress.getToken_(str, i);
|
|
if (separatorChecker(token) ||
|
|
(token == ' ' && parser(email).isValid())) {
|
|
if (!goog.string.isEmptyOrWhitespace(email)) {
|
|
result.push(parser(email));
|
|
}
|
|
email = '';
|
|
i++;
|
|
continue;
|
|
}
|
|
email += token;
|
|
i += token.length;
|
|
}
|
|
|
|
// Add the final token.
|
|
if (!goog.string.isEmptyOrWhitespace(email)) {
|
|
result.push(parser(email));
|
|
}
|
|
return result;
|
|
};
|
|
|
|
|
|
/**
|
|
* Parses a string containing email addresses of the form
|
|
* "name" <address> into an array of email addresses.
|
|
* @param {string} str The address list.
|
|
* @return {!Array<!goog.format.EmailAddress>} The parsed emails.
|
|
*/
|
|
goog.format.EmailAddress.parseList = function(str) {
|
|
return goog.format.EmailAddress.parseListInternal(
|
|
str, goog.format.EmailAddress.parse,
|
|
goog.format.EmailAddress.isAddressSeparator);
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the next token from a position in an address string.
|
|
* @param {string} str the string.
|
|
* @param {number} pos the position.
|
|
* @return {string} the token.
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.getToken_ = function(str, pos) {
|
|
var ch = str.charAt(pos);
|
|
var p = goog.format.EmailAddress.OPENERS_.indexOf(ch);
|
|
if (p == -1) {
|
|
return ch;
|
|
}
|
|
if (goog.format.EmailAddress.isEscapedDlQuote_(str, pos)) {
|
|
|
|
// If an opener is an escaped quote we do not treat it as a real opener
|
|
// and keep accumulating the token.
|
|
return ch;
|
|
}
|
|
var closerChar = goog.format.EmailAddress.CLOSERS_.charAt(p);
|
|
var endPos = str.indexOf(closerChar, pos + 1);
|
|
|
|
// If the closer is a quote we go forward skipping escaped quotes until we
|
|
// hit the real closing one.
|
|
while (endPos >= 0 &&
|
|
goog.format.EmailAddress.isEscapedDlQuote_(str, endPos)) {
|
|
endPos = str.indexOf(closerChar, endPos + 1);
|
|
}
|
|
var token = (endPos >= 0) ? str.substring(pos, endPos + 1) : ch;
|
|
return token;
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks if the character in the current position is an escaped double quote
|
|
* ( \" ).
|
|
* @param {string} str the string.
|
|
* @param {number} pos the position.
|
|
* @return {boolean} true if the char is escaped double quote.
|
|
* @private
|
|
*/
|
|
goog.format.EmailAddress.isEscapedDlQuote_ = function(str, pos) {
|
|
if (str.charAt(pos) != '"') {
|
|
return false;
|
|
}
|
|
var slashCount = 0;
|
|
for (var idx = pos - 1; idx >= 0 && str.charAt(idx) == '\\'; idx--) {
|
|
slashCount++;
|
|
}
|
|
return ((slashCount % 2) != 0);
|
|
};
|
|
|
|
|
|
/**
|
|
* @param {string} ch The character to test.
|
|
* @return {boolean} Whether the provided character is an address separator.
|
|
*/
|
|
goog.format.EmailAddress.isAddressSeparator = function(ch) {
|
|
return goog.string.contains(goog.format.EmailAddress.ADDRESS_SEPARATORS_, ch);
|
|
};
|