File "jquery.easy-autocomplete.js"

Full Path: /home/vantageo/public_html/cache/cache/cache/cache/cache/cache/.wp-cli/wp-content/plugins/woocommerce-products-filter/js/easy-autocomplete/jquery.easy-autocomplete.js
File size: 54.23 KB
MIME-type: text/plain
Charset: utf-8

/*
 * easy-autocomplete
 * jQuery plugin for autocompletion
 * 
 * @author Łukasz Pawełczak (http://github.com/pawelczak)
 * @version 1.4.0
 * Copyright  License: 
 */

/*
 * EasyAutocomplete - Configuration 
 */

"use strict";

(function ($) {
    var EasyAutocomplete = (function (scope) {

        scope.Configuration = function Configuration(options) {
            var defaults = {
                data: 'list-required',
                url: 'list-required',
                dataType: 'json',

                listLocation: function (data) {
                    return data;
                },

                xmlElementName: '',

                getValue: function (element) {
                    return element;
                },

                autocompleteOff: true,

                placeholder: false,

                ajaxCallback: function () {
                },

                matchResponseProperty: false,

                list: {
                    sort: {
                        enabled: false,
                        method: function (a, b) {
                            a = defaults.getValue(a);
                            b = defaults.getValue(b);
                            if (a < b) {
                                return -1;
                            }
                            if (a > b) {
                                return 1;
                            }
                            return 0;
                        }
                    },

                    maxNumberOfElements: 6,

                    hideOnEmptyPhrase: true,

                    match: {
                        enabled: false,
                        caseSensitive: false,
                        method: function (element, phrase) {

                            return element.search(phrase) > -1;
                        }
                    },

                    showAnimation: {
                        type: 'normal', //normal|slide|fade
                        time: 400,
                        callback: function () {
                        }
                    },

                    hideAnimation: {
                        type: 'normal',
                        time: 400,
                        callback: function () {
                        }
                    },

                    /* Events */
                    onClickEvent: function () {
                    },
                    onSelectItemEvent: function () {
                    },
                    onLoadEvent: function () {
                    },
                    onChooseEvent: function () {
                    },
                    onKeyEnterEvent: function () {
                    },
                    onMouseOverEvent: function () {
                    },
                    onMouseOutEvent: function () {
                    },
                    onShowListEvent: function () {
                    },
                    onHideListEvent: function () {
                    }
                },

                highlightPhrase: true,

                theme: '',

                cssClasses: '',

                minCharNumber: 0,

                requestDelay: 0,

                adjustWidth: true,

                ajaxSettings: {},

                preparePostData: function (data, inputPhrase) {
                    return data;
                },

                loggerEnabled: true,

                template: '',

                categoriesAssigned: false,

                categories: [{
                        maxNumberOfElements: 4
                    }]

            };

            var externalObjects = ['ajaxSettings', 'template'];

            this.get = function (propertyName) {
                return defaults[propertyName];
            };

            this.equals = function (name, value) {
                if (isAssigned(name)) {
                    if (defaults[name] === value) {
                        return true;
                    }
                }

                return false;
            };

            this.checkDataUrlProperties = function () {
                return !(defaults.url === 'list-required' && defaults.data === 'list-required');

            };
            this.checkRequiredProperties = function () {
                for (var propertyName in defaults) {
                    if (defaults[propertyName] === 'required') {
                        logger.error('Option ' + propertyName + ' must be defined');
                        return false;
                    }
                }
                return true;
            };

            this.printPropertiesThatDoesntExist = function (consol, optionsToCheck) {
                printPropertiesThatDoesntExist(consol, optionsToCheck);
            };


            prepareDefaults();

            mergeOptions();

            if (defaults.loggerEnabled === true) {
                printPropertiesThatDoesntExist(console, options);
            }

            addAjaxSettings();

            processAfterMerge();
            function prepareDefaults() {

                if (options.dataType === 'xml') {

                    if (!options.getValue) {

                        options.getValue = function (element) {
                            return $(element).text();
                        };
                    }


                    if (!options.list) {

                        options.list = {};
                    }

                    if (!options.list.sort) {
                        options.list.sort = {};
                    }


                    options.list.sort.method = function (a, b) {
                        a = options.getValue(a);
                        b = options.getValue(b);
                        if (a < b) {
                            return -1;
                        }
                        if (a > b) {
                            return 1;
                        }
                        return 0;
                    };

                    if (!options.list.match) {
                        options.list.match = {};
                    }

                    options.list.match.method = function (element, phrase) {

                        return element.search(phrase) > -1;
                    };

                }
                if (options.categories !== undefined && options.categories instanceof Array) {

                    var categories = [];

                    for (var i = 0, length = options.categories.length; i < length; i += 1) {

                        var category = options.categories[i];

                        for (var property in defaults.categories[0]) {

                            if (category[property] === undefined) {
                                category[property] = defaults.categories[0][property];
                            }
                        }

                        categories.push(category);
                    }

                    options.categories = categories;
                }
            }

            function mergeOptions() {

                defaults = mergeObjects(defaults, options);

                function mergeObjects(source, target) {
                    var mergedObject = source || {};

                    for (var propertyName in source) {
                        if (target[propertyName] !== undefined && target[propertyName] !== null) {

                            if (typeof target[propertyName] !== 'object' ||
                                    target[propertyName] instanceof Array) {
                                mergedObject[propertyName] = target[propertyName];
                            } else {
                                mergeObjects(source[propertyName], target[propertyName]);
                            }
                        }
                    }

                    /* If data is an object */
                    if (target.data !== undefined && target.data !== null && typeof target.data === 'object') {
                        mergedObject.data = target.data;
                    }

                    return mergedObject;
                }
            }

            function processAfterMerge() {

                if (defaults.url !== 'list-required' && typeof defaults.url !== 'function') {
                    var defaultUrl = defaults.url;
                    defaults.url = function () {
                        return defaultUrl;
                    };
                }

                if (defaults.ajaxSettings.url !== undefined && typeof defaults.ajaxSettings.url !== 'function') {
                    var defaultUrl = defaults.ajaxSettings.url;
                    defaults.ajaxSettings.url = function () {
                        return defaultUrl;
                    };
                }

                if (typeof defaults.listLocation === 'string') {
                    var defaultlistLocation = defaults.listLocation;

                    if (defaults.dataType.toUpperCase() === 'XML') {
                        defaults.listLocation = function (data) {
                            return $(data).find(defaultlistLocation);
                        };
                    } else {
                        defaults.listLocation = function (data) {
                            return data[defaultlistLocation];
                        };
                    }
                }

                if (typeof defaults.getValue === 'string') {
                    var defaultsGetValue = defaults.getValue;
                    defaults.getValue = function (element) {
                        return element[defaultsGetValue];
                    };
                }

                if (options.categories !== undefined) {
                    defaults.categoriesAssigned = true;
                }

            }

            function addAjaxSettings() {

                if (options.ajaxSettings !== undefined && typeof options.ajaxSettings === 'object') {
                    defaults.ajaxSettings = options.ajaxSettings;
                } else {
                    defaults.ajaxSettings = {};
                }

            }

            function isAssigned(name) {
                return defaults[name] !== undefined && defaults[name] !== null;
            }
            function printPropertiesThatDoesntExist(consol, optionsToCheck) {

                checkPropertiesIfExist(defaults, optionsToCheck);

                function checkPropertiesIfExist(source, target) {
                    for (var property in target) {
                        if (source[property] === undefined) {
                            consol.log('Property \'' + property + '\' does not exist in EasyAutocomplete options API.');
                        }

                        if (typeof source[property] === 'object' && $.inArray(property, externalObjects) === -1) {
                            checkPropertiesIfExist(source[property], target[property]);
                        }
                    }
                }
            }
        };

        return scope;

    })(EasyAutocomplete || {});

    /*
     * EasyAutocomplete - Logger 
     */
    var EasyAutocomplete = (function (scope) {

        scope.Logger = function Logger() {

            this.error = function (message) {
                console.log('ERROR: ' + message);
            };

            this.warning = function (message) {
                console.log('WARNING: ' + message);
            };
        };

        return scope;

    })(EasyAutocomplete || {});


    /*
     * EasyAutocomplete - Constants
     */
    var EasyAutocomplete = (function (scope) {

        scope.Constants = function Constants() {

            var constants = {
                CONTAINER_CLASS: 'easy-autocomplete-container',
                CONTAINER_ID: 'eac-container-',
                WRAPPER_CSS_CLASS: 'easy-autocomplete'
            };

            this.getValue = function (propertyName) {
                return constants[propertyName];
            };

        };

        return scope;

    })(EasyAutocomplete || {});

    /*
     * EasyAutocomplete - ListBuilderService 
     *
     * @author Łukasz Pawełczak 
     *
     */
    var EasyAutocomplete = (function (scope) {

        scope.ListBuilderService = function ListBuilderService(configuration, proccessResponseData) {


            this.init = function (data) {
                var listBuilder = [],
                        builder = {};

                builder.data = configuration.get('listLocation')(data);
                builder.getValue = configuration.get('getValue');
                builder.maxListSize = configuration.get('list').maxNumberOfElements;


                listBuilder.push(builder);

                return listBuilder;
            };

            this.updateCategories = function (listBuilder, data) {

                if (configuration.get('categoriesAssigned')) {

                    listBuilder = [];

                    for (var i = 0; i < configuration.get("categories").length; i += 1) {

                        var builder = convertToListBuilder(configuration.get('categories')[i], data);

                        listBuilder.push(builder);
                    }

                }

                return listBuilder;
            };

            this.convertXml = function (listBuilder) {
                if (configuration.get('dataType').toUpperCase() === 'XML') {

                    for (var i = 0; i < listBuilder.length; i += 1) {
                        listBuilder[i].data = convertXmlToList(listBuilder[i]);
                    }
                }

                return listBuilder;
            };

            this.processData = function (listBuilder, inputPhrase) {

                for (var i = 0, length = listBuilder.length; i < length; i += 1) {
                    listBuilder[i].data = proccessResponseData(configuration, listBuilder[i], inputPhrase);
                }

                return listBuilder;
            };

            this.checkIfDataExists = function (listBuilders) {

                for (var i = 0, length = listBuilders.length; i < length; i += 1) {

                    if (listBuilders[i].data !== undefined && listBuilders[i].data instanceof Array) {
                        if (listBuilders[i].data.length > 0) {
                            return true;
                        }
                    }
                }

                return false;
            };


            function convertToListBuilder(category, data) {

                var builder = {};

                if (configuration.get('dataType').toUpperCase() === 'XML') {

                    builder = convertXmlToListBuilder();
                } else {

                    builder = convertDataToListBuilder();
                }


                if (category.header !== undefined) {
                    builder.header = category.header;
                }

                if (category.maxNumberOfElements !== undefined) {
                    builder.maxNumberOfElements = category.maxNumberOfElements;
                }

                if (configuration.get('list').maxNumberOfElements !== undefined) {

                    builder.maxListSize = configuration.get('list').maxNumberOfElements;
                }

                if (category.getValue !== undefined) {

                    if (typeof category.getValue === 'string') {
                        var defaultsGetValue = category.getValue;
                        builder.getValue = function (element) {
                            return element[defaultsGetValue];
                        };
                    } else if (typeof category.getValue === 'function') {
                        builder.getValue = category.getValue;
                    }

                } else {
                    builder.getValue = configuration.get('getValue');
                }


                return builder;


                function convertXmlToListBuilder() {

                    var builder = {},
                            listLocation;

                    if (category.xmlElementName !== undefined) {
                        builder.xmlElementName = category.xmlElementName;
                    }

                    if (category.listLocation !== undefined) {

                        listLocation = category.listLocation;
                    } else if (configuration.get('listLocation') !== undefined) {

                        listLocation = configuration.get('listLocation');
                    }

                    if (listLocation !== undefined) {
                        if (typeof listLocation === 'string') {
                            builder.data = $(data).find(listLocation);
                        } else if (typeof listLocation === 'function') {

                            builder.data = listLocation(data);
                        }
                    } else {

                        builder.data = data;
                    }

                    return builder;
                }


                function convertDataToListBuilder() {

                    var builder = {};

                    if (category.listLocation !== undefined) {

                        if (typeof category.listLocation === 'string') {
                            builder.data = data[category.listLocation];
                        } else if (typeof category.listLocation === 'function') {
                            builder.data = category.listLocation(data);
                        }
                    } else {
                        builder.data = data;
                    }

                    return builder;
                }
            }

            function convertXmlToList(builder) {
                var simpleList = [];

                if (builder.xmlElementName === undefined) {
                    builder.xmlElementName = configuration.get('xmlElementName');
                }


                $(builder.data).find(builder.xmlElementName).each(function () {
                    simpleList.push(this);
                });

                return simpleList;
            }

        };

        return scope;

    })(EasyAutocomplete || {});


    /*
     * EasyAutocomplete - Data proccess module
     *
     * Process list to display:
     * - sort 
     * - decrease number to specific number
     * - show only matching list
     *
     */
    var EasyAutocomplete = (function (scope) {

        scope.proccess = function DataProcessor(config, listBuilder, phrase) {

            scope.proccess.match = match;

            var list = listBuilder.data,
                    inputPhrase = phrase; // TODO REFACTOR

            list = findMatch(list, inputPhrase);
            list = reduceElementsInList(list);
            list = sort(list);

            return list;

            function findMatch(list, phrase) {
                var preparedList = [],
                        value = '';

                if (config.get('list').match.enabled) {

                    for (var i = 0, length = list.length; i < length; i += 1) {

                        value = config.get('getValue')(list[i]);

                        if (match(value, phrase)) {
                            preparedList.push(list[i]);
                        }

                    }

                } else {
                    preparedList = list;
                }

                return preparedList;
            }

            function match(value, phrase) {

                if (!config.get('list').match.caseSensitive) {

                    if (typeof value === 'string') {
                        value = value.toLowerCase();
                    }

                    phrase = phrase.toLowerCase();
                }

                return (config.get('list').match.method(value, phrase));
            }

            function reduceElementsInList(list) {
                if (listBuilder.maxNumberOfElements !== undefined && list.length > listBuilder.maxNumberOfElements) {
                    list = list.slice(0, listBuilder.maxNumberOfElements);
                }

                return list;
            }

            function sort(list) {
                if (config.get('list').sort.enabled) {
                    list.sort(config.get('list').sort.method);
                }

                return list;
            }

        };


        return scope;


    })(EasyAutocomplete || {});


    /*
     * EasyAutocomplete - Template 
     *
     * 
     *
     */
    var EasyAutocomplete = (function (scope) {

        scope.Template = function Template(options) {

            var genericTemplates = {
                basic: {
                    type: 'basic',
                    method: function (element) {
                        return element;
                    },
                    cssClass: ''
                },
                description: {
                    type: 'description',
                    fields: {
                        description: 'description'
                    },
                    method: function (element) {
                        return element + ' - description';
                    },
                    cssClass: 'eac-description'
                },
                iconLeft: {
                    type: 'iconLeft',
                    fields: {
                        icon: ''
                    },
                    method: function (element) {
                        return element;
                    },
                    cssClass: 'eac-icon-left'
                },
                iconRight: {
                    type: 'iconRight',
                    fields: {
                        iconSrc: ''
                    },
                    method: function (element) {
                        return element;
                    },
                    cssClass: 'eac-icon-right'
                },
                links: {
                    type: 'links',
                    fields: {
                        link: ''
                    },
                    method: function (element) {
                        return element;
                    },
                    cssClass: ''
                },
                custom: {
                    type: 'custom',
                    method: function () {
                    },
                    cssClass: ''
                }
            },
                    /*
                     * Converts method with {{text}} to function
                     */
                    convertTemplateToMethod = function (template) {


                        var _fields = template.fields,
                                buildMethod;

                        if (template.type === 'description') {

                            buildMethod = genericTemplates.description.method;

                            if (typeof _fields.description === 'string') {
                                buildMethod = function (elementValue, element) {
                                    return elementValue + ' - <span>' + element[_fields.description] + '</span>';
                                };
                            } else if (typeof _fields.description === 'function') {
                                buildMethod = function (elementValue, element) {
                                    return elementValue + ' - <span>' + _fields.description(element) + '</span>';
                                };
                            }

                            return buildMethod;
                        }

                        if (template.type === 'iconRight') {

                            if (typeof _fields.iconSrc === 'string') {
                                buildMethod = function (elementValue, element) {
                                    return elementValue + '<img class=\'eac-icon\' src=\'' + element[_fields.iconSrc] + '\' />';
                                };
                            } else if (typeof _fields.iconSrc === 'function') {
                                buildMethod = function (elementValue, element) {
                                    return elementValue + '<img class=\'eac-icon\' src=\'' + _fields.iconSrc(element) + '\' />';
                                };
                            }

                            return buildMethod;
                        }


                        if (template.type === 'iconLeft') {

                            if (typeof _fields.iconSrc === 'string') {
                                buildMethod = function (elementValue, element) {
                                    return '<img class=\'eac-icon\' src=\'' + element[_fields.iconSrc] + '\' />' + elementValue;
                                };
                            } else if (typeof _fields.iconSrc === 'function') {
                                buildMethod = function (elementValue, element) {
                                    return '<img class=\'eac-icon\' src=\'' + _fields.iconSrc(element) + '\' />' + elementValue;
                                };
                            }

                            return buildMethod;
                        }

                        if (template.type === 'links') {

                            if (typeof _fields.link === 'string') {
                                buildMethod = function (elementValue, element) {
                                    return '<a href=\'' + element[_fields.link] + '\' >' + elementValue + '</a>';
                                };
                            } else if (typeof _fields.link === 'function') {
                                buildMethod = function (elementValue, element) {
                                    return '<a href=\'' + _fields.link(element) + '\' >' + elementValue + '</a>';
                                };
                            }

                            return buildMethod;
                        }


                        if (template.type === 'custom') {

                            return template.method;
                        }

                        return genericTemplates.basic.method;

                    },
                    prepareBuildMethod = function (options) {
                        if (!options || !options.type) {

                            return genericTemplates.basic.method;
                        }

                        if (options.type && genericTemplates[options.type]) {

                            return convertTemplateToMethod(options);
                        } else {

                            return genericTemplates.basic.method;
                        }

                    },
                    templateClass = function (options) {
                        var emptyStringFunction = function () {
                            return '';
                        };

                        if (!options || !options.type) {

                            return emptyStringFunction;
                        }

                        if (options.type && genericTemplates[options.type]) {
                            return (function () {
                                var _cssClass = genericTemplates[options.type].cssClass;
                                return function () {
                                    return _cssClass;
                                };
                            })();
                        } else {
                            return emptyStringFunction;
                        }
                    };

            this.getTemplateClass = templateClass(options);

            this.build = prepareBuildMethod(options);

        };

        return scope;

    })(EasyAutocomplete || {});

    /*
     * EasyAutocomplete - jQuery plugin for autocompletion
     *
     */
    var EasyAutocomplete = (function (scope) {

        scope.main = function Core($input, options) {

            var module = {
                name: 'EasyAutocomplete',
                shortcut: 'eac'
            };

            var consts = new scope.Constants(),
                    config = new scope.Configuration(options),
                    logger = new scope.Logger(),
                    template = new scope.Template(options.template),
                    listBuilderService = new scope.ListBuilderService(config, scope.proccess),
                    checkParam = config.equals,
                    $field = $input,
                    $container = '',
                    elementsList = [],
                    selectedElement = -1,
                    requestDelayTimeoutId;

            scope.consts = consts;

            this.getConstants = function () {
                return consts;
            };

            this.getConfiguration = function () {
                return config;
            };

            this.getContainer = function () {
                return $container;
            };

            this.getSelectedItemIndex = function () {
                return selectedElement;
            };

            this.getItems = function () {
                return elementsList;
            };

            this.getItemData = function (index) {

                if (elementsList.length < index || elementsList[index] === undefined) {
                    return -1;
                } else {
                    return elementsList[index];
                }
            };

            this.getSelectedItemData = function () {
                return this.getItemData(selectedElement);
            };

            this.build = function () {
                prepareField();
            };

            this.init = function () {
                init();
            };
            function init() {

                if ($field.length === 0) {
                    logger.error('Input field doesn\'t exist.');
                    return;
                }

                if (!config.checkDataUrlProperties()) {
                    logger.error('One of options variables \'data\' or \'url\' must be defined.');
                    return;
                }

                if (!config.checkRequiredProperties()) {
                    logger.error('Will not work without mentioned properties.');
                    return;
                }


                prepareField();
                bindEvents();

            }
            function prepareField() {


                if ($field.parent().hasClass(consts.getValue('WRAPPER_CSS_CLASS'))) {
                    removeContainer();
                    removeWrapper();
                }

                createWrapper();
                createContainer();

                $container = $('#' + getContainerId());
                if (config.get('placeholder')) {
                    $field.attr('placeholder', config.get('placeholder'));
                }


                function createWrapper() {
                    var $wrapper = $('<div>'),
                            classes = consts.getValue('WRAPPER_CSS_CLASS');


                    if (config.get('theme') && config.get('theme') !== '') {
                        classes += ' eac-' + config.get('theme');
                    }

                    if (config.get('cssClasses') && config.get('cssClasses') !== '') {
                        classes += ' ' + config.get('cssClasses');
                    }

                    if (template.getTemplateClass() !== '') {
                        classes += ' ' + template.getTemplateClass();
                    }


                    $wrapper
                            .addClass(classes);
                    $field.wrap($wrapper);


                    if (config.get('adjustWidth') === true) {
                        adjustWrapperWidth();
                    }


                }

                function adjustWrapperWidth() {
                    var fieldWidth = $field.outerWidth();

                    $field.parent().css('width', fieldWidth);
                }

                function removeWrapper() {
                    $field.unwrap();
                }

                function createContainer() {
                    var $elements_container = $('<div>').addClass(consts.getValue('CONTAINER_CLASS'));

                    $elements_container
                            .attr('id', getContainerId())
                            .prepend($('<ul>'));


                    (function () {

                        $elements_container
                                /* List show animation */
                                .on('show.eac', function () {
                                    if (!$field.is(':focus')) {
                                        return
                                    }

                                    switch (config.get('list').showAnimation.type) {

                                        case 'slide':
                                            var animationTime = config.get('list').showAnimation.time,
                                                    callback = config.get('list').showAnimation.callback;

                                            $elements_container.find('ul').slideDown(animationTime, callback);
                                            break;

                                        case 'fade':
                                            var animationTime = config.get('list').showAnimation.time,
                                                    callback = config.get('list').showAnimation.callback;

                                            $elements_container.find('ul').fadeIn(animationTime), callback;
                                            break;

                                        default:
                                            $elements_container.find('ul').show();
                                            break;
                                    }

                                    config.get('list').onShowListEvent();

                                })
                                /* List hide animation */
                                .on('hide.eac', function () {

                                    switch (config.get('list').hideAnimation.type) {

                                        case 'slide':
                                            var animationTime = config.get('list').hideAnimation.time,
                                                    callback = config.get('list').hideAnimation.callback;

                                            $elements_container.find('ul').slideUp(animationTime, callback);
                                            break;

                                        case 'fade':
                                            var animationTime = config.get('list').hideAnimation.time,
                                                    callback = config.get('list').hideAnimation.callback;

                                            $elements_container.find('ul').fadeOut(animationTime, callback);
                                            break;

                                        default:
                                            $elements_container.find('ul').hide();
                                            break;
                                    }

                                    config.get('list').onHideListEvent();

                                })
                                .on('selectElement.eac', function () {
                                    $elements_container.find('ul li').removeClass('selected');
                                    $elements_container.find('ul li').eq(selectedElement).addClass('selected');

                                    config.get('list').onSelectItemEvent();
                                })
                                .on('loadElements.eac', function (event, listBuilders, phrase) {


                                    var $item = '',
                                            $listContainer = $elements_container.find('ul');

                                    $listContainer
                                            .empty()
                                            .detach();

                                    elementsList = [];
                                    var counter = 0;
                                    for (var builderIndex = 0, listBuildersLength = listBuilders.length; builderIndex < listBuildersLength; builderIndex += 1) {

                                        var listData = listBuilders[builderIndex].data;

                                        if (listData.length === 0) {
                                            continue;
                                        }

                                        if (listBuilders[builderIndex].header !== undefined && listBuilders[builderIndex].header.length > 0) {
                                            $listContainer.append('<div class=\'eac-category\' >' + listBuilders[builderIndex].header + '</div>');
                                        }

                                        for (var i = 0, listDataLength = listData.length; i < listDataLength && counter < listBuilders[builderIndex].maxListSize; i += 1) {
                                            $item = $('<li><div class=\'eac-item\'></div></li>');


                                            (function () {
                                                var j = i,
                                                        itemCounter = counter,
                                                        elementsValue = listBuilders[builderIndex].getValue(listData[j]);

                                                $item.find(' > div')
                                                        .on('click', function () {

                                                            $field.val(elementsValue).trigger('change');

                                                            selectedElement = itemCounter;
                                                            selectElement(itemCounter);

                                                            config.get('list').onClickEvent();
                                                            config.get('list').onChooseEvent();
                                                        })
                                                        .mouseover(function () {

                                                            selectedElement = itemCounter;
                                                            selectElement(itemCounter);

                                                            config.get('list').onMouseOverEvent();
                                                        })
                                                        .mouseout(function () {
                                                            config.get('list').onMouseOutEvent();
                                                        })
                                                        .html(template.build(highlight(elementsValue, phrase), listData[j]));
                                            })();

                                            $listContainer.append($item);
                                            elementsList.push(listData[i]);
                                            counter += 1;
                                        }
                                    }

                                    $elements_container.append($listContainer);

                                    config.get('list').onLoadEvent();
                                });

                    })();

                    $field.after($elements_container);
                }

                function removeContainer() {
                    $field.next('.' + consts.getValue('CONTAINER_CLASS')).remove();
                }

                function highlight(string, phrase) {

                    if (config.get('highlightPhrase') && phrase !== '') {
                        return highlightPhrase(string, phrase);
                    } else {
                        return string;
                    }

                }

                function escapeRegExp(str) {
                    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
                }

                function highlightPhrase(string, phrase) {
                    var escapedPhrase = escapeRegExp(phrase);
                    return (string + '').replace(new RegExp('(' + escapedPhrase + ')', 'gi'), '<b>$1</b>');
                }


            }
            function getContainerId() {

                var elementId = $field.attr('id');

                elementId = consts.getValue('CONTAINER_ID') + elementId;

                return elementId;
            }
            function bindEvents() {

                bindAllEvents();


                function bindAllEvents() {
                    if (checkParam('autocompleteOff', true)) {
                        removeAutocomplete();
                    }

                    bindFocusOut();
                    bindKeyup();
                    bindKeydown();
                    bindKeypress();
                    bindFocus();
                    bindBlur();
                }

                function bindFocusOut() {
                    $field.focusout(function () {

                        var fieldValue = $field.val(),
                                phrase;

                        if (!config.get('list').match.caseSensitive) {
                            fieldValue = fieldValue.toLowerCase();
                        }

                        for (var i = 0, length = elementsList.length; i < length; i += 1) {

                            phrase = config.get('getValue')(elementsList[i]);
                            if (!config.get('list').match.caseSensitive) {
                                phrase = phrase.toLowerCase();
                            }

                            if (phrase === fieldValue) {
                                selectedElement = i;
                                selectElement(selectedElement);
                                return;
                            }
                        }
                    });
                }

                function bindKeyup() {
                    $field
                            .off('keyup')
                            .keyup(function (event) {

                                switch (event.keyCode) {

                                    case 27:

                                        hideContainer();
                                        loseFieldFocus();
                                        break;

                                    case 38:

                                        event.preventDefault();

                                        if (elementsList.length > 0 && selectedElement > 0) {

                                            selectedElement -= 1;

                                            $field.val(config.get('getValue')(elementsList[selectedElement]));

                                            selectElement(selectedElement);

                                        }
                                        break;

                                    case 40:

                                        event.preventDefault();

                                        if (elementsList.length > 0 && selectedElement < elementsList.length - 1) {

                                            selectedElement += 1;

                                            $field.val(config.get('getValue')(elementsList[selectedElement]));

                                            selectElement(selectedElement);

                                        }

                                        break;

                                    default:


                                        if (event.keyCode > 40 || event.keyCode === 8 || event.keyCode === 0) {

                                            var inputPhrase = $field.val();

                                            if (!(config.get('list').hideOnEmptyPhrase === true && event.keyCode === 8 && inputPhrase === '')) {

                                                if (config.get('requestDelay') > 0) {
                                                    if (requestDelayTimeoutId !== undefined) {
                                                        clearTimeout(requestDelayTimeoutId);
                                                    }

                                                    requestDelayTimeoutId = setTimeout(function () {
                                                        loadData(inputPhrase);
                                                    }, config.get('requestDelay'));
                                                } else {
                                                    loadData(inputPhrase);
                                                }

                                            } else {
                                                hideContainer();
                                            }

                                        }


                                        break;
                                }


                                function loadData(inputPhrase) {


                                    if (inputPhrase.length < config.get('minCharNumber')) {
                                        return;
                                    }


                                    if (config.get('data') !== 'list-required') {

                                        var data = config.get('data');

                                        var listBuilders = listBuilderService.init(data);

                                        listBuilders = listBuilderService.updateCategories(listBuilders, data);

                                        listBuilders = listBuilderService.processData(listBuilders, inputPhrase);

                                        loadElements(listBuilders, inputPhrase);

                                        if ($field.parent().find('li').length > 0) {
                                            showContainer();
                                        } else {
                                            hideContainer();
                                        }

                                    }

                                    var settings = createAjaxSettings();

                                    if (settings.url === undefined || settings.url === '') {
                                        settings.url = config.get('url');
                                    }

                                    if (settings.dataType === undefined || settings.dataType === '') {
                                        settings.dataType = config.get('dataType');
                                    }


                                    if (settings.url !== undefined && settings.url !== 'list-required') {

                                        settings.url = settings.url(inputPhrase);

                                        settings.data = config.get('preparePostData')(settings.data, inputPhrase);

                                        $.ajax(settings)
                                                .done(function (data) {

                                                    var listBuilders = listBuilderService.init(data);

                                                    listBuilders = listBuilderService.updateCategories(listBuilders, data);

                                                    listBuilders = listBuilderService.convertXml(listBuilders);
                                                    if (checkInputPhraseMatchResponse(inputPhrase, data)) {

                                                        listBuilders = listBuilderService.processData(listBuilders, inputPhrase);

                                                        loadElements(listBuilders, inputPhrase);

                                                    }

                                                    if (listBuilderService.checkIfDataExists(listBuilders) && $field.parent().find('li').length > 0) {
                                                        showContainer();
                                                    } else {
                                                        hideContainer();
                                                    }

                                                    config.get('ajaxCallback')();

                                                })
                                                .fail(function () {
                                                    logger.warning('Fail to load response data');
                                                })
                                                .always(function () {

                                                });
                                    }


                                    function createAjaxSettings() {

                                        var settings = {},
                                                ajaxSettings = config.get('ajaxSettings') || {};

                                        for (var set in ajaxSettings) {
                                            settings[set] = ajaxSettings[set];
                                        }

                                        return settings;
                                    }

                                    function checkInputPhraseMatchResponse(inputPhrase, data) {

                                        if (config.get('matchResponseProperty') !== false) {
                                            if (typeof config.get('matchResponseProperty') === 'string') {
                                                return (data[config.get('matchResponseProperty')] === inputPhrase);
                                            }

                                            if (typeof config.get('matchResponseProperty') === 'function') {
                                                return (config.get('matchResponseProperty')(data) === inputPhrase);
                                            }

                                            return true;
                                        } else {
                                            return true;
                                        }

                                    }

                                }


                            });
                }

                function bindKeydown() {
                    $field
                            .on('keydown', function (evt) {
                                evt = evt || window.event;
                                var keyCode = evt.keyCode;
                                if (keyCode === 38) {
                                    suppressKeypress = true;
                                    return false;
                                }
                            })
                            .keydown(function (event) {

                                if (event.keyCode === 13 && selectedElement > -1) {

                                    $field.val(config.get('getValue')(elementsList[selectedElement]));

                                    config.get('list').onKeyEnterEvent();
                                    config.get('list').onChooseEvent();

                                    selectedElement = -1;
                                    hideContainer();

                                    event.preventDefault();
                                }
                            });
                }

                function bindKeypress() {
                    $field
                            .off('keypress');
                }

                function bindFocus() {
                    $field.focus(function () {

                        if ($field.val() !== '' && elementsList.length > 0) {

                            selectedElement = -1;
                            showContainer();
                        }

                    });
                }

                function bindBlur() {
                    $field.blur(function () {
                        setTimeout(function () {

                            selectedElement = -1;
                            hideContainer();
                        }, 250);
                    });
                }

                function removeAutocomplete() {
                    $field.attr('autocomplete', 'off');
                }

            }

            function showContainer() {
                $container.trigger('show.eac');
            }

            function hideContainer() {
                $container.trigger('hide.eac');
            }

            function selectElement(index) {

                $container.trigger('selectElement.eac', index);
            }

            function loadElements(list, phrase) {
                $container.trigger('loadElements.eac', [list, phrase]);
            }

            function loseFieldFocus() {
                $field.trigger('blur');
            }


        };
        scope.eacHandles = [];

        scope.getHandle = function (id) {
            return scope.eacHandles[id];
        };

        scope.inputHasId = function (input) {

            if ($(input).attr('id') !== undefined && $(input).attr('id').length > 0) {
                return true;
            } else {
                return false;
            }

        };

        scope.assignRandomId = function (input) {

            var fieldId = '';

            do {
                fieldId = 'eac-' + Math.floor(Math.random() * 10000);
            } while ($('#' + fieldId).length !== 0);

            elementId = scope.consts.getValue('CONTAINER_ID') + fieldId;

            $(input).attr('id', fieldId);

        };

        scope.setHandle = function (handle, id) {
            scope.eacHandles[id] = handle;
        };


        return scope;

    })(EasyAutocomplete || {});



    $.fn.easyAutocomplete = function (options) {

        return this.each(function () {
            var $this = $(this),
                    eacHandle = new EasyAutocomplete.main($this, options);

            if (!EasyAutocomplete.inputHasId($this)) {
                EasyAutocomplete.assignRandomId($this);
            }

            eacHandle.init();

            EasyAutocomplete.setHandle(eacHandle, $this.attr('id'));

        });
    };

    $.fn.getSelectedItemIndex = function () {

        var inputId = $(this).attr('id');

        if (inputId !== undefined) {
            return EasyAutocomplete.getHandle(inputId).getSelectedItemIndex();
        }

        return -1;
    };

    $.fn.getItems = function () {

        var inputId = $(this).attr('id');

        if (inputId !== undefined) {
            return EasyAutocomplete.getHandle(inputId).getItems();
        }

        return -1;
    };

    $.fn.getItemData = function (index) {

        var inputId = $(this).attr('id');

        if (inputId !== undefined && index > -1) {
            return EasyAutocomplete.getHandle(inputId).getItemData(index);
        }

        return -1;
    };

    $.fn.getSelectedItemData = function () {

        var inputId = $(this).attr('id');

        if (inputId !== undefined) {
            return EasyAutocomplete.getHandle(inputId).getSelectedItemData();
        }

        return -1;
    };

})(jQuery);