Commits

Anonymous committed 3ce246f

Update JS project to Java project revision 46. Patch submitted by tronikos.

Comments (0)

Files changed (10)

javascript/README

 
 How to update:
 ==============
-The JavaScript library is ported from the Java implementation (revision 39).
+The JavaScript library is ported from the Java implementation (revision 46).
 When the Java project gets updated follow these steps to update the JavaScript
 project:
 
         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
+      /java/resources/com/google/i18n/phonenumbers/BuildMetadataJsonFromXml.java
 
 2.  If the phone number metadata in the XML format has changed
     (java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml)
 
     ant -f java/build.xml
     java -cp java/build/classes \
-      com.google.i18n.phonenumbers.BuildMetadataProtoFromXml \
+      com.google.i18n.phonenumbers.BuildMetadataJsonFromXml \
       java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml \
-      javascript/i18n/phonenumbers/metadata.js false json
+      javascript/i18n/phonenumbers/metadata.js false
     java -cp java/build/classes \
-      com.google.i18n.phonenumbers.BuildMetadataProtoFromXml \
+      com.google.i18n.phonenumbers.BuildMetadataJsonFromXml \
+      java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml \
+      javascript/i18n/phonenumbers/metadatalite.js true
+    java -cp java/build/classes \
+      com.google.i18n.phonenumbers.BuildMetadataJsonFromXml \
       java/resources/com/google/i18n/phonenumbers/test/PhoneNumberMetaDataForTesting.xml \
-      javascript/i18n/phonenumbers/metadatafortesting.js false json
+      javascript/i18n/phonenumbers/metadatafortesting.js false
 
 3.  Manually port any changes of the Java code to the JavaScript code:
       PhoneNumberUtil.java => phonenumberutil.js

javascript/i18n/phonenumbers/asyoutypeformatter.js

  */
 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].
+   * @const
    * @type {RegExp}
    * @private
    */
    * (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.
+   * @const
    * @type {RegExp}
    * @private
    */
   this.STANDALONE_DIGIT_PATTERN_ = /\d(?=[^,}][^,}])/g;
   /**
+   * This is the minimum length of national number accrued that is required to
+   * trigger the formatter. The first element of the leadingDigitsPattern of
+   * each numberFormat contains a regular expression that matches up to this
+   * number of digits.
+   * @const
+   * @type {number}
+   * @private
+   */
+  this.MIN_LEADING_DIGITS_LENGTH_ = 3;
+  /**
+   * The digits that have not been entered yet will be represented by a \u2008,
+   * the punctuation space.
+   * @const
+   * @type {string}
+   * @private
+   */
+  this.digitPlaceholder_ = '\u2008';
+  /**
+   * @type {RegExp}
+   * @private
+   */
+  this.digitPattern_ = new RegExp(this.digitPlaceholder_);
+
+  /**
+   * @type {string}
+   * @private
+   */
+  this.currentOutput_ = '';
+  /**
+   * @type {!goog.string.StringBuffer}
+   * @private
+   */
+  this.formattingTemplate_ = new goog.string.StringBuffer();
+  /**
+   * The pattern from numberFormat that is currently used to create
+   * formattingTemplate.
+   * @type {string}
+   * @private
+   */
+  this.currentFormattingPattern_ = '';
+  /**
    * @type {!goog.string.StringBuffer}
    * @private
    */
    */
   this.accruedInputWithoutFormatting_ = new goog.string.StringBuffer();
   /**
-   * @type {!goog.string.StringBuffer}
+   * @type {boolean}
    * @private
    */
-  this.currentOutput_ = new goog.string.StringBuffer();
+  this.ableToFormat_ = true;
+  /**
+   * @type {boolean}
+   * @private
+   */
+  this.isInternationalFormatting_ = false;
+  /**
+   * @type {boolean}
+   * @private
+   */
+  this.isExpectingCountryCode_ = false;
+  /**
+   * @type {i18n.phonenumbers.PhoneNumberUtil}
+   * @private
+   */
+  this.phoneUtil_ = i18n.phonenumbers.PhoneNumberUtil.getInstance();
+  /**
+   * @type {number}
+   * @private
+   */
+  this.lastMatchPosition_ = 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;
+  /**
+   * The position of a digit upon which inputDigitAndRememberPosition is most
+   * recently invoked, as found in accruedInputWithoutFormatting.
+   * entered.
+   * @type {number}
+   * @private
+   */
+  this.positionToRemember_ = 0;
   /**
    * @type {!goog.string.StringBuffer}
    * @private
    */
   this.nationalNumber_ = new goog.string.StringBuffer();
   /**
+   * @type {Array.<i18n.phonenumbers.NumberFormat>}
+   * @private
+   */
+  this.possibleFormats_ = [];
+
+  /**
    *  @type {string}
    * @private
    */
 };
 
 /**
- * @param {string} leadingFourDigitsOfNationalNumber
+ * @return {boolean} true if a new template is created as opposed to reusing the
+ *     existing template.
  * @private
  */
-i18n.phonenumbers.AsYouTypeFormatter.prototype.chooseFormatAndCreateTemplate_ =
-  function(leadingFourDigitsOfNationalNumber) {
+i18n.phonenumbers.AsYouTypeFormatter.prototype.maybeCreateNewTemplate_ =
+  function() {
 
-  /** @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.
+  // When there are multiple available formats, the formatter uses the first
+  // format where a formatting template could be created.
+  /** @type {number} */
+  var possibleFormatsLength = this.possibleFormats_.length;
+  for (var i = 0; i < possibleFormatsLength; ++i) {
     /** @type {i18n.phonenumbers.NumberFormat} */
-    var format = formatList[0];
-    if (!this.createFormattingTemplate_(format)) {
-      this.ableToFormat_ = false;
-    } else {
-      this.currentOutput_ =
-          new goog.string.StringBuffer(this.formattingTemplate_);
+    var numberFormat = this.possibleFormats_[i];
+    /** @type {string} */
+    var pattern = numberFormat.getPatternOrDefault();
+    if (this.currentFormattingPattern_ == pattern) {
+      return false;
+    }
+    if (this.createFormattingTemplate_(numberFormat)) {
+      this.currentFormattingPattern_ = pattern;
+      return true;
     }
   }
+  this.ableToFormat_ = false;
+  return false;
 };
 
 /**
- * @param {string} leadingFourDigits
- * @return {Array.<i18n.phonenumbers.NumberFormat>}
+ * @param {string} leadingThreeDigits
  * @private
  */
 i18n.phonenumbers.AsYouTypeFormatter.prototype.getAvailableFormats_ =
-  function(leadingFourDigits) {
+  function(leadingThreeDigits) {
 
   /** @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();
+  this.possibleFormats_ = formatList;
+  this.narrowDownPossibleFormats_(leadingThreeDigits);
+};
+
+/**
+ * @param {string} leadingDigits
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.narrowDownPossibleFormats_ =
+  function(leadingDigits) {
+
+  /** @type {Array.<i18n.phonenumbers.NumberFormat>} */
+  var possibleFormats = [];
   /** @type {number} */
-  var formatListLength = formatList.length;
-  for (var i = 0; i < formatListLength; ++i) {
+  var lengthOfLeadingDigits = leadingDigits.length;
+  /** @type {number} */
+  var indexOfLeadingDigitsPattern =
+      lengthOfLeadingDigits - this.MIN_LEADING_DIGITS_LENGTH_;
+  /** @type {number} */
+  var possibleFormatsLength = this.possibleFormats_.length;
+  for (var i = 0; i < possibleFormatsLength; ++i) {
     /** @type {i18n.phonenumbers.NumberFormat} */
-    var format = formatList[i];
-    if (format.hasLeadingDigits()) {
+    var format = this.possibleFormats_[i];
+    if (format.leadingDigitsPatternCount() > indexOfLeadingDigitsPattern) {
       /** @type {RegExp} */
-      var leadingDigitsPattern =
-          new RegExp('^(' + format.getLeadingDigits() + ')');
-      if (leadingDigitsPattern.test(leadingFourDigits)) {
-        matchedList.push(format);
+      var leadingDigitsPattern = new RegExp('^(' +
+          format.getLeadingDigitsPattern(indexOfLeadingDigitsPattern) + ')');
+      if (leadingDigitsPattern.test(leadingDigits)) {
+        possibleFormats.push(this.possibleFormats_[i]);
       }
     } else {
-      matchedList.push(format);
+      // else the particular format has no more specific leadingDigitsPattern,
+      // and it should be retained.
+      possibleFormats.push(this.possibleFormats_[i]);
     }
   }
-  return matchedList;
+  this.possibleFormats_ = possibleFormats;
 };
 
 /**
 
   // 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);
+  this.formattingTemplate_.clear();
+  this.formattingTemplate_.append(this.getFormattingTemplate_(numberPattern,
+      numberFormat));
   return true;
 };
 
  * Clears the internal state of the formatter, so it could be reused.
  */
 i18n.phonenumbers.AsYouTypeFormatter.prototype.clear = function() {
+  this.currentOutput_ = '';
   this.accruedInput_.clear();
   this.accruedInputWithoutFormatting_.clear();
-  this.currentOutput_.clear();
+  this.formattingTemplate_.clear();
   this.lastMatchPosition_ = 0;
+  this.currentFormattingPattern_ = '';
   this.prefixBeforeNationalNumber_.clear();
   this.nationalNumber_.clear();
   this.ableToFormat_ = true;
-  this.positionRemembered_ = 0;
+  this.positionToRemember_ = 0;
   this.originalPosition_ = 0;
   this.isInternationalFormatting_ = false;
+  this.isExpectingCountryCode_ = false;
+  this.possibleFormats_ = [];
   if (this.currentMetaData_ != this.defaultMetaData_) {
     this.initializeCountrySpecificInfo_(this.defaultCountry_);
   }
  * @return {string} the partially formatted phone number.
  */
 i18n.phonenumbers.AsYouTypeFormatter.prototype.inputDigit = function(nextChar) {
-  return this.inputDigitWithOptionToRememberPosition_(nextChar, false);
+  this.currentOutput_ =
+      this.inputDigitWithOptionToRememberPosition_(nextChar, false);
+  return this.currentOutput_;
 };
 
 /**
 i18n.phonenumbers.AsYouTypeFormatter.prototype.inputDigitAndRememberPosition =
   function(nextChar) {
 
-  return this.inputDigitWithOptionToRememberPosition_(nextChar, true);
+  this.currentOutput_ =
+      this.inputDigitWithOptionToRememberPosition_(nextChar, true);
+  return this.currentOutput_;
 };
 
 /**
  * @param {string} nextChar
  * @param {boolean} rememberPosition
  * @return {string}
+ * @private
  */
 i18n.phonenumbers.AsYouTypeFormatter.prototype.
     inputDigitWithOptionToRememberPosition_ = function(nextChar,
 
   this.accruedInput_.append(nextChar);
   if (rememberPosition) {
-    this.positionRemembered_ = this.accruedInput_.getLength();
-    this.originalPosition_ = this.positionRemembered_;
+    this.originalPosition_ = this.accruedInput_.getLength();
   }
   // We do formatting on-the-fly only when each character entered is either a
   // plus sign or a digit.
     this.ableToFormat_ = false;
   }
   if (!this.ableToFormat_) {
-    this.resetPositionOnFailureToFormat_();
     return this.accruedInput_.toString();
   }
 
-  nextChar = this.normalizeAndAccrueDigitsAndPlusSign_(nextChar);
+  nextChar = this.normalizeAndAccrueDigitsAndPlusSign_(nextChar,
+                                                       rememberPosition);
 
-  // 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.
+  // We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH
+  // 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:
+    return this.accruedInput_.toString();
   case 3:
+    if (this.attemptToExtractIdd_()) {
+      this.isExpectingCountryCode_ = true;
+    } else {
+      // No IDD or plus sign is found, must be entering in national format.
+      this.removeNationalPrefixFromNationalNumber_();
+      return this.attemptToChooseFormattingPattern_();
+    }
   case 4:
   case 5:
-    return this.accruedInput_.toString();
+    if (this.isExpectingCountryCode_) {
+      if (this.attemptToExtractCountryCode_()) {
+        this.isExpectingCountryCode_ = false;
+      }
+      return this.prefixBeforeNationalNumber_.toString() +
+          this.nationalNumber_.toString();
+    }
+  // We make a last attempt to extract a country code at the 6th digit because
+  // the maximum length of IDD and country code are both 3.
   case 6:
-    if (!this.extractIddAndValidCountryCode_()) {
+    if (this.isExpectingCountryCode_ && !this.attemptToExtractCountryCode_()) {
       this.ableToFormat_ = false;
       return this.accruedInput_.toString();
     }
-    this.removeNationalPrefixFromNationalNumber_();
-    return this.attemptToChooseFormattingPattern_(rememberPosition);
   default:
-    if (this.nationalNumber_.getLength() > 4) {
+    if (this.possibleFormats_.length > 0) {
       // The formatting pattern is already chosen.
       /** @type {string} */
-      var temp = this.inputDigitHelper_(nextChar, rememberPosition);
+      var tempNationalNumber = this.inputDigitHelper_(nextChar);
+      // See if the accrued digits can be formatted properly already. If not,
+      // use the results from inputDigitHelper, which does formatting based on
+      // the formatting pattern chosen.
+      /** @type {string} */
+      var formattedNumber = this.attemptToFormatAccruedDigits_();
+      if (formattedNumber.length > 0) {
+        return formattedNumber;
+      }
+      this.narrowDownPossibleFormats_(this.nationalNumber_.toString());
+      if (this.maybeCreateNewTemplate_()) {
+        return this.inputAccruedNationalNumber_();
+      }
       return this.ableToFormat_ ?
-          this.prefixBeforeNationalNumber_.toString() + temp : temp;
+          this.prefixBeforeNationalNumber_.toString() + tempNationalNumber :
+          tempNationalNumber;
     } else {
-      return this.attemptToChooseFormattingPattern_(rememberPosition);
+      return this.attemptToChooseFormattingPattern_();
     }
   }
 };
 
 /**
+ * @return {string}
  * @private
  */
-i18n.phonenumbers.AsYouTypeFormatter.prototype.resetPositionOnFailureToFormat_ =
+i18n.phonenumbers.AsYouTypeFormatter.prototype.attemptToFormatAccruedDigits_ =
   function() {
 
-  if (this.positionRemembered_ > 0) {
-    this.positionRemembered_ = this.originalPosition_;
-    this.currentOutput_.clear();
+  /** @type {string} */
+  var nationalNumber = this.nationalNumber_.toString();
+  /** @type {number} */
+  var possibleFormatsLength = this.possibleFormats_.length;
+  for (var i = 0; i < possibleFormatsLength; ++i) {
+    /** @type {i18n.phonenumbers.NumberFormat} */
+    var numFormat = this.possibleFormats_[i];
+    /** @type {string} */
+    var pattern = numFormat.getPatternOrDefault();
+    /** @type {RegExp} */
+    var patternRegExp = new RegExp('^(' + pattern + ')$');
+    if (patternRegExp.test(nationalNumber)) {
+      /** @type {string} */
+      var formattedNumber = nationalNumber.replace(new RegExp(pattern, 'g'),
+                                                   numFormat.getFormat());
+      return this.prefixBeforeNationalNumber_.toString() + formattedNumber;
+    }
   }
+  return '';
 };
 
 /**
 i18n.phonenumbers.AsYouTypeFormatter.prototype.getRememberedPosition =
   function() {
 
-  return this.positionRemembered_;
+  if (!this.ableToFormat_) {
+    return this.originalPosition_;
+  }
+  /** @type {number} */
+  var accruedInputIndex = 0;
+  /** @type {number} */
+  var currentOutputIndex = 0;
+  /** @type {string} */
+  var accruedInputWithoutFormatting =
+      this.accruedInputWithoutFormatting_.toString();
+  /** @type {string} */
+  var currentOutput = this.currentOutput_.toString();
+  while (accruedInputIndex < this.positionToRemember_) {
+    if (accruedInputWithoutFormatting.charAt(accruedInputIndex) ==
+        currentOutput.charAt(currentOutputIndex)) {
+      accruedInputIndex++;
+      currentOutputIndex++;
+    } else {
+      currentOutputIndex++;
+    }
+  }
+  return currentOutputIndex;
 };
 
 /**
  * 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) {
+    attemptToChooseFormattingPattern_ = function() {
 
   /** @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);
+  // We start to attempt to format only when as least MIN_LEADING_DIGITS_LENGTH
+  // digits of national number (excluding national prefix) have been entered.
+  if (nationalNumber.length >= this.MIN_LEADING_DIGITS_LENGTH_) {
+    this.getAvailableFormats_(
+        nationalNumber.substring(0, this.MIN_LEADING_DIGITS_LENGTH_));
+    this.maybeCreateNewTemplate_();
+    return this.inputAccruedNationalNumber_();
   } else {
-    if (rememberPosition) {
-      this.positionRemembered_ =
-          this.prefixBeforeNationalNumber_.length() + nationalNumberLength;
-    }
-    return this.prefixBeforeNationalNumber_.toString() +
-        this.nationalNumber_.toString();
+    return this.prefixBeforeNationalNumber_.toString() + nationalNumber;
   }
 };
 
  * 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) {
+  function() {
 
+  /** @type {string} */
+  var nationalNumber = this.nationalNumber_.toString();
   /** @type {number} */
-  var lengthOfNationalNumber = this.nationalNumber_.getLength();
+  var lengthOfNationalNumber = nationalNumber.length;
   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;
-      }
+          this.inputDigitHelper_(nationalNumber.charAt(i));
     }
     return this.ableToFormat_ ?
         this.prefixBeforeNationalNumber_.toString() + tempNationalNumber :
         tempNationalNumber;
   } else {
-    if (rememberPosition) {
-      this.positionRemembered_ = this.prefixBeforeNationalNumber_.length();
-    }
     return this.prefixBeforeNationalNumber_.toString();
   }
 };
       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_++;
-    }
+    this.isInternationalFormatting_ = true;
   } else if (this.currentMetaData_.hasNationalPrefix()) {
     /** @type {Array.<string>} */
     var m = nationalNumber.match(this.nationalPrefixForParsing_);
 };
 
 /**
- * Extracts IDD, plus sign and country code to prefixBeforeNationalNumber when
- * they are available, and places the remaining input into nationalNumber.
+ * Extracts IDD and plus sign 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.
+ * @return {boolean} true when accruedInputWithoutFormatting begins with the
+ *     plus sign or valid IDD for defaultCountry.
  * @private
  */
-i18n.phonenumbers.AsYouTypeFormatter.prototype.extractIddAndValidCountryCode_ =
+i18n.phonenumbers.AsYouTypeFormatter.prototype.attemptToExtractIdd_ =
   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;
+    this.nationalNumber_.clear();
+    this.nationalNumber_.append(
+        accruedInputWithoutFormatting.substring(startOfCountryCode));
+    this.prefixBeforeNationalNumber_.append(
+        accruedInputWithoutFormatting.substring(0, startOfCountryCode));
+    if (accruedInputWithoutFormatting.charAt(0) !=
+        i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN) {
+      this.prefixBeforeNationalNumber_.append(' ');
+    }
+    return true;
+  }
+  return false;
+};
+
+/**
+ * Extracts country code from the beginning of nationalNumber to
+ * prefixBeforeNationalNumber when they are available, and places the remaining
+ * input into nationalNumber.
+ *
+ * @return {boolean} true when a valid country code can be found.
+ * @private
+ */
+i18n.phonenumbers.AsYouTypeFormatter.prototype.attemptToExtractCountryCode_ =
+  function() {
+
+    if (this.nationalNumber_.getLength() == 0) {
+      return false;
+    }
     /** @type {!goog.string.StringBuffer} */
-    var numberIncludeCountryCode = new goog.string.StringBuffer(
-        accruedInputWithoutFormatting.substring(startOfCountryCode));
+    var numberWithoutCountryCode = new goog.string.StringBuffer();
     /** @type {number} */
     var countryCode = this.phoneUtil_.extractCountryCode(
-        numberIncludeCountryCode, this.nationalNumber_);
+        this.nationalNumber_, numberWithoutCountryCode);
     if (countryCode == 0) {
       return false;
     } else {
+      this.nationalNumber_.clear();
+      this.nationalNumber_.append(numberWithoutCountryCode.toString());
       /** @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;
 };
 
  * in non-ASCII format.
  *
  * @param {string} nextChar
+ * @param {boolean} rememberPosition
  * @return {string}
  * @private
  */
 i18n.phonenumbers.AsYouTypeFormatter.prototype.
-    normalizeAndAccrueDigitsAndPlusSign_ = function(nextChar) {
+    normalizeAndAccrueDigitsAndPlusSign_ = function(nextChar,
+                                                    rememberPosition) {
 
   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);
   }
+  if (rememberPosition) {
+    this.positionToRemember_ = this.accruedInputWithoutFormatting_.getLength();
+  }
   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();
-  }
+  function(nextChar) {
 
   /** @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);
+  var formattingTemplate = this.formattingTemplate_.toString();
+  if (formattingTemplate.substring(this.lastMatchPosition_)
+      .search(this.digitPattern_) >= 0) {
+    /** @type {number} */
+    var digitPatternStart = formattingTemplate.search(this.digitPattern_);
+    /** @type {string} */
+    var tempTemplate = formattingTemplate.replace(this.digitPattern_, nextChar);
+    this.formattingTemplate_.clear();
+    this.formattingTemplate_.append(tempTemplate);
+    this.lastMatchPosition_ = digitPatternStart;
+    return tempTemplate.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();
   }
 };

javascript/i18n/phonenumbers/asyoutypeformatter_test.js

 goog.require('goog.testing.jsunit');
 goog.require('i18n.phonenumbers.AsYouTypeFormatter');
 
-function testAsYouTypeFormatterUS() {
+function testAYTFUS() {
   /** @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 2', f.inputDigit('2'));
+  assertEquals('650 25', f.inputDigit('5'));
   assertEquals('650 253', f.inputDigit('3'));
-  assertEquals('650 253 2', f.inputDigit('2'));
+  // Note this is how a US local number (without area code) should be formatted.
+  assertEquals('650 2532', 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 65', f.inputDigit('5'));
+  assertEquals('1 650', f.inputDigit('0'));
+  assertEquals('1 650 2', 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'));
   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 ', f.inputDigit('1'));
+  assertEquals('011 4', f.inputDigit('4'));
+  assertEquals('011 44 ', 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 12', 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'));
   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 ', f.inputDigit('1'));
+  assertEquals('011 5', f.inputDigit('5'));
+  assertEquals('011 54 ', 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', 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 1234', f.inputDigit('4'));
 
   f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('01', f.inputDigit('1'));
+  assertEquals('011 ', f.inputDigit('1'));
+  assertEquals('011 2', f.inputDigit('2'));
+  assertEquals('011 24', f.inputDigit('4'));
+  assertEquals('011 244 ', f.inputDigit('4'));
+  assertEquals('011 244 2', f.inputDigit('2'));
+  assertEquals('011 244 28', f.inputDigit('8'));
+  assertEquals('011 244 280', f.inputDigit('0'));
+  assertEquals('011 244 280 0', f.inputDigit('0'));
+  assertEquals('011 244 280 00', f.inputDigit('0'));
+  assertEquals('011 244 280 000', f.inputDigit('0'));
+  assertEquals('011 244 280 000 0', f.inputDigit('0'));
+  assertEquals('011 244 280 000 00', f.inputDigit('0'));
+  assertEquals('011 244 280 000 000', f.inputDigit('0'));
+
+  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 ', f.inputDigit('8'));
+  assertEquals('+48 8', f.inputDigit('8'));
+  assertEquals('+48 88', f.inputDigit('8'));
+  assertEquals('+48 88 1', 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 12', f.inputDigit('2'));
 }
 
-function testAsYouTypeFormatterUSFullWidthCharacters() {
+function testAYTFUSFullWidthCharacters() {
   /** @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', f.inputDigit('\uFF10'));
+  assertEquals('650 2', f.inputDigit('\uFF12'));
+  assertEquals('650 25', f.inputDigit('\uFF15'));
   assertEquals('650 253', f.inputDigit('\uFF13'));
-  assertEquals('650 253 2', f.inputDigit('\uFF12'));
+  assertEquals('650 2532', 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() {
+function testAYTFUSMobileShortCode() {
   /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
   var f = new i18n.phonenumbers.AsYouTypeFormatter('US');
   assertEquals('*', f.inputDigit('*'));
   assertEquals('*121#', f.inputDigit('#'));
 }
 
-function testAsYouTypeFormatterUSVanityNumber() {
+function testAYTFUSVanityNumber() {
   /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
   var f = new i18n.phonenumbers.AsYouTypeFormatter('US');
   assertEquals('8', f.inputDigit('8'));
   assertEquals('800 MY APPLE', f.inputDigit('E'));
 }
 
-function testAsYouTypeFormatterAndRememberPositionUS() {
+function testAYTFAndRememberPositionUS() {
   /** @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 65', f.inputDigit('5'));
   assertEquals(1, f.getRememberedPosition());
-  assertEquals('1650', f.inputDigitAndRememberPosition('0'));
-  assertEquals(4, f.getRememberedPosition());
-  assertEquals('16502', f.inputDigit('2'));
+  assertEquals('1 650', f.inputDigitAndRememberPosition('0'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('1 650 2', 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.
 
   f.clear();
   assertEquals('1', f.inputDigit('1'));
-  assertEquals('16', f.inputDigit('6'));
-  assertEquals('165', f.inputDigitAndRememberPosition('5'));
-  assertEquals('1650', f.inputDigit('0'));
+  assertEquals('16', f.inputDigitAndRememberPosition('6'));
+  assertEquals(2, f.getRememberedPosition());
+  assertEquals('1 65', f.inputDigit('5'));
+  assertEquals('1 650', f.inputDigit('0'));
   assertEquals(3, f.getRememberedPosition());
-  assertEquals('16502', f.inputDigit('2'));
+  assertEquals('1 650 2', f.inputDigit('2'));
   assertEquals('1 650 25', f.inputDigit('5'));
-  assertEquals(4, f.getRememberedPosition());
+  assertEquals(3, 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(3, 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(2, f.getRememberedPosition());
   assertEquals('1650253222222', f.inputDigit('2'));
-  assertEquals(3, f.getRememberedPosition());
+  assertEquals(2, 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 2', f.inputDigit('2'));
+  assertEquals('650 25', f.inputDigit('5'));
   assertEquals('650 253', f.inputDigit('3'));
-  assertEquals(6, f.getRememberedPosition());
-  assertEquals('650 253 2', f.inputDigit('2'));
+  assertEquals('650 2532', f.inputDigitAndRememberPosition('2'));
+  assertEquals(8, f.getRememberedPosition());
   assertEquals('650 253 22', f.inputDigit('2'));
+  assertEquals(9, f.getRememberedPosition());
   assertEquals('650 253 222', f.inputDigit('2'));
   // No more formatting when semicolon is entered.
   assertEquals('650253222;', f.inputDigit(';'));
-  assertEquals(5, f.getRememberedPosition());
+  assertEquals(7, f.getRememberedPosition());
   assertEquals('650253222;2', f.inputDigit('2'));
 
   f.clear();
   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 ', f.inputDigit('1'));
+  assertEquals('011 4', f.inputDigitAndRememberPosition('4'));
+  assertEquals('011 48 ', f.inputDigit('8'));
+  assertEquals(5, 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 1', f.inputDigit('1'));
   assertEquals('011 48 88 12', f.inputDigit('2'));
   assertEquals(5, f.getRememberedPosition());
   assertEquals('011 48 88 123', f.inputDigit('3'));
   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 6', f.inputDigitAndRememberPosition('6'));
+  assertEquals('+1 65', f.inputDigit('5'));
+  assertEquals('+1 650', f.inputDigit('0'));
+  assertEquals(4, f.getRememberedPosition());
   assertEquals('+1 650 2', f.inputDigit('2'));
   assertEquals(4, f.getRememberedPosition());
   assertEquals('+1 650 25', f.inputDigit('5'));
   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 6', f.inputDigitAndRememberPosition('6'));
+  assertEquals('+1 65', f.inputDigit('5'));
+  assertEquals('+1 650', f.inputDigit('0'));
+  assertEquals(4, f.getRememberedPosition());
   assertEquals('+1 650 2', f.inputDigit('2'));
   assertEquals(4, f.getRememberedPosition());
   assertEquals('+1 650 25', f.inputDigit('5'));
   assertEquals(3, f.getRememberedPosition());
 }
 
-function testAsYouTypeFormatterGBFixedLine() {
+function testAYTFGBFixedLine() {
   /** @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 7', f.inputDigitAndRememberPosition('7'));
+  assertEquals(5, f.getRememberedPosition());
+  assertEquals('020 70', f.inputDigit('0'));
   assertEquals('020 703', f.inputDigit('3'));
   assertEquals(5, f.getRememberedPosition());
   assertEquals('020 7031', f.inputDigit('1'));
   assertEquals('020 7031 3000', f.inputDigit('0'));
 }
 
-function testAsYouTypeFormatterGBTollFree() {
-  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+function testAYTFGBTollFree() {
+   /** @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 7', f.inputDigit('7'));
+  assertEquals('080 70', 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 3000', f.inputDigit('0'));
 }
 
-function testAsYouTypeFormatterGBPremiumRate() {
+function testAYTFGBPremiumRate() {
   /** @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 7', f.inputDigit('7'));
+  assertEquals('090 70', 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 3000', f.inputDigit('0'));
 }
 
-function testAsYouTypeFormatterNZMobile() {
+function testAYTFNZMobile() {
   /** @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'));
+  assertEquals('02-11', f.inputDigit('1'));
+  assertEquals('02-112', 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 3456', f.inputDigit('6'));
 }
 
-function testAsYouTypeFormatterDE() {
+function testAYTFDE() {
   /** @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 1', f.inputDigit('1'));
+  assertEquals('030 12', f.inputDigit('2'));
   assertEquals('030 123', f.inputDigit('3'));
   assertEquals('030 1234', f.inputDigit('4'));
+
+  // 08021 2345
+  f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('08', f.inputDigit('8'));
+  assertEquals('080', f.inputDigit('0'));
+  assertEquals('0802', f.inputDigit('2'));
+  assertEquals('08021', f.inputDigit('1'));
+  assertEquals('08021 2', f.inputDigit('2'));
+  assertEquals('08021 23', f.inputDigit('3'));
+  assertEquals('08021 234', f.inputDigit('4'));
+  assertEquals('08021 2345', f.inputDigit('5'));
+
+  // 00 1 650 253 2250
+  f.clear();
+  assertEquals('0', f.inputDigit('0'));
+  assertEquals('00', f.inputDigit('0'));
+  assertEquals('00 1 ', f.inputDigit('1'));
+  assertEquals('00 1 6', f.inputDigit('6'));
+  assertEquals('00 1 65', f.inputDigit('5'));
+  assertEquals('00 1 650', f.inputDigit('0'));
+  assertEquals('00 1 650 2', f.inputDigit('2'));
+  assertEquals('00 1 650 25', f.inputDigit('5'));
+  assertEquals('00 1 650 253', f.inputDigit('3'));
+  assertEquals('00 1 650 253 2', f.inputDigit('2'));
+  assertEquals('00 1 650 253 22', f.inputDigit('2'));
+  assertEquals('00 1 650 253 222', f.inputDigit('2'));
+  assertEquals('00 1 650 253 2222', f.inputDigit('2'));
 }
 
-function testAsYouTypeFormatterAR() {
+function testAYTFAR() {
   /** @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 7', f.inputDigit('7'));
+  assertEquals('011 70', 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-3000', f.inputDigit('0'));
 }
 
-function testAsYouTypeFormatterARMobile() {
+function testAYTFARMobile() {
   /** @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 ', f.inputDigit('4'));
+  assertEquals('+54 9', f.inputDigit('9'));
+  assertEquals('+54 91', f.inputDigit('1'));
+  assertEquals('+54 9 11', 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 1234', f.inputDigit('4'));
 }
 
-function testAsYouTypeFormatterKR() {
+function testAYTFKR() {
   // +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 ', f.inputDigit('2'));
+  assertEquals('+82 5', f.inputDigit('5'));
+  assertEquals('+82 51', f.inputDigit('1'));
+  assertEquals('+82 51-2', 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'));
   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 ', f.inputDigit('2'));
+  assertEquals('+82 2', f.inputDigit('2'));
+  assertEquals('+82 25', f.inputDigit('5'));
+  assertEquals('+82 2-53', 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'));
   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 ', f.inputDigit('2'));
+  assertEquals('+82 2', f.inputDigit('2'));
+  assertEquals('+82 23', f.inputDigit('3'));
+  assertEquals('+82 2-36', 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-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.
+  // 02-114
   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'));
+  assertEquals('02-11', f.inputDigit('1'));
+  assertEquals('02-114', 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-13', f.inputDigit('3'));
+  assertEquals('02-130', f.inputDigit('0'));
   assertEquals('02-1300', f.inputDigit('0'));
 
   // 011-456-7890
   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-4', f.inputDigit('4'));
+  assertEquals('011-45', f.inputDigit('5'));
   assertEquals('011-456', f.inputDigit('6'));
   assertEquals('011-456-7', f.inputDigit('7'));
   assertEquals('011-456-78', f.inputDigit('8'));
   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-9', f.inputDigit('9'));
+  assertEquals('011-98', 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-789', f.inputDigit('9'));
   assertEquals('011-9876-7890', f.inputDigit('0'));
 }
+
+function testAYTFMultipleLeadingDigitPatterns() {
+  // +81 50 2345 6789
+  /** @type {i18n.phonenumbers.AsYouTypeFormatter} */
+  var f = new i18n.phonenumbers.AsYouTypeFormatter('JP');
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+8', f.inputDigit('8'));
+  assertEquals('+81 ', f.inputDigit('1'));
+  assertEquals('+81 5', f.inputDigit('5'));
+  assertEquals('+81 50', f.inputDigit('0'));
+  assertEquals('+81 50 2', f.inputDigit('2'));
+  assertEquals('+81 50 23', f.inputDigit('3'));
+  assertEquals('+81 50 234', f.inputDigit('4'));
+  assertEquals('+81 50 2345', f.inputDigit('5'));
+  assertEquals('+81 50 2345 6', f.inputDigit('6'));
+  assertEquals('+81 50 2345 67', f.inputDigit('7'));
+  assertEquals('+81 50 2345 678', f.inputDigit('8'));
+  assertEquals('+81 50 2345 6789', f.inputDigit('9'));
+
+  // +81 222 12 5678
+  f.clear();
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+8', f.inputDigit('8'));
+  assertEquals('+81 ', f.inputDigit('1'));
+  assertEquals('+81 2', f.inputDigit('2'));
+  assertEquals('+81 22', f.inputDigit('2'));
+  assertEquals('+81 22 2', f.inputDigit('2'));
+  assertEquals('+81 22 21', f.inputDigit('1'));
+  assertEquals('+81 2221 2', f.inputDigit('2'));
+  assertEquals('+81 222 12 5', f.inputDigit('5'));
+  assertEquals('+81 222 12 56', f.inputDigit('6'));
+  assertEquals('+81 222 12 567', f.inputDigit('7'));
+  assertEquals('+81 222 12 5678', f.inputDigit('8'));
+
+  // +81 3332 2 5678
+  f.clear();
+  assertEquals('+', f.inputDigit('+'));
+  assertEquals('+8', f.inputDigit('8'));
+  assertEquals('+81 ', f.inputDigit('1'));
+  assertEquals('+81 3', f.inputDigit('3'));
+  assertEquals('+81 33', f.inputDigit('3'));
+  assertEquals('+81 33 3', f.inputDigit('3'));
+  assertEquals('+81 3332', f.inputDigit('2'));
+  assertEquals('+81 3332 2', f.inputDigit('2'));
+  assertEquals('+81 3332 2 5', f.inputDigit('5'));
+  assertEquals('+81 3332 2 56', f.inputDigit('6'));
+  assertEquals('+81 3332 2 567', f.inputDigit('7'));
+  assertEquals('+81 3332 2 5678', f.inputDigit('8'));
+}

javascript/i18n/phonenumbers/demo.html

 <script src="phonenumber.pb.js"></script>
 <script src="metadata.js"></script>
 <script src="phonenumberutil.js"></script>
+<script src="asyoutypeformatter.js"></script>
 </head>
 <body>
 
   var $ = goog.dom.getElement;
   var phoneNumber = $('phoneNumber').value;
   var regionCode = $('defaultCountry').value;
+  var carrierCode = $('carrierCode').value;
   var output = new goog.string.StringBuffer();
   try {
     var phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
-    var number = phoneUtil.parse(phoneNumber, regionCode);
+    var number = phoneUtil.parseAndKeepRawInput(phoneNumber, regionCode);
     output.append('****Parsing Result:****\n');
     output.append(goog.json.serialize(new goog.proto2.ObjectSerializer(
         goog.proto2.ObjectSerializer.KeyOption.NAME).serialize(number)));
     }
     var PNF = i18n.phonenumbers.PhoneNumberFormat;
     output.append('\n\n****Formatting Results:**** ');
+    output.append('\nOriginal format: ');
+    output.append(phoneUtil.formatInOriginalFormat(number, regionCode));
     output.append('\nE164 format: ');
     output.append(phoneUtil.format(number, PNF.E164));
     output.append('\nInternational format: ');
     output.append(phoneUtil.format(number, PNF.NATIONAL));
     output.append('\nOut-of-country format from US: ');
     output.append(phoneUtil.formatOutOfCountryCallingNumber(number, 'US'));
+    if (carrierCode.length > 0) {
+      output.append('\nNational format with carrier code: ');
+      output.append(phoneUtil.formatNationalNumberWithCarrierCode(number,
+                                                                  carrierCode));
+    }
+    output.append('\n\n****AsYouTypeFormatter Results****');
+    var formatter = new i18n.phonenumbers.AsYouTypeFormatter(regionCode);
+    var phoneNumberLength = phoneNumber.length;
+    for (var i = 0; i < phoneNumberLength; ++i) {
+      var inputChar = phoneNumber.charAt(i);
+      output.append('\nChar entered: ');
+      output.append(inputChar);
+      output.append(' Output: ');
+      output.append(formatter.inputDigit(inputChar));
+    }
   } catch (e) {
     output.append('\n' + e);
   }
   <input type="text" name="defaultCountry" id="defaultCountry" size="2" />
   (ISO 3166-1 two-letter country code)
   </p>
+  <p>
+  Specify a Carrier Code:
+  <input type="text" name="carrierCode" id="carrierCode" size="2" />
+  (optional, only valid for some countries)
+  </p>
   <input type="submit" value="Submit" onclick="return phoneNumberParser();" />
   <input type="reset" value="Reset" />
   <p>
-  <textarea id="output" rows="15" cols="60"></textarea>
+  <textarea id="output" rows="30" cols="80"></textarea>
   </p>
 </form>
 

javascript/i18n/phonenumbers/metadata.js

 
 /**
  * @fileoverview Generated metadata for file
- * PhoneNumberMetaData.xml
+ * java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml
  * @author Nikolaos Trogkanis
  */
 
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"AD",376,"00",,,,,,,,[[,"(\\d{3})(\\d{3})","$1 $2","[346-9]","",""],
-[,"(180[02])(\\d{4})","$1 $2","1","",""]]]
+,"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"]
 ,[,,"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",""]]]
+,"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"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"AF",93,"00","0",,,"0",,,,[[,"([2-7]\\d)(\\d{3})(\\d{4})","$1 $2 $3",,"0$1",""]]]
+,"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"]
 ,[,,"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",""]]]
+,"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{3,5})","$1 $2",["[235][16-9]|8[016-9]|[79]"]
+,"0$1",""]
+]
+]
 ,
   "AM": [,[]
 ,[]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"AO",244,"00",,,,,,,,[[,"(\\d{3})(\\d{3})(\\d{3})","$1 $2 $3",,"",""]]]
+,"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"]
 ,[,,"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]",,""]]]
+,"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[2369]|3[458])","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-469]|3[3-578])","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[2369]|3[458])","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-469]|3[3-578])","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"]
 ,[,,"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",""]]]
+,"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"]
 ,[,,"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",""]]]
+,"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]0|9)","1(?:[38]00|9)"]
+,"$1",""]
+,[,"(180)(\\d{4})","$1 $2",["180","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"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"AW",297,"00",,,,,,,,[[,"([5-9]\\d{2})(\\d{4})","$1 $2",,"",""]]]
+,"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"]
+  "AZ": [,[,,"[1-8]\\d{7,8}","\\d{8,9}"]
+,[,,"(?:1(?:(?:[28]\\d|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)|365?\\d)\\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]","",""]]]
+,"AZ",994,"00",,,,,,,,[[,"(\\d{2})(\\d{3})(\\d{3,4})","$1 $2 $3",["1[28]|22|[3-8]"]
+,"",""]
+,[,"([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"]
 ,[,,"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",""]]]
+,"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"]
 ,[,,"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",""]]]
+,"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"]
 ,[,,"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",""]]]
+,"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[0178]|8(?:0[1-9]|[1-79])"]
+,"0$1",""]
+,[,"([89]\\d{2})(\\d{2})(\\d{3})","$1 $2 $3",["(?:80|9)0"]
+,"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"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"BF",226,"00",,,,,,,,[[,"(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,"",""]]]
+,"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"]
 ,[,,"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",""]]]
+,"BG",359,"00","0",,,"0",,,,[[,"(2)(\\d{3})(\\d{3,4})","$1 $2 $3",["2"]
+,"0$1",""]
+,[,"(\\d{3})(\\d{4,5})","$1 $2",["43|800"]
+,"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",""]
+,[,"(\\d{2})(\\d{3})(\\d{3,4})","$1 $2 $3",["48|8[7-9]|9[08]"]
+,"0$1",""]
+]
+]
 ,
   "BH": [,[]
 ,[]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"BI",257,"00",,,,,,,,[[,"([27]\\d)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,"",""]]]
+,"BI",257,"00",,,,,,,,[[,"([27]\\d)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,"",""]
+]
+]
 ,
   "BJ": [,[,,"[2689]\\d{7}|7\\d{3}","\\d{4,8}"]
 ,[,,"2(?:02|1[037]|2[45]|3[68])\\d{5}","\\d{8}",,,"20211234"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"857[58]\\d{4}","\\d{8}",,,"85751234"]
-,"BJ",229,"00",,,,,,,,[[,"(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,"",""]]]
+,"BJ",229,"00",,,,,,,,[[,"(\\d{2})(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",,"",""]
+]
+]
 ,
   "BL": [,[,,"[56]\\d{8}","\\d{9}"]
 ,[,,"590(?:2[7-9]|5[12]|87)\\d{4}","\\d{9}",,,"590271234"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"BN",673,"00","0",,,"0",,,,[[,"([2-578]\\d{2})(\\d{4})","$1 $2",,"0$1",""]]]
+,"BN",673,"00","0",,,"0",,,,[[,"([2-578]\\d{2})(\\d{4})","$1 $2",,"0$1",""]
+]
+]
 ,
   "BO": [,[]
 ,[]
 ,[,,"(?:400\\d|3003)\\d{4}","\\d{8,10}",,,"40041234"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"BR",55,"00(?:1[45]|2[135]|[34]1|43)","0",,,"0(?:(?:1[245]|2[135]|[34]1)(\\d{10}))?","$1",,,[[,"(\\d{2})(\\d{4})(\\d{4})","$1 $2-$3","[1-9][1-9]","($1)","0 $CC $1"],
-[,"([34]00\\d)(\\d{4})","$1-$2","400|3003","",""],
-[,"([3589]00)(\\d{2,3})(\\d{4})","$1 $2 $3","[3589]00","0$1",""]]]
+,"BR",55,"00(?:1[45]|2[135]|[34]1|43)","0",,,"0(?:(?:1[245]|2[135]|[34]1)(\\d{10}))?","$1",,,[[,"(\\d{2})(\\d{4})(\\d{4})","$1 $2-$3",["[1-9][1-9]"]
+,"($1)","0 $CC $1"]
+,[,"([34]00\\d)(\\d{4})","$1-$2",["[34]00","400|3003"]
+,"",""]
+,[,"([3589]00)(\\d{2,3})(\\d{4})","$1 $2 $3",["[3589]00"]
+,"0$1",""]
+]
+]
 ,
   "BS": [,[,,"[289]\\d{9}","\\d{7,10}"]
 ,[,,"242(?:3(?:02|[236][1-9]|4[0-24-9]|5[0-68]|7[3467]|8[0-4]|9[2-467])|461|502|6(?:12|7[67]|8[78]|9[89])|702)\\d{4}","\\d{7,10}",,,"2423456789"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"BT",975,"00",,,,,,,,[[,"(17)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4","1","",""],
-[,"([2-8])(\\d{3})(\\d{3})","$1 $2 $3","[2-8]","",""]]]
+,"BT",975,"00",,,,,,,,[[,"(17)(\\d{2})(\\d{2})(\\d{2})","$1 $2 $3 $4",["1"]
+,"",""]
+,[,"([2-8])(\\d{3})(\\d{3})","$1 $2 $3",["[2-8]"]
+,"",""]
+]
+]
 ,
   "BW": [,[,,"[2-9]\\d{6,7}","\\d{7,8}"]
 ,[,,"(?:2(?:4[0-48]|6[0-24]|9[0578])|3(?:1[0235-9]|55|6\\d|7[01]|9[0-57])|4(?:6[03]|7[1267]|9[0-5])|5(?:3[0389]|4[0489]|7[1-47]|88|9[0-49])|6(?:2[1-35]|5[149]|8[067]))\\d{4}","\\d{7}",,,"2401234"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
 ,[,,"NA","NA"]
-,"BW",267,"00",,,,,,,,[[,"(7[1-4])(\\d{3})(\\d{3})","$1 $2 $3","7","",""],
-[,"(90)(\\d{5})","$1 $2","9","",""]]]
+,"BW",267,"00",,,,,,,,[[,"(7[1-4])(\\d{3})(\\d{3})","$1 $2 $3",["7"]
+,"",""]
+,[,"(90)(\\d{5})","$1 $2",["9"]
+,"",""]
+]
+]
 ,
   "BY": [,[,,"[12-4]\\d{8}|[89]\\d{9}","\\d{7,10}"]
 ,[,,"(?:1(?:5(?:1[1-5]|2\\d|6[1-4]|9[1-7])|6(?:[235]\\d|4[1-7])|7\\d{2})|2(?:1(?:[246]\\d|3[0-35-9]|5[1-9])|2(?:[235]\\d|4[0-8])|3(?:2\\d|3[02-79]|4[024-7]|5[0-7])))\\d{5}","\\d{7,9}",,,"152450911"]
 ,[,,"NA","NA"]