source : gazAutoComplete.js

(function (angular) {
    'use strict';
    /**
     * @memberof spApp
     * @ngdoc directive
     * @name gazAutoComplete
     * @description
     *   Gazetteer autocomplete
     */
    angular.module('gaz-auto-complete-directive', ['gaz-auto-complete-service']).directive('gazAutoComplete',
        ['$timeout', 'GazAutoCompleteService', function ($timeout, GazAutoCompleteService) {
            return {
                scope: {
                    _userobjects: '=',
                    _custom: '&onCustom',
                },
                link: function (scope, iElement, iAttrs) {
                    var gaz_selected = false;
                    var a = iElement.autocomplete({
                        source: function (searchTerm, response) {
                            GazAutoCompleteService.search(searchTerm.term).then(function (data) {
                                var fields = new Set(data.map(function(item){return item.fieldname}))
                                var fidx = 0 //bypass possible disturbing chars by using assigned id
                                fields.forEach(function(item){
                                    data.unshift({label: item, fieldIdx: fidx, isField: true});
                                    fidx++;
                                })
                                response(
                                    $.map(data, function (item) {
                                        if (item.isField){
                                            return item;
                                        }else if (item.fid !== $SH.userObjectsField || scope._userobjects) {
                                            return {
                                                label: item.name,
                                                info: item.description ? item.description + " (" + item.fieldname + ")" : "(" + item.fieldname + ")",
                                                fieldIdx: data.find(function(a){return a.isField && a.label == item.fieldname }).fieldIdx,
                                                value: item
                                            }
                                        } else {
                                            return null
                                        }
                                })
                                )
                            });
                        },
                        select: function (event, ui) {
                            //check on filter checkbox
                            // click on radio button or label or outside label
                            if (ui.item.isField){
                                event.stopPropagation();
                                event.preventDefault();
                                //Tried to use radio button, Strangely, click on radio button does not make it checked
                                //Others worked properly
                                // if ($(event.toElement).is('input[name=filterOnFields]')) //click on checkbox
                                //      $(event.toElement).first().prop('checked', true);
                                //  else //click on lable
                                //     $(event.toElement).first().find('input[name=filterOnFields]').prop('checked', true);
                                // if (!$(event.toElement).is('input[name=filterOnFields]'))
                                //     $(event.toElement).first().find('input[name=filterOnFields]').prop('checked', true);
                                $(event.currentTarget).find('label span.glyphicon').removeClass('glyphicon-check').addClass('glyphicon-unchecked')
                                if($(event.toElement).is('span.glyphicon')) //Click on span icon
                                    $(event.toElement).removeClass('glyphicon-unchecked').addClass('glyphicon-check')
                                else //click on other area
                                    $(event.toElement).find('span.glyphicon').removeClass('glyphicon-unchecked').addClass('glyphicon-check')
                                // on UL level
                                $(event.currentTarget).first().find("li[field][field!=" + ui.item.fieldIdx + "]").hide();
                                $(event.currentTarget).first().find("li[field=" + ui.item.fieldIdx + "]").show();
                                gaz_selected = false;
                                //$(iElement).autocomplete('focus')
                                return false;
                            }else {
                                scope._custom()(ui.item.value.pid);
                                scope.label = ui.item.label;
                                gaz_selected = true;
                                $timeout(function () {
                                    iElement.val(scope.label);
                                }, 0)
                            }
                        },
                        close: function(event, ui){
                            // This function fires after select: and after autocomplete has already "closed" everything.  This is why event.preventDefault() won't work.
                            // ** ui is an empty object here so we have to use our own variable to check if the selected item is "selectable" or not..
                            if (! gaz_selected){
                                // We need to undo what autocomplete has already done..
                                $('#'+event.currentTarget.id).show(); // Keep the selection window open
                                // ta-da!  To the end user, nothing changes when clicking on an item that was not selectable.
                            }
                        },
                        open: function( event, ui ) {
                            $('.ui-autocomplete').css('height', 'auto');
                            //Get some values needed to determine whether the widget is on
                            //the screen
                            var $input = $(event.target),
                                inputTop = $input.offset().top,
                                inputHeight = $input.height(),
                                autocompleteHeight = $('.ui-autocomplete').height(),
                                windowHeight = $(window).height();
                            //The widget has left the screen if the input's height plus it's offset from the top of
                            //the screen, plus the height of the autocomplete are greater than the height of the
                            //window.
                            if ((inputHeight + inputTop + autocompleteHeight) > windowHeight) {
                                //Set the new height of the autocomplete to the height of the window, minus the
                                //height of the input and the offset of the input from the top of the screen.  The
                                //20 is simply there to give some spacing between the bottom of the screen and the
                                //bottom of the autocomplete widget.
                                $('.ui-autocomplete')
                                    .css('max-height', (windowHeight - inputHeight - inputTop - 20) + 'px');
                                $('.ui-autocomplete').css('overflow-y', 'auto');
                            }
                        }
                    })
                    a.data("ui-autocomplete")._renderItem = function (ul, item) {
                        if(item.isField){
                            var html = "<label><span class='glyphicon glyphicon-unchecked'></span>"+item.label+"</label>";
                            return $("<li class='autocomplete-item'>")
                                .append($("<a>").append(html))
                                .appendTo(ul);
                        }
                        else{
                            return $("<li field="+item.fieldIdx+" class='autocomplete-item'>")
                                .append($("<a>").append(item.label+ "<br><i>" + item.info + "</i>"))
                                .appendTo(ul);
                        }
                    };
                }
            };
        }])
}(angular));