text: /**
   * @license
   * Copyright The Closure Library Authors.
   * SPDX-License-Identifier: Apache-2.0
   */
  
  /**
   * @fileoverview Basic class for matching words in an array.
   */
  
  
  goog.provide('goog.ui.ac.ArrayMatcher');
  
  goog.require('goog.string');
  
  
  
  /**
   * Basic class for matching words in an array
   * @constructor
   * @param {Array<?>} rows Dictionary of items to match.  Can be objects if they
   *     have a toString method that returns the value to match against.
   * @param {boolean=} opt_noSimilar if true, do not do similarity matches for the
   *     input token against the dictionary.
   */
  goog.ui.ac.ArrayMatcher = function(rows, opt_noSimilar) {
    'use strict';
    this.rows_ = rows || [];
    this.useSimilar_ = !opt_noSimilar;
  };
  
  
  /**
   * Replaces the rows that this object searches over.
   * @param {Array<?>} rows Dictionary of items to match.
   */
  goog.ui.ac.ArrayMatcher.prototype.setRows = function(rows) {
    'use strict';
    this.rows_ = rows || [];
  };
  
  
  /**
   * Function used to pass matches to the autocomplete
   * @param {string} token Token to match.
   * @param {number} maxMatches Max number of matches to return.
   * @param {Function} matchHandler callback to execute after matching.
   * @param {string=} opt_fullString The full string from the input box.
   */
  goog.ui.ac.ArrayMatcher.prototype.requestMatchingRows = function(
      token, maxMatches, matchHandler, opt_fullString) {
    'use strict';
    var matches = this.useSimilar_ ?
        goog.ui.ac.ArrayMatcher.getMatchesForRows(token, maxMatches, this.rows_) :
        this.getPrefixMatches(token, maxMatches);
  
    matchHandler(token, matches);
  };
  
  
  /**
   * Matches the token against the specified rows, first looking for prefix
   * matches and if that fails, then looking for similar matches.
   *
   * @param {string} token Token to match.
   * @param {number} maxMatches Max number of matches to return.
   * @param {!Array<?>} rows Rows to search for matches. Can be objects if they
   *     have a toString method that returns the value to match against.
   * @return {!Array<?>} Rows that match.
   */
  goog.ui.ac.ArrayMatcher.getMatchesForRows = function(token, maxMatches, rows) {
    'use strict';
    var matches =
        goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows(token, maxMatches, rows);
  
    if (matches.length == 0) {
      matches = goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows(
          token, maxMatches, rows);
    }
    return matches;
  };
  
  
  /**
   * Matches the token against the start of words in the row.
   * @param {string} token Token to match.
   * @param {number} maxMatches Max number of matches to return.
   * @return {!Array<?>} Rows that match.
   */
  goog.ui.ac.ArrayMatcher.prototype.getPrefixMatches = function(
      token, maxMatches) {
    'use strict';
    return goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows(
        token, maxMatches, this.rows_);
  };
  
  
  /**
   * Matches the token against the start of words in the row.
   * @param {string} token Token to match.
   * @param {number} maxMatches Max number of matches to return.
   * @param {!Array<?>} rows Rows to search for matches. Can be objects if they
   * have
   *     a toString method that returns the value to match against.
   * @return {!Array<?>} Rows that match.
   */
  goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows = function(
      token, maxMatches, rows) {
    'use strict';
    var matches = [];
  
    if (token != '') {
      var escapedToken = goog.string.regExpEscape(token);
      var matcher = new RegExp('(^|\\W+)' + escapedToken, 'i');
  
      for (var i = 0; i < rows.length && matches.length < maxMatches; i++) {
        var row = rows[i];
        if (String(row).match(matcher)) {
          matches.push(row);
        }
      }
    }
    return matches;
  };
  
  
  /**
   * Matches the token against similar rows, by calculating "distance" between the
   * terms.
   * @param {string} token Token to match.
   * @param {number} maxMatches Max number of matches to return.
   * @return {!Array<?>} The best maxMatches rows.
   */
  goog.ui.ac.ArrayMatcher.prototype.getSimilarRows = function(token, maxMatches) {
    'use strict';
    return goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows(
        token, maxMatches, this.rows_);
  };
  
  
  /**
   * Matches the token against similar rows, by calculating "distance" between the
   * terms.
   * @param {string} token Token to match.
   * @param {number} maxMatches Max number of matches to return.
   * @param {!Array<?>} rows Rows to search for matches. Can be objects
   *     if they have a toString method that returns the value to
   *     match against.
   * @return {!Array<?>} The best maxMatches rows.
   */
  goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows = function(
      token, maxMatches, rows) {
    'use strict';
    var results = [];
  
    for (var index = 0; index < rows.length; index++) {
      var row = rows[index];
      var str = token.toLowerCase();
      var txt = String(row).toLowerCase();
      var score = 0;
  
      if (txt.indexOf(str) != -1) {
        score = parseInt((txt.indexOf(str) / 4).toString(), 10);
  
      } else {
        var arr = str.split('');
  
        var lastPos = -1;
        var penalty = 10;
  
        for (var i = 0, c; c = arr[i]; i++) {
          var pos = txt.indexOf(c);
  
          if (pos > lastPos) {
            var diff = pos - lastPos - 1;
  
            if (diff > penalty - 5) {
              diff = penalty - 5;
            }
  
            score += diff;
  
            lastPos = pos;
          } else {
            score += penalty;
            penalty += 5;
          }
        }
      }
  
      if (score < str.length * 6) {
        results.push({str: row, score: score, index: index});
      }
    }
  
    results.sort(function(a, b) {
      'use strict';
      var diff = a.score - b.score;
      if (diff != 0) {
        return diff;
      }
      return a.index - b.index;
    });
  
    var matches = [];
    for (var i = 0; i < maxMatches && i < results.length; i++) {
      matches.push(results[i].str);
    }
  
    return matches;
  };
  