"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = parse;
exports.extractFormattedPhoneNumber = extractFormattedPhoneNumber;
exports.stripNationalPrefixAndCarrierCode = stripNationalPrefixAndCarrierCode;
exports.findCountryCode = findCountryCode;
exports.extractCountryCallingCode = extractCountryCallingCode;

var _constants = require("./constants");

var _util = require("./util");

var _ParseError = _interopRequireDefault(require("./ParseError"));

var _metadata = _interopRequireDefault(require("./metadata"));

var _isViablePhoneNumber = _interopRequireDefault(require("./isViablePhoneNumber"));

var _extension = require("./extension");

var _parseIncompletePhoneNumber = _interopRequireDefault(require("./parseIncompletePhoneNumber"));

var _getCountryCallingCode = _interopRequireDefault(require("./getCountryCallingCode"));

var _getNumberType_ = _interopRequireWildcard(require("./getNumberType_"));

var _isPossibleNumber_ = require("./isPossibleNumber_");

var _IDD = require("./IDD");

var _RFC = require("./RFC3966");

var _PhoneNumber = _interopRequireDefault(require("./PhoneNumber"));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

// This is a port of Google Android `libphonenumber`'s
// `phonenumberutil.js` of December 31th, 2018.
//
// https://github.com/googlei18n/libphonenumber/commits/master/javascript/i18n/phonenumbers/phonenumberutil.js
// We don't allow input strings for parsing to be longer than 250 chars.
// This prevents malicious input from consuming CPU.
var MAX_INPUT_STRING_LENGTH = 250; // This consists of the plus symbol, digits, and arabic-indic digits.

var PHONE_NUMBER_START_PATTERN = new RegExp('[' + _constants.PLUS_CHARS + _constants.VALID_DIGITS + ']'); // Regular expression of trailing characters that we want to remove.

var AFTER_PHONE_NUMBER_END_PATTERN = new RegExp('[^' + _constants.VALID_DIGITS + ']+$'); // `options`:
//  {
//    country:
//    {
//      restrict - (a two-letter country code)
//                 the phone number must be in this country
//
//      default - (a two-letter country code)
//                default country to use for phone number parsing and validation
//                (if no country code could be derived from the phone number)
//    }
//  }
//
// Returns `{ country, number }`
//
// Example use cases:
//
// ```js
// parse('8 (800) 555-35-35', 'RU')
// parse('8 (800) 555-35-35', 'RU', metadata)
// parse('8 (800) 555-35-35', { country: { default: 'RU' } })
// parse('8 (800) 555-35-35', { country: { default: 'RU' } }, metadata)
// parse('+7 800 555 35 35')
// parse('+7 800 555 35 35', metadata)
// ```
//

function parse(text) {
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  var metadata = arguments.length > 2 ? arguments[2] : undefined;
  metadata = new _metadata["default"](metadata); // Validate `defaultCountry`.

  if (options.defaultCountry && !metadata.hasCountry(options.defaultCountry)) {
    if (options.v2) {
      throw new _ParseError["default"]('INVALID_COUNTRY');
    }

    throw new Error("Unknown country: ".concat(options.defaultCountry));
  } // Parse the phone number.


  var _parseInput = parseInput(text, options.v2),
      formattedPhoneNumber = _parseInput.number,
      ext = _parseInput.ext; // If the phone number is not viable then return nothing.


  if (!formattedPhoneNumber) {
    if (options.v2) {
      throw new _ParseError["default"]('NOT_A_NUMBER');
    }

    return {};
  }

  var _parsePhoneNumber = parsePhoneNumber(formattedPhoneNumber, options.defaultCountry, metadata),
      country = _parsePhoneNumber.country,
      nationalNumber = _parsePhoneNumber.nationalNumber,
      countryCallingCode = _parsePhoneNumber.countryCallingCode,
      carrierCode = _parsePhoneNumber.carrierCode;

  if (!metadata.selectedCountry()) {
    if (options.v2) {
      throw new _ParseError["default"]('INVALID_COUNTRY');
    }

    return {};
  } // Validate national (significant) number length.


  if (!nationalNumber || nationalNumber.length < _constants.MIN_LENGTH_FOR_NSN) {
    // Won't throw here because the regexp already demands length > 1.

    /* istanbul ignore if */
    if (options.v2) {
      throw new _ParseError["default"]('TOO_SHORT');
    } // Google's demo just throws an error in this case.


    return {};
  } // Validate national (significant) number length.
  //
  // A sidenote:
  //
  // They say that sometimes national (significant) numbers
  // can be longer than `MAX_LENGTH_FOR_NSN` (e.g. in Germany).
  // https://github.com/googlei18n/libphonenumber/blob/7e1748645552da39c4e1ba731e47969d97bdb539/resources/phonenumber.proto#L36
  // Such numbers will just be discarded.
  //


  if (nationalNumber.length > _constants.MAX_LENGTH_FOR_NSN) {
    if (options.v2) {
      throw new _ParseError["default"]('TOO_LONG');
    } // Google's demo just throws an error in this case.


    return {};
  }

  if (options.v2) {
    var phoneNumber = new _PhoneNumber["default"](countryCallingCode, nationalNumber, metadata.metadata);

    if (country) {
      phoneNumber.country = country;
    }

    if (carrierCode) {
      phoneNumber.carrierCode = carrierCode;
    }

    if (ext) {
      phoneNumber.ext = ext;
    }

    return phoneNumber;
  } // Check if national phone number pattern matches the number.
  // National number pattern is different for each country,
  // even for those ones which are part of the "NANPA" group.


  var valid = country && (0, _util.matchesEntirely)(nationalNumber, metadata.nationalNumberPattern()) ? true : false;

  if (!options.extended) {
    return valid ? result(country, nationalNumber, ext) : {};
  }

  return {
    country: country,
    countryCallingCode: countryCallingCode,
    carrierCode: carrierCode,
    valid: valid,
    possible: valid ? true : options.extended === true && metadata.possibleLengths() && (0, _isPossibleNumber_.is_possible_number)(nationalNumber, countryCallingCode !== undefined, metadata),
    phone: nationalNumber,
    ext: ext
  };
}
/**
 * Extracts a formatted phone number from text.
 * Doesn't guarantee that the extracted phone number
 * is a valid phone number (for example, doesn't validate its length).
 * @param  {string} text
 * @return {string}
 * @example
 * // Returns "(213) 373-4253".
 * extractFormattedPhoneNumber("Call (213) 373-4253 for assistance.")
 */


function extractFormattedPhoneNumber(text, v2) {
  if (!text) {
    return;
  }

  if (text.length > MAX_INPUT_STRING_LENGTH) {
    if (v2) {
      throw new _ParseError["default"]('TOO_LONG');
    }

    return;
  } // Attempt to extract a possible number from the string passed in


  var startsAt = text.search(PHONE_NUMBER_START_PATTERN);

  if (startsAt < 0) {
    return;
  }

  return text // Trim everything to the left of the phone number
  .slice(startsAt) // Remove trailing non-numerical characters
  .replace(AFTER_PHONE_NUMBER_END_PATTERN, '');
}
/**
 * Strips any national prefix (such as 0, 1) present in the number provided.
 * "Carrier codes" are only used  in Colombia and Brazil,
 * and only when dialing within those countries from a mobile phone to a fixed line number.
 * Sometimes it won't actually strip national prefix
 * and will instead prepend some digits to the `number`:
 * for example, when number `2345678` is passed with `VI` country selected,
 * it will return `{ number: "3402345678" }`, because `340` area code is prepended.
 * @param {string} number — National number digits.
 * @param {object} metadata — Metadata with country selected.
 * @return {object} `{ number, carrierCode }`.
 */


function stripNationalPrefixAndCarrierCode(number, metadata) {
  if (!number) {
    return {
      number: number
    };
  }

  if (!metadata.nationalPrefixForParsing()) {
    return {
      number: number
    };
  } // See METADATA.md for the description of
  // `national_prefix_for_parsing` and `national_prefix_transform_rule`.
  // Attempt to parse the first digits as a national prefix.


  var prefixPattern = new RegExp('^(?:' + metadata.nationalPrefixForParsing() + ')');
  var prefixMatch = prefixPattern.exec(number);

  if (!prefixMatch) {
    return {
      number: number
    };
  }

  var nationalSignificantNumber;
  var carrierCode; // If a "capturing group" didn't match
  // then its element in `prefixMatch[]` array will be `undefined`.

  var capturedGroupsCount = prefixMatch.length - 1;

  if (metadata.nationalPrefixTransformRule() && capturedGroupsCount > 0 && prefixMatch[capturedGroupsCount]) {
    nationalSignificantNumber = number.replace(prefixPattern, metadata.nationalPrefixTransformRule()); // Carrier code is the last captured group,
    // but only when there's more than one captured group.

    if (capturedGroupsCount > 1 && prefixMatch[capturedGroupsCount]) {
      carrierCode = prefixMatch[1];
    }
  } // If it's a simple-enough case then just
  // strip the national prefix from the number.
  else {
      // National prefix is the whole substring matched by
      // the `national_prefix_for_parsing` regexp.
      var nationalPrefix = prefixMatch[0];
      nationalSignificantNumber = number.slice(nationalPrefix.length); // Carrier code is the last captured group.

      if (capturedGroupsCount > 0) {
        carrierCode = prefixMatch[1];
      }
    } // The following is done in `get_country_and_national_number_for_local_number()` instead.
  //
  // // Verify the parsed national (significant) number for this country
  // const national_number_rule = new RegExp(metadata.nationalNumberPattern())
  // //
  // // If the original number (before stripping national prefix) was viable,
  // // and the resultant number is not, then prefer the original phone number.
  // // This is because for some countries (e.g. Russia) the same digit could be both
  // // a national prefix and a leading digit of a valid national phone number,
  // // like `8` is the national prefix for Russia and both
  // // `8 800 555 35 35` and `800 555 35 35` are valid numbers.
  // if (matchesEntirely(number, national_number_rule) &&
  // 	!matchesEntirely(nationalSignificantNumber, national_number_rule)) {
  // 	return number
  // }
  // Return the parsed national (significant) number


  return {
    number: nationalSignificantNumber,
    carrierCode: carrierCode
  };
}

function findCountryCode(countryCallingCode, nationalPhoneNumber, metadata) {
  // Is always non-empty, because `countryCallingCode` is always valid
  var possibleCountries = metadata.countryCallingCodes()[countryCallingCode]; // If there's just one country corresponding to the country code,
  // then just return it, without further phone number digits validation.

  if (possibleCountries.length === 1) {
    return possibleCountries[0];
  }

  return _findCountryCode(possibleCountries, nationalPhoneNumber, metadata.metadata);
} // Changes `metadata` `country`.


function _findCountryCode(possibleCountries, nationalPhoneNumber, metadata) {
  metadata = new _metadata["default"](metadata);

  for (var _iterator = possibleCountries, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
    var _ref;

    if (_isArray) {
      if (_i >= _iterator.length) break;
      _ref = _iterator[_i++];
    } else {
      _i = _iterator.next();
      if (_i.done) break;
      _ref = _i.value;
    }

    var country = _ref;
    metadata.country(country); // Leading digits check would be the simplest one

    if (metadata.leadingDigits()) {
      if (nationalPhoneNumber && nationalPhoneNumber.search(metadata.leadingDigits()) === 0) {
        return country;
      }
    } // Else perform full validation with all of those
    // fixed-line/mobile/etc regular expressions.
    else if ((0, _getNumberType_["default"])({
        phone: nationalPhoneNumber,
        country: country
      }, undefined, metadata.metadata)) {
        return country;
      }
  }
}
/**
 * @param  {string} text - Input.
 * @return {object} `{ ?number, ?ext }`.
 */


function parseInput(text, v2) {
  // Parse RFC 3966 phone number URI.
  if (text && text.indexOf('tel:') === 0) {
    return (0, _RFC.parseRFC3966)(text);
  }

  var number = extractFormattedPhoneNumber(text, v2); // If the phone number is not viable, then abort.

  if (!number || !(0, _isViablePhoneNumber["default"])(number)) {
    return {};
  } // Attempt to parse extension first, since it doesn't require region-specific
  // data and we want to have the non-normalised number here.


  var with_extension_stripped = (0, _extension.extractExtension)(number);

  if (with_extension_stripped.ext) {
    return with_extension_stripped;
  }

  return {
    number: number
  };
}
/**
 * Creates `parse()` result object.
 */


function result(country, national_number, ext) {
  var result = {
    country: country,
    phone: national_number
  };

  if (ext) {
    result.ext = ext;
  }

  return result;
}
/**
 * Parses a viable phone number.
 * @param {string} formattedPhoneNumber
 * @param {string} [defaultCountry]
 * @param {Metadata} metadata
 * @return {object} Returns `{ country: string?, countryCallingCode: string?, nationalNumber: string? }`.
 */


function parsePhoneNumber(formattedPhoneNumber, defaultCountry, metadata) {
  var _extractCountryCallin = extractCountryCallingCode(formattedPhoneNumber, defaultCountry, metadata.metadata),
      countryCallingCode = _extractCountryCallin.countryCallingCode,
      number = _extractCountryCallin.number;

  var country;

  if (countryCallingCode) {
    metadata.chooseCountryByCountryCallingCode(countryCallingCode);
  } // If `formattedPhoneNumber` is in "national" format
  // then `number` is defined and `countryCallingCode` isn't.
  else if (number && defaultCountry) {
      metadata.country(defaultCountry);
      country = defaultCountry;
      countryCallingCode = (0, _getCountryCallingCode["default"])(defaultCountry, metadata.metadata);
    } else return {};

  if (!number) {
    return {
      countryCallingCode: countryCallingCode
    };
  }

  var _parseNationalNumber = parseNationalNumber(number, metadata),
      nationalNumber = _parseNationalNumber.nationalNumber,
      carrierCode = _parseNationalNumber.carrierCode; // Sometimes there are several countries
  // corresponding to the same country phone code
  // (e.g. NANPA countries all having `1` country phone code).
  // Therefore, to reliably determine the exact country,
  // national (significant) number should have been parsed first.
  //
  // When `metadata.json` is generated, all "ambiguous" country phone codes
  // get their countries populated with the full set of
  // "phone number type" regular expressions.
  //


  var exactCountry = findCountryCode(countryCallingCode, nationalNumber, metadata);

  if (exactCountry) {
    country = exactCountry;
    metadata.country(country);
  }

  return {
    country: country,
    countryCallingCode: countryCallingCode,
    nationalNumber: nationalNumber,
    carrierCode: carrierCode
  };
}

function parseNationalNumber(number, metadata) {
  var nationalNumber = (0, _parseIncompletePhoneNumber["default"])(number);
  var carrierCode; // Parsing national prefixes and carrier codes
  // is only required for local phone numbers
  // but some people don't understand that
  // and sometimes write international phone numbers
  // with national prefixes (or maybe even carrier codes).
  // http://ucken.blogspot.ru/2016/03/trunk-prefixes-in-skype4b.html
  // Google's original library forgives such mistakes
  // and so does this library, because it has been requested:
  // https://github.com/catamphetamine/libphonenumber-js/issues/127

  var _stripNationalPrefixA = stripNationalPrefixAndCarrierCode(nationalNumber, metadata),
      potentialNationalNumber = _stripNationalPrefixA.number,
      potentialCarrierCode = _stripNationalPrefixA.carrierCode; // If metadata has "possible lengths" then employ the new algorythm.


  if (metadata.possibleLengths()) {
    // We require that the NSN remaining after stripping the national prefix and
    // carrier code be long enough to be a possible length for the region.
    // Otherwise, we don't do the stripping, since the original number could be
    // a valid short number.
    switch ((0, _getNumberType_.checkNumberLengthForType)(potentialNationalNumber, undefined, metadata)) {
      case 'TOO_SHORT': // case 'IS_POSSIBLE_LOCAL_ONLY':

      case 'INVALID_LENGTH':
        break;

      default:
        nationalNumber = potentialNationalNumber;
        carrierCode = potentialCarrierCode;
    }
  } else {
    // If the original number (before stripping national prefix) was viable,
    // and the resultant number is not, then prefer the original phone number.
    // This is because for some countries (e.g. Russia) the same digit could be both
    // a national prefix and a leading digit of a valid national phone number,
    // like `8` is the national prefix for Russia and both
    // `8 800 555 35 35` and `800 555 35 35` are valid numbers.
    if ((0, _util.matchesEntirely)(nationalNumber, metadata.nationalNumberPattern()) && !(0, _util.matchesEntirely)(potentialNationalNumber, metadata.nationalNumberPattern())) {// Keep the number without stripping national prefix.
    } else {
      nationalNumber = potentialNationalNumber;
      carrierCode = potentialCarrierCode;
    }
  }

  return {
    nationalNumber: nationalNumber,
    carrierCode: carrierCode
  };
} // Parses a formatted phone number
// and returns `{ countryCallingCode, number }`
// where `number` is just the "number" part
// which is left after extracting `countryCallingCode`
// and is not necessarily a "national (significant) number"
// and might as well contain national prefix.
//


function extractCountryCallingCode(number, country, metadata) {
  number = (0, _parseIncompletePhoneNumber["default"])(number);

  if (!number) {
    return {};
  } // If this is not an international phone number,
  // then don't extract country phone code.


  if (number[0] !== '+') {
    // Convert an "out-of-country" dialing phone number
    // to a proper international phone number.
    var numberWithoutIDD = (0, _IDD.stripIDDPrefix)(number, country, metadata); // If an IDD prefix was stripped then
    // convert the number to international one
    // for subsequent parsing.

    if (numberWithoutIDD && numberWithoutIDD !== number) {
      number = '+' + numberWithoutIDD;
    } else {
      return {
        number: number
      };
    }
  } // Fast abortion: country codes do not begin with a '0'


  if (number[1] === '0') {
    return {};
  }

  metadata = new _metadata["default"](metadata); // The thing with country phone codes
  // is that they are orthogonal to each other
  // i.e. there's no such country phone code A
  // for which country phone code B exists
  // where B starts with A.
  // Therefore, while scanning digits,
  // if a valid country code is found,
  // that means that it is the country code.
  //

  var i = 2;

  while (i - 1 <= _constants.MAX_LENGTH_COUNTRY_CODE && i <= number.length) {
    var countryCallingCode = number.slice(1, i);

    if (metadata.countryCallingCodes()[countryCallingCode]) {
      return {
        countryCallingCode: countryCallingCode,
        number: number.slice(i)
      };
    }

    i++;
  }

  return {};
}
//# sourceMappingURL=parse_.js.map