Your IP : 18.219.40.177


Current Path : /usr/local/mgr5/skins/orion/src/
Upload File :
Current File : //usr/local/mgr5/skins/orion/src/App.Forms.js

/**
 * Form module
 *  @param {object} window  global object
 *  @param {function} $ jQuery library
 *  @param {object} EventMgr EventMgr library
 *  @param {object} App Application
 *  @param {object} ScrollHandler Scrollbar library
 */
App.Forms = function(window, $, EventMgr, App, ScrollHandler) {
  'use strict';
  function init() {
    EventMgr.on($content(), headerFormSelector, 'click', collapsedForm);
    EventMgr.on($content(), unlimitBtnSelector, 'click', insertUnlimValue);
    //zoom
    EventMgr.on($content(), zoomBtnSelector, 'click', zoomHandler);
    EventMgr.on($content(), zoomTextareaSelector, 'change', syncZoomValue);
    EventMgr.on($content(), zoomTextareaSelector,
        'keyup', addLineBreakToZoomField);
    EventMgr.on($content(), zoomTextareaELSelector, 'keyup', syncZoomValue);
    EventMgr.on($content(), passwdBtnSelector, 'click', genPassword);
    EventMgr.on($content(), showpwdBtnSelector, 'click', showPassword);
    EventMgr.on($content(), calendarBtnSelector, 'click', calendarHandler);
    //checkbox
    EventMgr.on($content(), checkboxChangeSelector, 'change', changeElHandler);
    EventMgr.on($content(), checkboxSelector, 'click', changeCheckboxValue);
    //radio
    EventMgr.on($content(), radiobuttonSelector, 'click', changeRadioValue);
    EventMgr.on($content(), descRadioSelector, 'click', radioLabelClickHandler);
    EventMgr.on($content(), radiobuttonSelector,
        'keydown', radioBtnKeyDownHandler);

    EventMgr.on($content(), bandShowSelector, 'click', bandShowHandler);
    EventMgr.on($content(), dependSlistSelector, 'change', dependSlistHandler);
    EventMgr.on($content(), setvalueSelector, 'change', setValueHandler);
    EventMgr.on($content(), setvalueBtnSelector, 'click', setValueHandler);
    EventMgr.on($content(), resetBtnSelector, 'click', resetHandler);
    EventMgr.on($content(), userexpLinkSelector, 'click', levelUp);
    EventMgr.on($content(), nestedreportSelector, 'click', nestedReportHandler);
    EventMgr.on($content(), fakePasswdSelector, 'change', fakePasswdCopy);
    EventMgr.bind('forceSetValues', forceSetValues);
    EventMgr.bind('appendForm,appendReport,appendedFilter,forceDepend',
        firstDependSlistHandler);
    EventMgr.bind('loadPage', loadPassList);
    EventMgr.bind('appendForm,appendReport,switchTabForm,setFocus', setFocus);
    EventMgr.bind('appendForm', scrollToSelectedBranch);
    EventMgr.bind('formSetValues', formSetValues);
    EventMgr.bind('syncInputToZoom', syncInputWithZoom);
    EventMgr.on($content(), passwdField, 'change', checkPass);
    EventMgr.on($content(), inputTypeTextSelector,
        'keydown', submitFormByEnter);
    EventMgr.on($content(), checkBoxSelectTextareaRadioFormSelector,
        'keydown', submitFormByCtrlEnter);
    EventMgr.on($content(), passwdField, 'keyup', checkPass);
    EventMgr.on($content(), novalueSelector,
        'change', changeNovalueFieldHandler);
    EventMgr.on($content(), formBtnSelector, 'keydown', formBtnKeyDownHandler);
    EventMgr.on($content(), descLabelCheckboxSelector,
        'click', checkboxDescClickHandler);
    EventMgr.bind('gotPasswdList', gotPasswdListHandler);
    EventMgr.on($content(), treeHitarea, 'click', treeCollpasedControl);
    EventMgr.on($content(), treeLabelSelector, 'click', treeSelectedControl);
    EventMgr.on($content(), treeMultiLabelSelector, 'click', treeMultiSelectedControl);
    EventMgr.on($content(), treeCommonLabelSelector,
        'dblclick', treeCollpasedControlWrapper);
    EventMgr.on($content(), fakeFileInput, 'click', fakeFileInputHandler);
    EventMgr.on($content(), FileInput, 'change', fileInputChangeHandler);
    EventMgr.on($content(), treeReszieConrtolSelector,
        'mousedown', treeResizeControl);
    EventMgr.on($content(), confirmField, 'keyup, change', checkConfirmField);
    EventMgr.on($content(), quoteSelector, 'click', quoteHandler);
    //clear tab cache
    EventMgr.bind('closeTabEvent,appendForm,appendFilter,appendReport',
        cleanTabCache);
    EventMgr.bind('appendForm', prefixFieldSetUp);
    EventMgr.bind('upPrefixField', prefixFieldSetUp);
    EventMgr.on($content(), formListRow, 'click', formListRowClickHandler);
    EventMgr.bind('focusOnErrorField', focusOnErrorField);
    EventMgr.on($content(), '.b-textarea_responsive_yes', 'keyup',
        textareaResize);
    EventMgr.bind('appendForm', checkTitleWidth);
    EventMgr.on($content(), ticketBtnUp, 'click', ticketMoveUpHandler);
    EventMgr.on($content(), ticketBtnDown, 'click', ticketMoveDownHandler);
    EventMgr.on($content(), ticketCollapse, 'click', ticketCollapseHandler);
  }

  var appDom = App.Dom;

  var CACHE = {};

  function cleanTabCache(e, data) {
    var tabId = data.tabId;
    if (CACHE[tabId]) {
      delete CACHE[tabId];
    }
  }

  function $content() {
    return App.Common.selectorCache('.i-form-wr');
  }

  var pageInfo = window.pageInfo,

      quoteSelector = '.b-quote',

      treeReszieConrtolSelector = '.b-resizer',

      fakeFileInput = '.b-input-file-fake',

      FileInput = '.b-input_type_file',

      treeHitarea = '.tree-wrapper .tree-hitarea',

      treeLabelSelector = '.b-tree_type_default .tree-handler',

      treeCommonLabelSelector = '.tree-wrapper .tree-handler',

      treeMultiLabelSelector = '.b-tree_type_multiple .tree-handler',

      fakePasswdSelector = 'input.fakePasswd',

      novalueSelector = 'input[data-novalue], textarea[data-novalue]',

      passwdField = '.b-form-passwd-field, .b-form-passwd-field + input',

      userexpLinkSelector = 'div.userexperience',

      //wrapper for zoom textarea
      zoomTextareaSelector = '.b-textarea_for_zoom',
      //zoom field in inline-edit
      zoomTextareaELSelector = '.edit-field-form .b-textarea_for_zoom',

      //wrapper for body

      //wrapper for radiobutton
      radiobuttonSelector = '.b-radio:not(".readonly")' +
          ' .b-radio__control',
      //wrapper for checkboxes
      checkboxSelector = '.b-checkbox__control',
      //wrapper for headers of form page
      headerFormSelector = '.b-form-page__title',

      bandShowSelector = '.band-content .band_show_button',
      //wrapper for unlim btn from form
      unlimitBtnSelector = '.b-input-btn_type_unlimit',
      //zoom button
      zoomBtnSelector = '.b-input-btn_type_zoom',
      //password button
      passwdBtnSelector = '.b-input-btn_type_passwd',
      //calendar button
      calendarBtnSelector = '.b-input-btn_type_calendar',
      //show passwd button
      showpwdBtnSelector = '.b-input-btn_type_showpwd',
      //checkbox onchange
      checkboxChangeSelector = 'input[type="checkbox"].onchange',

      //wrapper for control field select and radio
      dependSlistSelector = '.depend.b-myselect input[type="hidden"],' +
          ' .depend.b-select-ac input[type="hidden"],' +
          ' .depend.b-radio input[type="hidden"]',

      setvalueSelector = '.setvalue input[type="hidden"],' +
          ' .b-select-ac_setvalues_yes input[type="hidden"],' +
          ' .setvalue input[type="text"],' +
          ' .setvalue input[type="password"],' +
          ' .setvalue .b-textarea',

      setvalueBtnSelector = '.i-button_type_setvalues',

      resetBtnSelector = '.i-button_type_reset',

      formBtnSelector = '.i-button, .b-checkbox__control',

      inputTypeTextSelector = '.l-form__wrapper .i-input-control,' +
          ' .filter-wrapper .i-input-control',

      checkBoxSelectTextareaRadioFormSelector = '.l-form__wrapper' +
          ' .b-radio__control,' +
          ' .l-form__wrapper .b-textarea,' +
          ' .l-form__wrapper .b-myselect__select-value,' +
          ' .filter-wrapper .b-myselect__select-value,' +
          ' .l-form__wrapper .b-checkbox__control,' +
          ' .l-form__wrapper .b-mselect__view-value',

      nestedreportSelector = '.data-wrapper.nestedreport',

      descRadioSelector = '.b-label__visible_for_radio, .b-radio-img__row',

      descLabelCheckboxSelector = ' .l-form__row_type_checkbox .b-label__visible',

      confirmField = '.b-form-confirm-field, .b-form-passwd-field',

      ticketBtnUp = '.i-ticket__btn-move_up',

      ticketBtnDown = '.i-ticket__btn-move_down',

      ticketCollapse = '.i-ticket__btn-collapse',

      formListRow = '.b-form-list__row, .b-form-blocks__block';

  function $activeTab() {
    return $('.tab-content_st_active');
  }

  //check for title overwidth & set hint overwidth
  function checkTitleWidth(e, data) {
    $('#cont-' + data.tabId).find('.b-title_type-form').each(function() {
      var width = this.offsetWidth,
          scrollWidth = this.scrollWidth - 1;
      if (width < scrollWidth) {
        this.className += ' overwidth';
        this.removeAttribute('data-hint');
      }
    });
  }

  function quoteHandler(e) {
    e.preventDefault();
    var content = '', selection;
    if (typeof window.getSelection !== 'undefined') {
      var sel = window.getSelection();
      content = sel.toString();
    } else if (typeof document.selection !== 'undefined') {
      if (document.selection.type === 'Text') {
        selection = document.selection.createRange();
        content = selection.text;
      }
    }
    if (content === '') { return; }
    var lines = content.split('\n'),
        l = lines.length;
    content = '';
    for (var i = 0; i < l; i++) {
      content += '> ' + lines[i];
      if (i !== l - 1) {
        content += '\n';
      }
    }
    var targetId = this.getAttribute('data-target'),
        elem = App.Dom.byId(targetId);
    if (elem) {
      App.u.insertStringAfterCaret(elem, content);
    }
  }

  function fakeFileInputHandler() {
    var id = this.getAttribute('data-source');
    $('#' + id).trigger('click');
  }

  function checkFileSize(tabId, name) {
    var fileSizeRes = App.u.checkFileSize(tabId, name);
    if (fileSizeRes.msg) {
      EventMgr.trigger('errMsgValid', {
        self: fileSizeRes.field,
        err: fileSizeRes.msg,
        number: 0
      });
    } else {
       EventMgr.trigger('okMsgValid', {
         self: fileSizeRes.field,
         number: 0,
         notOk: true
       });
    }
  }

  function fileInputChangeHandler() {
    var id = this.id,
        tabId = this.getAttribute('data-tabid'),
        fElemInput = App.Dom.byId('f-' + id),
        value = this.value,
        startIndex = (value.indexOf('\\') >= 0 ?
            value.lastIndexOf('\\') : value.lastIndexOf('/')),
        filename = '',
        stateWrapper = App.Dom.byId('b-input-file__wr-' + tabId);
    if (this.files) {
      var filesList = this.files,
          l = filesList.length;
      for (var i = 0; i < l; i++) {
        if (i !== 0) {
          filename += ', ';
        }
        filename += filesList[i].name;
      }
      App.Dom.addClass(stateWrapper, 'b-input-file__wr_state_selected');
      //check file size
      checkFileSize(tabId, this.getAttribute('name'));
    } else {
      filename = value.substring(startIndex);
      if (filename.indexOf('\\') === 0 || filename.indexOf('/') === 0) {
        filename = filename.substring(1);
      }
      App.Dom.removeClass(stateWrapper, 'b-input-file__wr_state_selected');
    }
    filename = window.filterXSS(filename);
    fElemInput.innerHTML = filename;
    fElemInput.setAttribute('data-hint', filename);
  }
  //tree resizer
  var treeResizeObj = {};
  //tree resize drag
  function treeResizeControl(e) {
    var id = this.getAttribute('data-id'),
        tabId = this.getAttribute('data-tabid'),
        elem = App.Dom.byId(id),
        moveTrigger = this.getAttribute('data-move-trigger') || false,
        offsetTop = parseInt(this.offsetTop, 10),
        $tip = $('.b-tip_name_textarea_resize');
    //hide tip if it showing
    if ($tip.length) {
      $tip.find('.b-tip__close').trigger('click');
    }
    treeResizeObj.height = parseInt(elem.style.height, 10);
    if (!treeResizeObj.height) {
      treeResizeObj.height = elem.offsetHeight;
    }
    treeResizeObj.elem = elem;
    treeResizeObj.id = id;
    treeResizeObj.tabId = tabId;
    treeResizeObj.moveTrigger = moveTrigger;

    e = e || window.event;
    if (e.touches) {
      treeResizeObj.shiftY = e.touches[0].pageY;
      treeResizeObj.top = e.touches[0].pageY - offsetTop;
      document.ontouchmove = treeResizeMove;
      document.ontouchend = treeResizeDrop;
      document.ontouchcancel = treeResizeDrop;
    } else {
      treeResizeObj.shiftY = e.clientY;
      treeResizeObj.top = e.clientY - offsetTop;
      document.onmousemove = treeResizeMove;
      document.onmouseup = treeResizeDrop;
    }
    document.body.style.cursor = 'n-resize';
  }
  var treeResizerTimeId = 1;
  // tree resize move
  function treeResizeMove(e) {
    var diffY;
    if (e.touches) {
      diffY = e.touches[0].pageY - treeResizeObj.shiftY;
    } else {
      diffY = e.clientY - treeResizeObj.shiftY;
    }
    if (treeResizeObj.elem) {
      treeResizeObj.elem.style.height = (treeResizeObj.height + diffY) + 'px';
    }
    if (treeResizeObj.moveTrigger) {
      clearTimeout(treeResizerTimeId);
      treeResizerTimeId = setTimeout(function() {
        EventMgr.trigger(treeResizeObj.moveTrigger, {
          id: treeResizeObj.id, tabId: treeResizeObj.tabId });
      }, 100);
    }
    e.preventDefault();
  }
  // tree resize drop
  function treeResizeDrop(e) {
    if (e.touches) {
      document.ontouchmove = null;
      document.ontouchend = null;
      document.ontouchcancel = null;
    } else {
      document.onmousemove = null;
      document.onmouseup = null;
    }
    EventMgr.trigger('updFormHeight', { tabId: treeResizeObj.tabId });
    document.body.style.cursor = '';
    $(treeResizeObj.elem).removeClass('b-textarea_responsive_yes');
    e.preventDefault();
  }
  //dblclick handler
  function treeCollpasedControlWrapper(e) {
    e.preventDefault();
    var $self = $(this);
    $self.prev().trigger('click');
  }

  function treeCollpasedControl() {
    var parent = this.parentNode,
        className = parent.className,
        name, params, url, self, tabId, root, elem, img, src, id;
    self = $(this);
    root = self.parents('.tree-inner');
    tabId = root.attr('data-tabid');
    id = root.attr('id');
    //added class collapsed
    if (className.match(/t-opened/)) {
      className = className.replace(/t-opened/g, '');
      className += ' collapsed';
      //remove collapsed
    } else if (className.match(/collapsed/)) {
      //just open
      if (className.match(/loaded/)) {
        className = className.replace(/collapsed/g, '');
        className += ' t-opened';
        //load children
      } else if (!className.match(/loading/)) {
        name = App.Dom.byId(id + '-value').name;
        params = $('#frm-' + tabId).serializeObject();
        url = pageInfo.url;
        /* jslint camelcase:false */
        params.sv_field = name;
        params.sv_tree = 'yes';
        /* jslint camelcase:true */
        elem = self.siblings('.tree-handler');
        img = self.siblings('.tree-handler').children('.icon').children('img');
        src = img.attr('src');
        img.attr('src', pageInfo.theme + 'img/loader.gif');
        params[name] = elem.attr('data-val');
        //elem.parent().addClass('loading');
        className += ' loading';
        //get text node
        /* jshint camelcase: false */
        params.sv_text = $(elem).find('.tree-label').text();
        EventMgr.trigger('ajaxRequest', {
          url: url,
          param: params,
          trfunc: 'formGetTreeBranch',
          invar: {
            tabId: tabId,
            name: name,
            elem: elem,
            src: src,
            img: img,
            id: 'cont-' + id },
          type: 'get', outtype: 'json', queue: 'noqueue' });
      }
    }
    parent.className = className;
    EventMgr.trigger('updateScroll', { id: 'cont-' + id });
  }

  function treeSelectedControl(e) {
    var $self = $(this),
        id = $self.parents('.tree-inner').attr('id');
    //remove other selection
    $selectedTreeElem(id).removeClass('selected');
    var input = App.Dom.byId(id + '-value');
    input.value = this.getAttribute('data-val');
    $(input).trigger('change');
    this.className += ' selected';
  }

  function $selectedTreeElem(id) {
    return $('#' + id + ' .tree-handler.selected');
  }

  function treeMultiSelectedControl(e) {
    var $self = $(this),
        id = $self.parents('.tree-inner').attr('id'),
        value = '';
    //remove other selection
    var input = App.Dom.byId(id + '-value');
    if (e.ctrlKey || e.metaKey) {
      //unselected
      if ($self.hasClass('selected')) {
        $self.removeClass('selected');
      //add selection
      } else {
        $self.addClass('selected');
      }
      $selectedTreeElem(id).each(function(index) {
         if (index !== 0) {
           value += ',';
         }
         value += this.getAttribute('data-val');
      });
    } else {
      $selectedTreeElem(id).removeClass('selected');
      value = this.getAttribute('data-val');
      this.className += ' selected';
    }
    input.value = value;
    $(input).trigger('change');
  }

  function scrollToSelectedBranch() {
    setTimeout(function() {
      $('.tree-wrapper .selected').each(function() {
        var id = $(this).parents('.tree-wrapper').attr('id');
        EventMgr.trigger('scrollTo', { id: id, offsetTop: this.offsetTop });
      });
    }, 100);
  }

  function resetHandler() {
    var tabId = this.getAttribute('data-tabid'),
        form = App.Dom.byId('frm-' + tabId);
    form.reset();
  }

  function changeNovalueFieldHandler() {
    if (this.getAttribute('data-novalue')) {
      this.setAttribute('data-novalue', 'no');
      if (this.placeholder) {
        this.placeholder = '';
      }
    }
  }
  //submit form by ENTER keyup under text and password fields
  function submitFormByEnter(e) {
    e = e || window.event;
    var codeKey = e.which || e.keyCode,
        tabId;
    if (codeKey === ENTERKEY) {
      //sometime form submit native...
      e.preventDefault();
      var $self = $(this);
      tabId = this.getAttribute('data-tabid');
      $self.trigger('change');
      $self.trigger('blur');
      clickButtonTrigger(tabId);
    }
  }
  //submit form by ENTER + CTRL under textarea, select, radio, checkbox
  function submitFormByCtrlEnter(e) {
    e = e || window.event;
    var codeKey = e.which || e.keyCode,
        tabId;
    if (codeKey === ENTERKEY && (e.ctrlKey || e.metaKey)) {
      e.preventDefault();
      $(this).trigger('blur');
      tabId = this.getAttribute('data-tabid');
      clickButtonTrigger(tabId);
    }
  }
  /**
   * Ищем кнопку типа ok для отправки формы, сперва ищем дефлотную(default="yes"), затем (type=ok), (type=next), кнопку фильтра()
   */
  function clickButtonTrigger(tabId) {
    // ищем кнопку default, кнопку ok, потом первую попавшусю
    var $defaultButton = $('#cont-' + tabId + ' .i-button_default_yes');
    var $okButton = $('#cont-' + tabId + ' .b-button_act_ok');
    var $nextButton = $('#cont-' + tabId + ' .b-button_act_next');
    var $filterButton = $('#frm-' + tabId + ' .b-button__filter-set');

    setTimeout(function() {
      var $button;
      if ($defaultButton[0]) {
        $button = $defaultButton;
      } else if ($okButton[0]) {
        $button = $okButton;
      } else if ($nextButton[0]) {
        $button = $nextButton;
      } else if ($filterButton) {
        $button = $filterButton;
      }
      $button.first().trigger('click');
    }, 1);
  }

  function loadPassList() {
    var url,
        data = getLocalStorage('mgr5-passwdlist'),
        version = getLocalStorage('mgr5-passwd-ver'),
        newVersion = false;

    if (pageInfo.version !== version) {
      setLocalStorage('mgr5-passwd-ver', pageInfo.version);
      newVersion = true;
    }

    if (data && data !== '' && !newVersion) {
      passwdlist = data;
    } else {
      url = '/manimg/common/passwd.list';
      EventMgr.trigger('ajaxRequest', {
        url: url, param: {}, trfunc: 'gotPasswdList',
        outtype: 'html', type: 'get' });
    }
  }

  function getLocalStorage(itemId) {
    if (typeof localStorage !== 'undefined') {
      return localStorage.getItem(itemId);
    } else {
      return false;
    }
  }

  function setLocalStorage(itemId, data) {
    if (typeof localStorage !== 'undefined') {
      localStorage.setItem(itemId, data);
    } else {
      return false;
    }
  }

  var passwdlist = '';

  function gotPasswdListHandler(e, data) {
    passwdlist = '\n' + data + '\n';
    setLocalStorage('mgr5-passwdlist', passwdlist);
  }

  //The following function returns the logarithm of y with base x (ie. logx y):
  function getBaseLog(x, y) {
    return Math.log(y) / Math.log(x);
  }

  var passType = { 0: 'short', 1: 'weak', 2: 'good', 3: 'strong' };

  function checkConfirmField(e) {
    var self, tabId;
    if (this.getAttribute('data-check-field')) {
      tabId = this.getAttribute('data-tabid');
      self = App.Dom.byId(this.getAttribute('data-check-field') + '-' + tabId);
    } else {
      self = this;
    }
    var confirmId = self.getAttribute('data-confirm-field'),
        parentId = self.getAttribute('data-parent'),
        confirmField = App.Dom.byId(confirmId),
        messageElem = App.Dom.byId('check-confirm-' + parentId),
        isChanged = false, showConfirmMsg = false;
    if (App.Dom.hasClass(messageElem, 'b-form-confirm-message_show_true')) {
      showConfirmMsg = true;
    }
    if (!!confirmField && self.value !== confirmField.value) {
      if (!showConfirmMsg) {
        App.Dom.addClass(messageElem, 'b-form-confirm-message_show_true');
        isChanged = true;
      }
    } else {
      if (showConfirmMsg) {
        App.Dom.removeClass(messageElem, 'b-form-confirm-message_show_true');
        isChanged = true;
      }
    }
    if (isChanged) {
      EventMgr.trigger('updateScroll', { id: 'form-scroll-' + tabId });
    }
  }

  function checkPass() {
    var strength,
        value = App.u.escapeRegExp($.trim(this.value)),
        name = this.getAttribute('data-parent'),
        regexp = new RegExp('\n' + value + '\n', 'i'),
        tabId = this.getAttribute('data-tabid'),
        id = 'checkpass-indicator-wrapper-' + name,
        wr = App.Dom.byId(id),
        isChanged = false;
    if (wr) {
      if (this.value !== '') {
        if (passwdlist.match(regexp)) {
          strength = 0;
        } else {
          strength = checkPassStrength(this.value);
        }
        if (wr.className !== (passType[strength] + ' showed')) {
          wr.className = passType[strength] + ' showed';
          isChanged = true;
        }
      } else {
        if (wr.className !== '') {
          wr.className = '';
          isChanged = true;
        }
      }
    }
    if (isChanged) {
      EventMgr.trigger('updateScroll', { id: 'form-scroll-' + tabId });
    }
  }

  function checkPassStrength(value) {
    var len = value.length,
        str = value.split(''),
        uniq = 0,
        special = 0,
        repeatTypes = 0,
        varCase = 0,
        symbArr = {},
        strType = [],
        ind, i, lvl = 0;

    for (i = 0; i < len; i++) {
      //count uniq symbols
      if (!symbArr[str[i]]) {
        uniq++;
      }
      symbArr[str[i]] = true;
      //count special symbols
      if (str[i].match(/[!@#$%^&*?_~{}"'()|\\+\[\]]\s/)) {
        special++;
        strType.push('sn');
        //count lowcase
      } else if (str[i].match(/[a-z]/)) {
        strType.push('ll');
        //count uppercase
      } else if (str[i].match(/[A-Z]/)) {
        strType.push('lu');
        //count number
      } else if (str[i].match(/\d/)) {
        strType.push('dn');
        //any cyrillic symbol = 2 special
      } else {
        special++;
        special++;
        strType.push('sn');
      }
      if (i !== 0) {
        //check for repeat types symbol
        if (strType[i].substr(0, 1) === strType[i - 1].substr(0, 1)) {
          repeatTypes++;
        }
        //check for diffirent case
        if (strType[i].substr(1, 1) !== strType[i - 1].substr(1, 1)) {
          varCase++;
        }
      }
    }

    ind = (len + Math.sqrt(0.5 * special + varCase) -
        Math.sqrt(repeatTypes)) * (getBaseLog(len, uniq));
    if (ind < 3) {
      lvl = 0;
      //weak
    } else if (ind >= 3 && ind < 6) {
      lvl = 1;
      //good
    } else if (ind >= 6 && ind < 9) {
      lvl = 2;
      //hard
    } else if (ind >= 9) {
      lvl = 3;
    } else {
      lvl = 0;
    }
    return lvl;
  }
  //handler for level up link
  function levelUp(e) {
    e.preventDefault();
    var exp = this.getAttribute('data-level'),
        tabId = getActiveTabId(),
        form = $('#frm-' + tabId),
        param = form.serializeObject(),
        $mixedContols = form.find('.i-control-mixed');
    param.userexperience = exp;

    if ($mixedContols.length !== 0) {
      param = App.u.removeParam($mixedContols, param);
    }
    if (param.snext) {
      delete param.snext;
    }
    if (param.sback) {
      delete param.sback;
    }
    EventMgr.trigger('reloadTab', { tabId: tabId, param: param });
  }

  function nestedReportHandler(e) {
    e.preventDefault();
    var elid = this.getAttribute('data-elid'),
        $self = $(this),
        index = $self.parents('td').index(),
        th = $self.parents('table').find('th')[index],
        colname = th.getAttribute('data-colname'),
        nested = th.getAttribute('data-nestedreport'),
        parent = getActiveTabId(),
        plid = App.Dom.byId(parent + '-plid'),
        form = $('#frm-' + parent),
        param = form.serializeObject();
    elid = elid || $self.find('.b-list__table-col-content').first().html();
    param.func = nested;
    param.elid = elid;
    param.colname = colname;
    if (plid !== null) {
      param.plid = plid.value;
    }
    EventMgr.trigger('ajaxRequest', {
      param: param,
      invar: { parent: parent },
      type: 'get',
      outtype: 'json',
      trfunc: 'ajaxResponse',
      queue: 'nested-' + parent,
      failfunc: 'failCommonAjaxResponse' });
    EventMgr.trigger('tabLoading', { tabId: parent });
  }

  var isIE = '\v' === 'v';

  function scrollToFormRow($focusedField, tabId, animate) {
    setTimeout(function() {
      var formRow = $focusedField.closest('.l-form__row'),
          offsetTop = formRow[0] ? formRow[0].offsetTop : 0,
            $page = $focusedField.closest('.b-form-page'),
            formScrollEl = App.Dom.byId('form-scroll-' + tabId),
            heightViewPort = formScrollEl ? parseInt(formScrollEl.style.height, 10) : 0;
        if ($page[0]) {
          offsetTop += $page[0].offsetTop;
        }

      EventMgr.trigger('scrollTo', {
        id: 'form-scroll-' + tabId,
        offsetTop: offsetTop,
        animate: animate });
    }, 100);
  }

  function focusOnErrorField(e, data) {
    var $focusField = $('.row-error .b-input, .row-error .b-textarea, .row-error .b-checkbox__control'),
        tabId = data.tabId,
        $page;
    if ($focusField.length > 0) {
      $page = $focusField.closest('.b-form-page');
      if ($page.hasClass('b-form-page_st_collapsed')) {
        $page.find('.i-form-page__title').trigger('click');
      }

      $focusField.focus();
      scrollToFormRow($focusField, tabId, true);
    }
  }

  function setFocus(e, data) {
    if (isIE || window.pageInfo.mobile) { return; }
    var tabId = data.tabId,
        form = document.getElementById('frm-' + tabId);
    if (form === null) { return; }
    var len = form.length,
        elem = null,
        curElem, name, r1, readonly,
        forceFocused = $('#form-wrapper-' + tabId + ' .b-form__focus-field');
    //force focused field by attr focus="yes"
    if (forceFocused.length > 0) {
      forceFocused.focus();
      scrollToFormRow(forceFocused, tabId, false);
      return;
    }
    //find out accessable field
    for (var i = 0; i < len; i++) {
      elem = form[i];
      //for select
      if (elem.type === 'hidden' &&
          elem.getAttribute('data-type') === 'select') {
        curElem = $(elem).parent();
        if (!curElem.hasClass('readonly') && (curElem.width() !== 0)) {
          name = elem.getAttribute('name');
          $('#_' + name + '-' + tabId + ' .b-myselect__select-value').focus();
          window.scrollToTopLeft();
          return;
        }
        //for radio
      } else if (elem.type === 'hidden' &&
          elem.getAttribute('data-type') === 'radio') {
        curElem = $(elem).parent();
        if (!curElem.hasClass('readonly') && (curElem.width() !== 0)) {
          name = elem.getAttribute('name');
          $('.tab-content_st_active div[data-id="' + name + '"][tabindex="0"]').
              focus();
          window.scrollToTopLeft();
          return;
        }
      } else if (elem.type === 'hidden' &&
          elem.getAttribute('data-type') === 'checkbox') {
        name = elem.getAttribute('name');
        $('#' + tabId + '-' + name).focus();
        window.scrollToTopLeft();
        return;
      }
      r1 = elem.getAttribute('readonly');
      readonly = (r1 !== null);
      if (elem.type !== 'hidden' && (!readonly) && elem.offsetWidth !== 0) {
        elem.focus();
        window.scrollToTopLeft();
        return;
      }
    }
    form = null;
    elem = null;
  }

  function AttrIsSupported(element, attr) {
    var elem = document.createElement(element);
    return (attr in elem);
  }
  /**
   * forceSetValues
   * @param {object} e event object
   * @param {object} data object with data
   */
  function forceSetValues(e, data) {
    var tabId = data.tabId,
        url = pageInfo.url,
        name = data.name,
        params = $('#frm-' + tabId).serializeObject();
    /* jslint camelcase:false */
    params.sv_field = name;
    /* jslint camelcase:true */
    EventMgr.trigger('ajaxRequest', {
      url: url,
      param: params,
      trfunc: 'formSetValues',
      invar: { tabId: tabId },
      type: 'get',
      outtype: 'json',
      queue: 'multiload' });
  }

  /**
   * Callback setvalues field changed
   * send request for new values
   * @param {object} e  event object
   * @param {boolean} sv  setvalues once flag
   * @return {boolean}
   * @this {object} HTML node
   */
  var lastTargetElemId = 'default_value';
  function setValueHandler(e, sv, srcId) {
    var self = this;
    setTimeout(function() {
      EventMgr.trigger('preSetValues', [self, 100]);
      //check for loop
      var elemId = self.getAttribute('id'),
          $self = $(self),
          flags = $self.getFlags();
      if ($self.hasClass('b-textarea_for_zoom')) {
        return true;
      }
      if (lastTargetElemId === srcId) {
        return true;
      }
      lastTargetElemId = elemId;
      //check for setvalues once
      if (sv) { return true; }
      //var tabId = $activeTab().attr('data-tabid'),
      var tabId = self.getAttribute('data-tabid'),
          $form = $('#frm-' + tabId),
          params = $form.serializeObject(),
          addParams = {},
          url = pageInfo.url,
          once = App.Dom.hasClass(self.parentNode, 'sv_nochange'),
          blocking = App.Dom.hasClass(self.parentNode, 'sv_blocking') ||
              flags.blocking,
          name = (self.getAttribute('data-name')) ?
              self.getAttribute('data-name') : self.name,
          id = self.getAttribute('data-id'),
          isShowcaseForm = $form.hasClass('b-form_showcase_yes'),
          hasFile = $form.hasClass('withfiles'),
          skipFile = App.Dom.hasClass(self.parentNode, 'sv_skipfiles'),
          $noSetValues = $form.find('.i-nosetvalues');

      if ($noSetValues.length !== 0) {
        params = App.u.removeParam($noSetValues, params);
      }

      //check for button id
      if (id && id !== 'undefined') {
        id = id.split('=', 2);
        if (id[1]) {
          params[id[0]] = id[1];
          addParams[id[0]] = id[1];
        }
      }

      /* jshint camelcase: false */
      params.sv_field = name;
      var options = {
        url: url,
        param: params,
        failfunc: 'failFormAjaxResponse',
        trfunc: 'formSetValues',
        invar: { tabId: tabId, once: once, blocking: blocking },
        type: 'post',
        outtype: 'json',
        queue: 'multiload' };
      //if form has input@type=file
      if (hasFile && !skipFile) {
      /* jshint camelcase: false */
        options.invar.blocking = true;
        addParams.sv_field = name;
        addParams.sfrom = 'ajax';
        EventMgr.trigger('setBrandSettings', {
           options: options,
           tabId: tabId,
           param: params,
           addParams: addParams
        });
      } else {
        /* jslint camelcase:true */
        EventMgr.trigger('ajaxRequest', options);
        if (blocking) {
          EventMgr.trigger('tabLoading', { tabId: tabId });
        }
      }
    }, 0);
  }
  /**
   * Проверяем на изменение значения мультиселекта
   */
  function isMultiSelectValuesChanged(stringVal, arrVal) {
    //check for empty value - only slist rerender
    if (arrVal === '') { return true; }
    if (typeof stringVal === 'string') {
      if (typeof arrVal === 'string') {
        arrVal = arrVal.split(',');
      } else if (typeof arrVal.join !== 'function') {
        return false;
      }
      var oldValues = stringVal.split(','),
          l = oldValues.length;
      oldValues.sort();
      arrVal.sort();
      if (oldValues.length !== arrVal.length) {
        return true;
      }
      while (l--) {
        if (oldValues[l] !== arrVal[l]) {
          return true;
        }
      }
      return false;
    }
    return false;
  }

  /**
   * Setvalues handler
   * change values in fields from response
   * @param {object} e event object
   * @param {object} data object with data
   **/
  function formSetValues(e, data) {
    //check for auth
    if (data.ok && data.reload) {
      window.location.reload();
    }
    var tabId = data.tabId,
        form = App.Dom.byId('frm-' + tabId),
        once = data.once,
        blocking = data.blocking,
        readonlyArray = [];
    if (blocking) {
      EventMgr.trigger('tabLoadingHide', { tabId: tabId });
    }
    if (form === null || data.error) { return; }
    var setvalues = data.setvalues,
        elem = '',
        keyVar, tagName, value, changed, dType, type, liAct, elemId;
    if (typeof setvalues === 'object') {
      /* jslint forin:true */
      for (keyVar in setvalues) {
        elem = form.elements[keyVar];
        //check for bad words
        if (keyVar === 'item' || keyVar === 'length') {
          continue;
        }
        if (elem) {
          tagName = elem.tagName;
          value = setvalues[keyVar];
          changed = true;
          if (value === null) {
            continue;
          }
          if (tagName === 'INPUT') {
            type = elem.getAttribute('type');
            if (elem.value !== value) {
              //input@type=text
              if (type === 'text' || type === 'password') {
                changed = (elem.value !== value.value);
                elem.value = value.value;
                if (value.prefix) {
                  var $prefixNode = $(elem).parent().find('.i-input__prefix');
                  if ($prefixNode.length) {
                    $prefixNode.html(value.prefix);
                    EventMgr.trigger('upPrefixField', { tabId: tabId });
                  }
                }
                //slider logic
                if (value.min !== undefined || value.max !== undefined || value.step !== undefined) {
                  var id = elem.id,
                      $sliderNode = $('#' + id + '-slider');
                  if (value.min !== undefined) {
                    $sliderNode.attr('data-min', value.min);
                  }
                  if (value.max !== undefined) {
                    $sliderNode.attr('data-max', value.max);
                  }
                  if (value.step !== undefined) {
                    $sliderNode.attr('data-step', value.step);
                  }
                  EventMgr.trigger('reloadSlider', { tabId: tabId });
                }
                //mask logic
                if (value.mask !== undefined) {
                  EventMgr.trigger('inputMaskChangeBySetvalues', { elem: elem, mask: value.mask, value: value.value });
                }
              } else if (type === 'file') {
                //reset file value
                if (value.value === '') {
                  var $elem = $(elem);
                  $elem.replaceWith($elem.val('').clone(true));
                  $(form.elements[keyVar]).trigger('change');
                }
              } else if (type === 'hidden') {
                // #17927 setvalues[keyVar].slist
                changed = (elem.value !== value.value ||
                    setvalues[keyVar].slist);
                if (changed) {
                  elemId = elem.getAttribute('id');
                  dType = elem.getAttribute('data-type');
                  //select
                  if (dType === 'select') {
                    //replace slist values
                    if (value.slist) {
                      $('ul#' + tabId + '-' + elem.name).
                          html(renderSlistElem(setvalues[keyVar].slist));
                      //clean slist cache for searching
                      EventMgr.trigger('cleanSlistCache', {
                        tabId: tabId,
                        id: tabId + '-' + elem.name });
                      //check for search in list
                      if (setvalues[keyVar].slist && setvalues[keyVar].slist.length > 10) {
                         var $mySelect = $('#_' + elem.name + '-' + tabId);
                         if (!$mySelect.hasClass('sb-select')) {
                           $mySelect.addClass('sb-select');
                         }
                      }
                      EventMgr.trigger('forceDepend', { tabId: tabId });
                      if (setvalues[keyVar].slist &&
                          setvalues[keyVar].slist.length !== 0) {
                        //set active item
                        if (setvalues[keyVar].value !== '') {
                          liAct = $('#' + tabId + '-' + keyVar +
                              ' li[data-val="' +
                              setvalues[keyVar].value + '"]');
                        } else {
                          liAct = $('#' + tabId + '-' + keyVar +
                              ' li[data-val="' + elem.value + '"]');
                          if (liAct.length === 0) {
                            liAct = $('ul#' + tabId + '-' + elem.name +
                                ' li:first');
                          }
                        }
                      } else {
                        //set null msg
                        $('#_' + keyVar + '-' + tabId + ' .b-myselect__select-value').
                            html(setvalues.nullmsg);
                      }
                    } else {
                      // case without slist
                      //set active item
                      liAct = $('#' + tabId + '-' + keyVar +
                          ' li[data-val="' + value.value + '"]');
                    }
                    //set value for select
                    if (liAct.length > 0) {
                      var $selectElem = $('#_' + keyVar + '-' + tabId),
                          isReadonly = $selectElem.hasClass('readonly');
                      //check for readonly
                      if (isReadonly) {
                        $selectElem.removeClass('readonly');
                      }
                      liAct.trigger('click', [once, elemId])
                      if (isReadonly) {
                        $selectElem.addClass('readonly');
                      }
                    }
                  //radio
                  } else if (dType === 'radio') {
                    if (value.slist &&
                         App.Tabs.tabs[tabId] &&
                         App.Tabs.tabs[tabId].formSource &&
                         App.Tabs.tabs[tabId].formSource[keyVar]) {
                       var sourceElem = App.Tabs.tabs[tabId].formSource[keyVar],
                           radioHtml = '';
                       sourceElem.slist = value.slist;
                       if (value.value) {
                         sourceElem.value = value.value;
                       //set first value
                       } else if (sourceElem.slist[0]) {
                         sourceElem.value = sourceElem.slist[0].key;
                       }
                       radioHtml = templates.formItemRadio(sourceElem);
                       $('#' + tabId + '-' + keyVar + '-radio').replaceWith(radioHtml);
                    }
                    if (!value.slist) {
                      $('#' + 'frm-' + tabId +
                          ' .' + keyVar + '-' +
                          tabId + '-' + window.hash(value.value)).
                          trigger('click', [once, elemId]);
                    }
                  // checkbox
                  } else if (dType === 'checkbox') {
                    $('#' + tabId + '-' + keyVar).
                        trigger('click', [[once, true]]);
                  // multiselect
                  } else if (dType === 'multiple') {
                    // replace value.value with value
                    var mvalue = value.value !== undefined ? value.value : value;
                    changed = isMultiSelectValuesChanged(elem.value, mvalue);
                    // вызываем при изменение значений или наличие slist
                    if (changed || value.slist) {
                      var id = elem.getAttribute('data-id');
                      EventMgr.trigger('selectValues', {
                        id: id, sElems: setvalues[keyVar] });
                    }
                  // input@type=hidden
                  } else if (dType === 'tree') {
                    $('#' + keyVar + '-' + tabId +
                        ' li[data-val="' + setvalues[keyVar].value + '"]').
                        trigger('click', [once, true]);
                  } else {
                    elem.value = value.value;
                  }
                }
              }
              if (changed) {
                elemId = elem.getAttribute('id');
                $(elem).trigger('change', [once, elemId]);
              }
            }
          } else {
            changed = (elem.value !== value.value);
            elem.value = value.value;
            if (changed) {
              elemId = elem.getAttribute('id');
              $(elem).trigger('change', [once, elemId]);
              if (elem.classList && elem.classList.contains('b-text-editor')) {
                EventMgr.trigger('TinyMCE_setContent', { content: elem.value, id: elemId });
              }
            }
          }
          //set readonly
          if (value.readonly) {
            var readonly = value.readonly === 'yes';
            //changeReadOnly($(elem).closest('.l-form__row'), readonly);
            //add to array and set after all
            readonlyArray.push({ elem: elem, readonly: readonly });
          }
        } else {
          //textdata
          elem = App.Dom.byId(tabId + '-' + keyVar);
          if (elem) {
            type = elem.getAttribute('data-type');
            if (type === 'img') {
              elem.src = setvalues[keyVar].value;
            //ticket
            } else if (type === 'ticket') {
              var newTicketHtml = templates.formItemTicketMsg(setvalues[keyVar]);
              if (newTicketHtml) {
                $(newTicketHtml).appendTo(elem);
              }
//              EventMgr.trigger('setFocus', { tabId: tabId });
            //list
            } else if (type === 'list') {
              setvalues[keyVar].id = tabId;
              setvalues[keyVar].type = 'form';
              if (setvalues[keyVar].view === 'blocklist') {
                elem.innerHTML = templates.formListBlocksContent(setvalues[keyVar]);
              } else {
                elem.innerHTML = templates.formListContent(setvalues[keyVar]);
              }
            } else if (type === 'listfilter') {
              setvalues[keyVar].id = tabId;
              elem.innerHTML = templates.formListFilter(setvalues[keyVar]);
            //buttons
            } else if (type === 'buttons') {
              var buttons = setvalues[keyVar],
                  l = setvalues[keyVar].length;
              for (var i = 0; i < l; i++) {
                //check for same buttons
                if ((data.__formModel && data.__formModel.__buttons[i]) &&
                    buttons[i].name !== data.__formModel.__buttons[i].name) {
                  var parent = $('#' + tabId + '-buttons .b-button')
                      .first().attr('data-parent');
                  elem.innerHTML = templates.buttons({
                    buttons: buttons,
                    id: tabId,
                    parent: parent,
                    type: 'form',
                    cancelBtnClass: ''
                  });
                  //update form model
                  EventMgr.trigger('updateModel', {
                    tabId: tabId,
                    name: '__buttons',
                    value: buttons
                  });
                  break;
                }
              }
              if (l === 0) {
                elem.innerHTML = '';
                //update form model
                EventMgr.trigger('updateModel', {
                  tabId: tabId,
                  name: '__buttons',
                  value: buttons
                });
              }
            } else if (type === 'link') {
              //unescape &amp; -> &
              var hrefLink = String(setvalues[keyVar].value).replace(/&amp;/g, '&');
              elem.href = hrefLink;
              elem.setAttribute('data-url', App.u.escapeQuote(hrefLink));
            } else if (type === 'datetime') {
              var d = App.u.parseDate(setvalues[keyVar].value),
                  dt = (new Date().getTime() - d.getTime());
              elem.setAttribute('data-difftime', dt);
            } else if (type === 'textdata') {
              elem = App.Dom.byId(tabId + '-' + keyVar + '-inner');
              elem.innerHTML = window.htmlDecode(setvalues[keyVar].value);
             // EventMgr.trigger('updateScroll', { id: tabId + '-' + keyVar });
            } else {
              elem.innerHTML = window.htmlDecode(setvalues[keyVar].value);
            }
            EventMgr.trigger('updateScroll', { id: 'form-scroll-' + tabId });
          } else {
            //for frame
            elem = App.Dom.byId(keyVar + '-' + tabId);
            if (elem && elem.getAttribute('data-type') === 'frame') {
              elem.setAttribute('src', setvalues[keyVar].value);
            }
          }

        }
      }
    }
    //set readonly
    for (var i = 0, l = readonlyArray.length; i < l; i++) {
      App.FormUtils.changeReadOnly($(readonlyArray[i].elem).closest('.l-form__row'), readonlyArray[i].readonly);
    }
    EventMgr.trigger('setValuesDone', { tabId: tabId });
  }

  function renderSlistElem(elems) {
    var elemHTML = templates.formItemSelectList({ slist: elems, value: '' });
    return elemHTML;
  }
  //remove space and insert line break when press SPACE in zoom field
  function addLineBreakToZoomField(e) {
    var codeKey = e.which || e.keyCode;
    if (codeKey === SPACE) {
      e.preventDefault();
      var allText = this.value,
          currentPos = getCaret(this),
          beforeText = allText.substr(0, currentPos),
          afterText = allText.substr(currentPos);

      this.value = beforeText.replace(/((\s*\S+)*)\s*/, '$1') +
          '\n' + afterText.replace(/((\s*\S+)*)\s*/, '$1');
      setCaretPosition(this, beforeText.length);
    }
  }
  //from
  // http://stackoverflow.com/questions/11779140/how-to-push-cursor-to-the-next-line-when-user-hits-ctrl-enter
  function getCaret(el) {
    if (el.selectionStart) {
      return el.selectionStart;
    } else if (document.selection) {
      el.focus();

      var r = document.selection.createRange();
      if (r === null) {
        return 0;
      }

      var re = el.createTextRange(),
          rc = re.duplicate();
      re.moveToBookmark(r.getBookmark());
      rc.setEndPoint('EndToStart', re);

      return rc.text.length;
    }
    return 0;
  }

  function setCaretPosition(elem, caretPos) {
    if (elem !== null) {
      if (elem.createTextRange) {
        var range = elem.createTextRange();
        range.move('character', caretPos);
        range.select();
      }
      else {
        if (elem.selectionStart) {
          elem.focus();
          elem.setSelectionRange(caretPos, caretPos);
        }
        else {
          elem.focus();
        }
      }
    }
  }

  //sync value zoom textarea and input
  function syncZoomValue(e) {
    var self = $(this),
        controlField = self.attr('data-control-field'),
        tabId = $activeTab().attr('data-tabid'),
        input = $('#cont-' + tabId + ' input[name="' + controlField + '"],' +
            ' #editinlist-wrapper-' + tabId +
            ' input[name="' + controlField + '"]'),
        value = self.val().
            replace(/\n+/g, ' ')
            .replace(/(^\s*|\s*$)/g, '')
            .replace(/\s+/g, ' ');
    input.val(value).trigger('change');
  }

  function syncInputWithZoom(e, data) {
    var tabId = data.tabId,
        input = data.self,
        controlField, textarea, value;
    if (input && App.Dom.hasClass(input, 'testzoom')) {
      controlField = input.name;
      textarea = $('#cont-' + tabId +
          ' .b-textarea[name="zoom-' + controlField + '"],' +
          ' #editinlist-wrapper-' + tabId +
          ' .b-textarea[name="zoom-' + controlField + '"]');
      value = input.value.replace(/(^\s*|\s*$)/g, '').replace(/\s+/g, '\n');
      if (textarea.length > 0) {
        textarea[0].value = value;
      }
    }
  }
  //calendar handler
  function calendarHandler(e) {
    var self = $(this),
        calendarFieldName = self.attr('data-control-field'),
        type = self.attr('data-type'),
        activeTab = $activeTab().attr('data-tabid'),
        calendarField = App.Dom.byId(calendarFieldName + '-' + activeTab),
        syncFieldName, syncField;
    if (calendarField) {
      syncFieldName = calendarField.getAttribute('data-syncfield');
      if (syncFieldName) {
        syncField = App.Dom.byId(syncFieldName + '-' + activeTab);
      }
    }
    //not good
    if (calendarField !== null) {
      if (type === 'month') {
        App.Calendar.showMonth(calendarField, syncField, e);
      } else {
        App.Calendar.show(calendarField, syncField, e);
      }
    }

  }
  //zoom handler
  function zoomHandler() {
    var self = $(this),
        controlField = self.attr('data-control-field'),
        row = self.parents('.l-form__row'),
        tabId = $activeTab().attr('data-tabid'),
        input = $('#cont-' + tabId + ' input[name="' + controlField + '"],' +
            ' #editinlist-wrapper-' + tabId +
            ' input[name="' + controlField + '"]'),
        textarea = $('#cont-' + tabId +
            ' .b-textarea[name="zoom-' + controlField + '"],' +
            ' #editinlist-wrapper-' + tabId +
            ' .b-textarea[name="zoom-' + controlField + '"]'),
        value;
    if (row.hasClass('zoom')) {
      //state = 'zoom';
      row.removeClass('zoom');
      value = textarea[0].value.
          replace(/\n+/g, ' ').
          replace(/(^\s*|\s*$)/g, '').
          replace(/\s+/g, ' ');
      input.val(value);
    } else {
      // state = 'unzoom';
      row.addClass('zoom');
      value = input.val().replace(/(^\s*|\s*$)/g, '').replace(/\s+/g, '\n');
      textarea[0].value = value;
    }
    EventMgr.trigger('updFormHeight', { tabId: tabId });
    EventMgr.trigger('updateFixedField', { tabId: tabId });
  }
  //toggle password field type text/password
  function showPassword() {
    var pwdFieldName = this.getAttribute('data-control-field'),
        tabId = this.getAttribute('data-tabid'),
        pwdField = App.Dom.byId(pwdFieldName + '-' + tabId);
    if (pwdField) {
      var type = pwdField.getAttribute('type');
      if (type === 'text') {
        pwdField.setAttribute('type', 'password');
        $(this).removeClass('b-input-btn_pwd_liketext');
      } else if (type === 'password') {
        pwdField.setAttribute('type', 'text');
        $(this).addClass('b-input-btn_pwd_liketext');
      }
    }
  }

  function fakePasswdCopy() {
    var parentId = this.getAttribute('data-parent'),
        parent = App.Dom.byId(parentId);
    if (parent !== null) {
      parent.value = this.value;
      $(parent).trigger('change');
    }
  }
  //password generation
  function genPassword() {
    var pwdFieldName = this.getAttribute('data-control-field'),
        pwdField = $('.tab-content_st_active input[name="' + pwdFieldName + '"]'),
        current = pwdField.val(),
        confFieldName = pwdField.attr('data-check-field'),
        pwdLen = pageInfo.pwgenlen || 8,
        pwdChar = pageInfo.pwgencharacters || '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
        confField = $('.tab-content_st_active' +
            ' input[name="' + confFieldName + '"][type="password"]'),
        n = 0, rn, nnn = 0,
        tabId = $activeTab().attr('data-tabid'),
        pwdFake, confirmFake;
    if (pwdField.attr('readonly')) {
      return;
    }
    if (pwdFieldName) {
      $('.b-input-btn_type_showpwd[data-control-field="'+ pwdFieldName +'"]').remove();
    }
    pwdFake = App.Dom.byId(pwdFieldName + '-' + tabId + '-fake');
    confirmFake = App.Dom.byId(confFieldName + '-' + tabId + '-fake');

    if (pwdFake && pwdFake.offsetWidth === 0) {
      pwdFake.style.display = 'block';
      if (confirmFake) {
        confirmFake.style.display = 'block';
      }
      pwdField.hide();
      if (confField) {
        confField.hide();
      }
    }
    var lowerCase = pwdChar.match(/[a-z]/g),
        upperCase = pwdChar.match(/[A-Z]/g),
        number = pwdChar.match(/\d/g),
        special = pwdChar.match(/[!,@,#,$,%,^,&,*,?,_,~]/g),
        pwgenchar = {},
        typesPull = [],
        i, nn = 0, letters = 0;
    if (lowerCase || upperCase) {
      nn++;
      typesPull.push('l');
    }
    if (lowerCase) {
      lowerCase = lowerCase.join('');
      if (!upperCase) {
        pwgenchar.l = lowerCase;
      } else {
        pwgenchar.lc = lowerCase;
      }
      letters++;
    }
    if (upperCase) {
      upperCase = upperCase.join('');
      if (!lowerCase) {
        pwgenchar.l = upperCase;
      } else {
        pwgenchar.uc = upperCase;
      }

      letters++;
    }
    if (number) {
      number = number.join('');
      pwgenchar.d = number;
      typesPull.push('d');
      nn++;
    }
    if (special) {
      special = special.join('');
      pwgenchar.s = special;
      typesPull.push('s');
      nn++;
    }
    for (i = 0; i < pwdLen - nn; i++) {
      if (nnn === nn) {
        nnn = 0;
      }
      typesPull.push(typesPull[nnn]);
      nnn++;
    }
    rn = Math.floor(Math.random() * pwdLen + 1) % nn;
    typesPull = shiftArray(typesPull, rn);
    var elemObj = {
      passwdField: pwdField,
      confirmField: confField,
      passwdFake: pwdFake,
      confirmFake: confirmFake,
      pwdLen: pwdLen,
      //pwdChar : pwdChar,
      typesPull: typesPull,
      pwgenchar: pwgenchar,
      letters: letters
    };
    genPasswd(current, n, elemObj, 'lc');
  }

  //last to one
  function shiftArray(arr, n) {
    var elem,
        len = arr.length - 1;
    while (n--) {
      elem = arr[0];
      arr.splice(0, 1);
      arr[len] = elem;
    }
    return arr;
  }

  function genPasswd(current, n, elemObj, prevCase) {
    current = current.substr(0, n);
    var pwd = '',
        curCase = prevCase,
        curType,
        typesPull = elemObj.typesPull,
        pwgenchar = elemObj.pwgenchar,
        letters = elemObj.letters;

    for (var i = n; i < elemObj.pwdLen; i++) {
      curType = typesPull[i];
      if (curType === 'l' && letters === 2) {

        if (curCase === 'lc') {
          curCase = 'uc';
        } else {
          curCase = 'lc';
        }
        curType = curCase;
        if (i === n) {
          prevCase = curCase;
        }
      }
      pwd += pwgenchar[curType].
          substr(Math.floor((Math.random() * pwgenchar[curType].length)), 1);
    }
    n++;
    current += pwd;
    elemObj.passwdField.val(current);
    elemObj.confirmField.val(current);
    elemObj.passwdFake.value = current;
    if (elemObj.confirmFake) {
      setTimeout(function() {
        elemObj.confirmFake.value = current;
      }, 1);
    }
    if (n < elemObj.pwdLen) {
      setTimeout(function() { genPasswd(current, n, elemObj, prevCase);} , 35);
    } else {
      $(elemObj.passwdField).trigger('change');
      $(elemObj.passwdFake).trigger('change');
      $(elemObj.confirmField).trigger('change');
    }
  }


  function getActiveTabId() {
    return ($('.tab-content_st_active').attr('data-tabid') || false);
  }

  var UPKEY = 38,
      DOWNKEY = 40,
      LEFTKEY = 37,
      RIGHTKEY = 39,
      ENTERKEY = 13,
      TABKEY = 9,
      ESCKEY = 27,
      SPACE = 32;
  //trigger click if ENTER||SPACE KEYDOWN by element
  function formBtnKeyDownHandler(e) {
    e = e || window.event;
    var codeKey = e.which || e.keyCode;
    if (codeKey === ENTERKEY || codeKey === SPACE) {
      e.preventDefault();
      $(this).trigger('click');
    }
  }

  function radioBtnKeyDownHandler(e) {
    e = e || window.event;
    var codeKey = e.which || e.keyCode,
        name, id, tabId, radios;
    if (codeKey === UPKEY && !e.ctrlKey) {
      e.preventDefault();
      name = this.getAttribute('data-id');
      id = this.getAttribute('data-handler-val');
      radios = $(this).closest('.b-radio').find('div[data-id="' + name + '"]');
      var prev = null;
      radios.each(function() {
        var curId = this.getAttribute('data-handler-val');
        if (curId === id && prev !== null) {
          this.setAttribute('tabindex', '');
          prev.setAttribute('tabindex', '0');
          changeRadioValue.apply(prev);
          prev.focus();
          window.scrollToTopLeft();
          return false;
        }
        prev = this;
      });

    } else if (codeKey === DOWNKEY && !e.ctrlKey) {
      e.preventDefault();
      name = this.getAttribute('data-id');
      id = this.getAttribute('data-handler-val');
      radios = $(this).closest('.b-radio').find('div[data-id="' + name + '"]');
      var next = false;
      radios.each(function() {
        if (next) {
          this.setAttribute('tabindex', '0');
          changeRadioValue.apply(this);
          this.focus();
          return false;
        }
        var curId = this.getAttribute('data-handler-val');
        if (curId === id) {
          this.setAttribute('tabindex', '');
          next = true;
        }
      });

    }
  }

  function radioLabelClickHandler() {
    var rClass = this.getAttribute('data-r-class'),
        targetElem = $('.' + rClass);
    targetElem.trigger('click');
  }
  function checkboxDescClickHandler(e, sv) {
    var tabId = this.getAttribute('data-tabid'),
        id = this.getAttribute('data-id'),
        targetElem = $('.b-checkbox__control[data-tabid="' + tabId + '"]' +
            '[data-id="' + id + '"]');
    targetElem.trigger('click', [sv]);
  }
  //set radio value
  function changeRadioValue(e, sv) {
    var self = $(this),
        id = this.getAttribute('data-id'),
        val = this.getAttribute('data-val'),
        handlerVal = this.getAttribute('data-handler-val');

    if (self.hasClass('checked')) {
      return false;
    } else {
      $('.' + id).removeClass('checked').attr('tabindex', '');
      self.addClass('checked');
      this.setAttribute('tabindex', '0');
      this.focus();
      $('#' + id).val(val)
          .attr('data-handler-val', handlerVal).trigger('change', [sv]);

    }
  }
  /**
   * Callback checkbox click event
   * check/uncheck checkbox
   * @param  {object} e event object
   * @param {boolean} setvalues flag once
   * @return {boolean}
   * @this {object} HTML node
   */
  function changeCheckboxValue(e, param) {
    var $self = $(this),
        id = this.getAttribute('data-id'),
        elem;
    param = param || [];
    if ($self.hasClass('readonly') && !param[1]) {
      return false;
    }
    if ($self.hasClass('checked')) {
      $self.removeClass('checked');
      elem = $('input[name=' + id + ']').val('off')
          .attr('data-handler-val', hash('off'));
    } else {
      $self.addClass('checked');
      elem = $('input[name=' + id + ']').val('on')
          .attr('data-handler-val', hash('on'));
    }
    elem.trigger('change', [param[0]]);
  }

  function collapsedForm(e) {
    //dangerously
    var TIMEOUT = 300;
    e = e || window.event;
    //check for dashboard block reload click
    if (e && e.target) {
      if ($(e.target).hasClass('dashblock-reload')) {
        return;
      }
    }
    if (!App.Global.HeaderMoving) {
      var self = $(this),
          parent = self.parent(),
          elemSlide = parent.next(),
          elemParent = parent.parent(),
          tabId = elemParent.attr('data-tabid'),
          name = parent.attr('data-name'),
          type = parent.attr('data-type'),
          $stateElem = parent.find('.b-triangle'),
          collapsed = elemParent.hasClass('b-form-page_st_collapsed');
      if (collapsed) {
        elemSlide.slideDown(TIMEOUT);
        elemParent.removeClass('b-form-page_st_collapsed');
        $stateElem.attr('data-state', 'expanded');
      } else {
        elemSlide.slideUp(TIMEOUT);
        $stateElem.attr('data-state', 'collapsed');
        setTimeout(function() {
          elemParent.addClass('b-form-page_st_collapsed');
          elemParent = null;
        }, TIMEOUT - 20);
      }
      //form page
      if (type !== 'dashboard-block') {
        EventMgr.trigger('saveFormPageState', {
          tabId: tabId,
          name: name,
          collapsed: collapsed
        });
        setTimeout(function() {
          EventMgr.trigger('updFormHeight', { tabId: tabId });
          EventMgr.trigger('updateScroll', {});
          tabId = null;
        }, TIMEOUT);
      } else {
        //dashboard block
        setTimeout(function() {
          EventMgr.trigger('updateScroll', {});
        }, TIMEOUT);
        var position = self.parents('.b-dashboard_cell').attr('data-pos'),
            display = collapsed ? 'max' : 'min',
            block = self.parent().attr('data-name');
        var param = {
          func: 'dashboard.save', out: 'xml',
          block: block, display: display, position: position};
        var url = pageInfo.url;
        EventMgr.trigger('ajaxRequest', {
          url: url, param: param,
          trfunc: 'DoNothing', queue: 'noqueue' });
        if (!collapsed) {
          setTimeout(function() {
            EventMgr.trigger('updateScroll', {});
          }, TIMEOUT);
        } else {
          setTimeout(function() {
            EventMgr.trigger('updateScroll', {});
            EventMgr.trigger('upDashTableList', { tabId: 'block-' + block });
            block = null;
          }, 10);
        }
      }
    } else {
      App.Global.HeaderMoving = false;
    }
    e.preventDefault();
  }

  function insertUnlimValue() {
    var parId = this.getAttribute('data-control-field'),
        tabId = getActiveTabId(),
        parentEl = App.Dom.byId(parId + '-' + tabId),
        value = parentEl.getAttribute('data-unlimit');
    if (parentEl !== null) {
      parentEl.value = value;
      //for setvalues
      $(parentEl).trigger('change');
    }
  }

  function changeElHandler(e) {
    var showAttr = this.getAttribute('show'),
        show = showAttr.split(','),
        len = show.length,
        checked = this.getAttribute('checked'),
        className = 'hideEl';
    while (len--) {
      $('formItem' + show[len]).toggleClass(className);
    }
  }
  //hide/show depend items in select and radio
  function dependSlistHandler(e) {
    var value = this.value,
        name = this.getAttribute('name'),
        type = this.getAttribute('data-type'),
        tabId = this.getAttribute('data-tabid'),
        elem,
        selectElem, showElem;
    //hide select options
    $(`#frm-${tabId} div[data-depend="${name}"] .dependelem.b-myselect__select-li_show_yes`)
      .removeClass('b-myselect__select-li_show_yes');
    $(`#frm-${tabId} div[data-depend="${name}"] .dependelem[data-dependkey="${value}"]`)
      .addClass('b-myselect__select-li_show_yes');

    //uncheck radio
    selectElem = $('#frm-' + tabId +
        ' div[data-depend="' + name + '"] .b-myselect__select-li_show_yes .b-radio__control.checked');
    if (selectElem.length === 0) {
      $('#frm-' + tabId +
          ' div[data-depend="' + name + '"] .b-radio__control')
          .removeClass('checked');
      elem = $('#frm-' + tabId +
          ' div[data-depend="' + name + '"] .b-myselect__select-li_show_yes .b-radio__control')[0];
      if (elem) {
        changeRadioValue.apply(elem);
      }
    }
    //check for more one depend select
    var $dependSelect = $('#frm-' + tabId + ' div[data-depend="' + name + '"]');
    if ($dependSelect.length > 1) {
      $dependSelect.each(function() {
        var showElem = $(this).find('.dependelem[data-dependkey="' + value + '"].b-myselect__select-li_show_yes');
        dependSelectCheckForOprion(showElem, name, tabId, $(this));
      });
    } else {
      //unselect option
      showElem = $('#frm-' + tabId +
          ' div[data-depend="' + name + '"] ' +
          'li.dependelem[data-dependkey="' + value + '"].b-myselect__select-li_show_yes');
      dependSelectCheckForOprion(showElem, name, tabId);
    }
    //unselect multiselect
    if ($dependSelect.length) {
      $dependSelect.each(function() {
        if (appDom.hasClass(this, 'b-mselect')) {
          EventMgr.trigger('mselectUnselectByDepend', { self: this });
        }
      });
    }
    EventMgr.trigger('updateScroll', { id: 'form-scroll-' + tabId });
  }


  /**
   * Проверяем на наличия опций у зависимого селекта, если их нет скрываем поле
   */
  function dependSelectCheckForOprion(showElem, name, tabId, $self) {
    var $elem = $self || $('#frm-' + tabId + ' div[data-depend="' + name + '"]');
    if (showElem.length === 0) {
      $elem.closest('.l-form__row')
          .css('display', 'none');
    } else {
      $elem.closest('.l-form__row').css('display', '');
    }
  }

  //show depend items in append form
  function firstDependSlistHandler(e, data) {
    var tabId = data.tabId;
    $('#cont-' + tabId + ' tr:not(".row-error") .depend.b-myselect ' +
        'input[type="hidden"],' +
        ' #cont-' + tabId + ' .depend.b-radio input[type="hidden"],' +
        ' #cont-' + tabId + ' .depend.b-select-ac input[type="hidden"]').
        each(function() {
          dependSlistHandler.apply(this);
        });
    EventMgr.trigger('updFormHeight', { tabId: tabId });
    EventMgr.trigger('reloadSlider', { tabId: tabId });

  }


  function bandShowHandler(e) {
    var id = this.getAttribute('data-id'),
        self = $(this),
        toggleClass = 'data-table-hidden';
    if (self.hasClass(toggleClass)) {
      self.removeClass(toggleClass);
      $('#' + id).slideDown(300);
    } else {
      self.addClass(toggleClass);
      $('#' + id).slideUp(300);
    }
    setTimeout(function() {
      EventMgr.trigger('updateScroll', {});
    }, 350);
  }

  /**
   * setUp padding for input with prefix
   * @param {object} e
   * @param {object} data
   */
  function prefixFieldSetUp(e, data) {
    var tabId = data.tabId;
    setTimeout(function() {
      $('#cont-' + tabId + ' .i-input__prefix').each(function() {
        var elemWidth = this.offsetWidth,
            input = App.Common.getPreviousNode(this),
            inputWidth,
            PADDING = 7,
            BORDER = 1;
        if (input) {
          inputWidth = input.offsetWidth;
          input.style.width = (inputWidth - (elemWidth) -
              (PADDING + (BORDER * 2))) + 'px';
          input.style.paddingLeft = (elemWidth) + 'px';
        }
      });
    }, 1);
  }

  //click for row in form list
  function formListRowClickHandler(e) {
    //check for loop
    if (e.target) {
      var $target = $(e.target);
      if ($target.hasClass('i-button') ||
          $target.prop('tagName') === 'A' ||
          $target.hasClass('b-checkbox__control')) {
        return;
      }
    }
    var $self = $(this),
        buttons = $self.find('.i-button_default_yes'),
        checkbox = $self.find('.i-checkbox_type_default');

    if (buttons.length === 1) {
      buttons.trigger('click');
    } else if (checkbox.length === 1) {
     checkbox.trigger('click');
    }
  }

  //
  function textareaResize(e) {
    var lastHeight = parseInt(this.style.height, 10),
        scrollHeight,
        maxHeight = (this.getAttribute('data-max-rows') - 0) * 12,
        BTNBOXHEIGHT = 62,
        id = this.getAttribute('id'),
        tabId = this.getAttribute('data-tabid');
    this.style.height = 'auto';
    scrollHeight = this.scrollHeight - 0;
    if (maxHeight < scrollHeight) {
      $(this).removeClass('b-textarea_responsive_yes');
    }
    this.style.height = scrollHeight + 'px';
    if (lastHeight !== scrollHeight) {

      EventMgr.trigger('updFormHeight', {
        tabId: tabId });
      //scroll textarea for visible area
      var textareaBotPos = $(this).offset().top + scrollHeight + BTNBOXHEIGHT,
          windowHeight = window.innerHeight;
      if (textareaBotPos > windowHeight) {
        var $self = $(this),
            offsetTop = $self.closest('.l-form__row')[0] ?
                $self.closest('.l-form__row')[0].offsetTop : 0,
            $page = $self.closest('.b-form-page'),
            formScrollEl = App.Dom.byId('form-scroll-' + tabId),
            heightViewPort = formScrollEl ? parseInt(formScrollEl.style.height, 10) : 0;
        if ($page[0]) {
          offsetTop += $page[0].offsetTop;
        }
        offsetTop -= (heightViewPort - (scrollHeight + BTNBOXHEIGHT + this.offsetTop));
        EventMgr.trigger('scrollTo', {
          id: 'form-scroll-' + tabId,
          offsetTop: offsetTop,
          raw: false
        });
      }
    }
  }

  function ticketMoveUpHandler(e) {
    var tabId = this.getAttribute('data-tabid'),
        $self = $(this),
        $row = $self.closest('.l-form__row'),
        offsetTop = 0;
    if ($row[0]) {
      offsetTop = $row[0].offsetTop;
      EventMgr.trigger('scrollTo', {
        id: 'form-scroll-' + tabId,
        offsetTop: offsetTop,
        animate: true
      });
    }
  }

  function ticketMoveDownHandler(e) {
    var tabId = this.getAttribute('data-tabid'),
        $self = $(this),
        $row = $self.closest('.l-form__row'),
        $form = $self.closest('.l-form__wrapper'),
        offsetTop = 0,
        height = 0,
        formHeight = 0,
        MAGICNUMBER2 = 120,//min height item for work
        MAGICNUMBER = 160; //btn box height
    if ($row[0] && $form[0]) {
      offsetTop = $row[0].offsetTop;
      height = $row[0].offsetHeight;
      if (height < 120) {
        return;
      }
      formHeight = $form[0].offsetHeight;
      EventMgr.trigger('scrollTo', {
        id: 'form-scroll-' + tabId,
        offsetTop: offsetTop + height - formHeight + MAGICNUMBER,
        animate: true
      });
    }
  }

  function ticketCollapseHandler(e) {
    e.preventDefault();
    var $self = $(this),
        tabId = $self.closest('.tab-content').attr('data-tabid');
    if ($self.hasClass('mbar-showall')) {
      $self.removeClass('mbar-showall').addClass('mbar-hideall');
      $self.closest('.b-ticket__table-row_is_collapsible').removeClass('b-ticket__table-row_is_collapsed');
    } else {
      $self.removeClass('mbar-hideall').addClass('mbar-showall');
      $self.closest('.b-ticket__table-row_is_collapsible').addClass('b-ticket__table-row_is_collapsed');
    }
    EventMgr.trigger('updFormHeight', { tabId: tabId });
  }

  return {
    init: init,
    checkPassStrength: checkPassStrength,
    CACHE: CACHE
  };
}(window, $, EventMgr, App, ScrollHandler);