class Autocomplete constructor: (scope,factory_name, element,$filter)-> @element = element @attributes = element[0].attributes @scope = scope @model_name = @attributes['ng-model'].value @list_model = @attributes['ng-list-model'].value.split('.') @options = if @attributes['ng-list-options'] then eval("(" + @attributes['ng-list-options'].value + ")") else {} @context = if @attributes['ng-context'] then @attributes['ng-context'].value @sort_by = if @attributes['ng-sort-by'] then @attributes['ng-sort-by'].value else @list_attr @list_attr = @list_model[1] @factory = factory_name(@list_model[0]) @scopes = @context.split('.') if @context @parent_name = @scopes.pop() if @scopes @list = angular.element("<div class='autocomplete menu'></div>") @filter = $filter @existing_factory = @scope[@factory] || @scope.$parent[@factory] @listFactory = @element.injector().get(@factory) unless @existing_factory @list.insertAfter(@element[0]) if @parent_name @parent_id = @parent_name + if @parent_name.indexOf('_id') < 0 then '_id' else '' @load() parent_context: => hash = {} hash[@parent_id] = @parent() if @context return hash serialize: => hash = {} for key,val of @options hash[key] = val for key,val of @parent_context() hash[key] = val hash model: (val)=> return @scope.$eval(@model_name + '="' + val + '"') if val return @scope.$eval(@model_name) parent: => return null unless @context return @scope.$eval(@context) load: -> scope = @scope factory = @factory model = @model updateView = @updateView if @context return unless @parent() if @existing_factory scope[factory] = @existing_factory scope.$watchCollection factory, (newVal) -> updateView(model()) updateView(model()) else @listFactory.index @serialize(), (data) -> scope[factory] = data updateView(model()) updateView: (value) => object = {} object[@list_attr] = value || '' scope = @scope model = @model model_name = @model_name filtered = @filter('filter')((scope[@factory] || []), object ) filtered = @filter('orderBy')(filtered, @sort_by) items = [] for item in filtered item = angular.element "<a class='item'>" + item[@list_attr] + "</a>" item.bind 'click', (event) -> model(event.target.textContent) scope.$eval event.target.parentNode.previousSibling.attributes['ng-change-on-blur'].value items.push item @list.empty() @list.append(items) angular.module('AutoComplete', [ 'FactoryName']) .directive 'ngAutocomplete', -> restrict: 'E' replace: true require: 'ngModel' require: 'ngListModel' template: (element, attributes) -> newElement = angular.element('<input>') newElement.addClass('autocomplete') newElement[0].setAttribute('ng-update',attributes.ngModel) newElement[0].setAttribute('auto-complete',true) return newElement[0].outerHTML controller: ($scope, $element, $filter, $timeout, factoryName) -> ac = new Autocomplete($scope,factoryName,$element,$filter) if ac.context $scope.$watch ac.context, (newVal, oldVal) -> if newVal ac.load() $scope.$watch ac.model_name, (newVal) -> ac.updateView(newVal) .directive 'autoComplete', -> restrict: 'A' require: '?ngModel' link: (scope, element, attributes, ngModel) -> if element[0].tagName == 'INPUT' element.bind 'focus', -> pos = element.position() if element.position element.next()[0].style.left = '0px' element.bind 'blur', -> active = angular.element(element.next()[0].getElementsByClassName('active')[0]) active.removeClass('active') ngModel.$setViewValue(element.val()) ngModel.$render() element.bind 'keydown', (input)-> keypress = (direction) -> index = if direction == 'next' then 0 else element.next().find('a').length - 1 selected = angular.element(element.next()[0].getElementsByClassName('active')[0]) if selected.hasClass('active') selected.removeClass('active') until complete selected = angular.element(selected[0][direction + 'Sibling']) if selected[0] complete = !!selected[0] complete = selected[0].tagName == 'A' if complete complete = true if !selected[0] selected = angular.element(element.next().find('a')[index]) unless selected[0] ind = 0 for el,i in element.next()[0].getElementsByTagName('a') ind = i if el == selected[0] scroll = selected[0].scrollHeight * ind selected[0].parentElement.scrollTop = scroll selected.addClass('active') element.val(selected.text()) if input.keyCode == 40 keypress('next') if input.keyCode == 38 keypress('previous')