Source: AutoComplete.js

/**
 * @module nyc/AutoComplete
 */

import $ from 'jquery'

/**
 * @desc A class to provide fitering functionality for autocomplete
 * @public
 * @class
 */
class AutoComplete {
  /**
   * @desc Find matching li elements from inUl and move them to outUl
   * @public
   * @method
   * @param {jQuery|Element|string} inUl The ul element to search
   * @param {jQuery|Element|string} outUl The ul element to receive results
   * @param {string} typed The text for searching
   */
  filter(inUl, outUl, typed) {
    const long = typed.length > 3
    const veryLong = typed.length > 6
    const filtered = {exact: [], possible: []}
    const matchers = this.regexp(typed)
    const all = []
    const test = this.test
    $.merge($(outUl).find('li'), $(inUl).find('li')).each((_, li) => {
      all.push($(li))
      test(matchers, $(li), filtered, long, this.log)
    })
    $(inUl).append(all)
    if (filtered.exact.length) {
      $(outUl).prepend(filtered.exact)
    } else if (!veryLong) {
      $(outUl).prepend(filtered.possible)
    }
  }
  /**
   * @private
   * @method
   * @param {Object<string,RegExp>} matchers Matchers
   * @param {jQuery|string|number} item Item
   * @param {Object<string,Array<JQuery|string|number>>} filtered Filtered
   * @param {boolean} long If true use exact test
   */
  test(matchers, item, filtered, long) {
    const text = item.html()
    if (long) {
      if (matchers.exact.test(text)) {
        filtered.exact.push(item)
      }
    }
    if (matchers.possible.test(text)) {
      filtered.possible.push(item)
    }
  }
  /**
   * @private
   * @method
   * @param {string} typed Typed string
   * @return {Object<string,RegExp>} Exact and possible regexes
   */
  regexp(typed) {
    const possibleMatch = new String(typed.replace(/[^a-zA-Z0-9]/g, ''))
    const exactMatch = new String(typed.replace(/[^a-zA-Z0-9 ]/g, ''))
    let possible = '^'
    for (let i = 0; i < possibleMatch.length; i++) {
      possible += `(?=.*${possibleMatch.charAt(i)})|`
    }
    possible = possible.substr(0, possible.length - 1)
    possible += '.*$'
    return {
      exact: new RegExp(`(${exactMatch})`, 'i'),
      possible: new RegExp(possible, 'i')
    }
  }
}

export default AutoComplete