123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- // 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 if 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);
- };
|