text: (function() {
      /*!
       * Саджест
       * @namespace jquery.ui.suggest
       * @extends {jquery.ui.autocomplete} http://api.jqueryui.com/autocomplete/
       * @description
       *      Саджест это блок сотоящий из инпута и выпадающего списка.
       *      При вводе какого-либо значения в инпут это значение матчится на список
       *      слов из источника данных, и подходящие элементы из исходного списка
       *      показываются в выпадающем списке, в котором пользователь может выбрать
       *      нужный ему элемент.
       *      После выбора элемента значение инпута меняется на значение выбранного элемента
       *
       *      Поддерживаемые события:
       *        nb-type – всплывает при вводе значения в инпут
       *        nb-select – всплывает при выборе значения из саджеста
       *        nb-keypress-enter – всплывает при нажатии на энетер и отсутвии саджеста
       */
  
      /*!
       * Опции инициализации саджеста
       * @description
       *     Эти опции могут быть определены в yate шаблонах при описании наноблока.
       *     Опции можно менять в рантайме через событие setOption
       *
       * @example
       *     var sug = nb.find('#mysuggest');
       *     sug.setOption({ 'source','http://mydomain.com/user/search'});
       *
       * @type {Object}
       */
      var optionsSuggest = {
          /*!
           * Истоник данных
           * @description См. http://api.jqueryui.com/autocomplete/#option-source
           *
           * @type {(String|Array|Function)}
           */
          source: null,
  
          /*!
           * Количество элеметов, при котором в выпадающем списке появляется скролл
           *
           * @type {Number}
           */
          countMax: 10,
  
          /*!
           * Тип саджеста
           * @description
           *     Указывает из какого шаблона брать верстку для элемента выпадающего списка.
           *     См. файл suggest.yate: match /[.type].item nb-suggest
           *
           * @type {String}
           */
          type: 'default',
  
          /*!
           * Включение или отключение выделения жирным начертанием результатов
           * матчинга в выпадающем списке.
           *
           * @type {Boolean}
           */
          highlight: false,
  
          /*!
           * Размер блока.
           * @description Применятся на размер элементов в выпадающем списке.
           *
           * @type {String}
           */
          size: 's',
  
          /*!
           * Количесвто введенных символов, после которого начинать поиск слов
           */
          minLength: 2
      };
  
      $.widget("ui.suggest", $.ui.autocomplete, {
          options: optionsSuggest,
  
          _resizeMenu: function() {
              this.menu.element.width($(this.options.position.of).outerWidth());
          },
  
          _renderMenu: function(ul, items) {
              var that = this;
              var html = '';
  
              $.each(items, function(index, item) {
                  html += that._renderItem(item);
              });
  
              $(html).appendTo(ul);
  
              ul.children('li').each(function(index) {
                  $(this).data("ui-autocomplete-item", items[index]);
              });
          },
  
          _renderItem: function(item) {
              var clone = $.extend({}, item);
  
              if (this.options.highlight) {
                  if (typeof highlightings[this.options.type] == 'function') {
                      highlightings[this.options.type](clone, this._value());
                  } else if (typeof this.options.highlight == 'function') {
                      this.options.highlight(clone, this._value());
                  }
              }
  
              clone.labelContent = clone.label;
              delete clone.label;
  
              var renderData = {
                  item: clone,
                  type: this.options.type,
                  size: this.options.size
              };
  
              if ($.isFunction(this.options.renderItem)) {
                  return this.options.renderItem(renderData);
              } else {
                  return '<li><a href="#">' + clone.labelContent + '</a></li>';
              }
          },
  
          _suggest: function(items) {
              this._super(items);
  
              if (this.options.countMax && !this._heightMax) {
                  this._heightMax = this.menu.element.children().eq(0).height() * this.options.countMax;
                  this.menu.element.css({
                      'max-height': this._heightMax,
                      'overflow-y': 'auto',
                      'overflow-x': 'hidden'
                  });
              }
          },
  
          _value: function(value) {
              if (value) {
                  this._trigger('_setvalue', null, value);
                  return;
              }
  
              return this._super.apply(this, arguments);
          },
  
          search: function(value, event) {
              this._trigger('_search');
  
              return this._super(value, event);
          }
      });
  
      var highlightings = {
          'default': function(item, term) {
              var matcher = new RegExp('(' + $.ui.autocomplete.escapeRegex(term) + ')', "i");
              item.label = item.label.replace(matcher, '<b>$1</b>');
          },
  
          'username': function(item, term) {
              var matcher = new RegExp('(' + $.ui.autocomplete.escapeRegex(term) + ')', "ig");
  
              item.usernameHighlighted = item.username.replace(matcher, '<span class="_nb-suggest-hl">$1</span>');
  
              if (typeof item.email == 'string') {
                  item.emailHighlighted = item.email.replace(matcher, '<span class="_nb-suggest-hl">$1</span>');
              }
          }
      };
  
      /*
       * ### jQuery UI Depends:
       * - jquery.ui.autocomplete.js
       * - jquery.ui.button.js
       * - jquery.ui.core.js
       * - jquery.ui.widget.js
       * - jquery.ui.position.js
       * - jquery.ui.menu.js
       */
  
      /*!
       * @class nb.block.Suggest
       * @augments nb.block.Base
       */
      nb.define('suggest', {
  
          /**
           * Init select
           * @fires 'nb-suggest_inited'
           */
          oninit: function() {
              var that = this;
              var nodeTagName = this.$node[0].tagName.toLowerCase();
  
              if (nodeTagName === 'input' || nodeTagName === 'textarea') {
                  this.$control = this.$node;
              } else {
                  this.$control = this.$node.find('input');
                  this.input = nb.block(this.$node.find('.nb-input').get(0));
              }
  
              var source = this.$node.data('source');
  
              this.$control.on('keydown.nb-suggest', function(e) {
                  var keyCode = $.ui.keyCode;
  
                  if ($.inArray(e.keyCode, [keyCode.ENTER, keyCode.NUMPAD_ENTER]) !== -1) {
                      if (!this.$jUI.data().uiSuggest.menu.active) {
                          this.trigger('nb-keypress-enter', this, this.getValue());
                      }
                  }
              }.bind(this));
  
              this.$jUI = this.$control.suggest({
                  source: source,
                  position: {
                      of: this.$node.get(0)
                  },
                  countMax: this.$node.data('countMax'),
                  type: this.$node.data('type'),
                  size: this.$node.data('size'),
                  highlight: this.$node.data('highlight'),
                  minLength: this.$node.data('minLength'),
                  renderItem: function(data) {
                      return yr.run(that.getYateModuleName(), data, 'nb-suggest');
                  }
              });
  
              this.$suggest = this.$jUI.data().uiSuggest.menu.element;
  
              this.$suggest.addClass(this.$node.data('class-suggest'));
  
              this.$jUI.on('suggest_search.nb-suggest', function() {
                  this.trigger('nb-type', this, this.getValue());
              }.bind(this));
  
              this.$jUI.on('suggest_setvalue.nb-suggest', this.onSetValueToInput.bind(this));
  
              this.$jUI.on('suggestselect.nb-suggest', this.onSelectValue.bind(this));
  
              this.trigger('nb-inited', this);
          },
  
          /**
           * Callback of a `suggestselect` event
           * @param {$.Event} e
           * @param {Object} item data of selected item
           * @private
           */
          onSelectValue: function(e, item) {
              this.$selected = item.item;
  
              this.trigger('nb-select', this, item.item);
          },
  
          /**
           * Callback of a `suggest_setvalue` event
           * @param {$.Event} e
           * @param {string} value
           * @private
           */
          onSetValueToInput: function(e, value) {
              if (this.input) {
                  this.input.setValue(value);
              } else {
                  this.$control.val(value);
              }
          },
  
          /**
           * Get selected item from suggest
           * @return {Object}
           */
          getSelected: function() {
              return this.$selected;
          },
  
          /**
           * Sets option to the jUI widget
           * http://api.jqueryui.com/autocomplete/#method-option
           * @param  {Object} option `{ name: value }` имя и значение опции
           * @fires 'nb-option-set'
           * @returns {Object} nb.block
           */
          setOption: function(option) {
              this.$jUI.suggest('option', option);
              this.trigger('nb-option-set', this);
              return this;
          },
  
  
          /**
           * Gets option of the jUI widget
           * http://api.jqueryui.com/autocomplete/#method-option
           * @param {String} option
           * @returns {String} option value
           */
          getOption: function(option) {
              return this.$jUI.suggest('option', option);
          },
  
          /*
           * Set new items for suggest
           * @params {Array} source New source
           * @fires 'nb-source-changed'
           * @returns {Object} nb.block
           */
          setSource: function(source) {
              this.setOption({'source': source});
              this.trigger('nb-source-set', this);
              return this;
          },
  
          /*
           * Get items from suggest
           * @returns {Array} source
           */
          getSource: function() {
              return this.getOption('source');
          },
  
          /**
           * Скрывает список предложений
           * @fires 'nb-closed'
           * @returns {Object} nb.block
           */
          close: function() {
              this.$jUI.suggest('close');
              this.trigger('nb-closed', this);
              return this;
          },
  
          /**
           * Disables the suggest
           * @fires 'nb-disabled'
           * @returns {Object} nb.block
           */
          disable: function() {
              if (this.isEnabled()) {
                  if (this.input) {
                      this.input.disable();
                  } else {
                      this.$control.prop('disabled', true);
                  }
                  this.$node.addClass('_nb-is-disabled');
                  this.$jUI.suggest('disable');
                  this.trigger('nb-disabled', this);
              }
              return this;
          },
  
          /**
           * Enables the suggest
           * @fires 'nb-enabled'
           * @returns {Object} nb.block
           */
          enable: function() {
              if (!this.isEnabled()) {
                  if (this.input) {
                      this.input.enable();
                  } else {
                      this.$control.prop('disabled', false);
                  }
                  this.$node.removeClass('_nb-is-disabled');
                  this.$jUI.suggest('enable');
                  this.trigger('nb-enabled', this);
              }
              return this;
          },
  
          /**
           * Return state of the suggest
           * @returns {Boolean}
           */
          isEnabled: function() {
              return !this.$node.hasClass('_nb-is-disabled');
          },
  
          /**
           * Focus the suggest
           * @fires 'nb-focused'
           * @returns {Object} nb.block
           */
          focus: function() {
              if (this.isEnabled()) {
                  if (this.input) {
                      this.input.focus();
                  } else {
                      this.$control.focus();
                  }
              }
              this.trigger('nb-focused', this);
              return this;
          },
  
          /**
           * Get name of the suggest
           * @returns {String|Object} name
           */
          getName: function() {
              return this.$control.prop('name');
          },
  
          /**
           * Set name of the suggest
           * @param {string} name
           * @fires 'nb-name-set'
           * @returns {Object} nb.block
           */
          setName: function(name) {
              this.$control.prop('name', name);
              this.trigger('nb-name-set', this);
              return this;
          },
  
          /**
           * Blur the suggest
           * @fires 'nb-blured'
           * @returns {Object} nb.block
           */
          blur: function() {
              if (this.isEnabled()) {
                  if (this.input) {
                      this.input.blur();
                  } else {
                      this.$control.blur();
                  }
              }
              this.trigger('nb-blured', this);
              return this;
          },
  
          /**
           * Get current value of the suggest
           * @returns {String | Number}
           */
          getValue: function() {
              return this.$control.val();
          },
  
          /**
           * Get current value of the suggest
           * @param {String} value
           * @fires 'nb-value-set'
           * @returns {Object} nb.block
           */
          setValue: function(value) {
              if (this.isEnabled()) {
                  this.$control.val(value);
                  this.trigger('nb-value-set', this);
              }
              return this;
          },
  
          /**
           * Search value in the source array and open suggest popup
           * @param  {string|number} value
           * @returns {Object} nb.block
           */
          search: function(value) {
              this.$jUI.suggest("search", value);
              return this;
          },
  
          /**
           * Destroy the suggest
           * @fires 'nb-destroyed'
           */
          destroy: function() {
              if (this.$control && this.$jUI) {
                  var jUIData = this.$jUI.data();
                  this.$control.off('.nb-suggest');
                  this.$jUI.off('.nb-suggest');
                  if (jUIData && jUIData.uiSuggest) {
                      this.$jUI.suggest('destroy');
                  }
              }
              this.trigger('nb-destroyed', this);
              this.nbdestroy();
          }
  
      }, 'base');
  
  })();
  