Path: blob/trunk/third_party/closure/goog/format/emailaddress.js
2868 views
// Copyright 2010 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Provides functions to parse and manipulate email addresses.16*17*/1819goog.provide('goog.format.EmailAddress');2021goog.require('goog.string');22232425/**26* Formats an email address string for display, and allows for extraction of27* the individual components of the address.28* @param {string=} opt_address The email address.29* @param {string=} opt_name The name associated with the email address.30* @constructor31*/32goog.format.EmailAddress = function(opt_address, opt_name) {33/**34* The name or personal string associated with the address.35* @type {string}36* @private37*/38this.name_ = opt_name || '';3940/**41* The email address.42* @type {string}43* @protected44*/45this.address = opt_address || '';46};474849/**50* Match string for opening tokens.51* @type {string}52* @private53*/54goog.format.EmailAddress.OPENERS_ = '"<([';555657/**58* Match string for closing tokens.59* @type {string}60* @private61*/62goog.format.EmailAddress.CLOSERS_ = '">)]';636465/**66* Match string for characters that require display names to be quoted and are67* not address separators.68* @type {string}69* @const70* @package71*/72goog.format.EmailAddress.SPECIAL_CHARS = '()<>@:\\\".[]';737475/**76* Match string for address separators.77* @type {string}78* @const79* @private80*/81goog.format.EmailAddress.ADDRESS_SEPARATORS_ = ',;';828384/**85* Match string for characters that, when in a display name, require it to be86* quoted.87* @type {string}88* @const89* @private90*/91goog.format.EmailAddress.CHARS_REQUIRE_QUOTES_ =92goog.format.EmailAddress.SPECIAL_CHARS +93goog.format.EmailAddress.ADDRESS_SEPARATORS_;949596/**97* A RegExp to match all double quotes. Used in cleanAddress().98* @type {RegExp}99* @private100*/101goog.format.EmailAddress.ALL_DOUBLE_QUOTES_ = /\"/g;102103104/**105* A RegExp to match escaped double quotes. Used in parse().106* @type {RegExp}107* @private108*/109goog.format.EmailAddress.ESCAPED_DOUBLE_QUOTES_ = /\\\"/g;110111112/**113* A RegExp to match all backslashes. Used in cleanAddress().114* @type {RegExp}115* @private116*/117goog.format.EmailAddress.ALL_BACKSLASHES_ = /\\/g;118119120/**121* A RegExp to match escaped backslashes. Used in parse().122* @type {RegExp}123* @private124*/125goog.format.EmailAddress.ESCAPED_BACKSLASHES_ = /\\\\/g;126127128/**129* A string representing the RegExp for the local part of an email address.130* @private {string}131*/132goog.format.EmailAddress.LOCAL_PART_REGEXP_STR_ =133'[+a-zA-Z0-9_.!#$%&\'*\\/=?^`{|}~-]+';134135136/**137* A string representing the RegExp for the domain part of an email address.138* @private {string}139*/140goog.format.EmailAddress.DOMAIN_PART_REGEXP_STR_ =141'([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9]{2,63}';142143144/**145* A RegExp to match the local part of an email address.146* @private {!RegExp}147*/148goog.format.EmailAddress.LOCAL_PART_ =149new RegExp('^' + goog.format.EmailAddress.LOCAL_PART_REGEXP_STR_ + '$');150151152/**153* A RegExp to match the domain part of an email address.154* @private {!RegExp}155*/156goog.format.EmailAddress.DOMAIN_PART_ =157new RegExp('^' + goog.format.EmailAddress.DOMAIN_PART_REGEXP_STR_ + '$');158159160/**161* A RegExp to match an email address.162* @private {!RegExp}163*/164goog.format.EmailAddress.EMAIL_ADDRESS_ = new RegExp(165'^' + goog.format.EmailAddress.LOCAL_PART_REGEXP_STR_ + '@' +166goog.format.EmailAddress.DOMAIN_PART_REGEXP_STR_ + '$');167168169/**170* Get the name associated with the email address.171* @return {string} The name or personal portion of the address.172* @final173*/174goog.format.EmailAddress.prototype.getName = function() {175return this.name_;176};177178179/**180* Get the email address.181* @return {string} The email address.182* @final183*/184goog.format.EmailAddress.prototype.getAddress = function() {185return this.address;186};187188189/**190* Set the name associated with the email address.191* @param {string} name The name to associate.192* @final193*/194goog.format.EmailAddress.prototype.setName = function(name) {195this.name_ = name;196};197198199/**200* Set the email address.201* @param {string} address The email address.202* @final203*/204goog.format.EmailAddress.prototype.setAddress = function(address) {205this.address = address;206};207208209/**210* Return the address in a standard format:211* - remove extra spaces.212* - Surround name with quotes if it contains special characters.213* @return {string} The cleaned address.214* @override215*/216goog.format.EmailAddress.prototype.toString = function() {217return this.toStringInternal(goog.format.EmailAddress.CHARS_REQUIRE_QUOTES_);218};219220221/**222* Check if a display name requires quoting.223* @param {string} name The display name224* @param {string} specialChars String that contains the characters that require225* the display name to be quoted. This may change based in whereas we are226* in EAI context or not.227* @return {boolean}228* @private229*/230goog.format.EmailAddress.isQuoteNeeded_ = function(name, specialChars) {231for (var i = 0; i < specialChars.length; i++) {232var specialChar = specialChars[i];233if (goog.string.contains(name, specialChar)) {234return true;235}236}237return false;238};239240241/**242* Return the address in a standard format:243* - remove extra spaces.244* - Surround name with quotes if it contains special characters.245* @param {string} specialChars String that contains the characters that require246* the display name to be quoted.247* @return {string} The cleaned address.248* @protected249*/250goog.format.EmailAddress.prototype.toStringInternal = function(specialChars) {251var name = this.getName();252253// We intentionally remove double quotes in the name because escaping254// them to \" looks ugly.255name = name.replace(goog.format.EmailAddress.ALL_DOUBLE_QUOTES_, '');256257// If the name has special characters, we need to quote it and escape \'s.258if (goog.format.EmailAddress.isQuoteNeeded_(name, specialChars)) {259name = '"' +260name.replace(goog.format.EmailAddress.ALL_BACKSLASHES_, '\\\\') + '"';261}262263if (name == '') {264return this.address;265}266if (this.address == '') {267return name;268}269return name + ' <' + this.address + '>';270};271272273/**274* Determines if the current object is a valid email address.275* @return {boolean} Whether the email address is valid.276*/277goog.format.EmailAddress.prototype.isValid = function() {278return goog.format.EmailAddress.isValidAddrSpec(this.address);279};280281282/**283* Checks if the provided string is a valid email address. Supports both284* simple email addresses (address specs) and addresses that contain display285* names.286* @param {string} str The email address to check.287* @return {boolean} Whether the provided string is a valid address.288*/289goog.format.EmailAddress.isValidAddress = function(str) {290return goog.format.EmailAddress.parse(str).isValid();291};292293294/**295* Checks if the provided string is a valid address spec ([email protected]).296* @param {string} str The email address to check.297* @return {boolean} Whether the provided string is a valid address spec.298*/299goog.format.EmailAddress.isValidAddrSpec = function(str) {300// This is a fairly naive implementation, but it covers 99% of use cases.301// For more details, see http://en.wikipedia.org/wiki/Email_address#Syntax302return goog.format.EmailAddress.EMAIL_ADDRESS_.test(str);303};304305306/**307* Checks if the provided string is a valid local part (part before the '@') of308* an email address.309* @param {string} str The local part to check.310* @return {boolean} Whether the provided string is a valid local part.311*/312goog.format.EmailAddress.isValidLocalPartSpec = function(str) {313return goog.format.EmailAddress.LOCAL_PART_.test(str);314};315316317/**318* Checks if the provided string is a valid domain part (part after the '@') of319* an email address.320* @param {string} str The domain part to check.321* @return {boolean} Whether the provided string is a valid domain part.322*/323goog.format.EmailAddress.isValidDomainPartSpec = function(str) {324return goog.format.EmailAddress.DOMAIN_PART_.test(str);325};326327328/**329* Parses an email address of the form "name" <address> ("name" is330* optional) into an email address.331* @param {string} addr The address string.332* @param {function(new: goog.format.EmailAddress, string=,string=)} ctor333* EmailAddress constructor to instantiate the output address.334* @return {!goog.format.EmailAddress} The parsed address.335* @protected336*/337goog.format.EmailAddress.parseInternal = function(addr, ctor) {338// TODO(ecattell): Strip bidi markers.339var name = '';340var address = '';341for (var i = 0; i < addr.length;) {342var token = goog.format.EmailAddress.getToken_(addr, i);343if (token.charAt(0) == '<' && token.indexOf('>') != -1) {344var end = token.indexOf('>');345address = token.substring(1, end);346} else if (address == '') {347name += token;348}349i += token.length;350}351352// Check if it's a simple email address of the form "[email protected]".353if (address == '' && name.indexOf('@') != -1) {354address = name;355name = '';356}357358name = goog.string.collapseWhitespace(name);359name = goog.string.stripQuotes(name, '\'');360name = goog.string.stripQuotes(name, '"');361// Replace escaped quotes and slashes.362name = name.replace(goog.format.EmailAddress.ESCAPED_DOUBLE_QUOTES_, '"');363name = name.replace(goog.format.EmailAddress.ESCAPED_BACKSLASHES_, '\\');364address = goog.string.collapseWhitespace(address);365return new ctor(address, name);366};367368369/**370* Parses an email address of the form "name" <address> into371* an email address.372* @param {string} addr The address string.373* @return {!goog.format.EmailAddress} The parsed address.374*/375goog.format.EmailAddress.parse = function(addr) {376return goog.format.EmailAddress.parseInternal(addr, goog.format.EmailAddress);377};378379380/**381* Parse a string containing email addresses of the form382* "name" <address> into an array of email addresses.383* @param {string} str The address list.384* @param {function(string)} parser The parser to employ.385* @param {function(string):boolean} separatorChecker Accepts a character and386* returns whether it should be considered an address separator.387* @return {!Array<!goog.format.EmailAddress>} The parsed emails.388* @protected389*/390goog.format.EmailAddress.parseListInternal = function(391str, parser, separatorChecker) {392var result = [];393var email = '';394var token;395396// Remove non-UNIX-style newlines that would otherwise cause getToken_ to397// choke. Remove multiple consecutive whitespace characters for the same398// reason.399str = goog.string.collapseWhitespace(str);400401for (var i = 0; i < str.length;) {402token = goog.format.EmailAddress.getToken_(str, i);403if (separatorChecker(token) || (token == ' ' && parser(email).isValid())) {404if (!goog.string.isEmptyOrWhitespace(email)) {405result.push(parser(email));406}407email = '';408i++;409continue;410}411email += token;412i += token.length;413}414415// Add the final token.416if (!goog.string.isEmptyOrWhitespace(email)) {417result.push(parser(email));418}419return result;420};421422423/**424* Parses a string containing email addresses of the form425* "name" <address> into an array of email addresses.426* @param {string} str The address list.427* @return {!Array<!goog.format.EmailAddress>} The parsed emails.428*/429goog.format.EmailAddress.parseList = function(str) {430return goog.format.EmailAddress.parseListInternal(431str, goog.format.EmailAddress.parse,432goog.format.EmailAddress.isAddressSeparator);433};434435436/**437* Get the next token from a position in an address string.438* @param {string} str the string.439* @param {number} pos the position.440* @return {string} the token.441* @private442*/443goog.format.EmailAddress.getToken_ = function(str, pos) {444var ch = str.charAt(pos);445var p = goog.format.EmailAddress.OPENERS_.indexOf(ch);446if (p == -1) {447return ch;448}449if (goog.format.EmailAddress.isEscapedDlQuote_(str, pos)) {450// If an opener is an escaped quote we do not treat it as a real opener451// and keep accumulating the token.452return ch;453}454var closerChar = goog.format.EmailAddress.CLOSERS_.charAt(p);455var endPos = str.indexOf(closerChar, pos + 1);456457// If the closer is a quote we go forward skipping escaped quotes until we458// hit the real closing one.459while (endPos >= 0 &&460goog.format.EmailAddress.isEscapedDlQuote_(str, endPos)) {461endPos = str.indexOf(closerChar, endPos + 1);462}463var token = (endPos >= 0) ? str.substring(pos, endPos + 1) : ch;464return token;465};466467468/**469* Checks if the character in the current position is an escaped double quote470* ( \" ).471* @param {string} str the string.472* @param {number} pos the position.473* @return {boolean} true if the char is escaped double quote.474* @private475*/476goog.format.EmailAddress.isEscapedDlQuote_ = function(str, pos) {477if (str.charAt(pos) != '"') {478return false;479}480var slashCount = 0;481for (var idx = pos - 1; idx >= 0 && str.charAt(idx) == '\\'; idx--) {482slashCount++;483}484return ((slashCount % 2) != 0);485};486487488/**489* @param {string} ch The character to test.490* @return {boolean} Whether the provided character is an address separator.491*/492goog.format.EmailAddress.isAddressSeparator = function(ch) {493return goog.string.contains(goog.format.EmailAddress.ADDRESS_SEPARATORS_, ch);494};495496497