2014-10-20 16:48:07 -04:00
|
|
|
|
class @SearchAutocomplete
|
2016-03-14 17:14:29 -04:00
|
|
|
|
|
|
|
|
|
KEYCODE =
|
|
|
|
|
ESCAPE: 27
|
|
|
|
|
BACKSPACE: 8
|
|
|
|
|
TAB: 9
|
|
|
|
|
ENTER: 13
|
|
|
|
|
|
2016-03-08 02:56:43 -05:00
|
|
|
|
constructor: (opts = {}) ->
|
|
|
|
|
{
|
|
|
|
|
@wrap = $('.search')
|
2016-03-14 17:14:29 -04:00
|
|
|
|
|
2016-03-08 02:56:43 -05:00
|
|
|
|
@optsEl = @wrap.find('.search-autocomplete-opts')
|
|
|
|
|
@autocompletePath = @optsEl.data('autocomplete-path')
|
|
|
|
|
@projectId = @optsEl.data('autocomplete-project-id') || ''
|
|
|
|
|
@projectRef = @optsEl.data('autocomplete-project-ref') || ''
|
2016-03-14 17:14:29 -04:00
|
|
|
|
|
2016-03-08 02:56:43 -05:00
|
|
|
|
} = opts
|
2014-01-18 07:16:46 -05:00
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
# Dropdown Element
|
|
|
|
|
@dropdown = @wrap.find('.dropdown')
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
2016-03-18 18:35:26 -04:00
|
|
|
|
@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')
|
|
|
|
|
@scopeInputEl = @getElement('#scope')
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
|
|
|
|
@saveOriginalState()
|
2016-03-08 19:39:14 -05:00
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
@searchInput.addClass('disabled')
|
2016-03-18 22:50:49 -04:00
|
|
|
|
@autocomplete = false
|
2016-03-08 19:39:14 -05:00
|
|
|
|
|
2016-03-08 02:56:43 -05:00
|
|
|
|
@bindEvents()
|
|
|
|
|
|
2016-03-18 18:35:26 -04:00
|
|
|
|
# Finds an element inside wrapper element
|
|
|
|
|
getElement: (selector) ->
|
2016-03-08 02:56:43 -05:00
|
|
|
|
@wrap.find(selector)
|
|
|
|
|
|
|
|
|
|
saveOriginalState: ->
|
|
|
|
|
@originalState = @serializeState()
|
|
|
|
|
|
|
|
|
|
serializeState: ->
|
|
|
|
|
{
|
|
|
|
|
# Search Criteria
|
|
|
|
|
project_id: @projectInputEl.val()
|
|
|
|
|
group_id: @groupInputEl.val()
|
|
|
|
|
search_code: @searchCodeInputEl.val()
|
|
|
|
|
repository_ref: @repositoryInputEl.val()
|
|
|
|
|
|
|
|
|
|
# Location badge
|
|
|
|
|
_location: $.trim(@locationText.text())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bindEvents: ->
|
2016-03-08 19:39:14 -05:00
|
|
|
|
@searchInput.on 'keydown', @onSearchInputKeyDown
|
|
|
|
|
@searchInput.on 'focus', @onSearchInputFocus
|
|
|
|
|
@searchInput.on 'blur', @onSearchInputBlur
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
enableAutocomplete: ->
|
2016-03-18 22:50:49 -04:00
|
|
|
|
return if @autocomplete
|
|
|
|
|
|
2016-03-18 18:35:26 -04:00
|
|
|
|
dropdownMenu = @dropdown.find('.dropdown-menu')
|
|
|
|
|
_this = @
|
2016-03-18 22:50:49 -04:00
|
|
|
|
loading = false
|
|
|
|
|
|
2016-03-18 18:35:26 -04:00
|
|
|
|
@searchInput.glDropdown
|
2016-03-14 17:14:29 -04:00
|
|
|
|
filterInputBlur: false
|
|
|
|
|
filterable: true
|
|
|
|
|
filterRemote: true
|
|
|
|
|
highlight: true
|
|
|
|
|
filterInput: 'input#search'
|
|
|
|
|
search:
|
|
|
|
|
fields: ['text']
|
|
|
|
|
data: (term, callback) ->
|
2016-03-18 22:50:49 -04:00
|
|
|
|
# Ensure this is not called when autocomplete is disabled because
|
|
|
|
|
# this method still will be called because `GitLabDropdownFilter` is triggering this on keyup
|
|
|
|
|
return if _this.autocomplete is false
|
|
|
|
|
|
|
|
|
|
# Do not trigger request if input is empty
|
|
|
|
|
return if _this.searchInput.val() is ''
|
|
|
|
|
|
|
|
|
|
# Prevent multiple ajax calls
|
|
|
|
|
return if loading
|
|
|
|
|
|
|
|
|
|
loading = true
|
|
|
|
|
|
|
|
|
|
jqXHR = $.get(_this.autocompletePath, {
|
2016-03-18 18:35:26 -04:00
|
|
|
|
project_id: _this.projectId
|
|
|
|
|
project_ref: _this.projectRef
|
2016-03-14 17:14:29 -04:00
|
|
|
|
term: term
|
2016-03-18 18:35:26 -04:00
|
|
|
|
}, (response) ->
|
2016-03-14 17:14:29 -04:00
|
|
|
|
data = []
|
|
|
|
|
|
|
|
|
|
# Save groups ordering according to server response
|
|
|
|
|
groupNames = _.unique(_.pluck(response, 'category'))
|
|
|
|
|
|
|
|
|
|
# Group results by category name
|
|
|
|
|
groups = _.groupBy response, (item) ->
|
|
|
|
|
item.category
|
|
|
|
|
|
|
|
|
|
# List results
|
|
|
|
|
for groupName in groupNames
|
|
|
|
|
|
|
|
|
|
# Add group header before list each group
|
|
|
|
|
data.push
|
|
|
|
|
header: groupName
|
|
|
|
|
|
|
|
|
|
# List group
|
|
|
|
|
for item in groups[groupName]
|
|
|
|
|
data.push
|
|
|
|
|
text: item.label
|
|
|
|
|
url: item.url
|
|
|
|
|
callback(data)
|
2016-03-18 22:50:49 -04:00
|
|
|
|
).always ->
|
|
|
|
|
loading = false
|
2016-03-14 17:14:29 -04:00
|
|
|
|
|
|
|
|
|
@dropdown.addClass('open')
|
|
|
|
|
@searchInput.removeClass('disabled')
|
2016-03-18 18:35:26 -04:00
|
|
|
|
@autocomplete = true
|
2016-03-14 17:14:29 -04:00
|
|
|
|
|
|
|
|
|
onDropdownOpen: (e) =>
|
|
|
|
|
@dropdown.dropdown('toggle')
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
2016-03-08 19:39:14 -05:00
|
|
|
|
onSearchInputKeyDown: (e) =>
|
2016-03-18 22:50:49 -04:00
|
|
|
|
switch e.keyCode
|
|
|
|
|
when KEYCODE.BACKSPACE
|
|
|
|
|
if e.currentTarget.value is ''
|
|
|
|
|
@removeLocationBadge()
|
|
|
|
|
@searchInput.focus()
|
|
|
|
|
when KEYCODE.ESCAPE
|
|
|
|
|
if @badgePresent()
|
|
|
|
|
else
|
|
|
|
|
@restoreOriginalState()
|
2016-03-14 17:14:29 -04:00
|
|
|
|
|
2016-03-18 22:50:49 -04:00
|
|
|
|
# If after restoring there's a badge
|
|
|
|
|
@disableAutocomplete() if @badgePresent()
|
2016-03-08 21:26:24 -05:00
|
|
|
|
else
|
2016-03-14 17:14:29 -04:00
|
|
|
|
if @badgePresent()
|
|
|
|
|
@disableAutocomplete()
|
2016-03-18 22:50:49 -04:00
|
|
|
|
else
|
|
|
|
|
@enableAutocomplete()
|
|
|
|
|
|
|
|
|
|
# Avoid falsy value to be returned
|
|
|
|
|
return
|
2016-03-08 19:39:14 -05:00
|
|
|
|
|
|
|
|
|
onSearchInputFocus: =>
|
|
|
|
|
@wrap.addClass('search-active')
|
|
|
|
|
|
|
|
|
|
onSearchInputBlur: =>
|
|
|
|
|
@wrap.removeClass('search-active')
|
|
|
|
|
|
|
|
|
|
# If input is blank then restore state
|
2016-03-14 17:14:29 -04:00
|
|
|
|
if @searchInput.val() is ''
|
|
|
|
|
@restoreOriginalState()
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
|
|
|
|
addLocationBadge: (item) ->
|
|
|
|
|
category = if item.category? then "#{item.category}: " else ''
|
|
|
|
|
value = if item.value? then item.value else ''
|
|
|
|
|
|
2016-03-08 19:39:14 -05:00
|
|
|
|
html = "<span class='location-badge'>
|
2016-03-08 02:56:43 -05:00
|
|
|
|
<i class='location-text'>#{category}#{value}</i>
|
|
|
|
|
<a class='remove-badge' href='#'>x</a>
|
|
|
|
|
</span>"
|
|
|
|
|
@locationBadgeEl.html(html)
|
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
restoreOriginalState: ->
|
|
|
|
|
inputs = Object.keys @originalState
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
for input in inputs
|
2016-03-18 18:35:26 -04:00
|
|
|
|
@getElement("##{input}").val(@originalState[input])
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
if @originalState._location is ''
|
|
|
|
|
@locationBadgeEl.html('')
|
|
|
|
|
else
|
|
|
|
|
@addLocationBadge(
|
|
|
|
|
value: @originalState._location
|
|
|
|
|
)
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
@dropdown.removeClass 'open'
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
# Only add class if there's a badge
|
|
|
|
|
if @badgePresent()
|
|
|
|
|
@searchInput.addClass 'disabled'
|
|
|
|
|
|
|
|
|
|
badgePresent: ->
|
|
|
|
|
@locationBadgeEl.children().length
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
|
|
|
|
resetSearchState: ->
|
|
|
|
|
# Remove scope
|
|
|
|
|
@scopeInputEl.val('')
|
|
|
|
|
|
|
|
|
|
# Remove group
|
|
|
|
|
@groupInputEl.val('')
|
|
|
|
|
|
|
|
|
|
# Remove project id
|
|
|
|
|
@projectInputEl.val('')
|
|
|
|
|
|
|
|
|
|
# Remove code search
|
|
|
|
|
@searchCodeInputEl.val('')
|
|
|
|
|
|
|
|
|
|
# Remove repository ref
|
|
|
|
|
@repositoryInputEl.val('')
|
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
removeLocationBadge: ->
|
|
|
|
|
@locationBadgeEl.empty()
|
|
|
|
|
|
|
|
|
|
# Reset state
|
|
|
|
|
@resetSearchState()
|
2016-03-08 02:56:43 -05:00
|
|
|
|
|
2016-03-14 17:14:29 -04:00
|
|
|
|
disableAutocomplete: ->
|
2016-03-18 22:50:49 -04:00
|
|
|
|
if @autocomplete
|
2016-03-14 17:14:29 -04:00
|
|
|
|
@searchInput.addClass('disabled')
|
2016-03-18 22:50:49 -04:00
|
|
|
|
@autocomplete = false
|