Browse Source

Merge 068d0644bd into a1d3a79766

pull/2107/merge
David Humphrey 2 weeks ago
committed by GitHub
parent
commit
a38cf374b6
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
6 changed files with 2203 additions and 3 deletions
  1. +86
    -0
      javascript/i18n/phonenumbers/phonenumbermatch.js
  2. +68
    -0
      javascript/i18n/phonenumbers/phonenumbermatch_test.js
  3. +765
    -0
      javascript/i18n/phonenumbers/phonenumbermatcher.js
  4. +1106
    -0
      javascript/i18n/phonenumbers/phonenumbermatcher_test.js
  5. +172
    -2
      javascript/i18n/phonenumbers/phonenumberutil.js
  6. +6
    -1
      javascript/i18n/phonenumbers/phonenumberutil_test.html

+ 86
- 0
javascript/i18n/phonenumbers/phonenumbermatch.js View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2011 The Libphonenumber Authors
*
* 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.
*/
goog.provide('i18n.phonenumbers.PhoneNumberMatch');
/**
* The immutable match of a phone number within a piece of text. Matches may be found using
* {@link PhoneNumberUtil#findNumbers}.
*
* <p>A match consists of the {@linkplain #number() phone number} as well as the
* {@linkplain #start() start} and {@linkplain #end() end} offsets of the corresponding subsequence
* of the searched text. Use {@link #rawString()} to obtain a copy of the matched subsequence.
*
* <p>The following annotated example clarifies the relationship between the searched text, the
* match offsets, and the parsed number:
* <pre>
* CharSequence text = "Call me at +1 425 882-8080 for details.";
* String country = "US";
* PhoneNumberUtil util = PhoneNumberUtil.getInstance();
*
* // Find the first phone number match:
* PhoneNumberMatch m = util.findNumbers(text, country).iterator().next();
*
* // rawString() contains the phone number as it appears in the text.
* "+1 425 882-8080".equals(m.rawString());
*
* // start() and end() define the range of the matched subsequence.
* CharSequence subsequence = text.subSequence(m.start(), m.end());
* "+1 425 882-8080".contentEquals(subsequence);
*
* // number() returns the the same result as PhoneNumberUtil.{@link PhoneNumberUtil#parse parse()}
* // invoked on rawString().
* util.parse(m.rawString(), country).equals(m.number());
* </pre>
*/
i18n.phonenumbers.PhoneNumberMatch = function(start, rawString, number) {
if (start < 0) {
throw new Error('Start index must be >= 0.');
}
if (rawString == null) {
throw new Error('rawString must not be null');
}
if (number == null) {
throw new Error('number must not be null');
}
/** The start index into the text. */
this.start = start;
/** The raw substring matched. */
this.rawString = rawString;
/** The matched phone number. */
this.number = number;
/** The exclusive end index of the matched phone number within the searched text. */
this.end = start + rawString.length;
};
i18n.phonenumbers.PhoneNumberMatch.prototype.toString = function() {
return 'PhoneNumberMatch [' + this.start + ',' + this.end + ') ' + this.rawString;
};
i18n.phonenumbers.PhoneNumberMatch.prototype.equals = function(obj) {
if(this === obj) {
return true;
}
if(!(obj instanceof i18n.phonenumbers.PhoneNumberMatch)) {
return false;
}
return this.rawString == obj.rawString &&
this.start == obj.start &&
this.number.equals(obj.number);
};

+ 68
- 0
javascript/i18n/phonenumbers/phonenumbermatch_test.js View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2011 The Libphonenumber Authors
*
* 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.
*/
goog.require('goog.testing.jsunit');
goog.require('i18n.phonenumbers.PhoneNumber');
goog.require('i18n.phonenumbers.PhoneNumberMatch');
goog.require('i18n.phonenumbers.PhoneNumberUtil');
var phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
var PhoneNumber = i18n.phonenumbers.PhoneNumber;
var PhoneNumberMatch = i18n.phonenumbers.PhoneNumberMatch
/**
* Tests the value type semantics. Equality and hash code must be based on the covered range and
* corresponding phone number. Range and number correctness are tested by
* {@link PhoneNumberMatcherTest}.
*/
function testPhoneNumberMatchValueTypeSemantics() {
var number = new PhoneNumber();
var match1 = new PhoneNumberMatch(10, "1 800 234 45 67", number);
var match2 = new PhoneNumberMatch(10, "1 800 234 45 67", number);
assertEquals(match1.start, match2.start);
assertEquals(match1.end, match2.end);
assertEquals(match1.number, match2.number);
assertEquals(match1.rawString, match2.rawString);
assertEquals("1 800 234 45 67", match1.rawString);
}
/**
* Tests the value type semantics for matches with a null number.
*/
function testPhoneNumberMatchIllegalArguments() {
var number;
try {
number = new PhoneNumberMatch(-110, "1 800 234 45 67", new PhoneNumber());
fail();
} catch (e) { /* success */ }
try {
number = new PhoneNumberMatch(10, "1 800 234 45 67", null);
fail();
} catch (e) { /* success */ }
try {
number = new PhoneNumberMatch(10, null, new PhoneNumber());
fail();
} catch (e) { /* success */ }
try {
number = new PhoneNumberMatch(10, null, null);
fail();
} catch (e) { /* success */ }
}

+ 765
- 0
javascript/i18n/phonenumbers/phonenumbermatcher.js
File diff suppressed because it is too large
View File


+ 1106
- 0
javascript/i18n/phonenumbers/phonenumbermatcher_test.js
File diff suppressed because it is too large
View File


+ 172
- 2
javascript/i18n/phonenumbers/phonenumberutil.js View File

@ -43,8 +43,8 @@ goog.require('i18n.phonenumbers.PhoneNumber');
goog.require('i18n.phonenumbers.PhoneNumber.CountryCodeSource'); goog.require('i18n.phonenumbers.PhoneNumber.CountryCodeSource');
goog.require('i18n.phonenumbers.PhoneNumberDesc'); goog.require('i18n.phonenumbers.PhoneNumberDesc');
goog.require('i18n.phonenumbers.metadata'); goog.require('i18n.phonenumbers.metadata');
// XXX: closure wants this, but the tests fail with it. Circular ref?
//goog.require('i18n.phonenumbers.PhoneNumberMatcher');
/** /**
* @constructor * @constructor
@ -102,6 +102,8 @@ i18n.phonenumbers.PhoneNumberUtil.NANPA_COUNTRY_CODE_ = 1;
*/ */
i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 2; i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 2;
/** Flags to use when compiling regular expressions for phone numbers. */
i18n.phonenumbers.PhoneNumberUtil.REGEX_FLAGS = 'i'; // XXX: need ES6 regex for 'u' flag. Not sure about g...
/** /**
* The ITU says the maximum length should be 15, but we have found longer * The ITU says the maximum length should be 15, but we have found longer
@ -931,6 +933,18 @@ i18n.phonenumbers.PhoneNumberUtil.createExtnPattern_ =
+ onlyCommasExtn; + onlyCommasExtn;
}; };
// For parsing, we are slightly more lenient in our interpretation than for matching. Here we
// allow "comma" and "semicolon" as possible extension indicators. When matching, these are
// hardly ever used to indicate this.
i18n.phonenumbers.PhoneNumberUtil.EXTN_PATTERNS_FOR_MATCHING =
i18n.phonenumbers.PhoneNumberUtil.RFC3966_EXTN_PREFIX_ +
i18n.phonenumbers.PhoneNumberUtil.CAPTURING_EXTN_DIGITS_ + '|' +
'[ \u00A0\\t,]*' +
'(?:e?xt(?:ensi(?:o\u0301?|\u00F3))?n?|\uFF45?\uFF58\uFF54\uFF4E?|' +
'[x\uFF58#\uFF03~\uFF5E]|int|anexo|\uFF49\uFF4E\uFF54)' +
'[:\\.\uFF0E]?[ \u00A0\\t,-]*' +
i18n.phonenumbers.PhoneNumberUtil.CAPTURING_EXTN_DIGITS_ + '#?|' +
'[- ]+(' + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + '{1,5})#';
/** /**
* Regexp of all known extension prefixes used by different regions followed by * Regexp of all known extension prefixes used by different regions followed by
@ -1134,6 +1148,104 @@ i18n.phonenumbers.PhoneNumberUtil.ValidationResult = {
TOO_LONG: 3 TOO_LONG: 3
}; };
/**
* Leniency when {@linkplain PhoneNumberUtil#findNumbers finding} potential phone numbers in text
* segments. The levels here are ordered in increasing strictness.
*/
i18n.phonenumbers.PhoneNumberUtil.Leniency = {
/**
* Phone numbers accepted are {@linkplain PhoneNumberUtil#isPossibleNumber(PhoneNumber)
* possible}, but not necessarily {@linkplain PhoneNumberUtil#isValidNumber(PhoneNumber) valid}.
*/
POSSIBLE: {
value: 0,
verify: function(number, candidate, util) {
return util.isPossibleNumber(number);
}
},
/**
* Phone numbers accepted are {@linkplain PhoneNumberUtil#isPossibleNumber(PhoneNumber)
* possible} and {@linkplain PhoneNumberUtil#isValidNumber(PhoneNumber) valid}. Numbers written
* in national format must have their national-prefix present if it is usually written for a
* number of this type.
*/
VALID: {
value: 1,
verify: function(number, candidate, util) {
if (!util.isValidNumber(number)
|| !i18n.phonenumbers.PhoneNumberMatcher.containsOnlyValidXChars(
number, candidate, util))
{
return false;
}
return i18n.phonenumbers.PhoneNumberMatcher.isNationalPrefixPresentIfRequired(
number, util
);
}
},
/**
* Phone numbers accepted are {@linkplain PhoneNumberUtil#isValidNumber(PhoneNumber) valid} and
* are grouped in a possible way for this locale. For example, a US number written as
* "65 02 53 00 00" and "650253 0000" are not accepted at this leniency level, whereas
* "650 253 0000", "650 2530000" or "6502530000" are.
* Numbers with more than one '/' symbol in the national significant number are also dropped at
* this level.
* <p>
* Warning: This level might result in lower coverage especially for regions outside of country
* code "+1". If you are not sure about which level to use, email the discussion group
* libphonenumber-discuss@googlegroups.com.
*/
STRICT_GROUPING: {
value: 2,
verify: function(number, candidate, util) {
if (!util.isValidNumber(number)
|| !i18n.phonenumbers.PhoneNumberMatcher.containsOnlyValidXChars(number, candidate, util)
|| i18n.phonenumbers.PhoneNumberMatcher.containsMoreThanOneSlashInNationalNumber(number, candidate)
|| !i18n.phonenumbers.PhoneNumberMatcher.isNationalPrefixPresentIfRequired(number, util))
{
return false;
}
return i18n.phonenumbers.PhoneNumberMatcher.checkNumberGroupingIsValid(
number, candidate, util, {
checkGroups: function(util, number, normalizedCandidate, expectedNumberGroups) {
return i18n.phonenumbers.PhoneNumberMatcher.allNumberGroupsRemainGrouped(
util, number, normalizedCandidate, expectedNumberGroups);
}
}
);
}
},
/**
* Phone numbers accepted are {@linkplain PhoneNumberUtil#isValidNumber(PhoneNumber) valid} and
* are grouped in the same way that we would have formatted it, or as a single block. For
* example, a US number written as "650 2530000" is not accepted at this leniency level, whereas
* "650 253 0000" or "6502530000" are.
* Numbers with more than one '/' symbol are also dropped at this level.
* <p>
* Warning: This level might result in lower coverage especially for regions outside of country
* code "+1". If you are not sure about which level to use, email the discussion group
* libphonenumber-discuss@googlegroups.com.
*/
EXACT_GROUPING: {
value: 3,
verify: function(number, candidate, util) {
if (!util.isValidNumber(number)
|| !i18n.phonenumbers.PhoneNumberMatcher.containsOnlyValidXChars(number, candidate, util)
|| i18n.phonenumbers.PhoneNumberMatcher.containsMoreThanOneSlashInNationalNumber(number, candidate)
|| !i18n.phonenumbers.PhoneNumberMatcher.isNationalPrefixPresentIfRequired(number, util)) {
return false;
}
return i18n.phonenumbers.PhoneNumberMatcher.checkNumberGroupingIsValid(
number, candidate, util, {
checkGroups: function(util, number, normalizedCandidate, expectedNumberGroups) {
return i18n.phonenumbers.PhoneNumberMatcher.allNumberGroupsAreExactlyPresent(
util, number, normalizedCandidate, expectedNumberGroups);
}
}
);
}
}
};
/** /**
* Attempts to extract a possible number from the string passed in. This * Attempts to extract a possible number from the string passed in. This
@ -1259,6 +1371,19 @@ i18n.phonenumbers.PhoneNumberUtil.normalizeDigitsOnly = function(number) {
i18n.phonenumbers.PhoneNumberUtil.DIGIT_MAPPINGS, true); i18n.phonenumbers.PhoneNumberUtil.DIGIT_MAPPINGS, true);
}; };
i18n.phonenumbers.PhoneNumberUtil.normalizeDigits = function(number, keepNonDigits) {
var normalizedDigits = "";
for (var i = 0; i < number.length; i++) {
var c = number.charAt(i);
var digit = parseInt(c, 10);
if (!isNaN(digit)) {
normalizedDigits += c;
} else if (keepNonDigits) {
normalizedDigits += c;
}
}
return normalizedDigits;
}
/** /**
* Normalizes a string of characters representing a phone number. This strips * Normalizes a string of characters representing a phone number. This strips
@ -4394,6 +4519,21 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.parseHelper_ =
return phoneNumber; return phoneNumber;
}; };
/**
* Parses a string and returns it in proto buffer format. This method differs from {@link #parse}
* in that it always populates the raw_input field of the protocol buffer with numberToParse as
* well as the country_code_source field.
*
* @param numberToParse number that we are attempting to parse. This can contain formatting such
* as +, ( and -, as well as a phone number extension.
* @param defaultRegion region that we are expecting the number to be from. This is only used if
* the number being parsed is not written in international format. The country calling code
* for the number in this case would be stored as that of the default region supplied.
* @return a phone number proto buffer filled with the parsed number
*/
i18n.phonenumbers.PhoneNumberUtil.prototype.parseAndKeepRawInput = function(numberToParse, defaultRegion) {
return this.parseHelper_(numberToParse, defaultRegion, true, true);
};
/** /**
* Extracts the value of the phone-context parameter of numberToExtractFrom, * Extracts the value of the phone-context parameter of numberToExtractFrom,
@ -4731,6 +4871,36 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.isNationalNumberSuffixOfTheOther_ =
firstNumberNationalNumber); firstNumberNationalNumber);
}; };
/**
* Returns an iterable over all {@link PhoneNumberMatch PhoneNumberMatches} in {@code text}. This
* is a shortcut for {@link #findNumbers(CharSequence, String, Leniency, long)
* getMatcher(text, defaultRegion, Leniency.VALID, Long.MAX_VALUE)}.
*
* @param text the text to search for phone numbers, null for no text
* @param defaultRegion region that we are expecting the number to be from. This is only used if
* the number being parsed is not written in international format. The country_code for the
* number in this case would be stored as that of the default region supplied. May be null if
* only international numbers are expected.
* @param leniency the leniency to use when evaluating candidate phone numbers
* @param maxTries the maximum number of invalid numbers to try before giving up on the text.
* This is to cover degenerate cases where the text has a lot of false positives in it. Must
* be {@code >= 0}.
*/
i18n.phonenumbers.PhoneNumberUtil.prototype.findNumbers = function(text, defaultRegion, leniency, maxTries) {
if (!this.isValidRegionCode_(defaultRegion)) {
throw new Error('Invalid region code: ' + defaultRegion);
}
leniency = leniency || i18n.phonenumbers.PhoneNumberUtil.Leniency.VALID;
maxTries = maxTries || 9223372036854775807; // Java Long.MAX_VALUE = 9,223,372,036,854,775,807
return new i18n.phonenumbers.PhoneNumberMatcher(
this,
text,
defaultRegion,
leniency,
maxTries
);
};
/** /**
* Returns true if the number can be dialled from outside the region, or * Returns true if the number can be dialled from outside the region, or


+ 6
- 1
javascript/i18n/phonenumbers/phonenumberutil_test.html View File

@ -22,7 +22,8 @@ limitations under the License.
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>libphonenumber Unit Tests - i18n.phonenumbers - phonenumberutil.js</title> <title>libphonenumber Unit Tests - i18n.phonenumbers - phonenumberutil.js</title>
<script src="../../../../closure-library/closure/goog/base.js"></script>
<!-- XXX: restore this to local access, changed for ease of testing -->
<script src="https://cdn.rawgit.com/google/closure-library/master/closure/goog/base.js"></script>
<script> <script>
goog.require('goog.proto2.Message'); goog.require('goog.proto2.Message');
</script> </script>
@ -32,6 +33,10 @@ limitations under the License.
<script src="regioncodefortesting.js"></script> <script src="regioncodefortesting.js"></script>
<script src="phonenumberutil.js"></script> <script src="phonenumberutil.js"></script>
<script src="phonenumberutil_test.js"></script> <script src="phonenumberutil_test.js"></script>
<script src="phonenumbermatch.js"></script>
<script src="phonenumbermatch_test.js"></script>
<script src="phonenumbermatcher.js"></script>
<script src="phonenumbermatcher_test.js"></script>
</head> </head>
<body> <body>
</body> </body>


Loading…
Cancel
Save