kdf
Version:
412 lines (326 loc) • 12 kB
text/coffeescript
{JsPath} = require './../../../libs/jspath.js'
Inflector = require './../../../libs/inflector.js'
KDViewController = require './../../core/viewcontroller.coffee'
KDListViewController = require './../list/listviewcontroller.coffee'
KDLabelView = require './../inputs/labelview.coffee'
KDNotificationView = require './../notifications/notificationview.coffee'
KDAutoComplete = require './autocomplete.coffee'
KDAutoCompleteListView = require './autocompletelist.coffee'
KDAutoCompleteFetchingItem = require './autocompletefetchingitem.coffee'
module.exports = class KDAutoCompleteController extends KDViewController
constructor:(options = {},data)->
options = $.extend
view : mainView = options.view or new KDAutoComplete
name : options.name
placeholder : options.placeholder or ""
label : options.label or new KDLabelView
title : options.name
itemClass : KDAutoCompleteListItemView
selectedItemClass : KDAutoCompletedItem
nothingFoundItemClass : KDAutoCompleteNothingFoundItem
fetchingItemClass : KDAutoCompleteFetchingItem
fetchInterval : options.fetchInterval ? 300
listWrapperCssClass : ''
minSuggestionLength : 2
selectedItemsLimit : null
itemDataPath : ''
separator : ','
wrapper : 'parent'
submitValuesAsText : no
defaultValue : []
,options
super options, data
mainView.on 'focus', 'updateDropdownContents'
= null
= []
= {}
= 0
= yes
reset:->
subViews = .getSubViews().slice()
for item in subViews
item
loadView:(mainView)->
mainView.on 'keyup', 'keyUpOnInputView'
mainView.on 'keydown', (event)=> event
setDefaultValue:(defaultItems)->
{defaultValue, itemDataPath} =
defaultItems or= defaultValue
for item in defaultItems
, item
keyDownOnInputView:(event)->
autoCompleteView =
list = .getListView()
switch event.which
when 13, 9 #enter, tab
if autoCompleteView.getValue() isnt "" and event.shiftKey isnt yes
autoCompleteView.getValue()
= no
else yes
when 27 #escape
when 38 #uparrow
if
list.goUp()
return KD.utils.stopDOMEvent event
when 40 #downarrow
if
list.goDown()
return KD.utils.stopDOMEvent event
else
= yes
no
getPrefix:->
separator = .separator
items = .getValue().split separator
prefix = items[items.length-1]
prefix
createDropDown:(data = [])->
# log "#{data.length} items in auto complete"
= ""
= dropdownListView = new KDAutoCompleteListView {
itemClass : .itemClass
},{
items : data
}
dropdownListView.on 'ItemsDeselected', =>
view =
view.$input().trigger('focus')
dropdownListView.on 'KDAutoCompleteSubmit', 'submitAutoComplete'
windowController = KD.getSingleton('windowController')
= new KDListViewController
view : dropdownListView
dropdownWrapper = .getView()
dropdownWrapper.on 'ReceivedClickElsewhere', =>
dropdownWrapper.setClass "kdautocomplete hidden #{@getOptions().listWrapperCssClass}"
dropdownWrapper.appendToDomBody()
hideDropdown:->
dropdownWrapper = .getView()
= no
dropdownWrapper.hide()
showDropdown:->
return unless
= yes
windowController = KD.getSingleton('windowController')
dropdownWrapper = .getView()
dropdownWrapper.unsetClass "hidden"
input =
offset = input.$().offset()
offset.top += input.getHeight()
dropdownWrapper.$().css offset
dropdownWrapper.show()
windowController.addLayer dropdownWrapper
# parent =
# x = parent.getX()
# y = parent.getY()
# .getView().$().css
# top : y
# left : x
# log .getListView()
# .getListView().$().css
# left : mainView.getLeftOffset()
# top : mainView.getHeight()-1
refreshDropDown:(data = [])->
listView = .getListView()
.removeAllItems()
listView.userInput =
exactPattern = RegExp('^'+ .replace(/[^\s\w]/, '')+'$', 'i')
exactMatches = []
inexactMatches = []
{itemDataPath,allowNewSuggestions,minSuggestionLength} =
data.forEach (datum)=>
unless datum
match = JsPath.getAt datum, itemDataPath
if exactPattern.test match
exactMatches.push datum
else
inexactMatches.push datum
if ( .length >= minSuggestionLength) and allowNewSuggestions and not exactMatches.length
.getListView().addItemView
data = exactMatches.concat inexactMatches
.instantiateListItems data
.getListView().goDown()
submitAutoComplete:(item, data)->
inputView =
# log .selectedItemsLimit,
if .selectedItemsLimit is null or .selectedItemsLimit >
listView = .getListView()
activeItem = listView.getActiveItem()
listView.setActiveItem null
if activeItem.item
activeItem.item
'ItemListChanged',
else
inputView.setValue ''
KD.getSingleton("windowController").setKeyView null
new KDNotificationView
type : "mini"
title : "You can add up to #{@getOptions().selectedItemsLimit} items!"
duration : 4000
getAutoCompletedItemParent: ->
{outputWrapper} =
= if outputWrapper instanceof KDView
then outputWrapper
else
return
isItemAlreadySelected:(data)->
{itemDataPath,customCompare,isCaseSensitive} =
suggested = JsPath.getAt data, itemDataPath
for selectedData in
if customCompare?
alreadySelected = customCompare data, selectedData
return yes if alreadySelected
else
selected = JsPath.getAt selectedData, itemDataPath
unless isCaseSensitive
suggested = suggested.toLowerCase()
selected = selected.toLowerCase()
if suggested is selected
return yes
no
addHiddenInputItem:(name, value)->
.addSubView [name] = new KDInputView
type : "hidden"
name : name
defaultValue : value
removeHiddenInputItem:(name)->
delete [name]
addSelectedItem:(name,data)->
{selectedItemClass} =
.addSubView itemView = new selectedItemClass
cssClass : "kdautocompletedlistitem"
delegate : this
name : name
,data
itemView.setPartial "<span class='close-icon'></span>"
getSelectedItemData:->
addSelectedItemData:(data)->
.push data
removeSelectedItemData:(data)->
selectedItemData =
for selectedData,i in selectedItemData
if selectedData is data
selectedItemData.splice i,1
return
getCollectionPath:->
{name} =
throw new Error 'No name!' unless name
[path..., leaf] = name.split('.')
collectionName = Inflector.pluralize(leaf)
path.push collectionName
path.join('.')
addSuggestion:(title)->
'AutocompleteSuggestionWasAdded', title
addItemToSubmitQueue:(item,data)->
data or= item?.getData()
return unless data or item?.getOptions().userInput
{name, itemDataPath, form, submitValuesAsText} =
if data
itemValue = if submitValuesAsText then JsPath.getAt data, itemDataPath else data
else
itemValue = item.getOptions().userInput
data = JsPath itemDataPath, itemValue
return no if data
path =
itemName = "#{name}-#{@selectedItemCounter++}"
if form
collection = form.getCustomData(path) or []
collection.push(
if submitValuesAsText
itemValue
else if itemValue.getId?()
constructorName : itemValue.constructor.name
id : itemValue.getId()
title : itemValue.title
else
$suggest : itemValue
)
form.addCustomData path, collection
if item.getOptions().userInput is not ""
++
else
path, itemValue
data
itemName, data
.setValue = ""
removeFromSubmitQueue:(item, data)->
{itemDataPath,form} =
data or= item.getData()
path =
if form
collection = JsPath.getAt form.getCustomData(), path
collection = collection.filter (sibling)->
id = data.getId?()
unless id?
sibling.$suggest isnt data.title
else
sibling.id isnt id
JsPath.setAt form.getCustomData(), path, collection
else
path
data
--
item.destroy()
'ItemListChanged',
appendAutoCompletedItem:->
.setValue ""
.$input().trigger "focus"
updateDropdownContents:->
inputView =
value = inputView.getValue().trim()
return if value is ''
return if and value is
= value
{fetchInterval} =
KD.utils.debounce fetchInterval, (data) =>
if data.length > 0
data
else
log 'no data found'
keyUpOnInputView:(event)->
return if event.keyCode in [9,38,40] #tab
# else
# log "just wait for a selection"
no
#this one I guess should be overriden
fetch:(callback)->
{fetchInputName, dataSource} =
value = .getValue()
options = {}
if fetchInputName
then options[fetchInputName] = value
else options = inputValue : value
= value
dataSource options, callback
showFetching: ->
{fetchingItemClass} =
list = .getListView()
.removeAllItems()
list.addItemView new fetchingItemClass {}, {}
getNoItemFoundView: (suggestion) ->
{nothingFoundItemClass} =
view = new nothingFoundItemClass
delegate : .getListView()
userInput : suggestion or .getValue()
, {}
return view
showNoDataFound: ->
noItemFoundView =
.removeAllItems()
.getListView().addItemView noItemFoundView
destroy:->
.getView().destroy()
super