Commits

Anonymous committed 31c406a

Fixing issue 1. Patch submitted by tronikos.

  • Participants
  • Parent commits e07b01a
  • Branches default

Comments (0)

Files changed (12)

File javascript/README

+Info:
+=====
+Google's JavaScript library for parsing, formatting, and validating
+international phone numbers.
+
+
+How to setup:
+=============
+1.  Checkout closure-library next to libphonenumber:
+
+e.g.
+svn checkout http://libphonenumber.googlecode.com/svn/trunk/ ~/src/libphonenumber
+svn checkout http://closure-library.googlecode.com/svn/trunk/ ~/src/closure-library
+
+(Or change the path of the <script src=""> in the html pages to point to
+wherever base.js is located.)
+
+2.  Run the unit tests to make sure everything is working. Open the following
+pages with your web browser:
+  javascript/i18n/phonenumbers/phonenumberutil_test.html
+  javascript/i18n/phonenumbers/asyoutypeformatter_test.html
+
+3.  Run the demo: javascript/i18n/phonenumbers/demo.html
+
+
+How to update:
+==============
+The JavaScript library is ported from the Java implementation (revision 39).
+When the Java project gets updated follow these steps to update the JavaScript
+project:
+
+1.  If the protocol buffers (phonemetadata.proto and phonenumber.proto)
+    have changed:
+  a.  Manually update the .pb.js files with the changes of the .proto files.
+  b.  Manually update the following JavaScript functions in
+      javascript/i18n/phonenumbers/phonenumberutil.js:
+        i18n.phonenumbers.PhoneNumberDesc.prototype.exactlySameAs(other)
+        i18n.phonenumbers.PhoneNumber.prototype.exactlySameAs(other)
+  c.  Manually update the toJsArray() Java methods in
+        /java/resources/com/google/i18n/phonenumbers/BuildMetadataJSON.java
+
+2.  If the phone number metadata in the XML format has changed
+    (java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml)
+    run the following commands to regenerate metadata.js and
+    metadatafortesting.js:
+
+    ant -f java/build.xml
+    java -cp java/build/classes \
+      com.google.i18n.phonenumbers.BuildMetadataProtoFromXml \
+      java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml \
+      javascript/i18n/phonenumbers/metadata.js false json
+    java -cp java/build/classes \
+      com.google.i18n.phonenumbers.BuildMetadataProtoFromXml \
+      java/resources/com/google/i18n/phonenumbers/test/PhoneNumberMetaDataForTesting.xml \
+      javascript/i18n/phonenumbers/metadatafortesting.js false json
+
+3.  Manually port any changes of the Java code to the JavaScript code:
+      PhoneNumberUtil.java => phonenumberutil.js
+      AsYouTypeFormatter.java => asyoutypeformatter.js
+      PhoneNumberUtilTest.java => phonenumberutil_test.js
+      AsYouTypeFormatterTest.java => asyoutypeformatter_test.js

File javascript/i18n/phonenumbers/asyoutypeformatter.js

+// Copyright (C) 2010 Google Inc.
+//
+// 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  A formatter which formats phone numbers as they are entered.
+ * (based on the java implementation).
+ *
+ * An AsYouTypeFormatter could be created by new AsYouTypeFormatter(). After
+ * that digits could be added by invoking the inputDigit method on the formatter
+ * instance, and the partially formatted phone number will be returned each time
+ * a digit is added. The clear method should be invoked before a new number
+ * needs to be formatted.
+ *
+ * See testAsYouTypeFormatterUS(), testAsYouTestFormatterGB() and
+ * testAsYouTypeFormatterDE() in asyoutypeformatter_test.js for more details
+ * on how the formatter is to be used.
+ *
+ * @author Nikolaos Trogkanis
+ */
+
+goog.provide('i18n.phonenumbers.AsYouTypeFormatter');
+
+goog.require('goog.string.StringBuffer');
+goog.require('i18n.phonenumbers.NumberFormat');
+goog.require('i18n.phonenumbers.PhoneMetadata');
+goog.require('i18n.phonenumbers.PhoneMetadataCollection');
+goog.require('i18n.phonenumbers.PhoneNumber');
+goog.require('i18n.phonenumbers.PhoneNumber.CountryCodeSource');
+goog.require('i18n.phonenumbers.PhoneNumberDesc');
+goog.require('i18n.phonenumbers.PhoneNumberUtil');
+goog.require('i18n.phonenumbers.metadata');
+
+/**
+ * Constructs a light-weight formatter which does no formatting, but outputs
+ * exactly what is fed into the inputDigit method.
+ *
+ * @param {string} regionCode the country/region where the phone number is being
+ *     entered.
+ * @constructor
+ */
+i18n.phonenumbers.AsYouTypeFormatter = function(regionCode) {
+  /**
+   * @type {boolean}
+   * @private
+   */
+  this.ableToFormat_ = true;
+  /**
+   * @type {boolean}
+   * @private
+   */
+  this.isInternationalFormatting_ = false;
+  /**
+   * @type {i18n.phonenumbers.PhoneNumberUtil}
+   * @private
+   */
+  this.phoneUtil_ = i18n.phonenumbers.PhoneNumberUtil.getInstance();
+  // The digits that have not been entered yet will be represented by a \u2008,
+  // the punctuation space.
+  /**
+   * @type {string}
+   * @private
+   */
+  this.digitPlaceholder_ = '\u2008';
+  /**
+   * @type {RegExp}
+   * @private
+   */
+  this.digitPattern_ = new RegExp(this.digitPlaceholder_);
+  /**
+   * @type {number}
+   * @private
+   */
+  this.lastMatchPosition_ = 0;
+  /**
+   * The position of a digit upon which inputDigitAndRememberPosition is most
+   * recently invoked, as found in the current output.
+   * @type {number}
+   * @private
+   */
+  this.positionRemembered_ = 0;
+  /**
+   * The position of a digit upon which inputDigitAndRememberPosition is most
+   * recently invoked, as found in the original sequence of characters the user
+   * entered.
+   * @type {number}
+   * @private
+   */
+  this.originalPosition_ = 0;
+  /**
+   * A pattern that is used to match character classes in regular expressions.
+   * An example of a character class is [1-4].
+   * @type {RegExp}
+   * @private
+   */
+  this.CHARACTER_CLASS_PATTERN_ = /\[([^\[\]])*\]/g;
+  /**
+   * Any digit in a regular expression that actually denotes a digit. For
+   * example, in the regular expression 80[0-2]\d{6,10}, the first 2 digits
+   * (8 and 0) are standalone digits, but the rest are not.
+   * Two look-aheads are needed because the number following \\d could be a
+   * two-digit number, since the phone number can be as long as 15 digits.
+   * @type {RegExp}
+   * @private
+   */
+  this.STANDALONE_DIGIT_PATTERN_ = /\d(?=[^,}][^,}])/g;
+  /**
+   * @type {!goog.string.StringBuffer}
+   * @private
+   */
+  this.accruedInput_ = new goog.string.StringBuffer();
+  /**
+   * @type {!goog.string.StringBuffer}
+   * @private
+   */
+  this.accruedInputWithoutFormatting_ = new goog.string.StringBuffer();
+  /**
+   * @type {!goog.string.StringBuffer}
+   * @private
+   */
+  this.currentOutput_ = new goog.string.StringBuffer();
+  /**
+   * @type {!goog.string.StringBuffer}
+   * @private
+   */
+  this.prefixBeforeNationalNumber_ = new goog.string.StringBuffer();
+  /**
+   * @type {!goog.string.StringBuffer}
+   * @private
+   */
+  this.nationalNumber_ = new goog.string.StringBuffer();
+  /**
+   *  @type {string}
+   * @private
+   */
+  this.defaultCountry_ = regionCode;
+  this.initializeCountrySpecificInfo_(this.defaultCountry_);
+  /**
+   * @type {i18n.phonenumbers.PhoneMetadata}
+   * @private
+   */
+  this.defaultMetaData_ = this.currentMetaData_;
+};
+
+/**
+ * @param {string} regionCode
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.initializeCountrySpecificInfo_ =
+  function(regionCode) {
+
+  /** @type {i18n.phonenumbers.PhoneMetadata} */
+  this.currentMetaData_ = this.phoneUtil_.getMetadataForRegion(regionCode);
+  /** @type {RegExp} */
+  this.nationalPrefixForParsing_ = new RegExp('^(' + this.currentMetaData_
+      .getNationalPrefixForParsing() + ')');
+  /** @type {RegExp} */
+  this.internationalPrefix_ = new RegExp('^(' + '\\+|' +
+      this.currentMetaData_.getInternationalPrefix() + ')');
+};
+
+/**
+ * @param {string} leadingFourDigitsOfNationalNumber
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.chooseFormatAndCreateTemplate_ =
+  function(leadingFourDigitsOfNationalNumber) {
+
+  /** @type {Array.<i18n.phonenumbers.NumberFormat>} */
+  var formatList = this.getAvailableFormats_(leadingFourDigitsOfNationalNumber);
+  if (formatList.length < 1) {
+    this.ableToFormat_ = false;
+  } else {
+    // When there are multiple available formats, the formatter uses the first
+    // format.
+    /** @type {i18n.phonenumbers.NumberFormat} */
+    var format = formatList[0];
+    if (!this.createFormattingTemplate_(format)) {
+      this.ableToFormat_ = false;
+    } else {
+      this.currentOutput_ =
+          new goog.string.StringBuffer(this.formattingTemplate_);
+    }
+  }
+};
+
+/**
+ * @param {string} leadingFourDigits
+ * @return {Array.<i18n.phonenumbers.NumberFormat>}
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.getAvailableFormats_ =
+  function(leadingFourDigits) {
+
+  /** @type {Array.<i18n.phonenumbers.NumberFormat>} */
+  var matchedList = [];
+  /** @type {Array.<i18n.phonenumbers.NumberFormat>} */
+  var formatList = (this.isInternationalFormatting_ && this.currentMetaData_
+      .intlNumberFormatCount() > 0) ? this.currentMetaData_
+      .intlNumberFormatArray() : this.currentMetaData_.numberFormatArray();
+  /** @type {number} */
+  var formatListLength = formatList.length;
+  for (var i = 0; i < formatListLength; ++i) {
+    /** @type {i18n.phonenumbers.NumberFormat} */
+    var format = formatList[i];
+    if (format.hasLeadingDigits()) {
+      /** @type {RegExp} */
+      var leadingDigitsPattern =
+          new RegExp('^(' + format.getLeadingDigits() + ')');
+      if (leadingDigitsPattern.test(leadingFourDigits)) {
+        matchedList.push(format);
+      }
+    } else {
+      matchedList.push(format);
+    }
+  }
+  return matchedList;
+};
+
+/**
+ * @param {i18n.phonenumbers.NumberFormat} format
+ * @return {boolean}
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.createFormattingTemplate_ =
+  function(format) {
+
+  /** @type {string} */
+  var numberFormat = format.getFormatOrDefault();
+  /** @type {string} */
+  var numberPattern = format.getPatternOrDefault();
+
+  // The formatter doesn't format numbers when numberPattern contains '|', e.g.
+  // (20|3)\d{4}. In those cases we quickly return.
+  if (numberPattern.indexOf('|') != -1) {
+    return false;
+  }
+
+  // Replace anything in the form of [..] with \d
+  numberPattern = numberPattern.replace(this.CHARACTER_CLASS_PATTERN_, '\\d');
+
+  // Replace any standalone digit (not the one in d{}) with \d
+  numberPattern = numberPattern.replace(this.STANDALONE_DIGIT_PATTERN_, '\\d');
+
+  this.formattingTemplate_ = this.getFormattingTemplate_(numberPattern,
+      numberFormat);
+  return true;
+};
+
+/**
+ * Gets a formatting template which could be used to efficiently format a
+ * partial number where digits are added one by one.
+ *
+ * @param {string} numberPattern
+ * @param {string} numberFormat
+ * @return {string}
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.getFormattingTemplate_ =
+  function(numberPattern, numberFormat) {
+
+  // Creates a phone number consisting only of the digit 9 that matches the
+  // numberPattern by applying the pattern to the longestPhoneNumber string.
+  /** @type {string} */
+  var longestPhoneNumber = '999999999999999';
+  /** @type {Array.<string>} */
+  var m = longestPhoneNumber.match(numberPattern);
+  // this match will always succeed
+  /** @type {string} */
+  var aPhoneNumber = m[0];
+  // Formats the number according to numberFormat
+  /** @type {string} */
+  var template = aPhoneNumber.replace(new RegExp(numberPattern, 'g'),
+                                      numberFormat);
+  // Replaces each digit with character digitPlaceholder
+  template = template.replace(new RegExp('9', 'g'), this.digitPlaceholder_);
+  return template;
+};
+
+/**
+ * Clears the internal state of the formatter, so it could be reused.
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.clear = function() {
+  this.accruedInput_.clear();
+  this.accruedInputWithoutFormatting_.clear();
+  this.currentOutput_.clear();
+  this.lastMatchPosition_ = 0;
+  this.prefixBeforeNationalNumber_.clear();
+  this.nationalNumber_.clear();
+  this.ableToFormat_ = true;
+  this.positionRemembered_ = 0;
+  this.originalPosition_ = 0;
+  this.isInternationalFormatting_ = false;
+  if (this.currentMetaData_ != this.defaultMetaData_) {
+    this.initializeCountrySpecificInfo_(this.defaultCountry_);
+  }
+};
+
+/**
+ * Formats a phone number on-the-fly as each digit is entered.
+ *
+ * @param {string} nextChar the most recently entered digit of a phone number.
+ *     Formatting characters are allowed, but they are removed from the result.
+ * @return {string} the partially formatted phone number.
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.inputDigit = function(nextChar) {
+  return this.inputDigitWithOptionToRememberPosition_(nextChar, false);
+};
+
+/**
+ * Same as inputDigit, but remembers the position where nextChar is inserted, so
+ * that it could be retrieved later by using getRememberedPosition(). The
+ * remembered position will be automatically adjusted if additional formatting
+ * characters are later inserted/removed in front of nextChar.
+ *
+ * @param {string} nextChar
+ * @return {string}
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.inputDigitAndRememberPosition =
+  function(nextChar) {
+
+  return this.inputDigitWithOptionToRememberPosition_(nextChar, true);
+};
+
+/**
+ * @param {string} nextChar
+ * @param {boolean} rememberPosition
+ * @return {string}
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.
+    inputDigitWithOptionToRememberPosition_ = function(nextChar,
+                                                       rememberPosition) {
+
+  this.accruedInput_.append(nextChar);
+  if (rememberPosition) {
+    this.positionRemembered_ = this.accruedInput_.getLength();
+    this.originalPosition_ = this.positionRemembered_;
+  }
+  // We do formatting on-the-fly only when each character entered is either a
+  // plus sign or a digit.
+  if (!i18n.phonenumbers.PhoneNumberUtil.VALID_START_CHAR_PATTERN
+      .test(nextChar)) {
+    this.ableToFormat_ = false;
+  }
+  if (!this.ableToFormat_) {
+    this.resetPositionOnFailureToFormat_();
+    return this.accruedInput_.toString();
+  }
+
+  nextChar = this.normalizeAndAccrueDigitsAndPlusSign_(nextChar);
+
+  // We start to attempt to format only when at least 6 digits (the plus sign is
+  // counted as a digit as well for this purpose) have been entered.
+  switch (this.accruedInputWithoutFormatting_.getLength()) {
+  case 0: // this is the case where the first few inputs are neither digits nor
+          // the plus sign.
+  case 1:
+  case 2:
+  case 3:
+  case 4:
+  case 5:
+    return this.accruedInput_.toString();
+  case 6:
+    if (!this.extractIddAndValidCountryCode_()) {
+      this.ableToFormat_ = false;
+      return this.accruedInput_.toString();
+    }
+    this.removeNationalPrefixFromNationalNumber_();
+    return this.attemptToChooseFormattingPattern_(rememberPosition);
+  default:
+    if (this.nationalNumber_.getLength() > 4) {
+      // The formatting pattern is already chosen.
+      /** @type {string} */
+      var temp = this.inputDigitHelper_(nextChar, rememberPosition);
+      return this.ableToFormat_ ?
+          this.prefixBeforeNationalNumber_.toString() + temp : temp;
+    } else {
+      return this.attemptToChooseFormattingPattern_(rememberPosition);
+    }
+  }
+};
+
+/**
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.resetPositionOnFailureToFormat_ =
+  function() {
+
+  if (this.positionRemembered_ > 0) {
+    this.positionRemembered_ = this.originalPosition_;
+    this.currentOutput_.clear();
+  }
+};
+
+/**
+ * Returns the current position in the partially formatted phone number of the
+ * character which was previously passed in as the parameter of
+ * inputDigitAndRememberPosition().
+ *
+ * @return {number}
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.getRememberedPosition =
+  function() {
+
+  return this.positionRemembered_;
+};
+
+/**
+ * Attempts to set the formatting template and returns a string which contains
+ * the formatted version of the digits entered so far.
+ *
+ * @param {boolean} rememberPosition
+ * @return {string}
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.
+    attemptToChooseFormattingPattern_ = function(rememberPosition) {
+
+  /** @type {string} */
+  var nationalNumber = this.nationalNumber_.toString();
+  /** @type {number} */
+  var nationalNumberLength = nationalNumber.length;
+  // We start to attempt to format only when as least 4 digits of national
+  // number (excluding national prefix) have been entered.
+  if (nationalNumberLength >= 4) {
+    this.chooseFormatAndCreateTemplate_(nationalNumber.substring(0, 4));
+    return this.inputAccruedNationalNumber_(rememberPosition);
+  } else {
+    if (rememberPosition) {
+      this.positionRemembered_ =
+          this.prefixBeforeNationalNumber_.length() + nationalNumberLength;
+    }
+    return this.prefixBeforeNationalNumber_.toString() +
+        this.nationalNumber_.toString();
+  }
+};
+
+/**
+ * Invokes inputDigitHelper on each digit of the national number accrued, and
+ * returns a formatted string in the end.
+ *
+ * @param {boolean} rememberPosition
+ * @return {string}
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.inputAccruedNationalNumber_ =
+  function(rememberPosition) {
+
+  /** @type {number} */
+  var lengthOfNationalNumber = this.nationalNumber_.getLength();
+  if (lengthOfNationalNumber > 0) {
+    // The positionRemembered should be only adjusted once in the loop that
+    // follows.
+    /** @type {boolean} */
+    var positionAlreadyAdjusted = false;
+    /** @type {string} */
+    var tempNationalNumber = '';
+    for (var i = 0; i < lengthOfNationalNumber; i++) {
+      tempNationalNumber =
+          this.inputDigitHelper_(this.nationalNumber_.toString().charAt(i),
+                                 rememberPosition);
+      if (!positionAlreadyAdjusted &&
+          this.positionRemembered_ -
+              this.prefixBeforeNationalNumber_.getLength() == i + 1) {
+        this.positionRemembered_ =
+            this.prefixBeforeNationalNumber_.getLength() +
+            tempNationalNumber.length;
+        positionAlreadyAdjusted = true;
+      }
+    }
+    return this.ableToFormat_ ?
+        this.prefixBeforeNationalNumber_.toString() + tempNationalNumber :
+        tempNationalNumber;
+  } else {
+    if (rememberPosition) {
+      this.positionRemembered_ = this.prefixBeforeNationalNumber_.length();
+    }
+    return this.prefixBeforeNationalNumber_.toString();
+  }
+};
+
+/**
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.
+      removeNationalPrefixFromNationalNumber_ = function() {
+
+  /** @type {string} */
+  var nationalNumber = this.nationalNumber_.toString();
+  /** @type {number} */
+  var startOfNationalNumber = 0;
+  if (this.currentMetaData_.getCountryCode() == 1 &&
+      nationalNumber.charAt(0) == '1') {
+    startOfNationalNumber = 1;
+    this.prefixBeforeNationalNumber_.append('1 ');
+    // Since a space is inserted after the national prefix in this case, we
+    // increase the remembered position by 1 for anything that is after the
+    // national prefix.
+    if (this.positionRemembered_ >
+        this.prefixBeforeNationalNumber_.getLength() - 1) {
+      this.positionRemembered_++;
+    }
+  } else if (this.currentMetaData_.hasNationalPrefix()) {
+    /** @type {Array.<string>} */
+    var m = nationalNumber.match(this.nationalPrefixForParsing_);
+    if (m != null && m[0] != null && m[0].length > 0) {
+      startOfNationalNumber = m[0].length;
+      this.prefixBeforeNationalNumber_.append(nationalNumber.substring(0,
+          startOfNationalNumber));
+    }
+  }
+  this.nationalNumber_.clear();
+  this.nationalNumber_.append(nationalNumber.substring(startOfNationalNumber));
+};
+
+/**
+ * Extracts IDD, plus sign and country code to prefixBeforeNationalNumber when
+ * they are available, and places the remaining input into nationalNumber.
+ *
+ * @return {boolean} false when accruedInputWithoutFormatting begins with the
+ *     plus sign or valid IDD for defaultCountry, but the sequence of digits
+ *     after that does not form a valid country code. It returns true for all
+ *     other cases.
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.extractIddAndValidCountryCode_ =
+  function() {
+
+  /** @type {string} */
+  var accruedInputWithoutFormatting =
+      this.accruedInputWithoutFormatting_.toString();
+  this.nationalNumber_.clear();
+  /** @type {Array.<string>} */
+  var m = accruedInputWithoutFormatting.match(this.internationalPrefix_);
+  if (m != null && m[0] != null && m[0].length > 0) {
+    this.isInternationalFormatting_ = true;
+    /** @type {number} */
+    var startOfCountryCode = m[0].length;
+    /** @type {!goog.string.StringBuffer} */
+    var numberIncludeCountryCode = new goog.string.StringBuffer(
+        accruedInputWithoutFormatting.substring(startOfCountryCode));
+    /** @type {number} */
+    var countryCode = this.phoneUtil_.extractCountryCode(
+        numberIncludeCountryCode, this.nationalNumber_);
+    if (countryCode == 0) {
+      return false;
+    } else {
+      /** @type {string} */
+      var newRegionCode =
+          this.phoneUtil_.getRegionCodeForCountryCode(countryCode);
+      if (newRegionCode != this.defaultCountry_) {
+        this.initializeCountrySpecificInfo_(newRegionCode);
+      }
+      this.prefixBeforeNationalNumber_.append(accruedInputWithoutFormatting
+          .substring(0, startOfCountryCode));
+      if (accruedInputWithoutFormatting.charAt(0) !=
+          i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN) {
+        if (this.positionRemembered_ >
+            this.prefixBeforeNationalNumber_.getLength()) {
+          // Since a space will be inserted in front of the country code in this
+          // case, we increase the remembered position by 1.
+          this.positionRemembered_++;
+        }
+        this.prefixBeforeNationalNumber_.append(' ');
+      }
+      /** @type {string} */
+      var countryCodeString = '' + countryCode;
+      if (this.positionRemembered_ >
+          this.prefixBeforeNationalNumber_.getLength() +
+              countryCodeString.length) {
+        // Since a space will be inserted after the country code in this case,
+        // we increase the remembered position by 1.
+        this.positionRemembered_++;
+      }
+      this.prefixBeforeNationalNumber_.append(countryCodeString).append(' ');
+    }
+  } else {
+    this.nationalNumber_.clear();
+    this.nationalNumber_.append(accruedInputWithoutFormatting);
+  }
+  return true;
+};
+
+/**
+ * Accrues digits and the plus sign to accruedInputWithoutFormatting for later
+ * use. If nextChar contains a digit in non-ASCII format (e.g. the full-width
+ * version of digits), it is first normalized to the ASCII version. The return
+ * value is nextChar itself, or its normalized version, if nextChar is a digit
+ * in non-ASCII format.
+ *
+ * @param {string} nextChar
+ * @return {string}
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.
+    normalizeAndAccrueDigitsAndPlusSign_ = function(nextChar) {
+
+  if (nextChar == i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN) {
+    this.accruedInputWithoutFormatting_.append(nextChar);
+  }
+
+  if (nextChar in i18n.phonenumbers.PhoneNumberUtil.DIGIT_MAPPINGS) {
+    nextChar = i18n.phonenumbers.PhoneNumberUtil.DIGIT_MAPPINGS[nextChar];
+    this.accruedInputWithoutFormatting_.append(nextChar);
+    this.nationalNumber_.append(nextChar);
+  }
+  return nextChar;
+};
+
+/**
+ * @param {string} nextChar
+ * @param {boolean} rememberPosition
+ * @return {string}
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.inputDigitHelper_ =
+  function(nextChar, rememberPosition) {
+
+  if (!(nextChar in i18n.phonenumbers.PhoneNumberUtil.DIGIT_MAPPINGS)) {
+    return this.currentOutput_.toString();
+  }
+
+  /** @type {string} */
+  var currentOutput = this.currentOutput_.toString();
+  /** @type {string} */
+  var currentOutput2 = currentOutput.substring(this.lastMatchPosition_);
+  /** @type {number} */
+  var digitPatternStart = currentOutput2.search(this.digitPattern_);
+  if (digitPatternStart >= 0) {
+    this.currentOutput_ = new goog.string.StringBuffer(
+        currentOutput.substring(0, this.lastMatchPosition_) +
+        currentOutput2.replace(this.digitPattern_, nextChar));
+    this.lastMatchPosition_ += digitPatternStart;
+    if (rememberPosition) {
+      this.positionRemembered_ = this.prefixBeforeNationalNumber_.getLength() +
+          this.lastMatchPosition_ + 1;
+    }
+    return this.currentOutput_.toString()
+        .substring(0, this.lastMatchPosition_ + 1);
+  } else {
+    // More digits are entered than we could handle.
+    this.currentOutput_.append(nextChar);
+    this.ableToFormat_ = false;
+    this.resetPositionOnFailureToFormat_();
+    return this.accruedInput_.toString();
+  }
+};

File javascript/i18n/phonenumbers/asyoutypeformatter_test.html

+<!DOCTYPE html>
+<html>
+<!--
+Copyright (C) 2010 Google Inc.
+
+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.
+-->
+<!--
+  Author: Nikolaos Trogkanis
+-->
+<head>
+<title>libphonenumber Unit Tests - i18n.phonenumbers - asyoutypeformatter.js</title>
+<script src="../../../../closure-library/closure/goog/base.js"></script>
+<script>
+  goog.require('goog.proto2.Message');
+</script>
+<script src="phonemetadata.pb.js"></script>
+<script src="phonenumber.pb.js"></script>
+<script src="metadatafortesting.js"></script>
+<script src="phonenumberutil.js"></script>
+<script src="asyoutypeformatter.js"></script>
+<script src="asyoutypeformatter_test.js"></script>
+</head>
+<body>
+</body>
+</html>

File javascript/i18n/phonenumbers/asyoutypeformatter_test.js

+// Copyright (C) 2010 Google Inc.
+//
+// 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  Unit tests for the AsYouTypeFormatter.
+ *
+ * @author Nikolaos Trogkanis
+ */
+
+goog.require('goog.testing.jsunit');
+goog.require('i18n.phonenumbers.AsYouTypeFormatter');
+
+function testAsYouTypeFormatterUS() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('US');
+  assertEquals('6', f.inputDigit('6'));
+  assertEquals('65', f.inputDigit('5'));
+  assertEquals('650', f.inputDigit('0'));
+  assertEquals('6502', f.inputDigit('2'));
+  assertEquals('65025', f.inputDigit('5'));
+  assertEquals('650 253', f.inputDigit('3'));
+  assertEquals('650 253 2', f.inputDigit('2'));
+  assertEquals('650 253 22', f.inputDigit('2'));
+  assertEquals('650 253 222', f.inputDigit('2'));
+  assertEquals('650 253 2222', f.inputDigit('2'));
+
+  f.clear();
+  assertEquals('1', f.inputDigit('1'));
+  assertEquals('16', f.inputDigit('6'));
+  assertEquals('165', f.inputDigit('5'));
+  assertEquals('1650', f.inputDigit('0'));
+  assertEquals('16502', f.inputDigit('2'));
+  assertEquals('1 650 25', f.inputDigit('5'));
+  assertEquals('1 650 253', f.inputDigit('3'));
+  assertEquals('1 650 253 2', f.inputDigit('2'));
+  assertEquals('1 650 253 22', f.inputDigit('2'));
+  assertEquals('1 650 253 222', f.inputDigit('2'));
+  assertEquals('1 650 253 2222', f.inputDigit('2'));
+
+  f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('01', f.inputDigit('1'));
+  assertEquals('011', f.inputDigit('1'));
+  assertEquals('0114', f.inputDigit('4'));
+  assertEquals('01144', f.inputDigit('4'));
+  assertEquals('011 44 6', f.inputDigit('6'));
+  assertEquals('011 44 61', f.inputDigit('1'));
+  assertEquals('011 44 612', f.inputDigit('2'));
+  assertEquals('011 44 6 123', f.inputDigit('3'));
+  assertEquals('011 44 6 123 1', f.inputDigit('1'));
+  assertEquals('011 44 6 123 12', f.inputDigit('2'));
+  assertEquals('011 44 6 123 123', f.inputDigit('3'));
+  assertEquals('011 44 6 123 123 1', f.inputDigit('1'));
+  assertEquals('011 44 6 123 123 12', f.inputDigit('2'));
+  assertEquals('011 44 6 123 123 123', f.inputDigit('3'));
+
+  f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('01', f.inputDigit('1'));
+  assertEquals('011', f.inputDigit('1'));
+  assertEquals('0115', f.inputDigit('5'));
+  assertEquals('01154', f.inputDigit('4'));
+  assertEquals('011 54 9', f.inputDigit('9'));
+  assertEquals('011 54 91', f.inputDigit('1'));
+  assertEquals('011 54 911', f.inputDigit('1'));
+  assertEquals('011 54 9 11 2', f.inputDigit('2'));
+  assertEquals('011 54 9 11 23', f.inputDigit('3'));
+  assertEquals('011 54 9 11 231', f.inputDigit('1'));
+  assertEquals('011 54 9 11 2312', f.inputDigit('2'));
+  assertEquals('011 54 9 11 2312 1', f.inputDigit('1'));
+  assertEquals('011 54 9 11 2312 12', f.inputDigit('2'));
+  assertEquals('011 54 9 11 2312 123', f.inputDigit('3'));
+  assertEquals('011 54 9 11 2312 1234', f.inputDigit('4'));
+
+  f.clear();
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+4', f.inputDigit('4'));
+  assertEquals('+48', f.inputDigit('8'));
+  assertEquals('+488', f.inputDigit('8'));
+  assertEquals('+4888', f.inputDigit('8'));
+  assertEquals('+48 881', f.inputDigit('1'));
+  assertEquals('+48 88 12', f.inputDigit('2'));
+  assertEquals('+48 88 123', f.inputDigit('3'));
+  assertEquals('+48 88 123 1', f.inputDigit('1'));
+  assertEquals('+48 88 123 12', f.inputDigit('2'));
+  assertEquals('+48 88 123 12 1', f.inputDigit('1'));
+  assertEquals('+48 88 123 12 12', f.inputDigit('2'));
+}
+
+function testAsYouTypeFormatterUSFullWidthCharacters() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('US');
+  assertEquals('\uFF16', f.inputDigit('\uFF16'));
+  assertEquals('\uFF16\uFF15', f.inputDigit('\uFF15'));
+  assertEquals('\uFF16\uFF15\uFF10', f.inputDigit('\uFF10'));
+  assertEquals('\uFF16\uFF15\uFF10\uFF12', f.inputDigit('\uFF12'));
+  assertEquals('\uFF16\uFF15\uFF10\uFF12\uFF15', f.inputDigit('\uFF15'));
+  assertEquals('650 253', f.inputDigit('\uFF13'));
+  assertEquals('650 253 2', f.inputDigit('\uFF12'));
+  assertEquals('650 253 22', f.inputDigit('\uFF12'));
+  assertEquals('650 253 222', f.inputDigit('\uFF12'));
+  assertEquals('650 253 2222', f.inputDigit('\uFF12'));
+}
+
+function testAsYouTypeFormatterUSMobileShortCode() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('US');
+  assertEquals('*', f.inputDigit('*'));
+  assertEquals('*1', f.inputDigit('1'));
+  assertEquals('*12', f.inputDigit('2'));
+  assertEquals('*121', f.inputDigit('1'));
+  assertEquals('*121#', f.inputDigit('#'));
+}
+
+function testAsYouTypeFormatterUSVanityNumber() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('US');
+  assertEquals('8', f.inputDigit('8'));
+  assertEquals('80', f.inputDigit('0'));
+  assertEquals('800', f.inputDigit('0'));
+  assertEquals('800 ', f.inputDigit(' '));
+  assertEquals('800 M', f.inputDigit('M'));
+  assertEquals('800 MY', f.inputDigit('Y'));
+  assertEquals('800 MY ', f.inputDigit(' '));
+  assertEquals('800 MY A', f.inputDigit('A'));
+  assertEquals('800 MY AP', f.inputDigit('P'));
+  assertEquals('800 MY APP', f.inputDigit('P'));
+  assertEquals('800 MY APPL', f.inputDigit('L'));
+  assertEquals('800 MY APPLE', f.inputDigit('E'));
+}
+
+function testAsYouTypeFormatterAndRememberPositionUS() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('US');
+  assertEquals('1', f.inputDigitAndRememberPosition('1'));
+  assertEquals(1, f.getRememberedPosition());
+  assertEquals('16', f.inputDigit('6'));
+  assertEquals('165', f.inputDigit('5'));
+  assertEquals(1, f.getRememberedPosition());
+  assertEquals('1650', f.inputDigitAndRememberPosition('0'));
+  assertEquals(4, f.getRememberedPosition());
+  assertEquals('16502', f.inputDigit('2'));
+  assertEquals('1 650 25', f.inputDigit('5'));
+  // Note the remembered position for digit '0' changes from 4 to 5, because a
+  // space is now inserted in the front.
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('1 650 253', f.inputDigit('3'));
+  assertEquals('1 650 253 2', f.inputDigit('2'));
+  assertEquals('1 650 253 22', f.inputDigit('2'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('1 650 253 222', f.inputDigitAndRememberPosition('2'));
+  assertEquals(13, f.getRememberedPosition());
+  assertEquals('1 650 253 2222', f.inputDigit('2'));
+  assertEquals(13, f.getRememberedPosition());
+  assertEquals('165025322222', f.inputDigit('2'));
+  assertEquals(10, f.getRememberedPosition());
+  assertEquals('1650253222222', f.inputDigit('2'));
+  assertEquals(10, f.getRememberedPosition());
+
+  f.clear();
+  assertEquals('1', f.inputDigit('1'));
+  assertEquals('16', f.inputDigit('6'));
+  assertEquals('165', f.inputDigitAndRememberPosition('5'));
+  assertEquals('1650', f.inputDigit('0'));
+  assertEquals(3, f.getRememberedPosition());
+  assertEquals('16502', f.inputDigit('2'));
+  assertEquals('1 650 25', f.inputDigit('5'));
+  assertEquals(4, f.getRememberedPosition());
+  assertEquals('1 650 253', f.inputDigit('3'));
+  assertEquals('1 650 253 2', f.inputDigit('2'));
+  assertEquals('1 650 253 22', f.inputDigit('2'));
+  assertEquals(4, f.getRememberedPosition());
+  assertEquals('1 650 253 222', f.inputDigit('2'));
+  assertEquals('1 650 253 2222', f.inputDigit('2'));
+  assertEquals('165025322222', f.inputDigit('2'));
+  assertEquals(3, f.getRememberedPosition());
+  assertEquals('1650253222222', f.inputDigit('2'));
+  assertEquals(3, f.getRememberedPosition());
+
+  f.clear();
+  assertEquals('6', f.inputDigit('6'));
+  assertEquals('65', f.inputDigit('5'));
+  assertEquals('650', f.inputDigit('0'));
+  assertEquals('6502', f.inputDigit('2'));
+  assertEquals('65025', f.inputDigitAndRememberPosition('5'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('650 253', f.inputDigit('3'));
+  assertEquals(6, f.getRememberedPosition());
+  assertEquals('650 253 2', f.inputDigit('2'));
+  assertEquals('650 253 22', f.inputDigit('2'));
+  assertEquals('650 253 222', f.inputDigit('2'));
+  // No more formatting when semicolon is entered.
+  assertEquals('650253222;', f.inputDigit(';'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('650253222;2', f.inputDigit('2'));
+
+  f.clear();
+  assertEquals('6', f.inputDigit('6'));
+  assertEquals('65', f.inputDigit('5'));
+  assertEquals('650', f.inputDigit('0'));
+  // No more formatting when users choose to do their own formatting.
+  assertEquals('650-', f.inputDigit('-'));
+  assertEquals('650-2', f.inputDigitAndRememberPosition('2'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('650-25', f.inputDigit('5'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('650-253', f.inputDigit('3'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('650-253-', f.inputDigit('-'));
+  assertEquals('650-253-2', f.inputDigit('2'));
+  assertEquals('650-253-22', f.inputDigit('2'));
+  assertEquals('650-253-222', f.inputDigit('2'));
+  assertEquals('650-253-2222', f.inputDigit('2'));
+
+  f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('01', f.inputDigit('1'));
+  assertEquals('011', f.inputDigit('1'));
+  assertEquals('0114', f.inputDigitAndRememberPosition('4'));
+  assertEquals('01148', f.inputDigit('8'));
+  assertEquals(4, f.getRememberedPosition());
+  assertEquals('011 48 8', f.inputDigit('8'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('011 48 88', f.inputDigit('8'));
+  assertEquals('011 48 881', f.inputDigit('1'));
+  assertEquals('011 48 88 12', f.inputDigit('2'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('011 48 88 123', f.inputDigit('3'));
+  assertEquals('011 48 88 123 1', f.inputDigit('1'));
+  assertEquals('011 48 88 123 12', f.inputDigit('2'));
+  assertEquals('011 48 88 123 12 1', f.inputDigit('1'));
+  assertEquals('011 48 88 123 12 12', f.inputDigit('2'));
+
+  f.clear();
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+1', f.inputDigit('1'));
+  assertEquals('+16', f.inputDigitAndRememberPosition('6'));
+  assertEquals('+165', f.inputDigit('5'));
+  assertEquals('+1650', f.inputDigit('0'));
+  assertEquals(3, f.getRememberedPosition());
+  assertEquals('+1 650 2', f.inputDigit('2'));
+  assertEquals(4, f.getRememberedPosition());
+  assertEquals('+1 650 25', f.inputDigit('5'));
+  assertEquals('+1 650 253', f.inputDigitAndRememberPosition('3'));
+  assertEquals('+1 650 253 2', f.inputDigit('2'));
+  assertEquals('+1 650 253 22', f.inputDigit('2'));
+  assertEquals('+1 650 253 222', f.inputDigit('2'));
+  assertEquals(10, f.getRememberedPosition());
+
+  f.clear();
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+1', f.inputDigit('1'));
+  assertEquals('+16', f.inputDigitAndRememberPosition('6'));
+  assertEquals('+165', f.inputDigit('5'));
+  assertEquals('+1650', f.inputDigit('0'));
+  assertEquals(3, f.getRememberedPosition());
+  assertEquals('+1 650 2', f.inputDigit('2'));
+  assertEquals(4, f.getRememberedPosition());
+  assertEquals('+1 650 25', f.inputDigit('5'));
+  assertEquals('+1 650 253', f.inputDigit('3'));
+  assertEquals('+1 650 253 2', f.inputDigit('2'));
+  assertEquals('+1 650 253 22', f.inputDigit('2'));
+  assertEquals('+1 650 253 222', f.inputDigit('2'));
+  assertEquals('+1650253222;', f.inputDigit(';'));
+  assertEquals(3, f.getRememberedPosition());
+}
+
+function testAsYouTypeFormatterGBFixedLine() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('GB');
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('02', f.inputDigit('2'));
+  assertEquals('020', f.inputDigit('0'));
+  assertEquals('0207', f.inputDigitAndRememberPosition('7'));
+  assertEquals(4, f.getRememberedPosition());
+  assertEquals('02070', f.inputDigit('0'));
+  assertEquals('020 703', f.inputDigit('3'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('020 7031', f.inputDigit('1'));
+  assertEquals('020 7031 3', f.inputDigit('3'));
+  assertEquals('020 7031 30', f.inputDigit('0'));
+  assertEquals('020 7031 300', f.inputDigit('0'));
+  assertEquals('020 7031 3000', f.inputDigit('0'));
+}
+
+function testAsYouTypeFormatterGBTollFree() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('gb');
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('08', f.inputDigit('8'));
+  assertEquals('080', f.inputDigit('0'));
+  assertEquals('0807', f.inputDigit('7'));
+  assertEquals('08070', f.inputDigit('0'));
+  assertEquals('080 703', f.inputDigit('3'));
+  assertEquals('080 7031', f.inputDigit('1'));
+  assertEquals('080 7031 3', f.inputDigit('3'));
+  assertEquals('080 7031 30', f.inputDigit('0'));
+  assertEquals('080 7031 300', f.inputDigit('0'));
+  assertEquals('080 7031 3000', f.inputDigit('0'));
+}
+
+function testAsYouTypeFormatterGBPremiumRate() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('GB');
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('09', f.inputDigit('9'));
+  assertEquals('090', f.inputDigit('0'));
+  assertEquals('0907', f.inputDigit('7'));
+  assertEquals('09070', f.inputDigit('0'));
+  assertEquals('090 703', f.inputDigit('3'));
+  assertEquals('090 7031', f.inputDigit('1'));
+  assertEquals('090 7031 3', f.inputDigit('3'));
+  assertEquals('090 7031 30', f.inputDigit('0'));
+  assertEquals('090 7031 300', f.inputDigit('0'));
+  assertEquals('090 7031 3000', f.inputDigit('0'));
+}
+
+function testAsYouTypeFormatterNZMobile() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('NZ');
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('02', f.inputDigit('2'));
+  assertEquals('021', f.inputDigit('1'));
+  assertEquals('0211', f.inputDigit('1'));
+  assertEquals('02112', f.inputDigit('2'));
+  // Note the unittest is using fake metadata which might produce non-ideal
+  // results.
+  assertEquals('02-112 3', f.inputDigit('3'));
+  assertEquals('02-112 34', f.inputDigit('4'));
+  assertEquals('02-112 345', f.inputDigit('5'));
+  assertEquals('02-112 3456', f.inputDigit('6'));
+}
+
+function testAsYouTypeFormatterDE() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('DE');
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('03', f.inputDigit('3'));
+  assertEquals('030', f.inputDigit('0'));
+  assertEquals('0301', f.inputDigit('1'));
+  assertEquals('03012', f.inputDigit('2'));
+  assertEquals('030 123', f.inputDigit('3'));
+  assertEquals('030 1234', f.inputDigit('4'));
+}
+
+function testAsYouTypeFormatterAR() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('AR');
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('01', f.inputDigit('1'));
+  assertEquals('011', f.inputDigit('1'));
+  assertEquals('0117', f.inputDigit('7'));
+  assertEquals('01170', f.inputDigit('0'));
+  assertEquals('011 703', f.inputDigit('3'));
+  assertEquals('011 7031', f.inputDigit('1'));
+  assertEquals('011 7031-3', f.inputDigit('3'));
+  assertEquals('011 7031-30', f.inputDigit('0'));
+  assertEquals('011 7031-300', f.inputDigit('0'));
+  assertEquals('011 7031-3000', f.inputDigit('0'));
+}
+
+function testAsYouTypeFormatterARMobile() {
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('AR');
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+5', f.inputDigit('5'));
+  assertEquals('+54', f.inputDigit('4'));
+  assertEquals('+549', f.inputDigit('9'));
+  assertEquals('+5491', f.inputDigit('1'));
+  assertEquals('+54 911', f.inputDigit('1'));
+  assertEquals('+54 9 11 2', f.inputDigit('2'));
+  assertEquals('+54 9 11 23', f.inputDigit('3'));
+  assertEquals('+54 9 11 231', f.inputDigit('1'));
+  assertEquals('+54 9 11 2312', f.inputDigit('2'));
+  assertEquals('+54 9 11 2312 1', f.inputDigit('1'));
+  assertEquals('+54 9 11 2312 12', f.inputDigit('2'));
+  assertEquals('+54 9 11 2312 123', f.inputDigit('3'));
+  assertEquals('+54 9 11 2312 1234', f.inputDigit('4'));
+}
+
+function testAsYouTypeFormatterKR() {
+  // +82 51 234 5678
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('KR');
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+8', f.inputDigit('8'));
+  assertEquals('+82', f.inputDigit('2'));
+  assertEquals('+825', f.inputDigit('5'));
+  assertEquals('+8251', f.inputDigit('1'));
+  assertEquals('+82 512', f.inputDigit('2'));
+  assertEquals('+82 51-23', f.inputDigit('3'));
+  assertEquals('+82 51-234', f.inputDigit('4'));
+  assertEquals('+82 51-234-5', f.inputDigit('5'));
+  assertEquals('+82 51-234-56', f.inputDigit('6'));
+  assertEquals('+82 51-234-567', f.inputDigit('7'));
+  assertEquals('+82 51-234-5678', f.inputDigit('8'));
+
+  // +82 2 531 5678
+  f.clear();
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+8', f.inputDigit('8'));
+  assertEquals('+82', f.inputDigit('2'));
+  assertEquals('+822', f.inputDigit('2'));
+  assertEquals('+8225', f.inputDigit('5'));
+  assertEquals('+82 253', f.inputDigit('3'));
+  assertEquals('+82 2-531', f.inputDigit('1'));
+  assertEquals('+82 2-531-5', f.inputDigit('5'));
+  assertEquals('+82 2-531-56', f.inputDigit('6'));
+  assertEquals('+82 2-531-567', f.inputDigit('7'));
+  assertEquals('+82 2-531-5678', f.inputDigit('8'));
+
+  // +82 2 3665 5678
+  f.clear();
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+8', f.inputDigit('8'));
+  assertEquals('+82', f.inputDigit('2'));
+  assertEquals('+822', f.inputDigit('2'));
+  assertEquals('+8223', f.inputDigit('3'));
+  assertEquals('+82 236', f.inputDigit('6'));
+  assertEquals('+82 2-366', f.inputDigit('6'));
+  assertEquals('+82 2-3665', f.inputDigit('5'));
+  assertEquals('+82 2-3665-5', f.inputDigit('5'));
+  assertEquals('+82 2-3665-56', f.inputDigit('6'));
+  assertEquals('+82 2-3665-567', f.inputDigit('7'));
+  assertEquals('+82 2-3665-5678', f.inputDigit('8'));
+
+  // 02-114 : This is too short to format. Checking that there are no
+  // side-effects.
+  f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('02', f.inputDigit('2'));
+  assertEquals('021', f.inputDigit('1'));
+  assertEquals('0211', f.inputDigit('1'));
+  assertEquals('02114', f.inputDigit('4'));
+
+  // 02-1300
+  f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('02', f.inputDigit('2'));
+  assertEquals('021', f.inputDigit('1'));
+  assertEquals('0213', f.inputDigit('3'));
+  assertEquals('02130', f.inputDigit('0'));
+  assertEquals('02-1300', f.inputDigit('0'));
+
+  // 011-456-7890
+  f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('01', f.inputDigit('1'));
+  assertEquals('011', f.inputDigit('1'));
+  assertEquals('0114', f.inputDigit('4'));
+  assertEquals('01145', f.inputDigit('5'));
+  assertEquals('011-456', f.inputDigit('6'));
+  assertEquals('011-456-7', f.inputDigit('7'));
+  assertEquals('011-456-78', f.inputDigit('8'));
+  assertEquals('011-456-789', f.inputDigit('9'));
+  assertEquals('011-456-7890', f.inputDigit('0'));
+
+  // 011-9876-7890
+  f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('01', f.inputDigit('1'));
+  assertEquals('011', f.inputDigit('1'));
+  assertEquals('0119', f.inputDigit('9'));
+  assertEquals('01198', f.inputDigit('8'));
+  assertEquals('011-987', f.inputDigit('7'));
+  assertEquals('011-9876', f.inputDigit('6'));
+  assertEquals('011-9876-7', f.inputDigit('7'));
+  assertEquals('011-9876-78', f.inputDigit('8'));
+  assertEquals('011-9876-789', f.inputDigit('9'));
+  assertEquals('011-9876-7890', f.inputDigit('0'));
+}

File javascript/i18n/phonenumbers/demo.html

+<!DOCTYPE html>
+<html>
+<!--
+Copyright (C) 2010 Google Inc.
+
+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.
+-->
+<!--
+  Author: Nikolaos Trogkanis
+-->
+<head>
+<title>Phone Number Parser Demo</title>
+<script src="../../../../closure-library/closure/goog/base.js"></script>
+<script>
+  goog.require('goog.dom');
+  goog.require('goog.json');
+  goog.require('goog.proto2.ObjectSerializer');
+  goog.require('goog.string.StringBuffer');
+</script>
+<script src="phonemetadata.pb.js"></script>
+<script src="phonenumber.pb.js"></script>
+<script src="metadata.js"></script>
+<script src="phonenumberutil.js"></script>
+</head>
+<body>
+
+<script>
+function phoneNumberParser() {
+  var $ = goog.dom.getElement;
+  var phoneNumber = $('phoneNumber').value;
+  var regionCode = $('defaultCountry').value;
+  var output = new goog.string.StringBuffer();
+  try {
+    var phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
+    var number = phoneUtil.parse(phoneNumber, regionCode);
+    output.append('****Parsing Result:****\n');
+    output.append(goog.json.serialize(new goog.proto2.ObjectSerializer(
+        goog.proto2.ObjectSerializer.KeyOption.NAME).serialize(number)));
+    output.append('\n\n****Validation Results:****');
+    output.append('\nResult from isValidNumber(): ');
+    output.append(phoneUtil.isValidNumber(number));
+    output.append('\nResult from isValidNumberForRegion(): ')
+        .append(phoneUtil.isValidNumberForRegion(number, regionCode));
+    output.append('\nResult from isPossibleNumber(): ')
+        .append(phoneUtil.isPossibleNumber(number));
+    output.append('\nResult from getNumberType(): ');
+    var PNT = i18n.phonenumbers.PhoneNumberType;
+    switch(phoneUtil.getNumberType(number)) {
+      case PNT.FIXED_LINE:
+        output.append('FIXED_LINE');
+        break;
+      case PNT.MOBILE:
+        output.append('MOBILE');
+        break;
+      case PNT.FIXED_LINE_OR_MOBILE:
+        output.append('FIXED_LINE_OR_MOBILE');
+        break;
+      case PNT.TOLL_FREE:
+        output.append('TOLL_FREE');
+        break;
+      case PNT.PREMIUM_RATE:
+        output.append('PREMIUM_RATE');
+        break;
+      case PNT.SHARED_COST:
+        output.append('SHARED_COST');
+        break;
+      case PNT.VOIP:
+        output.append('VOIP');
+        break;
+      case PNT.PERSONAL_NUMBER:
+        output.append('PERSONAL_NUMBER');
+        break;
+      case PNT.UNKNOWN:
+        output.append('UNKNOWN');
+        break;
+    }
+    var PNF = i18n.phonenumbers.PhoneNumberFormat;
+    output.append('\n\n****Formatting Results:**** ');
+    output.append('\nE164 format: ');
+    output.append(phoneUtil.format(number, PNF.E164));
+    output.append('\nInternational format: ');
+    output.append(phoneUtil.format(number, PNF.INTERNATIONAL));
+    output.append('\nNational format: ');
+    output.append(phoneUtil.format(number, PNF.NATIONAL));
+    output.append('\nOut-of-country format from US: ');
+    output.append(phoneUtil.formatOutOfCountryCallingNumber(number, 'US'));
+  } catch (e) {
+    output.append('\n' + e);
+  }
+  $('output').value = output.toString();
+  return false;
+}
+</script>
+
+<h2>Phone Number Parser Demo</h2>
+
+<form>
+  <p>
+  Specify a Phone Number:
+  <input type="text" name="phoneNumber" id="phoneNumber" size="25" />
+  </p>
+  <p>
+  Specify a Default Country:
+  <input type="text" name="defaultCountry" id="defaultCountry" size="2" />
+  (ISO 3166-1 two-letter country code)
+  </p>
+  <input type="submit" value="Submit" onclick="return phoneNumberParser();" />
+  <input type="reset" value="Reset" />
+  <p>
+  <textarea id="output" rows="15" cols="60"></textarea>
+  </p>
+</form>
+
+</body>
+</html>

File javascript/i18n/phonenumbers/metadata.js

+// Copyright (C) 2010 Google Inc.
+//
+// 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 Generated metadata for file
+ * PhoneNumberMetaData.xml
+ * @author Nikolaos Trogkanis
+ */
+
+goog.provide('i18n.phonenumbers.metadata');
+
+/**
+ * A mapping from a country code to the region codes which denote the
+ * country/region represented by that country code. In the case of multiple
+ * countries sharing a calling code, such as the NANPA countries, the one
+ * indicated with 'isMainCountryForCode' in the metadata should be first.
+ * @type {Object.<number, Array.<string>>}
+ */
+i18n.phonenumbers.metadata.countryCodeToRegionCodeMap = {
+  1: ['US','AG','AI','AS','BB','BM','BS','CA','DM','DO','GD','GU','JM','KN','KY','LC','MP','MS','PR','TC','TT','VC','VG','VI'],
+  7: ['RU','KZ'],
+  20: ['EG'],
+  27: ['ZA'],
+  30: ['GR'],
+  31: ['NL'],
+  32: ['BE'],
+  33: ['FR'],
+  34: ['ES'],
+  36: ['HU'],
+  39: ['IT'],
+  40: ['RO'],
+  41: ['CH'],
+  43: ['AT'],
+  44: ['GB','GG','IM','JE'],
+  45: ['DK'],
+  46: ['SE'],
+  47: ['NO'],
+  48: ['PL'],
+  49: ['DE'],
+  51: ['PE'],
+  52: ['MX'],
+  53: ['CU'],
+  54: ['AR'],
+  55: ['BR'],
+  56: ['CL'],
+  57: ['CO'],
+  58: ['VE'],
+  60: ['MY'],
+  61: ['AU'],
+  62: ['ID'],
+  63: ['PH'],
+  64: ['NZ'],
+  65: ['SG'],
+  66: ['TH'],
+  81: ['JP'],
+  82: ['KR'],
+  84: ['VN'],
+  86: ['CN'],
+  90: ['TR'],
+  91: ['IN'],
+  92: ['PK'],
+  93: ['AF'],
+  94: ['LK'],
+  95: ['MM'],
+  98: ['IR'],
+  212: ['MA'],
+  213: ['DZ'],
+  216: ['TN'],
+  218: ['LY'],
+  220: ['GM'],
+  221: ['SN'],
+  222: ['MR'],
+  223: ['ML'],
+  224: ['GN'],
+  225: ['CI'],
+  226: ['BF'],
+  227: ['NE'],
+  228: ['TG'],
+  229: ['BJ'],
+  230: ['MU'],
+  231: ['LR'],
+  232: ['SL'],
+  233: ['GH'],
+  234: ['NG'],
+  235: ['TD'],
+  236: ['CF'],
+  237: ['CM'],
+  238: ['CV'],
+  239: ['ST'],
+  240: ['GQ'],
+  241: ['GA'],
+  242: ['CG'],
+  243: ['CD'],
+  244: ['AO'],
+  245: ['GW'],
+  246: ['IO'],
+  248: ['SC'],
+  249: ['SD'],
+  250: ['RW'],
+  251: ['ET'],
+  252: ['SO'],
+  253: ['DJ'],
+  254: ['KE'],
+  255: ['TZ'],
+  256: ['UG'],
+  257: ['BI'],
+  258: ['MZ'],
+  260: ['ZM'],
+  261: ['MG'],
+  262: ['RE','TF','YT'],
+  263: ['ZW'],
+  264: ['NA'],
+  265: ['MW'],
+  266: ['LS'],
+  267: ['BW'],
+  268: ['SZ'],
+  269: ['KM'],
+  290: ['SH'],
+  291: ['ER'],
+  297: ['AW'],
+  298: ['FO'],
+  299: ['GL'],
+  350: ['GI'],
+  351: ['PT'],
+  352: ['LU'],
+  353: ['IE'],
+  354: ['IS'],
+  355: ['AL'],
+  356: ['MT'],
+  357: ['CY'],
+  358: ['FI'],
+  359: ['BG'],
+  370: ['LT'],
+  371: ['LV'],
+  372: ['EE'],
+  373: ['MD'],
+  374: ['AM'],
+  375: ['BY'],
+  376: ['AD'],
+  377: ['MC'],
+  378: ['SM'],
+  379: ['VA'],
+  380: ['UA'],
+  381: ['RS'],
+  382: ['ME'],
+  385: ['HR'],
+  386: ['SI'],
+  387: ['BA'],
+  389: ['MK'],
+  420: ['CZ'],
+  421: ['SK'],
+  423: ['LI'],
+  500: ['FK'],
+  501: ['BZ'],
+  502: ['GT'],
+  503: ['SV'],
+  504: ['HN'],
+  505: ['NI'],
+  506: ['CR'],
+  507: ['PA'],
+  508: ['PM'],
+  509: ['HT'],
+  590: ['GP','BL','MF'],
+  591: ['BO'],
+  592: ['GY'],
+  593: ['EC'],
+  594: ['GF'],
+  595: ['PY'],
+  596: ['MQ'],
+  597: ['SR'],
+  598: ['UY'],
+  599: ['AN'],
+  670: ['TL'],
+  672: ['NF'],
+  673: ['BN'],
+  674: ['NR'],
+  675: ['PG'],
+  676: ['TO'],
+  677: ['SB'],
+  678: ['VU'],
+  679: ['FJ'],
+  680: ['PW'],
+  681: ['WF'],
+  682: ['CK'],
+  683: ['NU'],
+  685: ['WS'],
+  686: ['KI'],
+  687: ['NC'],
+  688: ['TV'],
+  689: ['PF'],
+  690: ['TK'],
+  691: ['FM'],
+  692: ['MH'],
+  850: ['KP'],
+  852: ['HK'],
+  853: ['MO'],
+  855: ['KH'],
+  856: ['LA'],
+  880: ['BD'],
+  886: ['TW'],
+  960: ['MV'],
+  961: ['LB'],
+  962: ['JO'],
+  963: ['SY'],
+  964: ['IQ'],
+  965: ['KW'],
+  966: ['SA'],
+  967: ['YE'],
+  968: ['OM'],
+  970: ['PS'],
+  971: ['AE'],
+  972: ['IL'],
+  973: ['BH'],
+  974: ['QA'],
+  975: ['BT'],
+  976: ['MN'],
+  977: ['NP'],
+  992: ['TJ'],
+  993: ['TM'],
+  994: ['AZ'],
+  995: ['GE'],
+  996: ['KG'],
+  998: ['UZ']
+};
+
+/**
+ * A mapping from a region code to the PhoneMetadata for that region.
+ * @type {Object.<string, Array>}
+ */
+i18n.phonenumbers.metadata.countryToMetadata = {
+  "AD": [,[,,"(?:[346-9]|180)\\d{5}","\\d{6,8}"]
+,[,,"[78]\\d{5}","\\d{6}",,,"712345"]
+,[,,"[346]\\d{5}","\\d{6}",,,"312345"]
+,[,,"180[02]\\d{4}","\\d{8}",,,"18001234"]
+,[,,"9\\d{5}","\\d{6}",,,"912345"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AD",376,"00",,,,,,,,[[,"(\\d{3})(\\d{3})","$1 $2","[346-9]","",""],
+[,"(180[02])(\\d{4})","$1 $2","1","",""]]]
+,
+  "AE": [,[,,"[2-79]\\d{7,8}|800\\d{2,9}","\\d{5,12}"]
+,[,,"(?:[2-4679][2-8]\\d|600[25])\\d{5}","\\d{7,9}",,,"22345678"]
+,[,,"5[056]\\d{7}","\\d{9}",,,"501234567"]
+,[,,"400\\d{6}|800\\d{2,9}","\\d{5,12}",,,"800123456"]
+,[,,"900[02]\\d{5}","\\d{9}",,,"900234567"]
+,[,,"700[05]\\d{5}","\\d{9}",,,"700012345"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AE",971,"00","0",,,"0",,,,[[,"([2-4679])(\\d{3})(\\d{4})","$1 $2 $3","[2-4679][2-8]","0$1",""],
+[,"(5[056])(\\d{3})(\\d{4})","$1 $2 $3","5","0$1",""],
+[,"([4679]00)(\\d)(\\d{5})","$1 $2 $3","[4679]0","0$1",""],
+[,"(800)(\\d{2})(\\d{0,7})","$1 $2 $3","8","0$1",""]]]
+,
+  "AF": [,[,,"[2-7]\\d{8}","\\d{9}"]
+,[,,"(?:[25][0-8]|[34][0-4]|6[0-5])[2-9]\\d{6}","\\d{9}",,,"234567890"]
+,[,,"7[057-9]\\d{7}","\\d{9}",,,"701234567"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AF",93,"00","0",,,"0",,,,[[,"([2-7]\\d)(\\d{3})(\\d{4})","$1 $2 $3",,"0$1",""]]]
+,
+  "AG": [,[,,"[289]\\d{9}","\\d{7,10}"]
+,[,,"268(?:4(?:6[0-3]|84)|56[0-2])\\d{4}","\\d{7,10}",,,"2684601234"]
+,[,,"268(?:464|7(?:2[0-9]|64|7[0-5]|8[358]))\\d{4}","\\d{10}",,,"2684641234"]
+,[,,"8(?:00|66|77|88)[2-9]\\d{6}","\\d{10}",,,"8002123456"]
+,[,,"900[2-9]\\d{6}","\\d{10}",,,"9002123456"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"26848[01]\\d{4}","\\d{10}",,,"2684801234"]
+,"AG",1,"011",,,,,,,,,,,,"268"]
+,
+  "AI": [,[,,"[289]\\d{9}","\\d{7,10}"]
+,[,,"2644(?:6[12]|9[78])\\d{4}","\\d{7,10}",,,"2644612345"]
+,[,,"264(?:235|476|5(?:3[6-9]|8[1-4])|7(?:29|72))\\d{4}","\\d{10}",,,"2642351234"]
+,[,,"8(?:00|66|77|88)[2-9]\\d{6}","\\d{10}",,,"8002123456"]
+,[,,"900[2-9]\\d{6}","\\d{10}",,,"9002123456"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AI",1,"011",,,,,,,,,,,,"264"]
+,
+  "AL": [,[,,"[2-57]\\d{7}|6\\d{8}|8\\d{5,7}|9\\d{5}","\\d{5,9}"]
+,[,,"(?:2(?:[168][1-9]|[247]\\d|9[1-7])|3(?:1[1-3]|[2-6]\\d|[79][1-8]|8[1-9])|4\\d{2}|5(?:1[1-4]|[2-578]\\d|6[1-5]|9[1-7])|8(?:[19][1-5]|[2-6]\\d|[78][1-7]))\\d{5}","\\d{5,8}",,,"22345678"]
+,[,,"6[6-9]\\d{7}","\\d{9}",,,"661234567"]
+,[,,"800\\d{4}","\\d{7}",,,"8001234"]
+,[,,"900\\d{3}","\\d{6}",,,"900123"]
+,[,,"808\\d{3}","\\d{6}",,,"808123"]
+,[,,"700\\d{5}","\\d{8}",,,"70012345"]
+,[,,"NA","NA"]
+,"AL",355,"00","0",,,"0",,,,[[,"(4)(\\d{3})(\\d{4})","$1 $2 $3","4[0-6]","0$1",""],
+[,"(6[6-9])(\\d{3})(\\d{4})","$1 $2 $3","6","0$1",""],
+[,"(\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","[2358][2-5]|4[7-9]","0$1",""],
+[,"(\\d{3})(\\d{5})","$1 $2","[2358][16-9]","0$1",""],
+[,"(800)(\\d{4})","$1 $2","800","0$1",""],
+[,"(\\d{3})(\\d{3})","$1 $2","9|808","0$1",""],
+[,"(700)(\\d{5})","$1 $2","7","0$1",""]]]
+,
+  "AM": [,[]
+,[]
+,[]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AM",374,"00","8",,,"8",,,1]
+,
+  "AN": [,[]
+,[]
+,[]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AN",599,"00",,,,,,,1]
+,
+  "AO": [,[,,"[29]\\d{8}","\\d{9}"]
+,[,,"2\\d(?:[26-9]\\d|\\d[26-9])\\d{5}","\\d{9}",,,"222123456"]
+,[,,"9[1-3]\\d{7}","\\d{9}",,,"923123456"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AO",244,"00",,,,,,,,[[,"(\\d{3})(\\d{3})(\\d{3})","$1 $2 $3",,"",""]]]
+,
+  "AR": [,[,,"[1-9]\\d{9,11}","\\d{6,12}"]
+,[,,"[1-9]\\d{9}","\\d{6,10}",,,"1123456789"]
+,[,,"9(?:11[2-9]\\d{7}|(?:2(?:2[013]|37|6[14]|9[179])|3(?:4[1235]|5[138]|8[1578]))[2-9]\\d{6}|\\d{4}[2-9]\\d{5})","\\d{6,12}",,,"91123456789"]
+,[,,"80\\d{8}","\\d{10}",,,"8012345678"]
+,[,,"6(?:0\\d|10)\\d{7}","\\d{10}",,,"6001234567"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AR",54,"00","0",,,"0(?:(11|2(?:2(?:02?|[13]|2[13-79]|4[1-6]|5[2457]|6[124-8]|7[1-4]|8[13-6]|9[1-367])|3(?:[06]2|1[467]|2[02-6]|3[13-8]|[49][2-6]|5[2-8]|7)|47[3-578]|6(?:1|2[2-7]|4[6-8]?|5[125-8])|9(?:0[1-3]|[19]|2\\d|3[1-6]|4[0-24-68]|5[2-4]|6[2-6]|72?|8[23]?))|3(?:3(?:2[79]|8[2578])|4(?:0[124-9]|[12]|3[5-8]?|4[24-7]|5[4-68]?|6\\d|7[126]|8[237-9]|9[1-36-8])|5(?:1|2[1245]|3[2-4]?|4[1-46-9]|6[2-4]|7[1-6]|8[2-5]?)|7(?:1[15-8]|2[125]|3[1245]|4[13]|5[124-8]|7[2-57]|8[1-36])|8(?:1|2[125-7]|3[23578]|4[13-6]|5[4-8]?|6[1-357-9]|7[5-8]?|8[4-7]?|9[124])))15)?","9$1",,,[[,"([68]\\d{2})(\\d{3})(\\d{4})","$1-$2-$3","[68]","0$1",""],
+[,"9(11)(\\d{4})(\\d{4})","$1 15-$2-$3","91","0$1",""],
+[,"9(\\d{3})(\\d{3})(\\d{4})","$1 15-$2-$3","9(?:2(?:2[013]|37|6[14]|9[179])|3(?:4[1235]|5[138]|8[1578]))","0$1",""],
+[,"9(\\d{4})(\\d{2})(\\d{4})","$1 15-$2-$3","9(?:2(?:2[24-9]|3[0-69]|47|6[25]|9[02-68])|3(?:3[28]|4[046-9]|5[2467]|7[1-578]|8[23469]))","0$1",""],
+[,"(11)(\\d{4})(\\d{4})","$1 $2-$3","1","0$1",""],
+[,"(\\d{3})(\\d{3})(\\d{4})","$1 $2-$3","2(?:2[013]|37|6[14]|9[179])|3(?:4[1235]|5[138]|8[1578])","0$1",""],
+[,"(\\d{4})(\\d{2})(\\d{4})","$1 $2-$3","[23]","0$1",""]]
+,[[,"([68]\\d{2})(\\d{3})(\\d{4})","$1-$2-$3","[68]",,""],
+[,"9(11)(\\d{4})(\\d{4})","9 $1 $2-$3","91",,""],
+[,"9(\\d{3})(\\d{3})(\\d{4})","9 $1 $2-$3","9(?:2(?:2[013]|37|6[14]|9[179])|3(?:4[1235]|5[138]|8[1578]))",,""],
+[,"9(\\d{4})(\\d{2})(\\d{4})","9 $1 $2-$3","9(?:2(?:2[24-9]|3[0-69]|47|6[25]|9[02-68])|3(?:3[28]|4[046-9]|5[2467]|7[1-578]|8[23469]))",,""],
+[,"(11)(\\d{4})(\\d{4})","$1 $2-$3","1",,""],
+[,"(\\d{3})(\\d{3})(\\d{4})","$1 $2-$3","2(?:2[013]|37|6[14]|9[179])|3(?:4[1235]|5[138]|8[1578])",,""],
+[,"(\\d{4})(\\d{2})(\\d{4})","$1 $2-$3","[23]",,""]]]
+,
+  "AS": [,[,,"[689]\\d{9}","\\d{7,10}"]
+,[,,"6846(?:22|33|44|55|77|88|9[19])\\d{4}","\\d{7,10}",,,"6846221234"]
+,[,,"684(?:733|258)\\d{4}","\\d{10}",,,"6847331234"]
+,[,,"8(?:00|66|77|88)[2-9]\\d{6}","\\d{10}",,,"8002123456"]
+,[,,"900[2-9]\\d{6}","\\d{10}",,,"9002123456"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AS",1,"011",,,,,,,,,,,,"684"]
+,
+  "AT": [,[,,"\\d{4,13}","\\d{3,13}"]
+,[,,"1\\d{3,12}|(?:2(?:1[467]|2[134-8]|5[2357]|6[1-46-8]|7[1-8]|8[124-7]|8[1458])|3(?:1[1-8]|3[23568]|4[5-7]|5[1378]|6[1-38]|8[3-68])|4(?:2[1-8]|35|63|7[1368]|8[2457])|5(?:1[27]|2[1-8]|3[357]|4[147]|5[12578]|6[37])|6(?:13|2[1-47]|4[1-35-8]|5[468]|62)|7(?:2[1-8]|3[25]|4[13478]|5[68]|6[16-8]|7[1-6]|9[45]))\\d{3,10}|5(?:0[1-9]|[79]\\d)\\d{2,10}|720\\d{6,10}","\\d{3,13}",,,"1234567890"]
+,[,,"6(?:44|5[0-3579]|6[013-9]|[7-9]\\d)\\d{4,10}","\\d{7,13}",,,"644123456"]
+,[,,"80[02]\\d{6,10}","\\d{9,13}",,,"800123456"]
+,[,,"(?:711|9(?:0[01]|3[019]))\\d{6,10}","\\d{9,13}",,,"900123456"]
+,[,,"8(?:10|2[018])\\d{6,10}","\\d{9,13}",,,"810123456"]
+,[,,"NA","NA"]
+,[,,"780\\d{6,10}","\\d{9,13}",,,"780123456"]
+,"AT",43,"00","0",,,"0",,,,[[,"([15])(\\d{3,12})","$1 $2","1|5[079]","0$1",""],
+[,"(\\d{3})(\\d{3,10})","$1 $2","316|46|51|732|6(?:44|5[0-3579]|[6-9])|7(?:1|[28]0)|[89]","0$1",""],
+[,"(\\d{4})(\\d{3,9})","$1 $2","2|3(?:1[1-578]|[3-8])|4[2378]|5[2-6]|6(?:[12]|4[1-35-9]|5[468])|7(?:2[1-8]|35|4[1-8]|[57-9])","0$1",""]]]
+,
+  "AU": [,[,,"[1-578]\\d{5,9}","\\d{6,10}"]
+,[,,"[2378]\\d{8}","\\d{8,9}",,,"212345678"]
+,[,,"4[0-68]\\d{7}","\\d{9}",,,"412345678"]
+,[,,"1(?:80(?:0\\d{2})?|3(?:00\\d{2})?)\\d{4}","\\d{6,10}",,,"1800123456"]
+,[,,"190[0126]\\d{6}","\\d{10}",,,"1900123456"]
+,[,,"NA","NA"]
+,[,,"500\\d{6}","\\d{9}",,,"500123456"]
+,[,,"550\\d{6}","\\d{9}",,,"550123456"]
+,"AU",61,"(?:14(?:1[14]|34|4[17]|[56]6|7[47]|88))?001[14-689]","0",,,"0",,"0011",,[[,"([2378])(\\d{4})(\\d{4})","$1 $2 $3","[2378]","(0$1)",""],
+[,"(4\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","4","0$1",""],
+[,"(5[05]0)(\\d{3})(\\d{3})","$1 $2 $3","5","0$1",""],
+[,"(1[389]\\d{2})(\\d{3})(\\d{3})","$1 $2 $3","1(?:[38]00|9)","$1",""],
+[,"(180)(\\d{4})","$1 $2","180[1-9]","$1",""],
+[,"(13)(\\d{2})(\\d{2})","$1 $2 $3","13[1-9]","$1",""]]]
+,
+  "AW": [,[,,"[5-9]\\d{6}","\\d{7}"]
+,[,,"5(?:2\\d{2}|8(?:[2-7]\\d|8[0-79]|9[48]))\\d{3}","\\d{7}",,,"5212345"]
+,[,,"(?:5[69]\\d|660|9(?:6\\d|9[02-9])|7[34]\\d)\\d{4}","\\d{7}",,,"5601234"]
+,[,,"800\\d{4}","\\d{7}",,,"8001234"]
+,[,,"900\\d{4}","\\d{7}",,,"9001234"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AW",297,"00",,,,,,,,[[,"([5-9]\\d{2})(\\d{4})","$1 $2",,"",""]]]
+,
+  "AZ": [,[,,"[124-8]\\d{7,8}","\\d{8,9}"]
+,[,,"(?:1(?:(?:[28]\\d|36|9)\\d|02|1[0-589]|3[358]|4[013-79]|5[0-479]|6[0236-9]|7[0-24-8])|2(?:16|2\\d|3[0-24]|4[1468]|55|6[56]|79))\\d{5}","\\d{8,9}",,,"123123456"]
+,[,,"(?:4[04]|5[015]|60|7[07])\\d{7}","\\d{9}",,,"401234567"]
+,[,,"88\\d{7}","\\d{9}",,,"881234567"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"AZ",994,"00",,,,,,,,[[,"([4-8]\\d)(\\d{3})(\\d{4})","$1 $2 $3","[4-8]","",""],
+[,"([12]\\d)(\\d{3})(\\d{3,4})","$1 $2 $3","1[28]|22","",""],
+[,"([12]\\d{2})(\\d{5})","$1 $2","1[013-79]|2[013-9]","",""]]]
+,
+  "BA": [,[,,"[3-689]\\d{7}","\\d{6,8}"]
+,[,,"(?:[35]\\d|49|81)\\d{6}","\\d{6,8}",,,"30123456"]
+,[,,"6[1-356]\\d{6}","\\d{8}",,,"61123456"]
+,[,,"8[08]\\d{6}","\\d{8}",,,"80123456"]
+,[,,"9[0246]\\d{6}","\\d{8}",,,"90123456"]
+,[,,"82\\d{6}","\\d{8}",,,"82123456"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"BA",387,"00","0",,,"0",,,,[[,"([3-689]\\d)(\\d{3})(\\d{3})","$1 $2-$3",,"0$1",""]]]
+,
+  "BB": [,[,,"[289]\\d{9}","\\d{7,10}"]
+,[,,"246[2-9]\\d{6}","\\d{7,10}",,,"2462345678"]
+,[,,"246(?:(?:2[346]|45|82)\\d|25[0-4])\\d{4}","\\d{10}",,,"2462501234"]
+,[,,"8(?:00|66|77|88)[2-9]\\d{6}","\\d{10}",,,"8002123456"]
+,[,,"900[2-9]\\d{6}","\\d{10}",,,"9002123456"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"BB",1,"011",,,,,,,,,,,,"246"]
+,
+  "BD": [,[,,"[2-79]\\d{5,9}|1\\d{9}|8[0-7]\\d{4,8}","\\d{6,10}"]
+,[,,"2(?:7\\d1|8(?:[026]1|[1379][1-5]|8[1-8])|9(?:0[0-2]|1[1-4]|3[3-5]|5[56]|6[67]|71|8[078]))\\d{4}|3(?:[6-8]1|(?:0[23]|[25][12]|82|416)\\d|(?:31|12?[5-7])\\d{2})\\d{3}|4(?:(?:02|[49]6|[68]1)|(?:0[13]|21\\d?|[23]2|[457][12]|6[28])\\d|(?:23|[39]1)\\d{2}|1\\d{3})\\d{3}|5(?:(?:[457-9]1|62)|(?:1\\d?|2[12]|3[1-3]|52)\\d|61{2})|6(?:[45]1|(?:11|2[15]|[39]1)\\d|(?:[06-8]1|62)\\d{2})|7(?:(?:32|91)|(?:02|31|[67][12])\\d|[458]1\\d{2}|21\\d{3})\\d{3}|8(?:(?:4[12]|[5-7]2|1\\d?)|(?:0|3[12]|[5-7]1|217)\\d)\\d{4}|9(?:[35]1|(?:[024]2|81)\\d|(?:1|[24]1)\\d{2})\\d{3}","\\d{6,9}",,,"27111234"]
+,[,,"(?:1[13-9]\\d|(?:3[78]|44)[02-9]|6(?:44|6[02-9]))\\d{7}","\\d{10}",,,"1812345678"]
+,[,,"80[03]\\d{7}","\\d{10}",,,"8001234567"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"BD",880,"00[12]?","0",,,"0",,"00",,[[,"(2)(\\d{7})","$1 $2","2","0$1",""],
+[,"(\\d{2})(\\d{4,6})","$1 $2","[3-79]1","0$1",""],
+[,"(\\d{3})(\\d{3,7})","$1 $2","[3-79][2-9]|8","0$1",""],
+[,"(\\d{4})(\\d{6})","$1 $2","1","0$1",""]]]
+,
+  "BE": [,[,,"[1-9]\\d{7,8}","\\d{8,9}"]
+,[,,"(?:1[0-69]|[23][2-8]|[49][23]|5\\d|6[013-57-9]|7[18])\\d{6}|8(?:0[1-9]|[1-79]\\d)\\d{5}","\\d{8}",,,"12345678"]
+,[,,"4(?:7\\d|8[4-9]|9[1-9])\\d{6}","\\d{9}",,,"470123456"]
+,[,,"800\\d{5}","\\d{8}",,,"80012345"]
+,[,,"(?:90|7[07])\\d{6}","\\d{8}",,,"90123456"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"BE",32,"00","0",,,"0",,,,[[,"(4[7-9]\\d)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","4[7-9]","0$1",""],
+[,"([2-49])(\\d{3})(\\d{2})(\\d{2})","$1 $2 $3 $4","[23]|[49][23]","0$1",""],
+[,"([15-8]\\d)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","[156]|7[18]|8(?:0[1-9]|[1-79])","0$1",""],
+[,"([89]\\d{2})(\\d{2})(\\d{3})","$1 $2 $3","(?:80|9)0","0$1",""],
+[,"(7[07])(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","7[07]","0$1",""]]]
+,
+  "BF": [,[,,"[2457]\\d{7}","\\d{8}"]
+,[,,"(?:20(?:49|5[23]|9[016-9])|40(?:4[569]|55|7[0179])|50[34]\\d)\\d{4}","\\d{8}",,,"20491234"]
+,[,,"7(?:[04-6]\\d|1[0-489]|2[0-8]|8[013-9]|90)\\d{5}","\\d{8}",,,"70123456"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"BF",226,"00",,,,,,,,[[,"(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,"",""]]]
+,
+  "BG": [,[,,"[1-9]\\d{6,8}","\\d{7,9}"]
+,[,,"2\\d{6,7}|(?:[367]\\d|4[124-7]|5[1-9]|8[1-6]|9[1-7])\\d{5,6}|43[1-6]\\d{4,5}","\\d{7,8}",,,"2123456"]
+,[,,"(?:8[7-9]|98)\\d{7}|43[0789]\\d{5}|48\\d{6}","\\d{8,9}",,,"48123456"]
+,[,,"800\\d{5}","\\d{8}",,,"80012345"]
+,[,,"90\\d{6}","\\d{8}",,,"90123456"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"BG",359,"00","0",,,"0",,,,[[,"(2)(\\d{3})(\\d{3,4})","$1 $2 $3","2","0$1",""],
+[,"(43[1-6])(\\d{4,5})","$1 $2","43[1-6]","0$1",""],
+[,"(\\d{2})(\\d{2,3})(\\d{3})","$1 $2 $3","[35-7]|4[124-7]|8[1-6]|9[1-7]","0$1",""],
+[,"([89]\\d)(\\d{3})(\\d{4})","$1 $2 $3","8[7-9]|98","0$1",""],
+[,"([49]\\d)(\\d{3})(\\d{3})","$1 $2 $3","48|90","0$1",""],
+[,"(\\d{3})(\\d{5})","$1 $2","43[0789]|800","0$1",""]]]
+,
+  "BH": [,[]
+,[]
+,[]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,"BH",973,"00",,,,,,,1]
+,
+  "BI": [,[,,"[27]\\d{7}","\\d{8}"]
+,[,,"22(?:2[0-7]|[3-5]0)\\d{4}","\\d{8}",,,"22201234"]
+,[,,"(?:29\\d|7(?:1[1-3]|[4-9]\\d))\\d{5}","\\d{8}",,,"79561234"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]
+,[,,"NA","NA"]