text: /**
   * Created with JetBrains PhpStorm.
   * User: tm.baeva
   * Date: 22.04.13
   * Time: 14:15
   * To change this template use File | Settings | File Templates.
   */
  define('Deprecated/Controls/NavigationPanel/NavigationPanel', [
     'Core/helpers/String/escapeTagsFromStr',
     'Core/helpers/String/format',
     'Core/core-clone',
     'Core/core-instance',
     'Core/core-merge',
     'Deprecated/RecordSet',
     'Core/HashManager',
     'Env/Env',
     'Core/Context',
     'Core/Deferred',
     'Lib/Control/Control',
     'Lib/Type/TDataSource/TDataSource',
     'html!Deprecated/Controls/NavigationPanel/NavigationPanel',
     'Core/DependencyResolver',
     'Lib/NavigationController/NavigationController',
     'css!Deprecated/Controls/Accordion/Accordion',
     'css!Deprecated/Controls/NavigationPanel/NavigationPanel'
  ], function(
     escapeTagsFromStr,
     stringFormat,
     coreClone,
     cInstance,
     cMerge,
     dTransportRecordSet,
     HashManager,
     Env,
     cContext,
     cDeferred,
     Control,
     TDataSource,
     dotTplFn,
     DependencyResolver,
     NavigationController
  ) {
  
     'use strict';
  
     DependencyResolver.register('Deprecated/Controls/NavigationPanel/NavigationPanel', function(config){
        return config.isCollapsing ? ['Deprecated/Controls/NavigationPanel/plugins/Collapse-plugin'] : [];
     });
  
     /**
      * Панель навигации
      *
      * @class Deprecated/Controls/NavigationPanel/NavigationPanel
      * @public
      * @extends Lib/Control/Control
      * @author Крайнов Д.О.
      * @control
      * @category Navigation
      * @deprecated Используйте класс {@link WS3NavigationPanel/Right}.
      */
  
     var NavigationPanel = Control.Control.extend(/** @lends Deprecated/Controls/NavigationPanel/NavigationPanel.prototype */{
        /**
         * @event onElementClick Событие при клике на запись. Происходит при непосредственном клике на строку навигационной панели, либо после вызова метода openElement. Результат не обрабатывается
         * @param {Env/Event:Object} eventObject Дескриптор события
         * @param {Deprecated/Record} record Новая активная запись
         * @example
         * <pre>
         *    navigationPanel.subscribe('onElementClick', function(eventObject, record){
         *       CoreFunctions.alert("Выбрана запись с первичным ключем " + record.getKey());
         *    });
         * </pre>
         */
        /**
         * @event onAfterLoad Событие, происходящее после загрузки дынных. Результат не обрабатывается
         * @param {Env/Event:Object} eventObject Дескриптор события
         * @param {dTransportRecordSet} recordSet Экземпляр рекордсета
         * @param {Boolean} isSuccess успешна ли загрузка
         * @param {Error} error произошедшая ощибка (если загрузка неуспешная)
         */
        /**
         * @event onAfterRender Событие, происходящее после готовности контрола (контрол отрисован, повесил обработчики на свои элементы, установил активный пункт или не установил, если выбрана опция noSelectAtFirstLoad). Результат не обрабатывается
         * @param {Env/Event:Object} eventObject Дескриптор события
         */
        $protected: {
           _options : {
              /**
               * @cfg {Boolean} Сохранять состояние
               *
               * Этот параметр устанавливает необходимость сохранения состояния контрола
               */
              saveState : true,
              /**
               * @cfg {Object} Параметры получения данных, передаются непосредственно в dTransportRecordSet
               *
               * <pre>
               *    dataSource: {
               *       readerParams: {
               *          linkedObject: 'Сотрудники',
               *          queryName: 'Список'
               *       }
               *    }
               * </pre>
               * Подробный пример смотри в описании dTransportRecordSet
               * @editor TDataSourceEditor
               */
              dataSource: TDataSource,
              /**
               * @cfg {String} Поведение
               *
               * @variant default в той же позиции
               * @variant dragToTop вверху (активная вкладка открывается вверху панели)
               */
              behavior: 'default',
              autoHeight: false,
              /**
               * @cfg {String} Поле с названием иерархии
               *
               * Это свойство хранит имя отображаемого в панели поля записи
               * @editor BLFieldsChooser
               */
              altTreeNameFld: '',
              /**
               * @cfg {String} Поле иерархии
               *
               * Данное свойство указывает на поле, хранящее ключ иерархии (поле hierColumn каждой записи покажет ключ родителя этой записи)
               * По умолчанию это поле имеет значение "Раздел"
               * @editor BLFieldsChooser
               */
              hierColumn: 'Раздел',
              /**
               * @typedef {Object} FilterParam
               * @property {string} fieldName Имя поля
               * @property {boolean} [autoreload=true] Перезагружать данные при изменении поля в контексте
               */
              /**
               * @cfg {Object.<string, boolean|number|string|FilterParam>} Параметры фильтрации списочного метода бизнес-логики
               *
               * В данном свойстве можно указать параметры фильтрации источника данных
               * <pre>
               *    filterParams: {
               *       "ИдСотрудника": 12
               *    }
               * </pre>
               * @noShow
               */
              filterParams: {},
              /**
               * @cfg {Boolean} Отображать ли маркер
               *
               * Данный параметр указывает на то, будет ли отображаться маркер (обводка) на активном элементе панели
               */
              hasMarker: true,
              /**
               * @cfg {Function} Функция отрисовки строки
               *
               * Функция в качестве аргументов получает рекорд строки и jQuery обертку строки (!!! которой еще нет в DOM-дереве) и должна возвращать
               * jQuery элемент.
               * Для задания этой опции используйте функцию setRender, для получения - getRender
               * <pre>
               *    render: function(rec, $str){
               *       return rec.hasChildren() ?
               *          $str.append('<span>' + this.getRecordSet().recordChilds(rec.getKey()).length + '</span>') :
               *          $str.css('color', '#999');
               *    }
               * </pre>
               */
              render: undefined,
              /**
               * @cfg {Boolean} Оставлять ли открытыми вкладки при первой загрузке
               *
               * Параметр показывает, будут ли открыты какие-либо вкладки по умолчанию при первой загрузке контрола.
               * Следует учитывать, что при при активации адного из элементов контрол начнет работу в обычном режиме (активной будет только одна вкладка)
               */
              openDefault: false,
              /**
               * @cfg {String} Поле "Открыт по умолчанию"
               *
               * Этот параметр указывает на поле, которое отвечает за то, будет ли открыта какая-либо категория или раздел по умолчанию.
               * @editor BLFieldsChooser
               */
              openDefaultField: 'Открыт по умолчанию',
              /**
               * @cfg {Boolean} Не выделять запись при первой загрузке
               *
               * Дает возможность при первой загрузке контрола не активировать ни один из пунктов (Но в случае применения сохраненного состояние
               * из адресной строки, активация при первой загрузке произойдет)
               */
              noSelectAtFirstLoad: false,
              /**
               * @cfg {String} Поле "Фиктивный"
               * @editor BLFieldsChooser
               */
              fictitiousField: null,
              /**
               * @cfg {Boolean} Устанавливать ли название выбранного раздела в заголовок страницы
               *
               * Если включить данную опцию, то название выбранного раздела будет установлено в качестве заголовка страницы
               */
              setTitle: true,
              /**
               * @cfg {Boolean} Отображать ли счетчики
               */
              showCounters: false,
              cssClassName: 'ws-accordion ws-navigation-panel'
           },
           _dReady : null,
           _defTopParent: null,
           _roots: [],
           _areaHeight: [],
           _recordSet: null,
           _activeElement : null,
           _onAfterLoadHandler: null,
           _onAfterLoadRecordSet : null,
           _ftime: false,
           _activeGroup: null,
           _initialDocTitle: undefined,
           _systemFilterParams: {},
           _initialFilterParams: {}
        },
        $constructor: function(){
           this._publish('onElementClick', 'onReplaceMarker', 'onAfterLoad', 'onAfterRender');
  
           var self = this;
           this._onAfterLoadHandler = function(e, data, isSuccess, error){
              self._notify('onAfterLoad', data, isSuccess, error);
              if(!isSuccess){
                 Env.IoC.resolve('ILogger').error('NavigationPanel', error.message);
                 self._dReady.errback();
                 return;
              }
              self._recordSet = data;
              self._roots = data.recordChilds(null);
              self._container.html(dotTplFn(self));
              self._bindInternals();
              if(!self._options.noSelectAtFirstLoad || self._activeElement !== null){
                 if (self._activeElement !== null){
                    if(!self._isValidRecord(self._activeElement)){
                       if (!self._isValidRecord(self._activeGroup)) {
                          self._setActiveElement(self._roots[0]);
                          self._openGroup(self._activeElement);
                          self._selfNotify(self._activeElement, undefined, true);
                       } else {
                          self._setActiveElement(self._activeGroup);
                          self._ftime = false;
                          self.openElement(self._activeElement, self._ftime, true);
                       }
                    } else {
                       var state = NavigationController.getStateByKey(self.getStateKey()) || {};
                       self._ftime = false;
                       self.openElement(self._activeElement, self._ftime, false, !(state.state !== self._activeElement));
                       self._container
                          .find('.ws-accordion-wr.ws-navigation-panel-open-default')
                          .removeClass('ws-navigation-panel-open-default');
                    }
                 }
                 else if(self._roots.length){
                    self._setActiveElement(self._roots[0]);
                    self._openGroup(self._activeElement);
                    self._selfNotify(self._activeElement, undefined, true);
                 }
              }
              else{
                 self._notify('onElementClick', undefined);
              }
  
              self._dReady.callback();
              self._ftime = true;
              self._notifyBatchDelayed('onAfterRender');
           };
  
           this._dReady = new cDeferred();
           this._defTopParent = new cDeferred();
  
           function createMarker() {
              self._defTopParent.callback();
              self._toggleMarker(self._activeElement, true);
           }
  
           var topParent = this.getTopParent();
           if( cInstance.instanceOfModule(topParent, 'Deprecated/Controls/NavigationPanel/NavigationPanel') )
              createMarker();
           else {
              topParent.subscribe('onReady', function(){
                 self._dReady.addCallback(function(){
                    createMarker();
                 });
              });
           }
  
           if(this._options.autoHeight)
              this._container.css({
                 'height': 'auto',
                 'overflow-y': 'visible'
              });
           this._systemFilterParams = {
              'Разворот': 'С разворотом',
              'ЗаголовокИерархии': this._options.altTreeNameFld
           };
           this._options.dataSource.filterParams = coreClone(this._systemFilterParams);
           this._options.dataSource.filterParams = cMerge(this._options.dataSource.filterParams, this._options.filterParams, {rec: true});
           this._initialFilterParams = coreClone(this._options.dataSource.filterParams);
           this._options.dataSource.hierarchyField = this._options.hierColumn;
           this._deleteOpenDefault();
           this.subscribe('onReplaceMarker', function(e, key){
              self._onReplaceMarkerHandler(key);
           });
           this.once('onInit', function(){
              //обеспечиваем поднятие события строго после подписки на него NavigationController'ом
              self._notify('onStateChanged');
              self._createRecordSet(coreClone(self._options.dataSource), self._onAfterLoadHandler);
           });
        },
        _onReplaceMarkerHandler: function(key){
           this._toggleMarker(key, true);
        },
        /**
         * @param {Object} filterParams Параметры фильтрации источника данных
         *
         * Обновляет рекордсет панели. Используется в случае, если данные, отображаемые панелью, изменились
         */
        refresh: function(filterParams){
           var self = this;
           filterParams = filterParams || {};
           self._dReady = new cDeferred();
           self._areaHeight = [];
           self._options.dataSource.filterParams = coreClone(self._initialFilterParams);
           if (!$.isEmptyObject(filterParams)) {
              cMerge(self._options.dataSource.filterParams, filterParams, {rec: true, preferSource: false});
           }
  
           if (self._onAfterLoadRecordSet instanceof dTransportRecordSet && !self._onAfterLoadRecordSet.isInit()){
              self._onAfterLoadRecordSet.destroy();
           }
           self._onAfterLoadRecordSet = self._createRecordSet(coreClone(self._options.dataSource), self._onAfterLoadHandler)
        },
        //проверяет, есть ли в нашем рекордсете рекорд с заданным id
        _isValidRecord: function(id){
           try{
              this._recordSet.getRecordByPrimaryKey(id);
              return true;
           }
           catch(e){ return false; }
        },
        _createRecordSet : function(options, handler){
           options.handlers = options.handlers || {};
           options.handlers.onAfterLoad = handler;
           //Прокидываем свой контекст в рекордсет, чтобы рекордсет смог построить фильтр по полям этого контекста
           options.context = cContext.createContext(this, null, this.getLinkedContext());
           return new dTransportRecordSet(options);
        },
        /*
         * Проверка раздела на "фиктивность"
         * Вернет:
         * true — раздел фиктивный, открываем первый дочерний
         * false — раздел не фиктивный, ничего не делаем
         * */
        _checkFictitiousField: function(key) {
           var rs = this.getRecordSet(),
              isFictitiousField = this._options.fictitiousField && rs.getRecordByPrimaryKey(key).get(this._options.fictitiousField),
              childs = rs.recordChilds(key);
           if (isFictitiousField && childs.length) {
              this.openElement(childs[0], false, true);
              return true;
           }
           return false;
        },
        _labelClickHandler: function(key){
           if (this._checkFictitiousField(key)) return;
           this._setActiveElement(key);
           this._openGroup(key);
           this._selfNotify(key, undefined, true);
        },
        _bindInternals: function(){
           var self = this;
  
           this._container.find('.ws-accordion-wr>.ws-accordion-element-title')
              .hover(function(e){
                 $(this).parent().toggleClass('ws-accordion-wr-hover', e.type == 'mouseenter' && self.isEnabled());
              })
              .click(function(){
                 if (self.isEnabled()) {
                    self._labelClickHandler($(this).attr('element'));
                 }
              });
           this._container.find('.ws-navigation-panel-item .ws-navigation-panel-text-container')
              .click(function(){
                 if (self.isEnabled()) {
                    self.openElement($(this).attr('element'), false, true);
                 }
              });
        },
        /**
         * Удаляем классы open default, ведем себя как обычно
         */
        _deleteOpenDefault: function(){
           var self = this,
              deleteOpenDefaultClass = function(){
                 self._container
                    .find('.ws-accordion-wr.ws-navigation-panel-open-default')
                    .removeClass('ws-navigation-panel-open-default');
                 // запомним в историю браузера состояние "Ничего не выбрано"
                 var state = NavigationController.getStateByKey(self.getStateKey()) || {};
                 if (state.state !== self._activeElement) {
                    HashManager.pushState();
                 }
              },
              onElementClickHandler =  function(e, rec) {
                 if(rec) {
                    self.unsubscribe('onElementClick', onElementClickHandler);
                    deleteOpenDefaultClass();
                 }
              };
           self.subscribe('onElementClick', onElementClickHandler, true);
        },
        /**
         *
         * Делает активной запись панели(раздел или категорию)
         * @param {Number} key - id записи активизируемой
         * @param {Boolean} noNotifyClick - нужно ли активировать событие onElementClick
         * @param {Boolean} changeState - нужно ли не запоминать новое состояние в истории браузера
         * @param {Boolean} [noNotifyState]
         * @example
         * <pre>
         *    navigationPanel.subscribe('onElementClick', function(eventObject, record){
         *       if(record.getKey() == 1)
         *          this.openElement(2);
         *    });
         * </pre>
         */
        openElement : function(key, noNotifyClick, changeState, noNotifyState) {
           var element,
              parent,
              isRoot;
  
           if (key === undefined){
              this.clearActiveElement();
              this._selfNotify(key, noNotifyClick, changeState, noNotifyState);
              return;
           }
  
           if (this._checkFictitiousField(key)) return;
  
           element = this._container.find('[element="'+ key +'"]');
           parent = element.parent();
           isRoot = parent.hasClass('.ws-accordion-wr');
  
           if (!element.length) return;
  
           if(isRoot) {
              this._openGroup(key);
           } else {
              // Открывает родительскую группу (в которой сидит сам
              this._openGroup(element.parents('.ws-accordion-wr').children('.ws-accordion-element-title').attr('element'));
              // Показывает вложенные группы
              element.parent().children('ul.ws-navigation-panel-list').show();
              // Показывает собственного родителя
              element.parents('.ws-navigation-panel ul.ws-navigation-panel-list').show();
              // Для каждого из родителей, ищем соседние элементы и внутри каждого скрываем списки аккордеона.
              // Не поднимаемся выше собственного контейнера чтобы не искать лишнего
              element.parentsUntil('.ws-navigation-panel').siblings().find('ul.ws-navigation-panel-list').hide();
           }
           this._notifyOnSizeChanged(this, this);
           this._setActiveElement(key);
           this._selfNotify(key, noNotifyClick, changeState, noNotifyState);
        },
        _setDocumentTitle: function(title){
           if(title) {
              var docTitle = document.title;
              document.title = /\//.test(docTitle) ?
                 [title, '/', docTitle.split('/').pop()].join('') :
                 [title, '/', docTitle].join('');
              if (!this._initialDocTitle) {
                 this._initialDocTitle = (/^.+(?=\/)|[^\/]+$/.exec(docTitle) || ['']).pop();
              }
           }
        },
        /**
         *
         * Очистить значения счетчиков
         */
        clearCounters: function() {
           this._container.find('[countermenu]').html('');
           this._activeElement && this._notifyBatchDelayed('onReplaceMarker', this._activeElement);
        },
        /**
         *
         * Устанавливает счетчики
         * @param {Object} counters (key — идентификатор элемента меню, value — значение счетчика)
         * @example
         * <pre>
         *    navigationPanel.setCounters({
         *       'ИдентификаторМеню1': 'Значение счетчика 1'
         *       'ИдентификаторМеню2': 'Значение счетчика 2'
         *    });
         * </pre>
         */
        setCounters: function(counters) {
           for (var key in counters) {
              if (counters.hasOwnProperty(key) && this._isValidRecord(key)) {
                 this._container.find('[countermenu="' + key + '"]').html(counters[key]);
              }
           }
           this._activeElement && this._notifyBatchDelayed('onReplaceMarker', this._activeElement);
        },
        /**
         *
         * Применение сохраненного состояния
         * @param {Number} key - id записи
         */
        applyState : function(key){
           var self = this;
           if (this._recordSet === null){
              this._setActiveElement(key);
           }
           else{
              this._dReady.addCallback(function(){
                 var state = NavigationController.getStateByKey(self.getStateKey()) || {};
                 self.openElement(key, false, !(state.state === key), true);
              });
           }
        },
  
        applyEmptyState: function(){
           if(this._options.noSelectAtFirstLoad){
              this.redraw(true);
              this.clearActiveElement();
              this._deleteOpenDefault();
              this._setDocumentTitle(this._initialDocTitle);
              this._notify('onElementClick', undefined);
           }
        },
  
        _toggleMarker: function(key, show){
           var element = this.getContainer().find('[element="' + key + '"]'),
              parent = element.parent(),
              type = element.height() < 32 ? '' : ' big';
           if (parent.prop("tagName") == 'LI') {
              element = parent;
           }
           if (show) {
              if (this._options.hasMarker && this._defTopParent.isReady()) {
                 element.addClass('ws-navigator-panel-active-element' + type);
              }
           } else {
              element.removeClass('ws-navigator-panel-active-element' + type);
           }
        },
  
        _setActiveElement: function(key) {
           this.clearActiveElement();
           this._activeElement = key;
        },
        /**
         *
         * Очистить текущий выделеный элемент
         */
        clearActiveElement: function(){
           this._toggleMarker(this._activeElement, false);
           this._activeElement = null;
        },
  
        _openGroup: function(id) {
           this._activeGroup = id;
           this._setActiveElement(id);
           if(this._container.find('[element="'+ id +'"]').hasClass('ws-accordion-active'))
              return;
           this._closeActiveGroup();
           var
              wr = this._container.find('[element="'+ id +'"]').parent().addClass('ws-accordion-wrapper-active'),
              title = wr.children('.ws-accordion-element-title').addClass('ws-accordion-active'),
              area = wr.children('.ws-accordion-element-area').show();
           area.children('ul.ws-navigation-panel-list').css('display', 'block');
           this._container.find('.ws-accordion-element-title').trigger('mouseleave');
           if(!this._options.autoHeight && !this._areaHeight[id]){
              this._areaHeight[id] = this._container.outerHeight() - this._roots.length * title.outerHeight()
                    - this._container.find('.ws-accordion-shadow').height() - parseInt(area.css('padding-top'), 10);
           }
           if(this._options.behavior == 'dragToTop' && wr.prev().length){
              this._dragToTop(wr);
           }
           if(this._options.setTitle)
              this._setDocumentTitle(this.getActiveRecord().get(this._options.altTreeNameFld));
           this._notifyOnSizeChanged(this, this);
        },
  
        _dragToTop: function(wr){
           var
              wasActive = this._container.find('.ws-accordion-wr').first(),
              wasActiveId = wasActive.attr('id').replace(this.getId() + '-', ''),
              indexBefore = this._roots.indexOf(String(wasActiveId)) - 1,
              self = this,
              getWr = function(idx){
                 return self._container.find('#' + self.getId() + '-' + self._roots[idx]);
              },
              befored;
           //перетащим новую активную вкладку на самый верх
           wasActive.before(wr);
           //поставим бывшую активную вкладку на нужное место
           if(indexBefore > -1){
              if(!(befored = getWr(indexBefore)).hasClass('ws-accordion-wrapper-active'))
                 befored.after(wasActive);
              else
                 getWr(--indexBefore).after(wasActive);
           }
        },
  
        _closeActiveGroup: function(){
           this._container.find('.ws-accordion-wrapper-active')
              .removeClass('ws-accordion-wrapper-active')
              .children('.ws-accordion-element-title')
              .removeClass('ws-accordion-active')
              .end()
              .children('.ws-accordion-element-area')
              .hide()
              .end()
              .find('ul.ws-navigation-panel-list')
              .hide();
        },
        /**
         *
         * Проверяет, лежит ли запись в корне (является ли категорией)
         * @param {Number} id Идентификатор записи
         * @return {Boolean}
         * @example
         * <pre>
         *    navigationPanel.subscribe('onElementClick', function(eventObject, record){
         *       if(this.elementIsRoot(record.getKey()))
         *          CoreFunctions.alert("Выбрана категория " + record.get('Название'));
         *    });
         * </pre>
         */
        elementIsRoot: function(id){
           return (this._roots.indexOf(String(id)) != -1);
        },
        /**
         *
         * Возвращает jQuery обертку активного элемента
         * @return {jQuery}
         * @example
         * <pre>
         *    navigationPanel.getActiveElement().append('<img src="active.png"/>');
         * </pre>
         */
        getActiveElement : function(){
           return this._container.find('[element="'+ this._activeElement +'"]');
        },
        /**
         *
         * Возвращает рекорд активного элемента
         * @return {Deprecated/Record}
         * @example
         * <pre>
         *    var info = navigationPanel.getActiveRecord().get('Инфо');
         *    if(!!info)
         *       CoreFunctions.alert(info)
         * </pre>
         */
        getActiveRecord : function(){
           var res = null;
           if (this._recordSet instanceof dTransportRecordSet){
              try{
                 res = this._recordSet.getRecordByPrimaryKey(this._activeElement);
              }
              catch (e){
                 res = null;
              }
           }
           return res;
        },
        /**
         *
         * Возвращает рекордсет панели
         * @return {dTransportRecordSet}
         * @example
         * <pre>
         *    var recordSet = navigationPanel.getRecordSet();
         *    recordSet.appendRecord(new Record(cfg));
         *    navigationPanel.refresh();
         * </pre>
         */
        getRecordSet : function(){
           return this._recordSet;
        },
  
        _selfNotify: function(key, noNotifyClick, changeState, noNotifyState){
           this._notifyBatchDelayed('onReplaceMarker', key);
           if (!noNotifyClick) {
              this._notify('onElementClick', key !== undefined ? this._recordSet.getRecordByPrimaryKey(key): null);
           }
           if (!noNotifyState) {
              this._notify('onStateChanged', key !== undefined ? key : null, !changeState);
           }
        },
        /**
         *
         * Задает функцию отрисовки строки
         * @param {Function|undefined} fn функция отрисовки или undefined
         * @example
         * <pre>
         *    navigationPanel.setRender(function(rec, str){
         *       return str.css('color', '#f00').append('<span>123</span>');
         *    });
         *    navigationPanel.redraw()
         * </pre>
         */
        setRender: function(fn){
           if(typeof(fn) === 'function' || typeof(fn) === 'undefined')
              this._options.render = fn;
        },
        /**
         *
         * Возвращает функцию отрисовки
         * @return {Function}
         */
        getRender: function(){
           return this._options.render;
        },
        /**
         *
         * Перерисовывает панель, не запрашивая заново данные. Пример можно посмотреть в описании функции setRender
         */
        redraw: function(noNotify){
           this._container.html(dotTplFn(this));
           this._bindInternals();
           if(!noNotify){
              this.openElement(this._activeElement, true, true);
              this._notifyBatchDelayed('onAfterRender');
           }
           this._notifyOnSizeChanged(this, this);
        },
        /**
         *
         * Устанавливает флаг "Открывать вкладки при первой загрузке"
         * @param {Boolean} opDef открывать ли по умолчанию какие-либо вкладки
         */
        setOpenDefault: function(opDef){
           if(typeof(opDef) === 'boolean')
              this._options.openDefault = opDef;
        },
  
        _jQueryToString: function (jq) {
           var res = [];
           for (var i = 0, l = jq.length; i < l; i++) {
              res.push(jq.get(i).outerHTML);
           }
           return res.join('');
        },
  
        _rowRender: function(rec, cssClass, id){
           var
              render = this._options.render,
              opt = this._options,
              str;
           if (typeof(render) == 'function') {
              str = render.apply(this, [rec, $('<span>' + rec.get(this._options.altTreeNameFld) + '</span>')]);
              if (typeof(str) === 'object' && 'jquery' in str) {
                 str.addClass(cssClass + ' ws-navigation-panel-custom-render');
                 if (!opt.showCounters) {
                    str.attr('element', id);
                 }
                 str = escapeTagsFromStr(this._jQueryToString(str), ['script']);
              }
           }
           return str;
        },
        _buildBody: function(key){
           var
              self = this,
              rs = this._recordSet,
              opt = this._options,
              childs = rs.recordChilds(key),
              result = [],
              opDef = opt.openDefault && !this.elementIsRoot(key) && rs.getRecordByPrimaryKey(key).get(opt.openDefaultField) ?
                 ' ws-navigation-panel-open-default' : '',
              len = childs.length,
              value,
              wrapper,
              rec;
           if (len) {
              result.push('<ul id="ws-navigation-panel-list-', key, '" class="ws-navigation-panel-list', opDef, '">');
              for (var j = 0; j < len; j++) {
                 rec = rs.getRecordByPrimaryKey(childs[j]);
                 if (opt.showCounters) {
                    value = self._rowRender(rec, 'ws-navigation-panel-text', childs[j]) ||
                          '<div class="ws-navigation-panel-text">' + rec.get(opt.altTreeNameFld) + '</div>'
                    wrapper = '<li id="ws-navigation-panel-item-$elementName$s$" class="ws-navigation-panel-item"><span class="ws-navigation-panel-text-container counters ws-navigation-panel-no-render" element="$elementName$s$">$value$s$ $counter$s$</span>$body$s$</li>';
                 } else {
                    value = self._rowRender(rec, 'ws-navigation-panel-text-container', childs[j]) ||
                          '<span class="ws-navigation-panel-text-container ws-navigation-panel-no-render" element="' + childs[j] + '">' + rec.get(opt.altTreeNameFld) + '</span>'
                    wrapper = '<li id="ws-navigation-panel-item-$elementName$s$" class="ws-navigation-panel-item">$value$s$ $body$s$</li>';
                 }
                 result.push(stringFormat({
                    elementName: childs[j],
                    value: value,
                    body: self._buildBody(childs[j]),
                    counter: '<div countermenu="' + childs[j] + '" class="ws-navigation-panel-counter"></div>'
                 },
                 wrapper
                 ));
              }
              result.push('</ul>');
           }
           return result.join('');
        },
        _buildTitle: function(id) {
           var self = this,
              opt = this._options,
              record = self._recordSet.getRecordByPrimaryKey(id),
              prepareHtml;
  
           if (opt.showCounters) {
              return stringFormat({
                 elementName: id,
                 value: self._rowRender(record, 'ws-navigation-panel-text-container ws-navigation-panel-text', id) ||
                             '<div class="ws-navigation-panel-text-container ws-navigation-panel-text ws-navigation-panel-no-render">' + record.get(self._options.altTreeNameFld) + '</div>',
                 counter: '<div countermenu="' + id + '" class="ws-navigation-panel-counter"></div>'
              },
              '<span element="$elementName$s$" class="ws-accordion-element-title counters"><div class="ws-accordion-element-title-table-wrapper">$value$s$ $counter$s$</div></span>'
              );
           } else {
              return self._rowRender(record, 'ws-accordion-element-title', id) ||
                    '<span class="ws-accordion-element-title ws-navigation-panel-no-render" element="' + id + '">' + record.get(self._options.altTreeNameFld) + '</span>';
           }
        },
        destroy: function(){
           this._container.find('.ws-accordion-wr>.ws-accordion-element-title, ' +
                                '.ws-navigation-panel-item .ws-navigation-panel-text-container')
              .unbind();
           NavigationPanel.superclass.destroy.apply(this, arguments);
        }
     });
  
     return NavigationPanel;
  
  });
  