<commit_before>class @SearchAutocomplete

  KEYCODE =
    ESCAPE: 27
    BACKSPACE: 8
    ENTER: 13

  constructor: (opts = {}) ->
    {
      @wrap = $('.search')

      @optsEl = @wrap.find('.search-autocomplete-opts')
      @autocompletePath = @optsEl.data('autocomplete-path')
      @projectId = @optsEl.data('autocomplete-project-id') || ''
      @projectRef = @optsEl.data('autocomplete-project-ref') || ''

    } = opts

    # Dropdown Element
    @dropdown = @wrap.find('.dropdown')
    @dropdownContent = @dropdown.find('.dropdown-content')

    @locationBadgeEl = @getElement('.search-location-badge')
    @locationText = @getElement('.location-text')
    @scopeInputEl = @getElement('#scope')
    @searchInput = @getElement('.search-input')
    @projectInputEl = @getElement('#search_project_id')
    @groupInputEl = @getElement('#group_id')
    @searchCodeInputEl = @getElement('#search_code')
    @repositoryInputEl = @getElement('#repository_ref')
    @clearInput = @getElement('.js-clear-input')

    @saveOriginalState()

    @createAutocomplete()

    @searchInput.addClass('disabled')

    @saveTextLength()

    @bindEvents()

  # Finds an element inside wrapper element
  getElement: (selector) ->
    @wrap.find(selector)

  saveOriginalState: ->
    @originalState = @serializeState()

  saveTextLength: ->
    @lastTextLength = @searchInput.val().length

  createAutocomplete: ->
    @searchInput.glDropdown
        filterInputBlur: false
        filterable: true
        filterRemote: true
        highlight: true
        enterCallback: false
        filterInput: 'input#search'
        search:
          fields: ['text']
        data: @getData.bind(@)

  getData: (term, callback) ->
    _this = @

    # Do not trigger request if input is empty
    return if @searchInput.val() is ''

    # Prevent multiple ajax calls
    return if @loadingSuggestions

    # Do not request suggestions if dropdown is disabled
    return if @badgePresent()

    @loadingSuggestions = true

    jqXHR = $.get(@autocompletePath, {
        project_id: @projectId
        project_ref: @projectRef
        term: term
      }, (response) ->
        data = []

        # List results
        for suggestion in response

          # Add group header before list each group
          if lastCategory isnt suggestion.category
            data.push
              header: suggestion.category

            lastCategory = suggestion.category

          data.push
            text: suggestion.label
            url: suggestion.url

        callback(data)
    ).always ->
      _this.loadingSuggestions = false

  serializeState: ->
    {
      # Search Criteria
      search_project_id: @projectInputEl.val()
      group_id: @groupInputEl.val()
      search_code: @searchCodeInputEl.val()
      repository_ref: @repositoryInputEl.val()
      scope: @scopeInputEl.val()

      # Location badge
      _location: @locationText.text()
    }

  bindEvents: ->
    @searchInput.on 'keydown', @onSearchInputKeyDown
    @searchInput.on 'keyup', @onSearchInputKeyUp
    @searchInput.on 'click', @onSearchInputClick
    @searchInput.on 'focus', @onSearchInputFocus
    @searchInput.on 'blur', @onSearchInputBlur
    @clearInput.on 'click', @onRemoveLocationClick

  enableAutocomplete: ->
    dropdownMenu = @dropdown.find('.dropdown-menu')
    _this = @
    @loadingSuggestions = false

    @dropdown.addClass('open')
    @searchInput.removeClass('disabled')

  onDropdownOpen: (e) =>
    @dropdown.dropdown('toggle')

  onSearchInputKeyDown: =>
    # Saves last length of the entered text
    @saveTextLength()

  onSearchInputKeyUp: (e) =>
    switch e.keyCode
      when KEYCODE.BACKSPACE
        # when trying to remove the location badge
        if @lastTextLength is 0 and @badgePresent()
            @removeLocationBadge()

        # When removing the last character and no badge is present
        if @lastTextLength is 1 and !@badgePresent()
          @disableAutocomplete()
      when KEYCODE.ESCAPE
        if @badgePresent()
        else
          @restoreOriginalState()

          # If after restoring there's a badge
          @disableAutocomplete() if @badgePresent()
      else
        if @badgePresent()
          @disableAutocomplete()
        else

          # We should display the menu only when input is not empty
          if @searchInput.val() isnt ''
            @enableAutocomplete()

    # Avoid falsy value to be returned
    return

  onSearchInputClick: (e) =>
    # Prevents closing the dropdown menu
    e.stopImmediatePropagation()

  onSearchInputFocus: =>
    @wrap.addClass('search-active')

  onRemoveLocationClick: (e) =>
    e.preventDefault()
    @removeLocationBadge()
    @searchInput.val('').focus()
    @skipBlurEvent = true

  onSearchInputBlur: (e) =>
    @skipBlurEvent = false

    # We should wait to make sure we are not clearing the input instead
    setTimeout( =>
      return if @skipBlurEvent

      @wrap.removeClass('search-active')

      # If input is blank then restore state
      if @searchInput.val() is ''
        @restoreOriginalState()
    , 100)

  addLocationBadge: (item) ->
    category = if item.category? then "#{item.category}: " else ''<commit_msg>Increase time because of Safari<commit_after>class @SearchAutocomplete

  KEYCODE =
    ESCAPE: 27
    BACKSPACE: 8
    ENTER: 13

  constructor: (opts = {}) ->
    {
      @wrap = $('.search')

      @optsEl = @wrap.find('.search-autocomplete-opts')
      @autocompletePath = @optsEl.data('autocomplete-path')
      @projectId = @optsEl.data('autocomplete-project-id') || ''
      @projectRef = @optsEl.data('autocomplete-project-ref') || ''

    } = opts

    # Dropdown Element
    @dropdown = @wrap.find('.dropdown')
    @dropdownContent = @dropdown.find('.dropdown-content')

    @locationBadgeEl = @getElement('.search-location-badge')
    @locationText = @getElement('.location-text')
    @scopeInputEl = @getElement('#scope')
    @searchInput = @getElement('.search-input')
    @projectInputEl = @getElement('#search_project_id')
    @groupInputEl = @getElement('#group_id')
    @searchCodeInputEl = @getElement('#search_code')
    @repositoryInputEl = @getElement('#repository_ref')
    @clearInput = @getElement('.js-clear-input')

    @saveOriginalState()

    @createAutocomplete()

    @searchInput.addClass('disabled')

    @saveTextLength()

    @bindEvents()

  # Finds an element inside wrapper element
  getElement: (selector) ->
    @wrap.find(selector)

  saveOriginalState: ->
    @originalState = @serializeState()

  saveTextLength: ->
    @lastTextLength = @searchInput.val().length

  createAutocomplete: ->
    @searchInput.glDropdown
        filterInputBlur: false
        filterable: true
        filterRemote: true
        highlight: true
        enterCallback: false
        filterInput: 'input#search'
        search:
          fields: ['text']
        data: @getData.bind(@)

  getData: (term, callback) ->
    _this = @

    # Do not trigger request if input is empty
    return if @searchInput.val() is ''

    # Prevent multiple ajax calls
    return if @loadingSuggestions

    # Do not request suggestions if dropdown is disabled
    return if @badgePresent()

    @loadingSuggestions = true

    jqXHR = $.get(@autocompletePath, {
        project_id: @projectId
        project_ref: @projectRef
        term: term
      }, (response) ->
        data = []

        # List results
        for suggestion in response

          # Add group header before list each group
          if lastCategory isnt suggestion.category
            data.push
              header: suggestion.category

            lastCategory = suggestion.category

          data.push
            text: suggestion.label
            url: suggestion.url

        callback(data)
    ).always ->
      _this.loadingSuggestions = false

  serializeState: ->
    {
      # Search Criteria
      search_project_id: @projectInputEl.val()
      group_id: @groupInputEl.val()
      search_code: @searchCodeInputEl.val()
      repository_ref: @repositoryInputEl.val()
      scope: @scopeInputEl.val()

      # Location badge
      _location: @locationText.text()
    }

  bindEvents: ->
    @searchInput.on 'keydown', @onSearchInputKeyDown
    @searchInput.on 'keyup', @onSearchInputKeyUp
    @searchInput.on 'click', @onSearchInputClick
    @searchInput.on 'focus', @onSearchInputFocus
    @searchInput.on 'blur', @onSearchInputBlur
    @clearInput.on 'click', @onRemoveLocationClick

  enableAutocomplete: ->
    dropdownMenu = @dropdown.find('.dropdown-menu')
    _this = @
    @loadingSuggestions = false

    @dropdown.addClass('open')
    @searchInput.removeClass('disabled')

  onDropdownOpen: (e) =>
    @dropdown.dropdown('toggle')

  onSearchInputKeyDown: =>
    # Saves last length of the entered text
    @saveTextLength()

  onSearchInputKeyUp: (e) =>
    switch e.keyCode
      when KEYCODE.BACKSPACE
        # when trying to remove the location badge
        if @lastTextLength is 0 and @badgePresent()
            @removeLocationBadge()

        # When removing the last character and no badge is present
        if @lastTextLength is 1 and !@badgePresent()
          @disableAutocomplete()
      when KEYCODE.ESCAPE
        if @badgePresent()
        else
          @restoreOriginalState()

          # If after restoring there's a badge
          @disableAutocomplete() if @badgePresent()
      else
        if @badgePresent()
          @disableAutocomplete()
        else

          # We should display the menu only when input is not empty
          if @searchInput.val() isnt ''
            @enableAutocomplete()

    # Avoid falsy value to be returned
    return

  onSearchInputClick: (e) =>
    # Prevents closing the dropdown menu
    e.stopImmediatePropagation()

  onSearchInputFocus: =>
    @wrap.addClass('search-active')

  onRemoveLocationClick: (e) =>
    e.preventDefault()
    @removeLocationBadge()
    @searchInput.val('').focus()
    @skipBlurEvent = true

  onSearchInputBlur: (e) =>
    @skipBlurEvent = false

    # We should wait to make sure we are not clearing the input instead
    setTimeout( =>
      return if @skipBlurEvent

      @wrap.removeClass('search-active')

      # If input is blank then restore state
      if @searchInput.val() is ''
        @restoreOriginalState()
    , 150)

  addLocationBadge: (item) ->
    category = if item.category? then "#{item.category}: " else ''
{'fraction_non_alphanumeric': 0.11057876581065543, 'fraction_numerical': 0.0019164430816404753, 'mean_word_length': 2.699751861042184, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7244246', 'n_tokens_mistral': 3074, 'n_tokens_neox': 2901, 'n_words': 716}