text: /* ../../libs/bem-core/node_modules/ym/modules.js begin */
  /**
   * Modules
   *
   * Copyright (c) 2013 Filatov Dmitry (dfilatov@yandex-team.ru)
   * Dual licensed under the MIT and GPL licenses:
   * http://www.opensource.org/licenses/mit-license.php
   * http://www.gnu.org/licenses/gpl.html
   *
   * @version 0.0.15
   */
  
  (function(global) {
  
  var undef,
      DECL_STATES = {
          NOT_RESOLVED : 'NOT_RESOLVED',
          IN_RESOLVING : 'IN_RESOLVING',
          RESOLVED     : 'RESOLVED'
      },
  
      /**
       * Creates a new instance of modular system
       * @returns {Object}
       */
      create = function() {
          var curOptions = {
                  trackCircularDependencies : true,
                  allowMultipleDeclarations : true,
                  onError                   : function(e) {
                      throw e;
                  }
              },
  
              modulesStorage = {},
              declsToCalc = [],
              waitForNextTick = false,
              pendingRequires = [],
  
              /**
               * Defines module
               * @param {String} name
               * @param {String[]} [deps]
               * @param {Function} declFn
               */
              define = function(name, deps, declFn) {
                  if(!declFn) {
                      declFn = deps;
                      deps = [];
                  }
  
                  var module = modulesStorage[name];
                  if(module) {
                      if(!curOptions.allowMultipleDeclarations) {
                          onMultipleDeclarationDetected(name);
                          return;
                      }
                  }
                  else {
                      module = modulesStorage[name] = {
                          name : name,
                          decl : undef
                      };
                  }
  
                  declsToCalc.push(module.decl = {
                      name          : name,
                      fn            : declFn,
                      state         : DECL_STATES.NOT_RESOLVED,
                      deps          : deps,
                      prevDecl      : module.decl,
                      dependOnDecls : [],
                      dependents    : [],
                      exports       : undef
                  });
              },
  
              /**
               * Requires modules
               * @param {String[]} modules
               * @param {Function} cb
               */
              require = function(modules, cb) {
                  if(!waitForNextTick) {
                      waitForNextTick = true;
                      nextTick(onNextTick);
                  }
  
                  pendingRequires.push({
                      modules : modules,
                      cb      : cb
                  });
              },
  
              /**
               * Returns state of module
               * @param {String} name
               * @returns {String} state, possible values are NOT_DEFINED, NOT_RESOLVED, IN_RESOLVING, RESOLVED
               */
              getState = function(name) {
                  var module = modulesStorage[name];
                  return module?
                      DECL_STATES[module.decl.state] :
                      'NOT_DEFINED';
              },
  
              /**
               * Returns whether the module is defined
               * @param {String} name
               * @returns {Boolean}
               */
              isDefined = function(name) {
                  return !!modulesStorage[name];
              },
  
              /**
               * Sets options
               * @param {Object} options
               */
              setOptions = function(options) {
                  for(var name in options) {
                      if(options.hasOwnProperty(name)) {
                          curOptions[name] = options[name];
                      }
                  }
              },
  
              onNextTick = function() {
                  waitForNextTick = false;
                  if(calcDeclDeps()) {
                      applyRequires();
                  }
              },
  
              calcDeclDeps = function() {
                  var i = 0, decl, j, dep, dependOnDecls,
                      hasError = false;
                  while(decl = declsToCalc[i++]) {
                      j = 0;
                      dependOnDecls = decl.dependOnDecls;
                      while(dep = decl.deps[j++]) {
                          if(!isDefined(dep)) {
                              onModuleNotFound(dep, decl);
                              hasError = true;
                              break;
                          }
                          dependOnDecls.push(modulesStorage[dep].decl);
                      }
  
                      if(hasError) {
                          break;
                      }
  
                      if(decl.prevDecl) {
                          dependOnDecls.push(decl.prevDecl);
                          decl.prevDecl = undef;
                      }
                  }
  
                  declsToCalc = [];
                  return !hasError;
              },
  
              applyRequires = function() {
                  var requiresToProcess = pendingRequires,
                      require, i = 0, j, dep, dependOnDecls, applyCb;
  
                  pendingRequires = [];
  
                  while(require = requiresToProcess[i++]) {
                      j = 0; dependOnDecls = []; applyCb = true;
                      while(dep = require.modules[j++]) {
                          if(!isDefined(dep)) {
                              onModuleNotFound(dep);
                              applyCb = false;
                              break;
                          }
  
                          dependOnDecls.push(modulesStorage[dep].decl);
                      }
                      applyCb && applyRequire(dependOnDecls, require.cb);
                  }
              },
  
              applyRequire = function(dependOnDecls, cb) {
                  requireDecls(
                      dependOnDecls,
                      function(exports) {
                          cb.apply(global, exports);
                      },
                      []);
              },
  
              requireDecls = function(decls, cb, path) {
                  var unresolvedDeclCnt = decls.length;
  
                  if(unresolvedDeclCnt) {
                      var onDeclResolved,
                          i = 0, decl;
  
                      while(decl = decls[i++]) {
                          if(decl.state === DECL_STATES.RESOLVED) {
                              --unresolvedDeclCnt;
                          }
                          else {
                              if(curOptions.trackCircularDependencies && isDependenceCircular(decl, path)) {
                                  onCircularDependenceDetected(decl, path);
                              }
  
                              decl.state === DECL_STATES.NOT_RESOLVED && startDeclResolving(decl, path);
  
                              decl.state === DECL_STATES.RESOLVED? // decl resolved synchronously
                                  --unresolvedDeclCnt :
                                  decl.dependents.push(onDeclResolved || (onDeclResolved = function() {
                                      --unresolvedDeclCnt || onDeclsResolved(decls, cb);
                                  }));
                          }
                      }
                  }
  
                  unresolvedDeclCnt || onDeclsResolved(decls, cb);
              },
  
              onDeclsResolved = function(decls, cb) {
                  var exports = [],
                      i = 0, decl;
                  while(decl = decls[i++]) {
                      exports.push(decl.exports);
                  }
                  cb(exports);
              },
  
              startDeclResolving = function(decl, path) {
                  curOptions.trackCircularDependencies && (path = path.slice()).push(decl);
                  decl.state = DECL_STATES.IN_RESOLVING;
                  var isProvided = false;
                  requireDecls(
                      decl.dependOnDecls,
                      function(depDeclsExports) {
                          decl.fn.apply(
                              {
                                  name   : decl.name,
                                  deps   : decl.deps,
                                  global : global
                              },
                              [function(exports) {
                                  isProvided?
                                      onDeclAlreadyProvided(decl) :
                                      isProvided = true;
                                  provideDecl(decl, exports);
                                  return exports;
                              }].concat(depDeclsExports));
                      },
                      path);
              },
  
              provideDecl = function(decl, exports) {
                  decl.exports = exports;
                  decl.state = DECL_STATES.RESOLVED;
  
                  var i = 0, dependent;
                  while(dependent = decl.dependents[i++]) {
                      dependent(decl.exports);
                  }
  
                  decl.dependents = undef;
              },
  
              onError = function(e) {
                  nextTick(function() {
                      curOptions.onError(e);
                  });
              },
  
              onModuleNotFound = function(name, decl) {
                  onError(Error(
                      decl?
                          'Module "' + decl.name + '": can\'t resolve dependence "' + name + '"' :
                          'Required module "' + name + '" can\'t be resolved'));
              },
  
              onCircularDependenceDetected = function(decl, path) {
                  var strPath = [],
                      i = 0, pathDecl;
                  while(pathDecl = path[i++]) {
                      strPath.push(pathDecl.name);
                  }
                  strPath.push(decl.name);
  
                  onError(Error('Circular dependence is detected: "' + strPath.join(' -> ') + '"'));
              },
  
              onDeclAlreadyProvided = function(decl) {
                  onError(Error('Declaration of module "' + decl.name + '" is already provided'));
              },
  
              onMultipleDeclarationDetected = function(name) {
                  onError(Error('Multiple declarations of module "' + name + '" are detected'));
              };
  
          return {
              create     : create,
              define     : define,
              require    : require,
              getState   : getState,
              isDefined  : isDefined,
              setOptions : setOptions
          };
      },
  
      isDependenceCircular = function(decl, path) {
          var i = 0, pathDecl;
          while(pathDecl = path[i++]) {
              if(decl === pathDecl) {
                  return true;
              }
          }
          return false;
      },
  
      nextTick = (function() {
          var fns = [],
              enqueueFn = function(fn) {
                  return fns.push(fn) === 1;
              },
              callFns = function() {
                  var fnsToCall = fns, i = 0, len = fns.length;
                  fns = [];
                  while(i < len) {
                      fnsToCall[i++]();
                  }
              };
  
          if(typeof process === 'object' && process.nextTick) { // nodejs
              return function(fn) {
                  enqueueFn(fn) && process.nextTick(callFns);
              };
          }
  
          if(global.setImmediate) { // ie10
              return function(fn) {
                  enqueueFn(fn) && global.setImmediate(callFns);
              };
          }
  
          if(global.postMessage && !global.opera) { // modern browsers
              var isPostMessageAsync = true;
              if(global.attachEvent) {
                  var checkAsync = function() {
                          isPostMessageAsync = false;
                      };
                  global.attachEvent('onmessage', checkAsync);
                  global.postMessage('__checkAsync', '*');
                  global.detachEvent('onmessage', checkAsync);
              }
  
              if(isPostMessageAsync) {
                  var msg = '__modules' + (+new Date()),
                      onMessage = function(e) {
                          if(e.data === msg) {
                              e.stopPropagation && e.stopPropagation();
                              callFns();
                          }
                      };
  
                  global.addEventListener?
                      global.addEventListener('message', onMessage, true) :
                      global.attachEvent('onmessage', onMessage);
  
                  return function(fn) {
                      enqueueFn(fn) && global.postMessage(msg, '*');
                  };
              }
          }
  
          var doc = global.document;
          if('onreadystatechange' in doc.createElement('script')) { // ie6-ie8
              var head = doc.getElementsByTagName('head')[0],
                  createScript = function() {
                      var script = doc.createElement('script');
                      script.onreadystatechange = function() {
                          script.parentNode.removeChild(script);
                          script = script.onreadystatechange = null;
                          callFns();
                      };
                      head.appendChild(script);
                  };
  
              return function(fn) {
                  enqueueFn(fn) && createScript();
              };
          }
  
          return function(fn) { // old browsers
              enqueueFn(fn) && setTimeout(callFns, 0);
          };
      })();
  
  if(typeof exports === 'object') {
      module.exports = create();
  }
  else {
      global.modules = create();
  }
  
  })(this);
  
  /* ../../libs/bem-core/node_modules/ym/modules.js end */
  ;
  /* ../../libs/bem-core/common.blocks/i-bem/i-bem.vanilla.js begin */
  /**
   * @module i-bem
   */
  
  modules.define(
      'i-bem',
      [
          'i-bem__internal',
          'inherit',
          'identify',
          'next-tick',
          'objects',
          'functions',
          'events'
      ],
      function(
          provide,
          INTERNAL,
          inherit,
          identify,
          nextTick,
          objects,
          functions,
          events) {
  
  var undef,
  
      MOD_DELIM = INTERNAL.MOD_DELIM,
      ELEM_DELIM = INTERNAL.ELEM_DELIM,
  
      /**
       * Storage for block init functions
       * @private
       * @type Array
       */
      initFns = [],
  
      /**
       * Storage for block declarations (hash by block name)
       * @private
       * @type Object
       */
      blocks = {};
  
  /**
   * Builds the name of the handler method for setting a modifier
   * @param {String} prefix
   * @param {String} modName Modifier name
   * @param {String} modVal Modifier value
   * @param {String} [elemName] Element name
   * @returns {String}
   */
  function buildModFnName(prefix, modName, modVal, elemName) {
      return '__' + prefix +
          (elemName? '__elem_' + elemName : '') +
         '__mod' +
         (modName? '_' + modName : '') +
         (modVal? '_' + modVal : '');
  }
  
  /**
   * Transforms a hash of modifier handlers to methods
   * @param {String} prefix
   * @param {Object} modFns
   * @param {Object} props
   * @param {String} [elemName]
   */
  function modFnsToProps(prefix, modFns, props, elemName) {
      if(functions.isFunction(modFns)) {
          props[buildModFnName(prefix, '*', '*', elemName)] = modFns;
      } else {
          var modName, modVal, modFn;
          for(modName in modFns) {
              if(modFns.hasOwnProperty(modName)) {
                  modFn = modFns[modName];
                  if(functions.isFunction(modFn)) {
                      props[buildModFnName(prefix, modName, '*', elemName)] = modFn;
                  } else {
                      for(modVal in modFn) {
                          if(modFn.hasOwnProperty(modVal)) {
                              props[buildModFnName(prefix, modName, modVal, elemName)] = modFn[modVal];
                          }
                      }
                  }
              }
          }
      }
  }
  
  function buildCheckMod(modName, modVal) {
      return modVal?
          Array.isArray(modVal)?
              function(block) {
                  var i = 0, len = modVal.length;
                  while(i < len)
                      if(block.hasMod(modName, modVal[i++]))
                          return true;
                  return false;
              } :
              function(block) {
                  return block.hasMod(modName, modVal);
              } :
          function(block) {
              return block.hasMod(modName);
          };
  }
  
  function convertModHandlersToMethods(props) {
      if(props.beforeSetMod) {
          modFnsToProps('before', props.beforeSetMod, props);
          delete props.beforeSetMod;
      }
  
      if(props.onSetMod) {
          modFnsToProps('after', props.onSetMod, props);
          delete props.onSetMod;
      }
  
      var elemName;
      if(props.beforeElemSetMod) {
          for(elemName in props.beforeElemSetMod) {
              if(props.beforeElemSetMod.hasOwnProperty(elemName)) {
                  modFnsToProps('before', props.beforeElemSetMod[elemName], props, elemName);
              }
          }
          delete props.beforeElemSetMod;
      }
  
      if(props.onElemSetMod) {
          for(elemName in props.onElemSetMod) {
              if(props.onElemSetMod.hasOwnProperty(elemName)) {
                  modFnsToProps('after', props.onElemSetMod[elemName], props, elemName);
              }
          }
          delete props.onElemSetMod;
      }
  }
  
  /**
   * @class BEM
   * @description Base block for creating BEM blocks
   * @augments events:Emitter
   * @exports
   */
  var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
      /**
       * @constructor
       * @private
       * @param {Object} mods Block modifiers
       * @param {Object} params Block parameters
       * @param {Boolean} [initImmediately=true]
       */
      __constructor : function(mods, params, initImmediately) {
          /**
           * Cache of block modifiers
           * @member {Object}
           * @private
           */
          this._modCache = mods || {};
  
          /**
           * Current modifiers in the stack
           * @member {Object}
           * @private
           */
          this._processingMods = {};
  
          /**
           * Block parameters, taking into account the defaults
           * @member {Object}
           * @readonly
           */
          this.params = objects.extend(this.getDefaultParams(), params);
  
          initImmediately !== false?
              this._init() :
              initFns.push(this._init, this);
      },
  
      /**
       * Initializes the block
       * @private
       */
      _init : function() {
          return this.setMod('js', 'inited');
      },
  
      /**
       * Adds an event handler
       * @param {String|Object} e Event type
       * @param {Object} [data] Additional data that the handler gets as e.data
       * @param {Function} fn Handler
       * @param {Object} [ctx] Handler context
       * @returns {BEM} this
       */
      on : function(e, data, fn, ctx) {
          if(typeof e === 'object' && (functions.isFunction(data) || functions.isFunction(fn))) { // mod change event
              e = this.__self._buildModEventName(e);
          }
  
          return this.__base.apply(this, arguments);
      },
  
      /**
       * Removes event handler or handlers
       * @param {String|Object} [e] Event type
       * @param {Function} [fn] Handler
       * @param {Object} [ctx] Handler context
       * @returns {BEM} this
       */
      un : function(e, fn, ctx) {
          if(typeof e === 'object' && functions.isFunction(fn)) { // mod change event
              e = this.__self._buildModEventName(e);
          }
  
          return this.__base.apply(this, arguments);
      },
  
      /**
       * Executes the block's event handlers and live event handlers
       * @protected
       * @param {String} e Event name
       * @param {Object} [data] Additional information
       * @returns {BEM} this
       */
      emit : function(e, data) {
          var isModJsEvent = false;
          if(typeof e === 'object' && !(e instanceof events.Event)) {
              isModJsEvent = e.modName === 'js';
              e = this.__self._buildModEventName(e);
          }
  
          if(isModJsEvent || this.hasMod('js', 'inited')) {
              this.__base(e = this._buildEvent(e), data);
              this._ctxEmit(e, data);
          }
  
          return this;
      },
  
      _ctxEmit : function(e, data) {
          this.__self.emit(e, data);
      },
  
      /**
       * Builds event
       * @private
       * @param {String|events:Event} e
       * @returns {events:Event}
       */
      _buildEvent : function(e) {
          typeof e === 'string'?
              e = new events.Event(e, this) :
              e.target || (e.target = this);
  
          return e;
      },
  
      /**
       * Checks whether a block or nested element has a modifier
       * @param {Object} [elem] Nested element
       * @param {String} modName Modifier name
       * @param {String} [modVal] Modifier value
       * @returns {Boolean}
       */
      hasMod : function(elem, modName, modVal) {
          var len = arguments.length,
              invert = false;
  
          if(len === 1) {
              modVal = '';
              modName = elem;
              elem = undef;
              invert = true;
          } else if(len === 2) {
              if(typeof elem === 'string') {
                  modVal = modName;
                  modName = elem;
                  elem = undef;
              } else {
                  modVal = '';
                  invert = true;
              }
          }
  
          var res = this.getMod(elem, modName) === modVal;
          return invert? !res : res;
      },
  
      /**
       * Returns the value of the modifier of the block/nested element
       * @param {Object} [elem] Nested element
       * @param {String} modName Modifier name
       * @returns {String} Modifier value
       */
      getMod : function(elem, modName) {
          var type = typeof elem;
          if(type === 'string' || type === 'undefined') { // elem either omitted or undefined
              modName = elem || modName;
              var modCache = this._modCache;
              return modName in modCache?
                  modCache[modName] || '' :
                  modCache[modName] = this._extractModVal(modName);
          }
  
          return this._getElemMod(modName, elem);
      },
  
      /**
       * Returns the value of the modifier of the nested element
       * @private
       * @param {String} modName Modifier name
       * @param {Object} elem Nested element
       * @param {Object} [elemName] Nested element name
       * @returns {String} Modifier value
       */
      _getElemMod : function(modName, elem, elemName) {
          return this._extractModVal(modName, elem, elemName);
      },
  
      /**
       * Returns values of modifiers of the block/nested element
       * @param {Object} [elem] Nested element
       * @param {String} [...modNames] Modifier names
       * @returns {Object} Hash of modifier values
       */
      getMods : function(elem) {
          var hasElem = elem && typeof elem !== 'string',
              modNames = [].slice.call(arguments, hasElem? 1 : 0),
              res = this._extractMods(modNames, hasElem? elem : undef);
  
          if(!hasElem) { // caching
              modNames.length?
                  modNames.forEach(function(name) {
                      this._modCache[name] = res[name];
                  }, this) :
                  this._modCache = res;
          }
  
          return res;
      },
  
      /**
       * Sets the modifier for a block/nested element
       * @param {Object} [elem] Nested element
       * @param {String} modName Modifier name
       * @param {String} modVal Modifier value
       * @returns {BEM} this
       */
      setMod : function(elem, modName, modVal) {
          if(typeof modVal === 'undefined') {
              if(typeof elem === 'string') { // if no elem
                  modVal = typeof modName === 'undefined'?
                      true :  // e.g. setMod('focused')
                      modName; // e.g. setMod('js', 'inited')
                  modName = elem;
                  elem = undef;
              } else { // if elem
                  modVal = true; // e.g. setMod(elem, 'focused')
              }
          }
  
          if(!elem || elem[0]) {
              modVal === false && (modVal = '');
  
              var modId = (elem && elem[0]? identify(elem[0]) : '') + '_' + modName;
  
              if(this._processingMods[modId])
                  return this;
  
              var elemName,
                  curModVal = elem?
                      this._getElemMod(modName, elem, elemName = this.__self._extractElemNameFrom(elem)) :
                      this.getMod(modName);
  
              if(curModVal === modVal)
                  return this;
  
              this._processingMods[modId] = true;
  
              var needSetMod = true,
                  modFnParams = [modName, modVal, curModVal];
  
              elem && modFnParams.unshift(elem);
  
              var modVars = [['*', '*'], [modName, '*'], [modName, modVal]],
                  prefixes = ['before', 'after'],
                  i = 0, prefix, j, modVar;
  
              while(prefix = prefixes[i++]) {
                  j = 0;
                  while(modVar = modVars[j++]) {
                      if(this._callModFn(prefix, elemName, modVar[0], modVar[1], modFnParams) === false) {
                          needSetMod = false;
                          break;
                      }
                  }
  
                  if(!needSetMod) break;
  
                  if(prefix === 'before') {
                      elem || (this._modCache[modName] = modVal); // cache only block mods
                      this._onSetMod(modName, modVal, curModVal, elem, elemName);
                  }
              }
  
              this._processingMods[modId] = null;
              needSetMod && this._emitModChangeEvents(modName, modVal, curModVal, elem, elemName);
          }
  
          return this;
      },
  
      /**
       * Function after successfully changing the modifier of the block/nested element
       * @protected
       * @param {String} modName Modifier name
       * @param {String} modVal Modifier value
       * @param {String} oldModVal Old modifier value
       * @param {Object} [elem] Nested element
       * @param {String} [elemName] Element name
       */
      _onSetMod : function(modName, modVal, oldModVal, elem, elemName) {},
  
      _emitModChangeEvents : function(modName, modVal, oldModVal, elem, elemName) {
          var eventData = { modName : modName, modVal : modVal, oldModVal : oldModVal };
          elem && (eventData.elem = elem);
          this
              .emit({ modName : modName, modVal : '*', elem : elemName }, eventData)
              .emit({ modName : modName, modVal : modVal, elem : elemName }, eventData);
      },
  
      /**
       * Sets a modifier for a block/nested element, depending on conditions.
       * If the condition parameter is passed: when true, modVal1 is set; when false, modVal2 is set.
       * If the condition parameter is not passed: modVal1 is set if modVal2 was set, or vice versa.
       * @param {Object} [elem] Nested element
       * @param {String} modName Modifier name
       * @param {String} modVal1 First modifier value
       * @param {String} [modVal2] Second modifier value
       * @param {Boolean} [condition] Condition
       * @returns {BEM} this
       */
      toggleMod : function(elem, modName, modVal1, modVal2, condition) {
          if(typeof elem === 'string') { // if this is a block
              condition = modVal2;
              modVal2 = modVal1;
              modVal1 = modName;
              modName = elem;
              elem = undef;
          }
  
          if(typeof modVal1 === 'undefined') { // boolean mod
              modVal1 = true;
          }
  
          if(typeof modVal2 === 'undefined') {
              modVal2 = '';
          } else if(typeof modVal2 === 'boolean') {
              condition = modVal2;
              modVal2 = '';
          }
  
          var modVal = this.getMod(elem, modName);
          (modVal === modVal1 || modVal === modVal2) &&
              this.setMod(
                  elem,
                  modName,
                  typeof condition === 'boolean'?
                      (condition? modVal1 : modVal2) :
                      this.hasMod(elem, modName, modVal1)? modVal2 : modVal1);
  
          return this;
      },
  
      /**
       * Removes a modifier from a block/nested element
       * @protected
       * @param {Object} [elem] Nested element
       * @param {String} modName Modifier name
       * @returns {BEM} this
       */
      delMod : function(elem, modName) {
          if(!modName) {
              modName = elem;
              elem = undef;
          }
  
          return this.setMod(elem, modName, '');
      },
  
      /**
       * Executes handlers for setting modifiers
       * @private
       * @param {String} prefix
       * @param {String} elemName Element name
       * @param {String} modName Modifier name
       * @param {String} modVal Modifier value
       * @param {Array} modFnParams Handler parameters
       */
      _callModFn : function(prefix, elemName, modName, modVal, modFnParams) {
          var modFnName = buildModFnName(prefix, modName, modVal, elemName);
          return this[modFnName]?
             this[modFnName].apply(this, modFnParams) :
             undef;
      },
  
      /**
       * Retrieves the value of the modifier
       * @private
       * @param {String} modName Modifier name
       * @param {Object} [elem] Element
       * @returns {String} Modifier value
       */
      _extractModVal : function(modName, elem) {
          return '';
      },
  
      /**
       * Retrieves name/value for a list of modifiers
       * @private
       * @param {Array} modNames Names of modifiers
       * @param {Object} [elem] Element
       * @returns {Object} Hash of modifier values by name
       */
      _extractMods : function(modNames, elem) {
          return {};
      },
  
      /**
       * Returns a block's default parameters
       * @protected
       * @returns {Object}
       */
      getDefaultParams : function() {
          return {};
      },
  
      /**
       * Deletes a block
       * @private
       */
      _destruct : function() {
          this.delMod('js');
      },
  
      /**
       * Executes given callback on next turn eventloop in block's context
       * @protected
       * @param {Function} fn callback
       * @returns {BEM} this
       */
      nextTick : function(fn) {
          var _this = this;
          nextTick(function() {
              _this.hasMod('js', 'inited') && fn.call(_this);
          });
          return this;
      }
  }, /** @lends BEM */{
  
      _name : 'i-bem',
  
      /**
       * Storage for block declarations (hash by block name)
       * @type Object
       */
      blocks : blocks,
  
      /**
       * Declares blocks and creates a block class
       * @param {String|Object} decl Block name (simple syntax) or description
       * @param {String} decl.block|decl.name Block name
       * @param {String} [decl.baseBlock] Name of the parent block
       * @param {Array} [decl.baseMix] Mixed block names
       * @param {String} [decl.modName] Modifier name
       * @param {String|Array} [decl.modVal] Modifier value
       * @param {Object} [props] Methods
       * @param {Object} [staticProps] Static methods
       * @returns {Function}
       */
      decl : function(decl, props, staticProps) {
          // string as block
          typeof decl === 'string' && (decl = { block : decl });
          // inherit from itself
          if(arguments.length <= 2 &&
                  typeof decl === 'object' &&
                  (!decl || (typeof decl.block !== 'string' && typeof decl.modName !== 'string'))) {
              staticProps = props;
              props = decl;
              decl = {};
          }
          typeof decl.block === 'undefined' && (decl.block = this.getName());
  
          var baseBlock;
          if(typeof decl.baseBlock === 'undefined') {
              baseBlock = blocks[decl.block] || this;
          } else if(typeof decl.baseBlock === 'string') {
              baseBlock = blocks[decl.baseBlock];
              if(!baseBlock)
                  throw('baseBlock "' + decl.baseBlock + '" for "' + decl.block + '" is undefined');
          } else {
              baseBlock = decl.baseBlock;
          }
  
          convertModHandlersToMethods(props || (props = {}));
  
          if(decl.modName) {
              var checkMod = buildCheckMod(decl.modName, decl.modVal);
              objects.each(props, function(prop, name) {
                  functions.isFunction(prop) &&
                      (props[name] = function() {
                          var method;
                          if(checkMod(this)) {
                              method = prop;
                          } else {
                              var baseMethod = baseBlock.prototype[name];
                              baseMethod && baseMethod !== prop &&
                                  (method = this.__base);
                          }
                          return method?
                              method.apply(this, arguments) :
                              undef;
                      });
              });
          }
  
          if(staticProps && typeof staticProps.live === 'boolean') {
              var live = staticProps.live;
              staticProps.live = function() {
                  return live;
              };
          }
  
          var block, baseBlocks = baseBlock;
          if(decl.baseMix) {
              baseBlocks = [baseBlocks];
              decl.baseMix.forEach(function(mixedBlock) {
                  if(!blocks[mixedBlock]) {
                      throw('mix block "' + mixedBlock + '" for "' + decl.block + '" is undefined');
                  }
                  baseBlocks.push(blocks[mixedBlock]);
              });
          }
  
          if(decl.block === baseBlock.getName()) {
              // makes a new "live" if the old one was already executed
              (block = inherit.self(baseBlocks, props, staticProps))._processLive(true);
          } else {
              (block = blocks[decl.block] = inherit(baseBlocks, props, staticProps))._name = decl.block;
              delete block._liveInitable;
          }
  
          return block;
      },
  
      declMix : function(block, props, staticProps) {
          convertModHandlersToMethods(props || (props = {}));
          return blocks[block] = inherit(props, staticProps);
      },
  
      /**
       * Processes a block's live properties
       * @private
       * @param {Boolean} [heedLive=false] Whether to take into account that the block already processed its live properties
       * @returns {Boolean} Whether the block is a live block
       */
      _processLive : function(heedLive) {
          return false;
      },
  
      /**
       * Factory method for creating an instance of the block named
       * @param {String|Object} block Block name or description
       * @param {Object} [params] Block parameters
       * @returns {BEM}
       */
      create : function(block, params) {
          typeof block === 'string' && (block = { block : block });
  
          return new blocks[block.block](block.mods, params);
      },
  
      /**
       * Returns the name of the current block
       * @returns {String}
       */
      getName : function() {
          return this._name;
      },
  
      /**
       * Adds an event handler
       * @param {String|Object} e Event type
       * @param {Object} [data] Additional data that the handler gets as e.data
       * @param {Function} fn Handler
       * @param {Object} [ctx] Handler context
       * @returns {Function} this
       */
      on : function(e, data, fn, ctx) {
          if(typeof e === 'object' && (functions.isFunction(data) || functions.isFunction(fn))) { // mod change event
              e = this._buildModEventName(e);
          }
  
          return this.__base.apply(this, arguments);
      },
  
      /**
       * Removes event handler or handlers
       * @param {String|Object} [e] Event type
       * @param {Function} [fn] Handler
       * @param {Object} [ctx] Handler context
       * @returns {Function} this
       */
      un : function(e, fn, ctx) {
          if(typeof e === 'object' && functions.isFunction(fn)) { // mod change event
              e = this._buildModEventName(e);
          }
  
          return this.__base.apply(this, arguments);
      },
  
      _buildModEventName : function(modEvent) {
          var res = MOD_DELIM + modEvent.modName + MOD_DELIM + (modEvent.modVal === false? '' : modEvent.modVal);
          modEvent.elem && (res = ELEM_DELIM + modEvent.elem + res);
          return res;
      },
  
      /**
       * Retrieves the name of an element nested in a block
       * @private
       * @param {Object} elem Nested element
       * @returns {String|undefined}
       */
      _extractElemNameFrom : function(elem) {},
  
      /**
       * Executes the block init functions
       * @private
       */
      _runInitFns : function() {
          if(initFns.length) {
              var fns = initFns,
                  fn, i = 0;
  
              initFns = [];
              while(fn = fns[i]) {
                  fn.call(fns[i + 1]);
                  i += 2;
              }
          }
      }
  });
  
  provide(BEM);
  
  });
  
  /* ../../libs/bem-core/common.blocks/i-bem/i-bem.vanilla.js end */
  ;
  /* ../../libs/bem-core/common.blocks/i-bem/__internal/i-bem__internal.vanilla.js begin */
  /**
   * @module i-bem__internal
   */
  
  modules.define('i-bem__internal', function(provide) {
  
  var undef,
      /**
       * Separator for modifiers and their values
       * @const
       * @type String
       */
      MOD_DELIM = '_',
  
      /**
       * Separator between names of a block and a nested element
       * @const
       * @type String
       */
      ELEM_DELIM = '__',
  
      /**
       * Pattern for acceptable element and modifier names
       * @const
       * @type String
       */
      NAME_PATTERN = '[a-zA-Z0-9-]+';
  
  function isSimple(obj) {
      var typeOf = typeof obj;
      return typeOf === 'string' || typeOf === 'number' || typeOf === 'boolean';
  }
  
  function buildModPostfix(modName, modVal) {
      var res = '';
      /* jshint eqnull: true */
      if(modVal != null && modVal !== false) {
          res += MOD_DELIM + modName;
          modVal !== true && (res += MOD_DELIM + modVal);
      }
      return res;
  }
  
  function buildBlockClass(name, modName, modVal) {
      return name + buildModPostfix(modName, modVal);
  }
  
  function buildElemClass(block, name, modName, modVal) {
      return buildBlockClass(block, undef, undef) +
          ELEM_DELIM + name +
          buildModPostfix(modName, modVal);
  }
  
  provide(/** @exports */{
      NAME_PATTERN : NAME_PATTERN,
  
      MOD_DELIM : MOD_DELIM,
      ELEM_DELIM : ELEM_DELIM,
  
      buildModPostfix : buildModPostfix,
  
      /**
       * Builds the class of a block or element with a modifier
       * @param {String} block Block name
       * @param {String} [elem] Element name
       * @param {String} [modName] Modifier name
       * @param {String|Number} [modVal] Modifier value
       * @returns {String} Class
       */
      buildClass : function(block, elem, modName, modVal) {
          if(isSimple(modName)) {
              if(!isSimple(modVal)) {
                  modVal = modName;
                  modName = elem;
                  elem = undef;
              }
          } else if(typeof modName !== 'undefined') {
              modName = undef;
          } else if(elem && typeof elem !== 'string') {
              elem = undef;
          }
  
          if(!(elem || modName)) { // optimization for simple case
              return block;
          }
  
          return elem?
              buildElemClass(block, elem, modName, modVal) :
              buildBlockClass(block, modName, modVal);
      },
  
      /**
       * Builds full classes for a buffer or element with modifiers
       * @param {String} block Block name
       * @param {String} [elem] Element name
       * @param {Object} [mods] Modifiers
       * @returns {String} Class
       */
      buildClasses : function(block, elem, mods) {
          if(elem && typeof elem !== 'string') {
              mods = elem;
              elem = undef;
          }
  
          var res = elem?
              buildElemClass(block, elem, undef, undef) :
              buildBlockClass(block, undef, undef);
  
          if(mods) {
              for(var modName in mods) {
                  if(mods.hasOwnProperty(modName) && mods[modName]) {
                      res += ' ' + (elem?
                          buildElemClass(block, elem, modName, mods[modName]) :
                          buildBlockClass(block, modName, mods[modName]));
                  }
              }
          }
  
          return res;
      }
  });
  
  });
  
  /* ../../libs/bem-core/common.blocks/i-bem/__internal/i-bem__internal.vanilla.js end */
  ;
  /* ../../libs/bem-core/common.blocks/inherit/inherit.vanilla.js begin */
  /**
   * @module inherit
   * @version 2.2.1
   * @author Filatov Dmitry <dfilatov@yandex-team.ru>
   * @description This module provides some syntax sugar for "class" declarations, constructors, mixins, "super" calls and static members.
   */
  
  (function(global) {
  
  var hasIntrospection = (function(){'_';}).toString().indexOf('_') > -1,
      emptyBase = function() {},
      hasOwnProperty = Object.prototype.hasOwnProperty,
      objCreate = Object.create || function(ptp) {
          var inheritance = function() {};
          inheritance.prototype = ptp;
          return new inheritance();
      },
      objKeys = Object.keys || function(obj) {
          var res = [];
          for(var i in obj) {
              hasOwnProperty.call(obj, i) && res.push(i);
          }
          return res;
      },
      extend = function(o1, o2) {
          for(var i in o2) {
              hasOwnProperty.call(o2, i) && (o1[i] = o2[i]);
          }
  
          return o1;
      },
      toStr = Object.prototype.toString,
      isArray = Array.isArray || function(obj) {
          return toStr.call(obj) === '[object Array]';
      },
      isFunction = function(obj) {
          return toStr.call(obj) === '[object Function]';
      },
      noOp = function() {},
      needCheckProps = true,
      testPropObj = { toString : '' };
  
  for(var i in testPropObj) { // fucking ie hasn't toString, valueOf in for
      testPropObj.hasOwnProperty(i) && (needCheckProps = false);
  }
  
  var specProps = needCheckProps? ['toString', 'valueOf'] : null;
  
  function getPropList(obj) {
      var res = objKeys(obj);
      if(needCheckProps) {
          var specProp, i = 0;
          while(specProp = specProps[i++]) {
              obj.hasOwnProperty(specProp) && res.push(specProp);
          }
      }
  
      return res;
  }
  
  function override(base, res, add) {
      var addList = getPropList(add),
          j = 0, len = addList.length,
          name, prop;
      while(j < len) {
          if((name = addList[j++]) === '__self') {
              continue;
          }
          prop = add[name];
          if(isFunction(prop) &&
                  (!hasIntrospection || prop.toString().indexOf('.__base') > -1)) {
              res[name] = (function(name, prop) {
                  var baseMethod = base[name]?
                          base[name] :
                          name === '__constructor'? // case of inheritance from plane function
                              res.__self.__parent :
                              noOp;
                  return function() {
                      var baseSaved = this.__base;
                      this.__base = baseMethod;
                      var res = prop.apply(this, arguments);
                      this.__base = baseSaved;
                      return res;
                  };
              })(name, prop);
          } else {
              res[name] = prop;
          }
      }
  }
  
  function applyMixins(mixins, res) {
      var i = 1, mixin;
      while(mixin = mixins[i++]) {
          res?
              isFunction(mixin)?
                  inherit.self(res, mixin.prototype, mixin) :
                  inherit.self(res, mixin) :
              res = isFunction(mixin)?
                  inherit(mixins[0], mixin.prototype, mixin) :
                  inherit(mixins[0], mixin);
      }
      return res || mixins[0];
  }
  
  /**
  * Creates class
  * @exports
  * @param {Function|Array} [baseClass|baseClassAndMixins] class (or class and mixins) to inherit from
  * @param {Object} prototypeFields
  * @param {Object} [staticFields]
  * @returns {Function} class
  */
  function inherit() {
      var args = arguments,
          withMixins = isArray(args[0]),
          hasBase = withMixins || isFunction(args[0]),
          base = hasBase? withMixins? applyMixins(args[0]) : args[0] : emptyBase,
          props = args[hasBase? 1 : 0] || {},
          staticProps = args[hasBase? 2 : 1],
          res = props.__constructor || (hasBase && base.prototype.__constructor)?
              function() {
                  return this.__constructor.apply(this, arguments);
              } :
              hasBase?
                  function() {
                      return base.apply(this, arguments);
                  } :
                  function() {};
  
      if(!hasBase) {
          res.prototype = props;
          res.prototype.__self = res.prototype.constructor = res;
          return extend(res, staticProps);
      }
  
      extend(res, base);
  
      res.__parent = base;
  
      var basePtp = base.prototype,
          resPtp = res.prototype = objCreate(basePtp);
  
      resPtp.__self = resPtp.constructor = res;
  
      props && override(basePtp, resPtp, props);
      staticProps && override(base, res, staticProps);
  
      return res;
  }
  
  inherit.self = function() {
      var args = arguments,
          withMixins = isArray(args[0]),
          base = withMixins? applyMixins(args[0], args[0][0]) : args[0],
          props = args[1],
          staticProps = args[2],
          basePtp = base.prototype;
  
      props && override(basePtp, basePtp, props);
      staticProps && override(base, base, staticProps);
  
      return base;
  };
  
  var defineAsGlobal = true;
  if(typeof exports === 'object') {
      module.exports = inherit;
      defineAsGlobal = false;
  }
  
  if(typeof modules === 'object') {
      modules.define('inherit', function(provide) {
          provide(inherit);
      });
      defineAsGlobal = false;
  }
  
  if(typeof define === 'function') {
      define(function(require, exports, module) {
          module.exports = inherit;
      });
      defineAsGlobal = false;
  }
  
  defineAsGlobal && (global.inherit = inherit);
  
  })(this);
  
  /* ../../libs/bem-core/common.blocks/inherit/inherit.vanilla.js end */
  ;
  /* ../../libs/bem-core/common.blocks/identify/identify.vanilla.js begin */
  /**
   * @module identify
   */
  
  modules.define('identify', function(provide) {
  
  var counter = 0,
      expando = '__' + (+new Date),
      get = function() {
          return 'uniq' + (++counter);
      };
  
  provide(
      /**
       * Makes unique ID
       * @exports
       * @param {Object} obj Object that needs to be identified
       * @param {Boolean} [onlyGet=false] Return a unique value only if it had already been assigned before
       * @returns {String} ID
       */
      function(obj, onlyGet) {
          if(!obj) return get();
  
          var key = 'uniqueID' in obj? 'uniqueID' : expando; // Use when possible native uniqueID for elements in IE
  
          return onlyGet || key in obj?
              obj[key] :
              obj[key] = get();
      }
  );
  
  });
  
  /* ../../libs/bem-core/common.blocks/identify/identify.vanilla.js end */
  ;
  /* ../../libs/bem-core/common.blocks/next-tick/next-tick.vanilla.js begin */
  /**
   * @module next-tick
   */
  
  modules.define('next-tick', function(provide) {
  
  /**
   * Executes given function on next tick.
   * @exports
   * @type Function
   * @param {Function} fn
   */
  
  var global = this.global,
      fns = [],
      enqueueFn = function(fn) {
          return fns.push(fn) === 1;
      },
      callFns = function() {
          var fnsToCall = fns, i = 0, len = fns.length;
          fns = [];
          while(i < len) {
              fnsToCall[i++]();
          }
      };
  
      /* global process */
      if(typeof process === 'object' && process.nextTick) { // nodejs
          return provide(function(fn) {
              enqueueFn(fn) && process.nextTick(callFns);
          });
      }
  
      if(global.setImmediate) { // ie10
          return provide(function(fn) {
              enqueueFn(fn) && global.setImmediate(callFns);
          });
      }
  
      if(global.postMessage) { // modern browsers
          var isPostMessageAsync = true;
          if(global.attachEvent) {
              var checkAsync = function() {
                      isPostMessageAsync = false;
                  };
              global.attachEvent('onmessage', checkAsync);
              global.postMessage('__checkAsync', '*');
              global.detachEvent('onmessage', checkAsync);
          }
  
          if(isPostMessageAsync) {
              var msg = '__nextTick' + (+new Date),
                  onMessage = function(e) {
                      if(e.data === msg) {
                          e.stopPropagation && e.stopPropagation();
                          callFns();
                      }
                  };
  
              global.addEventListener?
                  global.addEventListener('message', onMessage, true) :
                  global.attachEvent('onmessage', onMessage);
  
              return provide(function(fn) {
                  enqueueFn(fn) && global.postMessage(msg, '*');
              });
          }
      }
  
      var doc = global.document;
      if('onreadystatechange' in doc.createElement('script')) { // ie6-ie8
          var head = doc.getElementsByTagName('head')[0],
              createScript = function() {
                  var script = doc.createElement('script');
                  script.onreadystatechange = function() {
                      script.parentNode.removeChild(script);
                      script = script.onreadystatechange = null;
                      callFns();
                  };
                  head.appendChild(script);
              };
  
          return provide(function(fn) {
              enqueueFn(fn) && createScript();
          });
      }
  
      provide(function(fn) { // old browsers
          enqueueFn(fn) && global.setTimeout(callFns, 0);
      });
  });
  
  /* ../../libs/bem-core/common.blocks/next-tick/next-tick.vanilla.js end */
  ;
  /* ../../libs/bem-core/common.blocks/objects/objects.vanilla.js begin */
  /**
   * @module objects
   * @description A set of helpers to work with JavaScript objects
   */
  
  modules.define('objects', function(provide) {
  
  var hasOwnProp = Object.prototype.hasOwnProperty;
  
  provide(/** @exports */{
      /**
       * Extends a given target by
       * @param {Object} target object to extend
       * @param {...Object} source
       * @returns {Object}
       */
      extend : function(target, source) {
          typeof target !== 'object' && (target = {});
  
          for(var i = 1, len = arguments.length; i < len; i++) {
              var obj = arguments[i];
              if(obj) {
                  for(var key in obj) {
                      hasOwnProp.call(obj, key) && (target[key] = obj[key]);
                  }
              }
          }
  
          return target;
      },
  
      /**
       * Check whether a given object is empty (contains no enumerable properties)
       * @param {Object} obj
       * @returns {Boolean}
       */
      isEmpty : function(obj) {
          for(var key in obj) {
              if(hasOwnProp.call(obj, key)) {
                  return false;
              }
          }
  
          return true;
      },
  
      /**
       * Generic iterator function over object
       * @param {Object} obj object to iterate
       * @param {Function} fn callback
       * @param {Object} [ctx] callbacks's context
       */
      each : function(obj, fn, ctx) {
          for(var key in obj) {
              if(hasOwnProp.call(obj, key)) {
                  ctx? fn.call(ctx, obj[key], key) : fn(obj[key], key);
              }
          }
      }
  });
  
  });
  
  /* ../../libs/bem-core/common.blocks/objects/objects.vanilla.js end */
  ;
  /* ../../libs/bem-core/common.blocks/functions/functions.vanilla.js begin */
  /**
   * @module functions
   * @description A set of helpers to work with JavaScript functions
   */
  
  modules.define('functions', function(provide) {
  
  var toStr = Object.prototype.toString;
  
  provide(/** @exports */{
      /**
       * Checks whether a given object is function
       * @param {*} obj
       * @returns {Boolean}
       */
      isFunction : function(obj) {
          return toStr.call(obj) === '[object Function]';
      },
  
      /**
       * Empty function
       */
      noop : function() {}
  });
  
  });
  
  /* ../../libs/bem-core/common.blocks/functions/functions.vanilla.js end */
  ;
  /* ../../libs/bem-core/common.blocks/events/events.vanilla.js begin */
  /**
   * @module events
   */
  
  modules.define(
      'events',
      ['identify', 'inherit', 'functions'],
      function(provide, identify, inherit, functions) {
  
  var undef,
      storageExpando = '__' + (+new Date) + 'storage',
      getFnId = function(fn, ctx) {
          return identify(fn) + (ctx? identify(ctx) : '');
      },
  
      /**
       * @class Event
       * @exports events:Event
       */
      Event = inherit(/** @lends Event.prototype */{
          /**
           * @constructor
           * @param {String} type
           * @param {Object} target
           */
          __constructor : function(type, target) {
              /**
               * Type
               * @member {String} Event
               */
              this.type = type;
  
              /**
               * Target
               * @member {String} Event
               */
              this.target = target;
  
              /**
               * Result
               * @member {*}
               */
              this.result = undef;
  
              /**
               * Data
               * @member {*}
               */
              this.data = undef;
  
              this._isDefaultPrevented = false;
              this._isPropagationStopped = false;
          },
  
          /**
           * Prevents default action
           */
          preventDefault : function() {
              this._isDefaultPrevented = true;
          },
  
          /**
           * Returns whether is default action prevented
           * @returns {Boolean}
           */
          isDefaultPrevented : function() {
              return this._isDefaultPrevented;
          },
  
          /**
           * Stops propagation
           */
          stopPropagation : function() {
              this._isPropagationStopped = true;
          },
  
          /**
           * Returns whether is propagation stopped
           * @returns {Boolean}
           */
          isPropagationStopped : function() {
              return this._isPropagationStopped;
          }
      }),
  
      /**
       * @lends Emitter
       * @lends Emitter.prototype
       */
      EmitterProps = {
          /**
           * Adds an event handler
           * @param {String} e Event type
           * @param {Object} [data] Additional data that the handler gets as e.data
           * @param {Function} fn Handler
           * @param {Object} [ctx] Handler context
           * @returns {Emitter} this
           */
          on : function(e, data, fn, ctx, _special) {
              if(typeof e === 'string') {
                  if(functions.isFunction(data)) {
                      ctx = fn;
                      fn = data;
                      data = undef;
                  }
  
                  var id = getFnId(fn, ctx),
                      storage = this[storageExpando] || (this[storageExpando] = {}),
                      eventTypes = e.split(' '), eventType,
                      i = 0, list, item,
                      eventStorage;
  
                  while(eventType = eventTypes[i++]) {
                      eventStorage = storage[eventType] || (storage[eventType] = { ids : {}, list : {} });
                      if(!(id in eventStorage.ids)) {
                          list = eventStorage.list;
                          item = { fn : fn, data : data, ctx : ctx, special : _special };
                          if(list.last) {
                              list.last.next = item;
                              item.prev = list.last;
                          } else {
                              list.first = item;
                          }
                          eventStorage.ids[id] = list.last = item;
                      }
                  }
              } else {
                  for(var key in e) {
                      e.hasOwnProperty(key) && this.on(key, e[key], data, _special);
                  }
              }
  
              return this;
          },
  
          /**
           * Adds a one time handler for the event.
           * Handler is executed only the next time the event is fired, after which it is removed.
           * @param {String} e Event type
           * @param {Object} [data] Additional data that the handler gets as e.data
           * @param {Function} fn Handler
           * @param {Object} [ctx] Handler context
           * @returns {Emitter} this
           */
          once : function(e, data, fn, ctx) {
              return this.on(e, data, fn, ctx, { once : true });
          },
  
          /**
           * Removes event handler or handlers
           * @param {String} [e] Event type
           * @param {Function} [fn] Handler
           * @param {Object} [ctx] Handler context
           * @returns {Emitter} this
           */
          un : function(e, fn, ctx) {
              if(typeof e === 'string' || typeof e === 'undefined') {
                  var storage = this[storageExpando];
                  if(storage) {
                      if(e) { // if event type was passed
                          var eventTypes = e.split(' '),
                              i = 0, eventStorage;
                          while(e = eventTypes[i++]) {
                              if(eventStorage = storage[e]) {
                                  if(fn) {  // if specific handler was passed
                                      var id = getFnId(fn, ctx),
                                          ids = eventStorage.ids;
                                      if(id in ids) {
                                          var list = eventStorage.list,
                                              item = ids[id],
                                              prev = item.prev,
                                              next = item.next;
  
                                          if(prev) {
                                              prev.next = next;
                                          } else if(item === list.first) {
                                              list.first = next;
                                          }
  
                                          if(next) {
                                              next.prev = prev;
                                          } else if(item === list.last) {
                                              list.last = prev;
                                          }
  
                                          delete ids[id];
                                      }
                                  } else {
                                      delete this[storageExpando][e];
                                  }
                              }
                          }
                      } else {
                          delete this[storageExpando];
                      }
                  }
              } else {
                  for(var key in e) {
                      e.hasOwnProperty(key) && this.un(key, e[key], fn);
                  }
              }
  
              return this;
          },
  
          /**
           * Fires event handlers
           * @param {String|events:Event} e Event
           * @param {Object} [data] Additional data
           * @returns {Emitter} this
           */
          emit : function(e, data) {
              var storage = this[storageExpando],
                  eventInstantiated = false;
  
              if(storage) {
                  var eventTypes = [typeof e === 'string'? e : e.type, '*'],
                      i = 0, eventType, eventStorage;
                  while(eventType = eventTypes[i++]) {
                      if(eventStorage = storage[eventType]) {
                          var item = eventStorage.list.first,
                              lastItem = eventStorage.list.last,
                              res;
                          while(item) {
                              if(!eventInstantiated) { // instantiate Event only on demand
                                  eventInstantiated = true;
                                  typeof e === 'string' && (e = new Event(e));
                                  e.target || (e.target = this);
                              }
  
                              e.data = item.data;
                              res = item.fn.apply(item.ctx || this, arguments);
                              if(typeof res !== 'undefined') {
                                  e.result = res;
                                  if(res === false) {
                                      e.preventDefault();
                                      e.stopPropagation();
                                  }
                              }
  
                              item.special && item.special.once &&
                                  this.un(e.type, item.fn, item.ctx);
  
                              if(item === lastItem) {
                                  break;
                              }
  
                              item = item.next;
                          }
                      }
                  }
              }
  
              return this;
          }
      },
      /**
       * @class Emitter
       * @exports events:Emitter
       */
      Emitter = inherit(
          EmitterProps,
          EmitterProps);
  
  provide({
      Emitter : Emitter,
      Event : Event
  });
  
  });
  
  /* ../../libs/bem-core/common.blocks/events/events.vanilla.js end */
  ;
  /* ../../libs/bem-core/common.blocks/i-bem/__dom/i-bem__dom.js begin */
  /**
   * @module i-bem__dom
   */
  
  modules.define(
      'i-bem__dom',
      ['i-bem', 'i-bem__internal', 'identify', 'objects', 'functions', 'jquery', 'dom'],
      function(provide, BEM, INTERNAL, identify, objects, functions, $, dom) {
  
  var undef,
      win = $(window),
      doc = $(document),
  
      /**
       * Storage for DOM elements by unique key
       * @type Object
       */
      uniqIdToDomElems = {},
  
      /**
       * Storage for blocks by unique key
       * @type Object
       */
      uniqIdToBlock = {},
  
      /**
       * Storage for DOM element's parent nodes
       * @type Object
       */
      domNodesToParents = {},
  
      /**
       * Storage for block parameters
       * @type Object
       */
      domElemToParams = {},
  
      /**
       * Storage for liveCtx event handlers
       * @type Object
       */
      liveEventCtxStorage = {},
  
      /**
       * Storage for liveClass event handlers
       * @type Object
       */
      liveClassEventStorage = {},
  
      blocks = BEM.blocks,
  
      BEM_CLASS = 'i-bem',
      BEM_SELECTOR = '.' + BEM_CLASS,
      BEM_PARAMS_ATTR = 'data-bem',
  
      NAME_PATTERN = INTERNAL.NAME_PATTERN,
  
      MOD_DELIM = INTERNAL.MOD_DELIM,
      ELEM_DELIM = INTERNAL.ELEM_DELIM,
  
      EXTRACT_MODS_RE = RegExp(
          '[^' + MOD_DELIM + ']' + MOD_DELIM + '(' + NAME_PATTERN + ')' +
          '(?:' + MOD_DELIM + '(' + NAME_PATTERN + '))?$'),
  
      buildModPostfix = INTERNAL.buildModPostfix,
      buildClass = INTERNAL.buildClass,
  
      reverse = Array.prototype.reverse;
  
  /**
   * Initializes blocks on a DOM element
   * @param {jQuery} domElem DOM element
   * @param {String} uniqInitId ID of the "initialization wave"
   */
  function initBlocks(domElem, uniqInitId) {
      var domNode = domElem[0],
          params = getParams(domNode),
          blockName;
  
      for(blockName in params)
          initBlock(
              blockName,
              domElem,
              processParams(params[blockName], blockName, uniqInitId));
  }
  
  /**
   * Initializes a specific block on a DOM element, or returns the existing block if it was already created
   * @param {String} blockName Block name
   * @param {jQuery} domElem DOM element
   * @param {Object} [params] Initialization parameters
   * @param {Boolean} [forceLive=false] Force live initialization
   * @param {Function} [callback] Handler to call after complete initialization
   */
  function initBlock(blockName, domElem, params, forceLive, callback) {
      var domNode = domElem[0];
  
      params || (params = processParams(getBlockParams(domNode, blockName), blockName));
  
      var uniqId = params.uniqId,
          block = uniqIdToBlock[uniqId];
  
      if(block) {
          if(block.domElem.index(domNode) < 0) {
              block.domElem = block.domElem.add(domElem);
              objects.extend(block.params, params);
          }
  
          return block;
      }
  
      uniqIdToDomElems[uniqId] = uniqIdToDomElems[uniqId]?
          uniqIdToDomElems[uniqId].add(domElem) :
          domElem;
  
      var parentDomNode = domNode.parentNode;
      if(!parentDomNode || parentDomNode.nodeType === 11) { // jquery doesn't unique disconnected node
          $.unique(uniqIdToDomElems[uniqId]);
      }
  
      var blockClass = blocks[blockName] || DOM.decl(blockName, {}, { live : true }, true);
      if(!(blockClass._liveInitable = !!blockClass._processLive()) || forceLive || params.live === false) {
          forceLive && domElem.addClass(BEM_CLASS); // add css class for preventing memory leaks in further destructing
  
          block = new blockClass(uniqIdToDomElems[uniqId], params, !!forceLive);
  
          delete uniqIdToDomElems[uniqId];
          callback && callback.apply(block, Array.prototype.slice.call(arguments, 4));
          return block;
      }
  }
  
  /**
   * Processes and adds necessary block parameters
   * @param {Object} params Initialization parameters
   * @param {String} blockName Block name
   * @param {String} [uniqInitId] ID of the "initialization wave"
   */
  function processParams(params, blockName, uniqInitId) {
      params.uniqId ||
          (params.uniqId = (params.id?
              blockName + '-id-' + params.id :
              identify()) + (uniqInitId || identify()));
  
      return params;
  }
  
  /**
   * Helper for searching for a DOM element using a selector inside the context, including the context itself
   * @param {jQuery} ctx Context
   * @param {String} selector CSS selector
   * @param {Boolean} [excludeSelf=false] Exclude context from search
   * @returns {jQuery}
   */
  function findDomElem(ctx, selector, excludeSelf) {
      var res = ctx.find(selector);
      return excludeSelf?
         res :
         res.add(ctx.filter(selector));
  }
  
  /**
   * Returns parameters of a block's DOM element
   * @param {HTMLElement} domNode DOM node
   * @returns {Object}
   */
  function getParams(domNode, blockName) {
      var uniqId = identify(domNode);
      return domElemToParams[uniqId] ||
          (domElemToParams[uniqId] = extractParams(domNode));
  }
  
  /**
   * Returns parameters of a block extracted from DOM node
   * @param {HTMLElement} domNode DOM node
   * @param {String} blockName
   * @returns {Object}
   */
  
  function getBlockParams(domNode, blockName) {
      var params = getParams(domNode);
      return params[blockName] || (params[blockName] = {});
  }
  
  /**
   * Retrieves block parameters from a DOM element
   * @param {HTMLElement} domNode DOM node
   * @returns {Object}
   */
  function extractParams(domNode) {
      var attrVal = domNode.getAttribute(BEM_PARAMS_ATTR);
      return attrVal? JSON.parse(attrVal) : {};
  }
  
  /**
   * Uncouple DOM node from the block. If this is the last node, then destroys the block.
   * @param {BEMDOM} block block
   * @param {HTMLElement} domNode DOM node
   */
  function removeDomNodeFromBlock(block, domNode) {
      block.domElem.length === 1?
          block._destruct() :
          block.domElem = block.domElem.not(domNode);
  }
  
  /**
   * Fills DOM node's parent nodes to the storage
   * @param {jQuery} domElem
   */
  function storeDomNodeParents(domElem) {
      domElem.each(function() {
          domNodesToParents[identify(this)] = this.parentNode;
      });
  }
  
  /**
   * @class BEMDOM
   * @description Base block for creating BEM blocks that have DOM representation
   * @exports
   */
  
  var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
      /**
       * @constructor
       * @private
       * @param {jQuery} domElem DOM element that the block is created on
       * @param {Object} params Block parameters
       * @param {Boolean} [initImmediately=true]
       */
      __constructor : function(domElem, params, initImmediately) {
          /**
           * DOM elements of block
           * @member {jQuery}
           * @readonly
           */
          this.domElem = domElem;
  
          /**
           * Cache for names of events on DOM elements
           * @member {Object}
           * @private
           */
          this._eventNameCache = {};
  
          /**
           * Cache for elements
           * @member {Object}
           * @private
           */
          this._elemCache = {};
  
          /**
           * @member {String} Unique block ID
           * @private
           */
          this._uniqId = params.uniqId;
  
          uniqIdToBlock[this._uniqId] = this;
  
          /**
           * @member {Boolean} Flag for whether it's necessary to unbind from the document and window when destroying the block
           * @private
           */
          this._needSpecialUnbind = false;
  
          this.__base(null, params, initImmediately);
      },
  
      /**
       * Finds blocks inside the current block or its elements (including context)
       * @param {String|jQuery} [elem] Block element
       * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
       * @returns {BEMDOM[]}
       */
      findBlocksInside : function(elem, block) {
          return this._findBlocks('find', elem, block);
      },
  
      /**
       * Finds the first block inside the current block or its elements (including context)
       * @param {String|jQuery} [elem] Block element
       * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
       * @returns {BEMDOM}
       */
      findBlockInside : function(elem, block) {
          return this._findBlocks('find', elem, block, true);
      },
  
      /**
       * Finds blocks outside the current block or its elements (including context)
       * @param {String|jQuery} [elem] Block element
       * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
       * @returns {BEMDOM[]}
       */
      findBlocksOutside : function(elem, block) {
          return this._findBlocks('parents', elem, block);
      },
  
      /**
       * Finds the first block outside the current block or its elements (including context)
       * @param {String|jQuery} [elem] Block element
       * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
       * @returns {BEMDOM}
       */
      findBlockOutside : function(elem, block) {
          return this._findBlocks('closest', elem, block)[0] || null;
      },
  
      /**
       * Finds blocks on DOM elements of the current block or its elements
       * @param {String|jQuery} [elem] Block element
       * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
       * @returns {BEMDOM[]}
       */
      findBlocksOn : function(elem, block) {
          return this._findBlocks('', elem, block);
      },
  
      /**
       * Finds the first block on DOM elements of the current block or its elements
       * @param {String|jQuery} [elem] Block element
       * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
       * @returns {BEMDOM}
       */
      findBlockOn : function(elem, block) {
          return this._findBlocks('', elem, block, true);
      },
  
      _findBlocks : function(select, elem, block, onlyFirst) {
          if(!block) {
              block = elem;
              elem = undef;
          }
  
          var ctxElem = elem?
                  (typeof elem === 'string'? this.findElem(elem) : elem) :
                  this.domElem,
              isSimpleBlock = typeof block === 'string',
              blockName = isSimpleBlock? block : (block.block || block.blockName),
              selector = '.' +
                  (isSimpleBlock?
                      buildClass(blockName) :
                      buildClass(blockName, block.modName, block.modVal)) +
                  (onlyFirst? ':first' : ''),
              domElems = ctxElem.filter(selector);
  
          select && (domElems = domElems.add(ctxElem[select](selector)));
  
          if(onlyFirst) {
              return domElems[0]? initBlock(blockName, domElems.eq(0), undef, true) : null;
          }
  
          var res = [],
              uniqIds = {};
  
          domElems.each(function(i, domElem) {
              var block = initBlock(blockName, $(domElem), undef, true);
              if(!uniqIds[block._uniqId]) {
                  uniqIds[block._uniqId] = true;
                  res.push(block);
              }
          });
  
          return res;
      },
  
      /**
       * Adds an event handler for any DOM element
       * @protected
       * @param {jQuery} domElem DOM element where the event will be listened for
       * @param {String|Object} event Event name or event object
       * @param {Object} [data] Additional event data
       * @param {Function} fn Handler function, which will be executed in the block's context
       * @returns {BEMDOM} this
       */
      bindToDomElem : function(domElem, event, data, fn) {
          if(functions.isFunction(data)) {
              fn = data;
              data = undef;
          }
  
          fn?
              domElem.bind(
                  this._buildEventName(event),
                  data,
                  $.proxy(fn, this)) :
              objects.each(event, function(fn, event) {
                  this.bindToDomElem(domElem, event, data, fn);
              }, this);
  
          return this;
      },
  
      /**
       * Adds an event handler to the document
       * @protected
       * @param {String|Object} event Event name or event object
       * @param {Object} [data] Additional event data
       * @param {Function} fn Handler function, which will be executed in the block's context
       * @returns {BEMDOM} this
       */
      bindToDoc : function(event, data, fn) {
          this._needSpecialUnbind = true;
          return this.bindToDomElem(doc, event, data, fn);
      },
  
      /**
       * Adds an event handler to the window
       * @protected
       * @param {String|Object} event Event name or event object
       * @param {Object} [data] Additional event data
       * @param {Function} fn Handler function, which will be executed in the block's context
       * @returns {BEMDOM} this
       */
      bindToWin : function(event, data, fn) {
          this._needSpecialUnbind = true;
          return this.bindToDomElem(win, event, data, fn);
      },
  
      /**
       * Adds an event handler to the block's main DOM elements or its nested elements
       * @protected
       * @param {jQuery|String} [elem] Element
       * @param {String|Object} event Event name or event object
       * @param {Object} [data] Additional event data
       * @param {Function} fn Handler function, which will be executed in the block's context
       * @returns {BEMDOM} this
       */
      bindTo : function(elem, event, data, fn) {
          var len = arguments.length;
          if(len === 3) {
              if(functions.isFunction(data)) {
                  fn = data;
                  if(typeof event === 'object') {
                      data = event;
                      event = elem;
                      elem = this.domElem;
                  }
              }
          } else if(len === 2) {
              if(functions.isFunction(event)) {
                  fn = event;
                  event = elem;
                  elem = this.domElem;
              } else if(!(typeof elem === 'string' || elem instanceof $)) {
                  data = event;
                  event = elem;
                  elem = this.domElem;
              }
          } else if(len === 1) {
              event = elem;
              elem = this.domElem;
          }
  
          typeof elem === 'string' && (elem = this.elem(elem));
  
          return this.bindToDomElem(elem, event, data, fn);
      },
  
      /**
       * Removes event handlers from any DOM element
       * @protected
       * @param {jQuery} domElem DOM element where the event was being listened for
       * @param {String|Object} event Event name or event object
       * @param {Function} [fn] Handler function
       * @returns {BEMDOM} this
       */
      unbindFromDomElem : function(domElem, event, fn) {
          if(typeof event === 'string') {
              event = this._buildEventName(event);
              fn?
                  domElem.unbind(event, fn) :
                  domElem.unbind(event);
          } else {
              objects.each(event, function(fn, event) {
                  this.unbindFromDomElem(domElem, event, fn);
              }, this);
          }
  
          return this;
      },
  
      /**
       * Removes event handler from document
       * @protected
       * @param {String|Object} event Event name or event object
       * @param {Function} [fn] Handler function
       * @returns {BEMDOM} this
       */
      unbindFromDoc : function(event, fn) {
          return this.unbindFromDomElem(doc, event, fn);
      },
  
      /**
       * Removes event handler from window
       * @protected
       * @param {String|Object} event Event name or event object
       * @param {Function} [fn] Handler function
       * @returns {BEMDOM} this
       */
      unbindFromWin : function(event, fn) {
          return this.unbindFromDomElem(win, event, fn);
      },
  
      /**
       * Removes event handlers from the block's main DOM elements or its nested elements
       * @protected
       * @param {jQuery|String} [elem] Nested element
       * @param {String|Object} event Event name or event object
       * @param {Function} [fn] Handler function
       * @returns {BEMDOM} this
       */
      unbindFrom : function(elem, event, fn) {
          var argLen = arguments.length;
          if(argLen === 1) {
              event = elem;
              elem = this.domElem;
          } else if(argLen === 2 && functions.isFunction(event)) {
              fn = event;
              event = elem;
              elem = this.domElem;
          } else if(typeof elem === 'string') {
              elem = this.elem(elem);
          }
  
          return this.unbindFromDomElem(elem, event, fn);
      },
  
      /**
       * Builds a full name for an event
       * @private
       * @param {String} event Event name
       * @returns {String}
       */
      _buildEventName : function(event) {
          return event.indexOf(' ') > 1?
              event.split(' ').map(function(e) {
                  return this._buildOneEventName(e);
              }, this).join(' ') :
              this._buildOneEventName(event);
      },
  
      /**
       * Builds a full name for a single event
       * @private
       * @param {String} event Event name
       * @returns {String}
       */
      _buildOneEventName : function(event) {
          var eventNameCache = this._eventNameCache;
  
          if(event in eventNameCache) return eventNameCache[event];
  
          var uniq = '.' + this._uniqId;
  
          if(event.indexOf('.') < 0) return eventNameCache[event] = event + uniq;
  
          var lego = '.bem_' + this.__self._name;
  
          return eventNameCache[event] = event.split('.').map(function(e, i) {
              return i === 0? e + lego : lego + '_' + e;
          }).join('') + uniq;
      },
  
      _ctxEmit : function(e, data) {
          this.__base.apply(this, arguments);
  
          var _this = this,
              storage = liveEventCtxStorage[_this.__self._buildCtxEventName(e.type)],
              ctxIds = {};
  
          storage && _this.domElem.each(function(_, ctx) {
              var counter = storage.counter;
              while(ctx && counter) {
                  var ctxId = identify(ctx, true);
                  if(ctxId) {
                      if(ctxIds[ctxId]) break;
                      var storageCtx = storage.ctxs[ctxId];
                      if(storageCtx) {
                          objects.each(storageCtx, function(handler) {
                              handler.fn.call(
                                  handler.ctx || _this,
                                  e,
                                  data);
                          });
                          counter--;
                      }
                      ctxIds[ctxId] = true;
                  }
                  ctx = ctx.parentNode || domNodesToParents[ctxId];
              }
          });
      },
  
      /**
       * Sets a modifier for a block/nested element
       * @param {jQuery} [elem] Nested element
       * @param {String} modName Modifier name
       * @param {String} modVal Modifier value
       * @returns {BEMDOM} this
       */
      setMod : function(elem, modName, modVal) {
          if(elem && typeof modVal !== 'undefined' && elem.length > 1) {
              var _this = this;
              elem.each(function() {
                  var item = $(this);
                  item.__bemElemName = elem.__bemElemName;
                  _this.setMod(item, modName, modVal);
              });
              return _this;
          }
          return this.__base(elem, modName, modVal);
      },
  
      /**
       * Retrieves modifier value from the DOM node's CSS class
       * @private
       * @param {String} modName Modifier name
       * @param {jQuery} [elem] Nested element
       * @param {String} [elemName] Name of the nested element
       * @returns {String} Modifier value
       */
      _extractModVal : function(modName, elem, elemName) {
          var domNode = (elem || this.domElem)[0],
              matches;
  
          domNode &&
              (matches = domNode.className
                  .match(this.__self._buildModValRE(modName, elemName || elem)));
  
          return matches? matches[2] || true : '';
      },
  
      /**
       * Retrieves a name/value list of modifiers
       * @private
       * @param {Array} [modNames] Names of modifiers
       * @param {Object} [elem] Element
       * @returns {Object} Hash of modifier values by names
       */
      _extractMods : function(modNames, elem) {
          var res = {},
              extractAll = !modNames.length,
              countMatched = 0;
  
          ((elem || this.domElem)[0].className
              .match(this.__self._buildModValRE(
                  '(' + (extractAll? NAME_PATTERN : modNames.join('|')) + ')',
                  elem,
                  'g')) || []).forEach(function(className) {
                      var matches = className.match(EXTRACT_MODS_RE);
                      res[matches[1]] = matches[2] || true;
                      ++countMatched;
                  });
  
          // empty modifier values are not reflected in classes; they must be filled with empty values
          countMatched < modNames.length && modNames.forEach(function(modName) {
              modName in res || (res[modName] = '');
          });
  
          return res;
      },
  
      /**
       * Sets a modifier's CSS class for a block's DOM element or nested element
       * @private
       * @param {String} modName Modifier name
       * @param {String} modVal Modifier value
       * @param {String} oldModVal Old modifier value
       * @param {jQuery} [elem] Element
       * @param {String} [elemName] Element name
       */
      _onSetMod : function(modName, modVal, oldModVal, elem, elemName) {
          if(modName !== 'js' || modVal !== '') {
              var _self = this.__self,
                  classPrefix = _self._buildModClassPrefix(modName, elemName),
                  classRE = _self._buildModValRE(modName, elemName),
                  needDel = modVal === '' || modVal === false;
  
              (elem || this.domElem).each(function() {
                  var className = this.className,
                      modClassName = classPrefix;
  
                  modVal !== true && (modClassName += MOD_DELIM + modVal);
  
                  (oldModVal === true?
                      classRE.test(className) :
                      className.indexOf(classPrefix + MOD_DELIM) > -1)?
                          this.className = className.replace(
                              classRE,
                              (needDel? '' : '$1' + modClassName)) :
                          needDel || $(this).addClass(modClassName);
              });
  
              elemName && this
                  .dropElemCache(elemName, modName, oldModVal)
                  .dropElemCache(elemName, modName, modVal);
          }
  
          this.__base.apply(this, arguments);
      },
  
      /**
       * Finds elements nested in a block
       * @param {jQuery} [ctx=this.domElem] Element where search is being performed
       * @param {String} names Nested element name (or names separated by spaces)
       * @param {String} [modName] Modifier name
       * @param {String} [modVal] Modifier value
       * @param {Boolean} [strictMode=false]
       * @returns {jQuery} DOM elements
       */
      findElem : function(ctx, names, modName, modVal, strictMode) {
          if(typeof ctx === 'string') {
              strictMode = modVal;
              modVal = modName;
              modName = names;
              names = ctx;
              ctx = this.domElem;
          }
  
          if(typeof modName === 'boolean') {
              strictMode = modName;
              modName = undef;
          }
  
          var _self = this.__self,
              selector = '.' +
                  names.split(' ').map(function(name) {
                      return _self.buildClass(name, modName, modVal);
                  }).join(',.'),
              res = findDomElem(ctx, selector);
  
          return strictMode? this._filterFindElemResults(res) : res;
      },
  
      /**
       * Filters results of findElem helper execution in strict mode
       * @param {jQuery} res DOM elements
       * @returns {jQuery} DOM elements
       */
      _filterFindElemResults : function(res) {
          var blockSelector = this.buildSelector(),
              domElem = this.domElem;
          return res.filter(function() {
              return domElem.index($(this).closest(blockSelector)) > -1;
          });
      },
  
      /**
       * Finds elements nested in a block
       * @private
       * @param {String} name Nested element name
       * @param {String} [modName] Modifier name
       * @param {String} [modVal] Modifier value
       * @returns {jQuery} DOM elements
       */
      _elem : function(name, modName, modVal) {
          var key = name + buildModPostfix(modName, modVal),
              res;
  
          if(!(res = this._elemCache[key])) {
              res = this._elemCache[key] = this.findElem(name, modName, modVal);
              res.__bemElemName = name;
          }
  
          return res;
      },
  
      /**
       * Lazy search for elements nested in a block (caches results)
       * @param {String} names Nested element name (or names separated by spaces)
       * @param {String} [modName] Modifier name
       * @param {String} [modVal] Modifier value
       * @returns {jQuery} DOM elements
       */
      elem : function(names, modName, modVal) {
          if(modName && typeof modName !== 'string') {
              modName.__bemElemName = names;
              return modName;
          }
  
          if(names.indexOf(' ') < 0) {
              return this._elem(names, modName, modVal);
          }
  
          var res = $([]);
          names.split(' ').forEach(function(name) {
              res = res.add(this._elem(name, modName, modVal));
          }, this);
          return res;
      },
  
      /**
       * Finds elements outside the context
       * @param {jQuery} ctx context
       * @param {String} elemName Element name
       * @returns {jQuery} DOM elements
       */
      closestElem : function(ctx, elemName) {
          return ctx.closest(this.buildSelector(elemName));
      },
  
      /**
       * Clearing the cache for elements
       * @protected
       * @param {String} [names] Nested element name (or names separated by spaces)
       * @param {String} [modName] Modifier name
       * @param {String} [modVal] Modifier value
       * @returns {BEMDOM} this
       */
      dropElemCache : function(names, modName, modVal) {
          if(names) {
              var modPostfix = buildModPostfix(modName, modVal);
              names.indexOf(' ') < 0?
                  delete this._elemCache[names + modPostfix] :
                  names.split(' ').forEach(function(name) {
                      delete this._elemCache[name + modPostfix];
                  }, this);
          } else {
              this._elemCache = {};
          }
  
          return this;
      },
  
      /**
       * Retrieves parameters of a block element
       * @param {String|jQuery} elem Element
       * @returns {Object} Parameters
       */
      elemParams : function(elem) {
          var elemName;
          if(typeof elem === 'string') {
              elemName = elem;
              elem = this.elem(elem);
          } else {
              elemName = this.__self._extractElemNameFrom(elem);
          }
  
          return extractParams(elem[0])[this.__self.buildClass(elemName)] || {};
      },
  
      /**
       * Elemify given element
       * @param {jQuery} elem Element
       * @param {String} elemName Name
       * @returns {jQuery}
       */
      elemify : function(elem, elemName) {
          (elem = $(elem)).__bemElemName = elemName;
          return elem;
      },
  
      /**
       * Checks whether a DOM element is in a block
       * @protected
       * @param {jQuery} [ctx=this.domElem] Element where check is being performed
       * @param {jQuery} domElem DOM element
       * @returns {Boolean}
       */
      containsDomElem : function(ctx, domElem) {
          if(arguments.length === 1) {
              domElem = ctx;
              ctx = this.domElem;
          }
  
          return dom.contains(ctx, domElem);
      },
  
      /**
       * Builds a CSS selector corresponding to a block/element and modifier
       * @param {String} [elem] Element name
       * @param {String} [modName] Modifier name
       * @param {String} [modVal] Modifier value
       * @returns {String}
       */
      buildSelector : function(elem, modName, modVal) {
          return this.__self.buildSelector(elem, modName, modVal);
      },
  
      /**
       * Destructs a block
       * @private
       */
      _destruct : function() {
          var _this = this,
              _self = _this.__self;
  
          _this._needSpecialUnbind && _self.doc.add(_self.win).unbind('.' + _this._uniqId);
  
          _this.__base();
  
          delete uniqIdToBlock[_this.un()._uniqId];
      }
  
  }, /** @lends BEMDOM */{
  
      /**
       * Scope, will be set on onDomReady to `<body>`
       * @type jQuery
       */
      scope : null,
  
      /**
       * Document shortcut
       * @type jQuery
       */
      doc : doc,
  
      /**
       * Window shortcut
       * @type jQuery
       */
      win : win,
  
      /**
       * Processes a block's live properties
       * @private
       * @param {Boolean} [heedLive=false] Whether to take into account that the block already processed its live properties
       * @returns {Boolean} Whether the block is a live block
       */
      _processLive : function(heedLive) {
          var res = this._liveInitable;
  
          if('live' in this) {
              var noLive = typeof res === 'undefined';
  
              if(noLive ^ heedLive) { // should be opposite to each other
                  res = this.live() !== false;
  
                  var blockName = this.getName(),
                      origLive = this.live;
  
                  this.live = function() {
                      return this.getName() === blockName?
                          res :
                          origLive.apply(this, arguments);
                  };
              }
          }
  
          return res;
      },
  
      /**
       * Initializes blocks on a fragment of the DOM tree
       * @param {jQuery|String} [ctx=scope] Root DOM node
       * @returns {jQuery} ctx Initialization context
       */
      init : function(ctx) {
          if(typeof ctx === 'string') {
              ctx = $(ctx);
          } else if(!ctx) ctx = DOM.scope;
  
          var uniqInitId = identify();
          findDomElem(ctx, BEM_SELECTOR).each(function() {
              initBlocks($(this), uniqInitId);
          });
  
          this._runInitFns();
  
          return ctx;
      },
  
      /**
       * Destroys blocks on a fragment of the DOM tree
       * @param {jQuery} ctx Root DOM node
       * @param {Boolean} [excludeSelf=false] Exclude the main domElem
       */
      destruct : function(ctx, excludeSelf) {
          var _ctx;
          if(excludeSelf) {
              storeDomNodeParents(_ctx = ctx.children());
              ctx.empty();
          } else {
              storeDomNodeParents(_ctx = ctx);
              ctx.remove();
          }
  
          reverse.call(findDomElem(_ctx, BEM_SELECTOR)).each(function(_, domNode) {
              var params = getParams(domNode);
              objects.each(params, function(blockParams) {
                  if(blockParams.uniqId) {
                      var block = uniqIdToBlock[blockParams.uniqId];
                      block?
                          removeDomNodeFromBlock(block, domNode) :
                          delete uniqIdToDomElems[blockParams.uniqId];
                  }
              });
              delete domElemToParams[identify(domNode)];
          });
  
          // flush parent nodes storage that has been filled above
          domNodesToParents = {};
      },
  
      /**
       * Replaces a fragment of the DOM tree inside the context, destroying old blocks and intializing new ones
       * @param {jQuery} ctx Root DOM node
       * @param {jQuery|String} content New content
       * @returns {jQuery} Updated root DOM node
       */
      update : function(ctx, content) {
          this.destruct(ctx, true);
          return this.init(ctx.html(content));
      },
  
      /**
       * Changes a fragment of the DOM tree including the context and initializes blocks.
       * @param {jQuery} ctx Root DOM node
       * @param {jQuery|String} content Content to be added
       * @returns {jQuery} New content
       */
      replace : function(ctx, content) {
          var prev = ctx.prev(),
              parent = ctx.parent();
  
          this.destruct(ctx);
  
          return this.init(prev.length?
              $(content).insertAfter(prev) :
              $(content).prependTo(parent));
      },
  
      /**
       * Adds a fragment of the DOM tree at the end of the context and initializes blocks
       * @param {jQuery} ctx Root DOM node
       * @param {jQuery|String} content Content to be added
       * @returns {jQuery} New content
       */
      append : function(ctx, content) {
          return this.init($(content).appendTo(ctx));
      },
  
      /**
       * Adds a fragment of the DOM tree at the beginning of the context and initializes blocks
       * @param {jQuery} ctx Root DOM node
       * @param {jQuery|String} content Content to be added
       * @returns {jQuery} New content
       */
      prepend : function(ctx, content) {
          return this.init($(content).prependTo(ctx));
      },
  
      /**
       * Adds a fragment of the DOM tree before the context and initializes blocks
       * @param {jQuery} ctx Contextual DOM node
       * @param {jQuery|String} content Content to be added
       * @returns {jQuery} New content
       */
      before : function(ctx, content) {
          return this.init($(content).insertBefore(ctx));
      },
  
      /**
       * Adds a fragment of the DOM tree after the context and initializes blocks
       * @param {jQuery} ctx Contextual DOM node
       * @param {jQuery|String} content Content to be added
       * @returns {jQuery} New content
       */
      after : function(ctx, content) {
          return this.init($(content).insertAfter(ctx));
      },
  
      /**
       * Builds a full name for a live event
       * @private
       * @param {String} e Event name
       * @returns {String}
       */
      _buildCtxEventName : function(e) {
          return this._name + ':' + e;
      },
  
      _liveClassBind : function(className, e, callback, invokeOnInit) {
          if(e.indexOf(' ') > -1) {
              e.split(' ').forEach(function(e) {
                  this._liveClassBind(className, e, callback, invokeOnInit);
              }, this);
          } else {
              var storage = liveClassEventStorage[e],
                  uniqId = identify(callback);
  
              if(!storage) {
                  storage = liveClassEventStorage[e] = {};
                  DOM.scope.bind(e, $.proxy(this._liveClassTrigger, this));
              }
  
              storage = storage[className] || (storage[className] = { uniqIds : {}, fns : [] });
  
              if(!(uniqId in storage.uniqIds)) {
                  storage.fns.push({ uniqId : uniqId, fn : this._buildLiveEventFn(callback, invokeOnInit) });
                  storage.uniqIds[uniqId] = storage.fns.length - 1;
              }
          }
  
          return this;
      },
  
      _liveClassUnbind : function(className, e, callback) {
          var storage = liveClassEventStorage[e];
          if(storage) {
              if(callback) {
                  if(storage = storage[className]) {
                      var uniqId = identify(callback);
                      if(uniqId in storage.uniqIds) {
                          var i = storage.uniqIds[uniqId],
                              len = storage.fns.length - 1;
                          storage.fns.splice(i, 1);
                          while(i < len) storage.uniqIds[storage.fns[i++].uniqId] = i - 1;
                          delete storage.uniqIds[uniqId];
                      }
                  }
              } else {
                  delete storage[className];
              }
          }
  
          return this;
      },
  
      _liveClassTrigger : function(e) {
          var storage = liveClassEventStorage[e.type];
          if(storage) {
              var node = e.target, classNames = [];
              for(var className in storage) {
                  classNames.push(className);
              }
              do {
                  var nodeClassName = ' ' + node.className + ' ', i = 0;
                  while(className = classNames[i++]) {
                      if(nodeClassName.indexOf(' ' + className + ' ') > -1) {
                          var j = 0, fns = storage[className].fns, fn, stopPropagationAndPreventDefault = false;
                          while(fn = fns[j++])
                              if(fn.fn.call($(node), e) === false) stopPropagationAndPreventDefault = true;
  
                          stopPropagationAndPreventDefault && e.preventDefault();
                          if(stopPropagationAndPreventDefault || e.isPropagationStopped()) return;
  
                          classNames.splice(--i, 1);
                      }
                  }
              } while(classNames.length && (node = node.parentNode));
          }
      },
  
      _buildLiveEventFn : function(callback, invokeOnInit) {
          var _this = this;
          return function(e) {
              e.currentTarget = this;
              var args = [
                      _this._name,
                      $(this).closest(_this.buildSelector()),
                      undef,
                      true
                  ],
                  block = initBlock.apply(null, invokeOnInit? args.concat([callback, e]) : args);
  
              if(block && !invokeOnInit && callback)
                  return callback.apply(block, arguments);
          };
      },
  
      /**
       * Helper for live initialization for an event on DOM elements of a block or its elements
       * @protected
       * @param {String} [elemName] Element name or names (separated by spaces)
       * @param {String} event Event name
       * @param {Function} [callback] Handler to call after successful initialization
       */
      liveInitOnEvent : function(elemName, event, callback) {
          return this.liveBindTo(elemName, event, callback, true);
      },
  
      /**
       * Helper for subscribing to live events on DOM elements of a block or its elements
       * @protected
       * @param {String|Object} [to] Description (object with modName, modVal, elem) or name of the element or elements (space-separated)
       * @param {String} event Event name
       * @param {Function} [callback] Handler
       */
      liveBindTo : function(to, event, callback, invokeOnInit) {
          if(!event || functions.isFunction(event)) {
              callback = event;
              event = to;
              to = undef;
          }
  
          if(!to || typeof to === 'string') {
              to = { elem : to };
          }
  
          if(to.elem && to.elem.indexOf(' ') > 0) {
              to.elem.split(' ').forEach(function(elem) {
                  this._liveClassBind(
                      this.buildClass(elem, to.modName, to.modVal),
                      event,
                      callback,
                      invokeOnInit);
              }, this);
              return this;
          }
  
          return this._liveClassBind(
              this.buildClass(to.elem, to.modName, to.modVal),
              event,
              callback,
              invokeOnInit);
      },
  
      /**
       * Helper for unsubscribing from live events on DOM elements of a block or its elements
       * @protected
       * @param {String} [elem] Name of the element or elements (space-separated)
       * @param {String} event Event name
       * @param {Function} [callback] Handler
       */
      liveUnbindFrom : function(elem, event, callback) {
  
          if(!event || functions.isFunction(event)) {
              callback = event;
              event = elem;
              elem = undef;
          }
  
          if(elem && elem.indexOf(' ') > 1) {
              elem.split(' ').forEach(function(elem) {
                  this._liveClassUnbind(
                      this.buildClass(elem),
                      event,
                      callback);
              }, this);
              return this;
          }
  
          return this._liveClassUnbind(
              this.buildClass(elem),
              event,
              callback);
      },
  
      /**
       * Helper for live initialization when a different block is initialized
       * @private
       * @param {String} event Event name
       * @param {String} blockName Name of the block that should trigger a reaction when initialized
       * @param {Function} callback Handler to be called after successful initialization in the new block's context
       * @param {String} findFnName Name of the method for searching
       */
      _liveInitOnBlockEvent : function(event, blockName, callback, findFnName) {
          var name = this._name;
          blocks[blockName].on(event, function(e) {
              var args = arguments,
                  blocks = e.target[findFnName](name);
  
              callback && blocks.forEach(function(block) {
                  callback.apply(block, args);
              });
          });
          return this;
      },
  
      /**
       * Helper for live initialization for a different block's event on the current block's DOM element
       * @protected
       * @param {String} event Event name
       * @param {String} blockName Name of the block that should trigger a reaction when initialized
       * @param {Function} callback Handler to be called after successful initialization in the new block's context
       */
      liveInitOnBlockEvent : function(event, blockName, callback) {
          return this._liveInitOnBlockEvent(event, blockName, callback, 'findBlocksOn');
      },
  
      /**
       * Helper for live initialization for a different block's event inside the current block
       * @protected
       * @param {String} event Event name
       * @param {String} blockName Name of the block that should trigger a reaction when initialized
       * @param {Function} [callback] Handler to be called after successful initialization in the new block's context
       */
      liveInitOnBlockInsideEvent : function(event, blockName, callback) {
          return this._liveInitOnBlockEvent(event, blockName, callback, 'findBlocksOutside');
      },
  
      /**
       * Adds a live event handler to a block, based on a specified element where the event will be listened for
       * @param {jQuery} [ctx] The element in which the event will be listened for
       * @param {String} e Event name
       * @param {Object} [data] Additional information that the handler gets as e.data
       * @param {Function} fn Handler
       * @param {Object} [fnCtx] Handler's context
       */
      on : function(ctx, e, data, fn, fnCtx) {
          return typeof ctx === 'object' && ctx.jquery?
              this._liveCtxBind(ctx, e, data, fn, fnCtx) :
              this.__base(ctx, e, data, fn);
      },
  
      /**
       * Removes the live event handler from a block, based on a specified element where the event was being listened for
       * @param {jQuery} [ctx] The element in which the event was being listened for
       * @param {String} e Event name
       * @param {Function} [fn] Handler
       * @param {Object} [fnCtx] Handler context
       */
      un : function(ctx, e, fn, fnCtx) {
          return typeof ctx === 'object' && ctx.jquery?
              this._liveCtxUnbind(ctx, e, fn, fnCtx) :
              this.__base(ctx, e, fn);
      },
  
      /**
       * Adds a live event handler to a block, based on a specified element where the event will be listened for
       * @private
       * @param {jQuery} ctx The element in which the event will be listened for
       * @param {String} e  Event name
       * @param {Object} [data] Additional information that the handler gets as e.data
       * @param {Function} fn Handler
       * @param {Object} [fnCtx] Handler context
       * @returns {BEMDOM} this
       */
      _liveCtxBind : function(ctx, e, data, fn, fnCtx) {
          if(typeof e === 'object') {
              if(functions.isFunction(data) || functions.isFunction(fn)) { // mod change event
                  e = this._buildModEventName(e);
              } else {
                  objects.each(e, function(fn, e) {
                      this._liveCtxBind(ctx, e, fn, data);
                  }, this);
                  return this;
              }
          }
  
          if(functions.isFunction(data)) {
              fnCtx = fn;
              fn = data;
              data = undef;
          }
  
          if(e.indexOf(' ') > -1) {
              e.split(' ').forEach(function(e) {
                  this._liveCtxBind(ctx, e, data, fn, fnCtx);
              }, this);
          } else {
              var ctxE = this._buildCtxEventName(e),
                  storage = liveEventCtxStorage[ctxE] ||
                      (liveEventCtxStorage[ctxE] = { counter : 0, ctxs : {} });
  
              ctx.each(function() {
                  var ctxId = identify(this),
                      ctxStorage = storage.ctxs[ctxId];
                  if(!ctxStorage) {
                      ctxStorage = storage.ctxs[ctxId] = {};
                      ++storage.counter;
                  }
                  ctxStorage[identify(fn) + (fnCtx? identify(fnCtx) : '')] = {
                      fn : fn,
                      data : data,
                      ctx : fnCtx
                  };
              });
          }
  
          return this;
      },
  
      /**
       * Removes a live event handler from a block, based on a specified element where the event was being listened for
       * @private
       * @param {jQuery} ctx The element in which the event was being listened for
       * @param {String|Object} e Event name
       * @param {Function} [fn] Handler
       * @param {Object} [fnCtx] Handler context
       */
      _liveCtxUnbind : function(ctx, e, fn, fnCtx) {
          if(typeof e === 'object' && functions.isFunction(fn)) { // mod change event
              e = this._buildModEventName(e);
          }
  
          var storage = liveEventCtxStorage[e = this._buildCtxEventName(e)];
  
          if(storage) {
              ctx.each(function() {
                  var ctxId = identify(this, true),
                      ctxStorage;
                  if(ctxId && (ctxStorage = storage.ctxs[ctxId])) {
                      fn && delete ctxStorage[identify(fn) + (fnCtx? identify(fnCtx) : '')];
                      if(!fn || objects.isEmpty(ctxStorage)) {
                          storage.counter--;
                          delete storage.ctxs[ctxId];
                      }
                  }
              });
              storage.counter || delete liveEventCtxStorage[e];
          }
  
          return this;
      },
  
      /**
       * Retrieves the name of an element nested in a block
       * @private
       * @param {jQuery} elem Nested element
       * @returns {String|undef}
       */
      _extractElemNameFrom : function(elem) {
          if(elem.__bemElemName) return elem.__bemElemName;
  
          var matches = elem[0].className.match(this._buildElemNameRE());
          return matches? matches[1] : undef;
      },
  
      /**
       * Builds a prefix for the CSS class of a DOM element or nested element of the block, based on modifier name
       * @private
       * @param {String} modName Modifier name
       * @param {jQuery|String} [elem] Element
       * @returns {String}
       */
      _buildModClassPrefix : function(modName, elem) {
          return this._name +
                 (elem?
                     ELEM_DELIM + (typeof elem === 'string'? elem : this._extractElemNameFrom(elem)) :
                     '') +
                 MOD_DELIM + modName;
      },
  
      /**
       * Builds a regular expression for extracting modifier values from a DOM element or nested element of a block
       * @private
       * @param {String} modName Modifier name
       * @param {jQuery|String} [elem] Element
       * @param {String} [quantifiers] Regular expression quantifiers
       * @returns {RegExp}
       */
      _buildModValRE : function(modName, elem, quantifiers) {
          return new RegExp(
              '(\\s|^)' +
              this._buildModClassPrefix(modName, elem) +
              '(?:' + MOD_DELIM + '(' + NAME_PATTERN + '))?(?=\\s|$)',
              quantifiers);
      },
  
      /**
       * Builds a regular expression for extracting names of elements nested in a block
       * @private
       * @returns {RegExp}
       */
      _buildElemNameRE : function() {
          return new RegExp(this._name + ELEM_DELIM + '(' + NAME_PATTERN + ')(?:\\s|$)');
      },
  
      /**
       * Builds a CSS class corresponding to the block/element and modifier
       * @param {String} [elem] Element name
       * @param {String} [modName] Modifier name
       * @param {String} [modVal] Modifier value
       * @returns {String}
       */
      buildClass : function(elem, modName, modVal) {
          return buildClass(this._name, elem, modName, modVal);
      },
  
      /**
       * Builds a CSS selector corresponding to the block/element and modifier
       * @param {String} [elem] Element name
       * @param {String} [modName] Modifier name
       * @param {String} [modVal] Modifier value
       * @returns {String}
       */
      buildSelector : function(elem, modName, modVal) {
          return '.' + this.buildClass(elem, modName, modVal);
      }
  });
  
  /**
   * Returns a block on a DOM element and initializes it if necessary
   * @param {String} blockName Block name
   * @param {Object} params Block parameters
   * @returns {BEMDOM}
   */
  $.fn.bem = function(blockName, params) {
      return initBlock(blockName, this, params, true);
  };
  
  // Set default scope after DOM ready
  $(function() {
      DOM.scope = $('body');
  });
  
  provide(DOM);
  
  });
  
  (function() {
  
  var origDefine = modules.define;
  
  modules.define = function(name, deps, decl) {
      origDefine.apply(modules, arguments);
  
      name !== 'i-bem__dom_init' && arguments.length > 2 && ~deps.indexOf('i-bem__dom') &&
          modules.define('i-bem__dom_init', [name], function(provide, _, prev) {
              provide(prev);
          });
  };
  
  })();
  
  /* ../../libs/bem-core/common.blocks/i-bem/__dom/i-bem__dom.js end */
  ;
  /* ../../libs/bem-core/common.blocks/jquery/jquery.js begin */
  /**
   * @module jquery
   * @description Provide jQuery (load if it does not exist).
   */
  
  modules.define(
      'jquery',
      ['loader_type_js', 'jquery__config'],
      function(provide, loader, cfg) {
  
  /* global jQuery */
  
  function doProvide(preserveGlobal) {
      /**
       * @exports
       * @type Function
       */
      provide(preserveGlobal? jQuery : jQuery.noConflict(true));
  }
  
  typeof jQuery !== 'undefined'?
      doProvide(true) :
      loader(cfg.url, doProvide);
  });
  
  /* ../../libs/bem-core/common.blocks/jquery/jquery.js end */
  ;
  /* ../../libs/bem-core/common.blocks/loader/_type/loader_type_js.js begin */
  /**
   * @module loader_type_js
   * @description Load JS from external URL.
   */
  
  modules.define('loader_type_js', function(provide) {
  
  var loading = {},
      loaded = {},
      head = document.getElementsByTagName('head')[0],
      onLoad = function(path) {
          loaded[path] = true;
          var cbs = loading[path], cb, i = 0;
          delete loading[path];
          while(cb = cbs[i++]) {
              cb();
          }
      };
  
  provide(
      /**
       * @exports
       * @param {String} path resource link
       * @param {Function} cb executes when resource is loaded
       */
      function(path, cb) {
          if(loaded[path]) {
              cb();
              return;
          }
  
          if(loading[path]) {
              loading[path].push(cb);
              return;
          }
  
          loading[path] = [cb];
  
          var script = document.createElement('script');
          script.type = 'text/javascript';
          script.charset = 'utf-8';
          script.src = (location.protocol === 'file:' && !path.indexOf('//')? 'http:' : '') + path;
          script.onreadystatechange === null?
              script.onreadystatechange = function() {
                  var readyState = this.readyState;
                  if(readyState === 'loaded' || readyState === 'complete') {
                      script.onreadystatechange = null;
                      onLoad(path);
                  }
              } :
              script.onload = script.onerror = function() {
                  script.onload = script.onerror = null;
                  onLoad(path);
              };
  
          head.insertBefore(script, head.lastChild);
      }
  );
  
  });
  
  /* ../../libs/bem-core/common.blocks/loader/_type/loader_type_js.js end */
  ;
  /* ../../libs/bem-core/common.blocks/jquery/__config/jquery__config.js begin */
  /**
   * @module jquery__config
   * @description Configuration for jQuery
   */
  
  modules.define('jquery__config', function(provide) {
  
  provide(/** @exports */{
      /**
       * URL for loading jQuery if it does not exist
       */
      url : '//yastatic.net/jquery/2.1.1/jquery.min.js'
  });
  
  });
  
  /* ../../libs/bem-core/common.blocks/jquery/__config/jquery__config.js end */
  ;
  /* ../../libs/bem-core/desktop.blocks/jquery/__config/jquery__config.js begin */
  /**
   * @module jquery__config
   * @description Configuration for jQuery
   */
  
  modules.define(
      'jquery__config',
      ['ua', 'objects'],
      function(provide, ua, objects, base) {
  
  provide(
      ua.msie && parseInt(ua.version, 10) < 9?
          objects.extend(
              base,
              {
                  url : '//yastatic.net/jquery/1.11.1/jquery.min.js'
              }) :
          base);
  
  });
  
  /* ../../libs/bem-core/desktop.blocks/jquery/__config/jquery__config.js end */
  ;
  /* ../../libs/bem-core/desktop.blocks/ua/ua.js begin */
  /** 
   * @module ua
   * @description Detect some user agent features (works like jQuery.browser in jQuery 1.8)
   * @see http://code.jquery.com/jquery-migrate-1.1.1.js
   */
  
  modules.define('ua', function(provide) {
  
  var ua = navigator.userAgent.toLowerCase(),
      match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
          /(webkit)[ \/]([\w.]+)/.exec(ua) ||
          /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
          /(msie) ([\w.]+)/.exec(ua) ||
          ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
          [],
      matched = {
          browser : match[1] || '',
          version : match[2] || '0'
      },
      browser = {};
  
  if(matched.browser) {
      browser[matched.browser] = true;
      browser.version = matched.version;
  }
  
  if(browser.chrome) {
      browser.webkit = true;
  } else if(browser.webkit) {
      browser.safari = true;
  }
  
  /**
   * @exports
   * @type Object
   */
  provide(browser);
  
  });
  
  /* ../../libs/bem-core/desktop.blocks/ua/ua.js end */
  ;
  /* ../../libs/bem-core/common.blocks/dom/dom.js begin */
  /**
   * @module dom
   * @description some DOM utils
   */
  
  modules.define('dom', ['jquery'], function(provide, $) {
  
  provide(/** @exports */{
      /**
       * Checks whether a DOM elem is in a context
       * @param {jQuery} ctx DOM elem where check is being performed
       * @param {jQuery} domElem DOM elem to check
       * @returns {Boolean}
       */
      contains : function(ctx, domElem) {
          var res = false;
  
          domElem.each(function() {
              var domNode = this;
              do {
                  if(~ctx.index(domNode)) return !(res = true);
              } while(domNode = domNode.parentNode);
  
              return res;
          });
  
          return res;
      },
  
      /**
       * Returns current focused DOM elem in document
       * @returns {jQuery}
       */
      getFocused : function() {
          // "Error: Unspecified error." in iframe in IE9
          try { return $(document.activeElement); } catch(e) {}
      },
  
      /**
       * Checks whether a DOM element contains focus
       * @param {jQuery} domElem
       * @returns {Boolean}
       */
      containsFocus : function(domElem) {
          return this.contains(domElem, this.getFocused());
      },
  
      /**
      * Checks whether a browser currently can set focus on DOM elem
      * @param {jQuery} domElem
      * @returns {Boolean}
      */
      isFocusable : function(domElem) {
          var domNode = domElem[0];
  
          if(!domNode) return false;
          if(domNode.hasAttribute('tabindex')) return true;
  
          switch(domNode.tagName.toLowerCase()) {
              case 'iframe':
                  return true;
  
              case 'input':
              case 'button':
              case 'textarea':
              case 'select':
                  return !domNode.disabled;
  
              case 'a':
                  return !!domNode.href;
          }
  
          return false;
      },
  
      /**
      * Checks whether a domElem is intended to edit text
      * @param {jQuery} domElem
      * @returns {Boolean}
      */
      isEditable : function(domElem) {
          var domNode = domElem[0];
  
          if(!domNode) return false;
  
          switch(domNode.tagName.toLowerCase()) {
              case 'input':
                  var type = domNode.type;
                  return (type === 'text' || type === 'password') && !domNode.disabled && !domNode.readOnly;
  
              case 'textarea':
                  return !domNode.disabled && !domNode.readOnly;
  
              default:
                  return domNode.contentEditable === 'true';
          }
      }
  });
  
  });
  
  /* ../../libs/bem-core/common.blocks/dom/dom.js end */
  ;
  /* ../../libs/bem-core/common.blocks/i-bem/__dom/_init/i-bem__dom_init.js begin */
  /**
   * @module i-bem__dom_init
   */
  
  modules.define('i-bem__dom_init', ['i-bem__dom'], function(provide, BEMDOM) {
  
  provide(
      /**
       * Initializes blocks on a fragment of the DOM tree
       * @exports
       * @param {jQuery} [ctx=scope] Root DOM node
       * @returns {jQuery} ctx Initialization context
       */
      function(ctx) {
          return BEMDOM.init(ctx);
      });
  });
  
  /* ../../libs/bem-core/common.blocks/i-bem/__dom/_init/i-bem__dom_init.js end */
  ;
  /* ../../libs/bem-core/common.blocks/i-bem/__dom/_init/i-bem__dom_init_auto.js begin */
  /**
   * Auto initialization on DOM ready
   */
  
  modules.require(
      ['i-bem__dom_init', 'jquery', 'next-tick'],
      function(init, $, nextTick) {
  
  $(function() {
      nextTick(init);
  });
  
  });
  
  /* ../../libs/bem-core/common.blocks/i-bem/__dom/_init/i-bem__dom_init_auto.js end */
  ;
  /* ../../desktop.blocks/sssr/sssr.js begin */
  modules.define('sssr', ['i-bem__dom', 'jquery'], function(provide, BEMDOM, $) {
      
      provide(BEMDOM.decl(this.name, {
          onSetMod: {
              js: {
                  inited: function() {
                      this._form = this.findBlockInside('form');
                      this._form.on('submit', function() {
                          this.setMod('loading');
                          this._sendRequest();
                      }, this);
                  }
              },
              loading: function(modName, modVal) {
                  this.findBlockInside('spin').setMod('progress', modVal);
              }
  
          },
          _sendRequest: function() {
              var formVal = this._form.getVal();
              this._xhr = $.ajax({
                  type: 'GET',
                  dataType: 'html',
                  cache: false,
                  url: 'https://sssr.bem.yandex.net/search/',
                  data: formVal + '&twitter=on',
                  success: this._onSuccess,
                  context: this
              });
          },
          _onSuccess: function(result) {
              this.delMod('loading');
              BEMDOM.update(this.findBlockInside('content').domElem, result);
          }
      }))
  })
  /* ../../desktop.blocks/sssr/sssr.js end */
  ;
  /* ../../desktop.blocks/form/form.js begin */
  modules.define('form', ['i-bem__dom'], function(provide, BEMDOM) {
      
      provide(BEMDOM.decl(this.name, {
          onSetMod: {
              js: {
                  inited: function() {
                      this.bindTo('submit', function(e) {
                          this._onSubmit(e);
                      });
                      // подписаться на БЭМ-событие `submit`
                      // и вывести в консоль сообщение 'BEM-event'
                      this.on('submit', function() {
                          console.log('BEM-event');
                      });
                  }
              }
          },
  
          _onSubmit: function(e) {
              // отменить дефолтное поведение 
              e.preventDefault();
              // сгенерировать БЭМ-событие `submit`
              this.emit('submit');
          },
          getVal: function() {
              return this.domElem.serialize();
          }
      }));
  });
  
  
  /* ../../desktop.blocks/form/form.js end */
  ;
  /* ../../libs/bem-components/common.blocks/input/input.js begin */
  /**
   * @module input
   */
  
  modules.define('input', ['i-bem__dom', 'base-control'], function(provide, BEMDOM, BaseControl) {
  
  /**
   * @exports
   * @class input
   * @augments base-control
   * @bem
   */
  provide(BEMDOM.decl({ block : this.name, baseBlock : BaseControl }, /** @lends input.prototype */{
      onSetMod : {
          'js' : {
              'inited' : function() {
                  this.__base.apply(this, arguments);
                  this._val = this.elem('control').val();
              }
          }
      },
  
      /**
       * Returns control value
       * @returns {String}
       * @override
       */
      getVal : function() {
          return this._val;
      },
  
      /**
       * Sets control value
       * @param {String} val value
       * @param {Object} [data] additional data
       * @returns {this}
       */
      setVal : function(val, data) {
          val = String(val);
  
          if(this._val !== val) {
              this._val = val;
  
              var control = this.elem('control');
              control.val() !== val && control.val(val);
  
              this.emit('change', data);
          }
  
          return this;
      }
  }, {
      live : function() {
          this.__base.apply(this, arguments);
          return false;
      }
  }));
  
  });
  
  /* ../../libs/bem-components/common.blocks/input/input.js end */
  ;
  /* ../../libs/bem-components/desktop.blocks/input/input.js begin */
  modules.define('input', ['tick', 'idle'], function(provide, tick, idle, Input) {
  
  var instances = [],
      boundToTick,
      bindToTick = function() {
          boundToTick = true;
          tick
              .on('tick', update)
              .start();
          idle
              .on({
                  idle : function() {
                      tick.un('tick', update);
                  },
                  wakeup : function() {
                      tick.on('tick', update);
                  }
              })
              .start();
      },
      update = function() {
          var instance, i = 0;
          while(instance = instances[i++]) {
              instance.setVal(instance.elem('control').val());
          }
      };
  
  provide(Input.decl({
      onSetMod : {
          'js' : {
              'inited' : function() {
                  this.__base.apply(this, arguments);
  
                  boundToTick || bindToTick();
  
                  // сохраняем индекс в массиве инстансов чтобы потом быстро из него удалять
                  this._instanceIndex = instances.push(this) - 1;
              },
  
              '' : function() {
                  this.__base.apply(this, arguments);
  
                  // удаляем из общего массива instances
                  instances.splice(this._instanceIndex, 1);
                  // понижаем _instanceIndex всем тем кто был добавлен в instances после нас
                  var i = this._instanceIndex, instance;
                  while(instance = instances[i++]) --instance._instanceIndex;
              }
          }
      },
  
      /**
       * Нормализация установки фокуса для IE
       * @private
       * @override
       */
      _focus : function() {
          var input = this.elem('control')[0];
          if(input.createTextRange && !input.selectionStart) {
              var range = input.createTextRange();
              range.move('character', input.value.length);
              range.select();
          } else {
              input.focus();
          }
      }
  }));
  
  });
  
  /* ../../libs/bem-components/desktop.blocks/input/input.js end */
  ;
  /* ../../libs/bem-components/common.blocks/base-control/base-control.js begin */
  /** @module base-control */
  
  modules.define(
      'base-control',
      ['i-bem__dom', 'dom', 'next-tick'],
      function(provide, BEMDOM, dom, nextTick) {
  
  /**
   * @exports
   * @class base-control
   * @abstract
   * @bem
   */
  provide(BEMDOM.decl(this.name, /** @lends base-control.prototype */{
      beforeSetMod : {
          'focused' : {
              'true' : function() {
                  return !this.hasMod('disabled');
              }
          }
      },
  
      onSetMod : {
          'js' : {
              'inited' : function() {
                  this._focused = dom.containsFocus(this.elem('control'));
                  this._focused?
                      // if control is already in focus, we need to set focused mod
                      this.setMod('focused') :
                      // if block already has focused mod, we need to focus control
                      this.hasMod('focused') && this._focus();
  
                  this._tabIndex = this.elem('control').attr('tabindex');
                  if(this.hasMod('disabled') && this._tabIndex !== 'undefined')
                      this.elem('control').removeAttr('tabindex');
              }
          },
  
          'focused' : {
              'true' : function() {
                  this._focused || this._focus();
              },
  
              '' : function() {
                  this._focused && this._blur();
              }
          },
  
          'disabled' : {
              '*' : function(modName, modVal) {
                  this.elem('control').prop(modName, !!modVal);
              },
  
              'true' : function() {
                  this.delMod('focused');
                  typeof this._tabIndex !== 'undefined' &&
                      this.elem('control').removeAttr('tabindex');
              },
  
              '' : function() {
                  typeof this._tabIndex !== 'undefined' &&
                      this.elem('control').attr('tabindex', this._tabIndex);
              }
          }
      },
  
      /**
       * Returns name of control
       * @returns {String}
       */
      getName : function() {
          return this.elem('control').attr('name') || '';
      },
  
      /**
       * Returns control value
       * @returns {String}
       */
      getVal : function() {
          return this.elem('control').val();
      },
  
      _onFocus : function() {
          this._focused = true;
          this.setMod('focused');
      },
  
      _onBlur : function() {
          this._focused = false;
          this.delMod('focused');
      },
  
      _focus : function() {
          dom.isFocusable(this.elem('control')) && this.elem('control').focus();
      },
  
      _blur : function() {
          this.elem('control').blur();
      }
  }, /** @lends base-control */{
      live : function() {
          this
              .liveBindTo('control', 'focusin', this.prototype._onFocus)
              .liveBindTo('control', 'focusout', this.prototype._onBlur);
  
          var focused = dom.getFocused();
          if(focused.hasClass(this.buildClass('control'))) {
              var _this = this; // TODO: https://github.com/bem/bem-core/issues/425
              nextTick(function() {
                  if(focused[0] === dom.getFocused()[0]) {
                      var block = focused.closest(_this.buildSelector());
                      block && block.bem(_this.getName());
                  }
              });
          }
      }
  }));
  
  });
  
  /* ../../libs/bem-components/common.blocks/base-control/base-control.js end */
  ;
  /* ../../libs/bem-core/common.blocks/jquery/__event/_type/jquery__event_type_pointerclick.js begin */
  /**
   * FastClick to jQuery module wrapper.
   * @see https://github.com/ftlabs/fastclick
   */
  modules.define('jquery', function(provide, $) {
  
  /**
   * FastClick: polyfill to remove click delays on browsers with touch UIs.
   *
   * @version 0.6.11
   * @copyright The Financial Times Limited [All Rights Reserved]
   * @license MIT License (see LICENSE.txt)
   */
  
  /**
   * @class FastClick
   */
  
  /**
   * Instantiate fast-clicking listeners on the specificed layer.
   *
   * @constructor
   * @param {Element} layer The layer to listen on
   */
  function FastClick(layer) {
      'use strict';
      var oldOnClick, self = this;
  
  
      /**
       * Whether a click is currently being tracked.
       *
       * @type boolean
       */
      this.trackingClick = false;
  
  
      /**
       * Timestamp for when when click tracking started.
       *
       * @type number
       */
      this.trackingClickStart = 0;
  
  
      /**
       * The element being tracked for a click.
       *
       * @type EventTarget
       */
      this.targetElement = null;
  
  
      /**
       * X-coordinate of touch start event.
       *
       * @type number
       */
      this.touchStartX = 0;
  
  
      /**
       * Y-coordinate of touch start event.
       *
       * @type number
       */
      this.touchStartY = 0;
  
  
      /**
       * ID of the last touch, retrieved from Touch.identifier.
       *
       * @type number
       */
      this.lastTouchIdentifier = 0;
  
  
      /**
       * Touchmove boundary, beyond which a click will be cancelled.
       *
       * @type number
       */
      this.touchBoundary = 10;
  
  
      /**
       * The FastClick layer.
       *
       * @type Element
       */
      this.layer = layer;
  
      if (!layer || !layer.nodeType) {
          throw new TypeError('Layer must be a document node');
      }
  
      /** @type function() */
      this.onClick = function() { return FastClick.prototype.onClick.apply(self, arguments); };
  
      /** @type function() */
      this.onMouse = function() { return FastClick.prototype.onMouse.apply(self, arguments); };
  
      /** @type function() */
      this.onTouchStart = function() { return FastClick.prototype.onTouchStart.apply(self, arguments); };
  
      /** @type function() */
      this.onTouchMove = function() { return FastClick.prototype.onTouchMove.apply(self, arguments); };
  
      /** @type function() */
      this.onTouchEnd = function() { return FastClick.prototype.onTouchEnd.apply(self, arguments); };
  
      /** @type function() */
      this.onTouchCancel = function() { return FastClick.prototype.onTouchCancel.apply(self, arguments); };
  
      if (FastClick.notNeeded(layer)) {
          return;
      }
  
      // Set up event handlers as required
      if (this.deviceIsAndroid) {
          layer.addEventListener('mouseover', this.onMouse, true);
          layer.addEventListener('mousedown', this.onMouse, true);
          layer.addEventListener('mouseup', this.onMouse, true);
      }
  
      layer.addEventListener('click', this.onClick, true);
      layer.addEventListener('touchstart', this.onTouchStart, false);
      layer.addEventListener('touchmove', this.onTouchMove, false);
      layer.addEventListener('touchend', this.onTouchEnd, false);
      layer.addEventListener('touchcancel', this.onTouchCancel, false);
  
      // Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
      // which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
      // layer when they are cancelled.
      if (!Event.prototype.stopImmediatePropagation) {
          layer.removeEventListener = function(type, callback, capture) {
              var rmv = Node.prototype.removeEventListener;
              if (type === 'click') {
                  rmv.call(layer, type, callback.hijacked || callback, capture);
              } else {
                  rmv.call(layer, type, callback, capture);
              }
          };
  
          layer.addEventListener = function(type, callback, capture) {
              var adv = Node.prototype.addEventListener;
              if (type === 'click') {
                  adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
                      if (!event.propagationStopped) {
                          callback(event);
                      }
                  }), capture);
              } else {
                  adv.call(layer, type, callback, capture);
              }
          };
      }
  
      // If a handler is already declared in the element's onclick attribute, it will be fired before
      // FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
      // adding it as listener.
      if (typeof layer.onclick === 'function') {
  
          // Android browser on at least 3.2 requires a new reference to the function in layer.onclick
          // - the old one won't work if passed to addEventListener directly.
          oldOnClick = layer.onclick;
          layer.addEventListener('click', function(event) {
              oldOnClick(event);
          }, false);
          layer.onclick = null;
      }
  }
  
  
  /**
   * Android requires exceptions.
   *
   * @type boolean
   */
  FastClick.prototype.deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0;
  
  
  /**
   * iOS requires exceptions.
   *
   * @type boolean
   */
  FastClick.prototype.deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent);
  
  
  /**
   * iOS 4 requires an exception for select elements.
   *
   * @type boolean
   */
  FastClick.prototype.deviceIsIOS4 = FastClick.prototype.deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);
  
  
  /**
   * iOS 6.0(+?) requires the target element to be manually derived
   *
   * @type boolean
   */
  FastClick.prototype.deviceIsIOSWithBadTarget = FastClick.prototype.deviceIsIOS && (/OS ([6-9]|\d{2})_\d/).test(navigator.userAgent);
  
  
  /**
   * Determine whether a given element requires a native click.
   *
   * @param {EventTarget|Element} target Target DOM element
   * @returns {boolean} Returns true if the element needs a native click
   */
  FastClick.prototype.needsClick = function(target) {
      'use strict';
      switch (target.nodeName.toLowerCase()) {
  
      // Don't send a synthetic click to disabled inputs (issue #62)
      case 'button':
      case 'select':
      case 'textarea':
          if (target.disabled) {
              return true;
          }
  
          break;
      case 'input':
  
          // File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
          if ((this.deviceIsIOS && target.type === 'file') || target.disabled) {
              return true;
          }
  
          break;
      case 'label':
      case 'video':
          return true;
      }
  
      return (/\bneedsclick\b/).test(target.className);
  };
  
  
  /**
   * Determine whether a given element requires a call to focus to simulate click into element.
   *
   * @param {EventTarget|Element} target Target DOM element
   * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
   */
  FastClick.prototype.needsFocus = function(target) {
      'use strict';
      switch (target.nodeName.toLowerCase()) {
      case 'textarea':
          return true;
      case 'select':
          return !this.deviceIsAndroid;
      case 'input':
          switch (target.type) {
          case 'button':
          case 'checkbox':
          case 'file':
          case 'image':
          case 'radio':
          case 'submit':
              return false;
          }
  
          // No point in attempting to focus disabled inputs
          return !target.disabled && !target.readOnly;
      default:
          return (/\bneedsfocus\b/).test(target.className);
      }
  };
  
  
  /**
   * Send a click event to the specified element.
   *
   * @param {EventTarget|Element} targetElement
   * @param {Event} event
   */
  FastClick.prototype.sendClick = function(targetElement, event) {
      'use strict';
      var clickEvent, touch;
  
      // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
      if (document.activeElement && document.activeElement !== targetElement) {
          document.activeElement.blur();
      }
  
      touch = event.changedTouches[0];
  
      // Synthesise a click event, with an extra attribute so it can be tracked
      clickEvent = document.createEvent('MouseEvents');
      clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
      clickEvent.forwardedTouchEvent = true;
      targetElement.dispatchEvent(clickEvent);
  };
  
  FastClick.prototype.determineEventType = function(targetElement) {
      'use strict';
  
      //Issue #159: Android Chrome Select Box does not open with a synthetic click event
      if (this.deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
          return 'mousedown';
      }
  
      return 'click';
  };
  
  
  /**
   * @param {EventTarget|Element} targetElement
   */
  FastClick.prototype.focus = function(targetElement) {
      'use strict';
      var length;
  
      // Issue #160: on iOS 7, some input elements (e.g. date datetime) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
      if (this.deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time') {
          length = targetElement.value.length;
          targetElement.setSelectionRange(length, length);
      } else {
          targetElement.focus();
      }
  };
  
  
  /**
   * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
   *
   * @param {EventTarget|Element} targetElement
   */
  FastClick.prototype.updateScrollParent = function(targetElement) {
      'use strict';
      var scrollParent, parentElement;
  
      scrollParent = targetElement.fastClickScrollParent;
  
      // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
      // target element was moved to another parent.
      if (!scrollParent || !scrollParent.contains(targetElement)) {
          parentElement = targetElement;
          do {
              if (parentElement.scrollHeight > parentElement.offsetHeight) {
                  scrollParent = parentElement;
                  targetElement.fastClickScrollParent = parentElement;
                  break;
              }
  
              parentElement = parentElement.parentElement;
          } while (parentElement);
      }
  
      // Always update the scroll top tracker if possible.
      if (scrollParent) {
          scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
      }
  };
  
  
  /**
   * @param {EventTarget} targetElement
   * @returns {Element|EventTarget}
   */
  FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
      'use strict';
  
      // On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
      if (eventTarget.nodeType === Node.TEXT_NODE) {
          return eventTarget.parentNode;
      }
  
      return eventTarget;
  };
  
  
  /**
   * On touch start, record the position and scroll offset.
   *
   * @param {Event} event
   * @returns {boolean}
   */
  FastClick.prototype.onTouchStart = function(event) {
      'use strict';
      var targetElement, touch, selection;
  
      // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
      if (event.targetTouches.length > 1) {
          return true;
      }
  
      targetElement = this.getTargetElementFromEventTarget(event.target);
      touch = event.targetTouches[0];
  
      if (this.deviceIsIOS) {
  
          // Only trusted events will deselect text on iOS (issue #49)
          selection = window.getSelection();
          if (selection.rangeCount && !selection.isCollapsed) {
              return true;
          }
  
          if (!this.deviceIsIOS4) {
  
              // Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
              // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
              // with the same identifier as the touch event that previously triggered the click that triggered the alert.
              // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
              // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
              if (touch.identifier === this.lastTouchIdentifier) {
                  event.preventDefault();
                  return false;
              }
  
              this.lastTouchIdentifier = touch.identifier;
  
              // If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
              // 1) the user does a fling scroll on the scrollable layer
              // 2) the user stops the fling scroll with another tap
              // then the event.target of the last 'touchend' event will be the element that was under the user's finger
              // when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
              // is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
              this.updateScrollParent(targetElement);
          }
      }
  
      this.trackingClick = true;
      this.trackingClickStart = event.timeStamp;
      this.targetElement = targetElement;
  
      this.touchStartX = touch.pageX;
      this.touchStartY = touch.pageY;
  
      // Prevent phantom clicks on fast double-tap (issue #36)
      if ((event.timeStamp - this.lastClickTime) < 200) {
          event.preventDefault();
      }
  
      return true;
  };
  
  
  /**
   * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
   *
   * @param {Event} event
   * @returns {boolean}
   */
  FastClick.prototype.touchHasMoved = function(event) {
      'use strict';
      var touch = event.changedTouches[0], boundary = this.touchBoundary;
  
      if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
          return true;
      }
  
      return false;
  };
  
  
  /**
   * Update the last position.
   *
   * @param {Event} event
   * @returns {boolean}
   */
  FastClick.prototype.onTouchMove = function(event) {
      'use strict';
      if (!this.trackingClick) {
          return true;
      }
  
      // If the touch has moved, cancel the click tracking
      if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
          this.trackingClick = false;
          this.targetElement = null;
      }
  
      return true;
  };
  
  
  /**
   * Attempt to find the labelled control for the given label element.
   *
   * @param {EventTarget|HTMLLabelElement} labelElement
   * @returns {Element|null}
   */
  FastClick.prototype.findControl = function(labelElement) {
      'use strict';
  
      // Fast path for newer browsers supporting the HTML5 control attribute
      if (labelElement.control !== undefined) {
          return labelElement.control;
      }
  
      // All browsers under test that support touch events also support the HTML5 htmlFor attribute
      if (labelElement.htmlFor) {
          return document.getElementById(labelElement.htmlFor);
      }
  
      // If no for attribute exists, attempt to retrieve the first labellable descendant element
      // the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
      return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
  };
  
  
  /**
   * On touch end, determine whether to send a click event at once.
   *
   * @param {Event} event
   * @returns {boolean}
   */
  FastClick.prototype.onTouchEnd = function(event) {
      'use strict';
      var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
  
      if (!this.trackingClick) {
          return true;
      }
  
      // Prevent phantom clicks on fast double-tap (issue #36)
      if ((event.timeStamp - this.lastClickTime) < 200) {
          this.cancelNextClick = true;
          return true;
      }
  
      // Reset to prevent wrong click cancel on input (issue #156).
      this.cancelNextClick = false;
  
      this.lastClickTime = event.timeStamp;
  
      trackingClickStart = this.trackingClickStart;
      this.trackingClick = false;
      this.trackingClickStart = 0;
  
      // On some iOS devices, the targetElement supplied with the event is invalid if the layer
      // is performing a transition or scroll, and has to be re-detected manually. Note that
      // for this to function correctly, it must be called *after* the event target is checked!
      // See issue #57; also filed as rdar://13048589 .
      if (this.deviceIsIOSWithBadTarget) {
          touch = event.changedTouches[0];
  
          // In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
          targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
          targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
      }
  
      targetTagName = targetElement.tagName.toLowerCase();
      if (targetTagName === 'label') {
          forElement = this.findControl(targetElement);
          if (forElement) {
              this.focus(targetElement);
              if (this.deviceIsAndroid) {
                  return false;
              }
  
              targetElement = forElement;
          }
      } else if (this.needsFocus(targetElement)) {
  
          // Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
          // Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
          if ((event.timeStamp - trackingClickStart) > 100 || (this.deviceIsIOS && window.top !== window && targetTagName === 'input')) {
              this.targetElement = null;
              return false;
          }
  
          this.focus(targetElement);
  
          // Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
          if (!this.deviceIsIOS4 || targetTagName !== 'select') {
              this.targetElement = null;
              event.preventDefault();
          }
  
          return false;
      }
  
      if (this.deviceIsIOS && !this.deviceIsIOS4) {
  
          // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
          // and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
          scrollParent = targetElement.fastClickScrollParent;
          if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
              return true;
          }
      }
  
      // Prevent the actual click from going though - unless the target node is marked as requiring
      // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
      if (!this.needsClick(targetElement)) {
          event.preventDefault();
          this.sendClick(targetElement, event);
      }
  
      return false;
  };
  
  
  /**
   * On touch cancel, stop tracking the click.
   *
   * @returns {void}
   */
  FastClick.prototype.onTouchCancel = function() {
      'use strict';
      this.trackingClick = false;
      this.targetElement = null;
  };
  
  
  /**
   * Determine mouse events which should be permitted.
   *
   * @param {Event} event
   * @returns {boolean}
   */
  FastClick.prototype.onMouse = function(event) {
      'use strict';
  
      // If a target element was never set (because a touch event was never fired) allow the event
      if (!this.targetElement) {
          return true;
      }
  
      if (event.forwardedTouchEvent) {
          return true;
      }
  
      // Programmatically generated events targeting a specific element should be permitted
      if (!event.cancelable) {
          return true;
      }
  
      // Derive and check the target element to see whether the mouse event needs to be permitted;
      // unless explicitly enabled, prevent non-touch click events from triggering actions,
      // to prevent ghost/doubleclicks.
      if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
  
          // Prevent any user-added listeners declared on FastClick element from being fired.
          if (event.stopImmediatePropagation) {
              event.stopImmediatePropagation();
          } else {
  
              // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
              event.propagationStopped = true;
          }
  
          // Cancel the event
          event.stopPropagation();
          event.preventDefault();
  
          return false;
      }
  
      // If the mouse event is permitted, return true for the action to go through.
      return true;
  };
  
  
  /**
   * On actual clicks, determine whether this is a touch-generated click, a click action occurring
   * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
   * an actual click which should be permitted.
   *
   * @param {Event} event
   * @returns {boolean}
   */
  FastClick.prototype.onClick = function(event) {
      'use strict';
      var permitted;
  
      // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
      if (this.trackingClick) {
          this.targetElement = null;
          this.trackingClick = false;
          return true;
      }
  
      // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
      if (event.target.type === 'submit' && event.detail === 0) {
          return true;
      }
  
      permitted = this.onMouse(event);
  
      // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
      if (!permitted) {
          this.targetElement = null;
      }
  
      // If clicks are permitted, return true for the action to go through.
      return permitted;
  };
  
  
  /**
   * Remove all FastClick's event listeners.
   *
   * @returns {void}
   */
  FastClick.prototype.destroy = function() {
      'use strict';
      var layer = this.layer;
  
      if (this.deviceIsAndroid) {
          layer.removeEventListener('mouseover', this.onMouse, true);
          layer.removeEventListener('mousedown', this.onMouse, true);
          layer.removeEventListener('mouseup', this.onMouse, true);
      }
  
      layer.removeEventListener('click', this.onClick, true);
      layer.removeEventListener('touchstart', this.onTouchStart, false);
      layer.removeEventListener('touchmove', this.onTouchMove, false);
      layer.removeEventListener('touchend', this.onTouchEnd, false);
      layer.removeEventListener('touchcancel', this.onTouchCancel, false);
  };
  
  
  /**
   * Check whether FastClick is needed.
   *
   * @param {Element} layer The layer to listen on
   */
  FastClick.notNeeded = function(layer) {
      'use strict';
      var metaViewport;
  
      // Devices that don't support touch don't need FastClick
      if (typeof window.ontouchstart === 'undefined') {
          return true;
      }
  
      if ((/Chrome\/[0-9]+/).test(navigator.userAgent)) {
  
          // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
          if (FastClick.prototype.deviceIsAndroid) {
              metaViewport = document.querySelector('meta[name=viewport]');
              if (metaViewport && metaViewport.content.indexOf('user-scalable=no') !== -1) {
                  return true;
              }
  
          // Chrome desktop doesn't need FastClick (issue #15)
          } else {
              return true;
          }
      }
  
      // IE10 with -ms-touch-action: none, which disables double-tap-to-zoom (issue #97)
      if (layer.style.msTouchAction === 'none') {
          return true;
      }
  
      return false;
  };
  
  
  /**
   * Factory method for creating a FastClick object
   *
   * @param {Element} layer The layer to listen on
   */
  FastClick.attach = function(layer) {
      'use strict';
      return new FastClick(layer);
  };
  
  var event = $.event.special.pointerclick = {
          setup : function() {
              $(this).on('click', event.handler);
          },
  
          teardown : function() {
              $(this).off('click', event.handler);
          },
  
          handler : function(e) {
              if(!e.button) {
                  e.type = 'pointerclick';
                  $.event.dispatch.apply(this, arguments);
                  e.type = 'click';
              }
          }
      };
  
  $(function() {
      FastClick.attach(document.body);
      provide($);
  });
  
  });
  
  /* ../../libs/bem-core/common.blocks/jquery/__event/_type/jquery__event_type_pointerclick.js end */
  ;
  /* ../../libs/bem-core/common.blocks/jquery/__event/_type/jquery__event_type_pointernative.js begin */
  /**
   * Basic pointer events polyfill
   */
  ;(function(global, factory) {
  
  if(typeof modules === 'object' && modules.isDefined('jquery')) {
      modules.define('jquery', function(provide, $) {
          factory(this.global, $);
          provide($);
      });
  } else if(typeof jQuery === 'function') {
      factory(global, jQuery);
  }
  
  }(this, function(window, $) {
  
  // include "jquery-pointerevents.js"
  /*!
   * Most of source code is taken from PointerEvents Polyfill
   * written by Polymer Team (https://github.com/Polymer/PointerEvents)
   * and licensed under the BSD License.
   */
  
  var doc = document,
      USE_NATIVE_MAP = window.Map && window.Map.prototype.forEach,
      HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number',
      POINTERS_FN = function() { return this.size };
  
  // NOTE: Remove jQuery special fixes for pointerevents – we fix them ourself
  delete $.event.special.pointerenter;
  delete $.event.special.pointerleave;
  
  /**
   * Returns a snapshot of inEvent, with writable properties.
   *
   * @param {Event} event An event that contains properties to copy.
   * @returns {Object} An object containing shallow copies of `inEvent`'s
   *    properties.
   */
  function cloneEvent(event) {
      var eventCopy = $.extend(new $.Event(), event);
      if(event.preventDefault) {
          eventCopy.preventDefault = function() {
              event.preventDefault();
          };
      }
      return eventCopy;
  }
  
  var MOUSE_PROPS = {
          bubbles : false,
          cancelable : false,
          view : null,
          detail : null,
          screenX : 0,
          screenY : 0,
          clientX : 0,
          clientY : 0,
          ctrlKey : false,
          altKey : false,
          shiftKey : false,
          metaKey : false,
          button : 0,
          relatedTarget : null,
          pageX : 0,
          pageY : 0
      },
      mouseProps = Object.keys(MOUSE_PROPS),
      mousePropsLen = mouseProps.length,
      mouseDefaults = mouseProps.map(function(prop) { return MOUSE_PROPS[prop] });
  
  /**
   * Pointer event constructor
   *
   * @param {String} type
   * @param {Object} [params]
   * @returns {Event}
   * @constructor
   */
  function PointerEvent(type, params) {
      params || (params = {});
  
      var e = $.Event(type);
  
      // define inherited MouseEvent properties
      for(var i = 0, p; i < mousePropsLen; i++) {
          p = mouseProps[i];
          e[p] = params[p] || mouseDefaults[i];
      }
  
      e.buttons = params.buttons || 0;
  
      // add x/y properties aliased to clientX/Y
      e.x = e.clientX;
      e.y = e.clientY;
  
      // Spec requires that pointers without pressure specified use 0.5 for down
      // state and 0 for up state.
      var pressure = 0;
      if(params.pressure) {
          pressure = params.pressure;
      } else {
          pressure = e.buttons? 0.5 : 0;
      }
  
      // define the properties of the PointerEvent interface
      e.pointerId = params.pointerId || 0;
      e.width = params.width || 0;
      e.height = params.height || 0;
      e.pressure = pressure;
      e.tiltX = params.tiltX || 0;
      e.tiltY = params.tiltY || 0;
      e.pointerType = params.pointerType || '';
      e.hwTimestamp = params.hwTimestamp || 0;
      e.isPrimary = params.isPrimary || false;
  
      // add some common jQuery properties
      e.which = params.which;
  
      return e;
  }
  
  /**
   * Implements a map of pointer states
   * @returns {PointerMap}
   * @constructor
   */
  function PointerMap() {
      if(USE_NATIVE_MAP) {
          var m = new Map();
          m.pointers = POINTERS_FN;
          return m;
      }
  
      this.keys = [];
      this.values = [];
  }
  
  PointerMap.prototype = {
      set : function(id, event) {
          var i = this.keys.indexOf(id);
          if(i > -1) {
              this.values[i] = event;
          } else {
              this.keys.push(id);
              this.values.push(event);
          }
      },
  
      has : function(id) {
          return this.keys.indexOf(id) > -1;
      },
  
      'delete' : function(id) {
          var i = this.keys.indexOf(id);
          if(i > -1) {
              this.keys.splice(i, 1);
              this.values.splice(i, 1);
          }
      },
  
      get : function(id) {
          var i = this.keys.indexOf(id);
          return this.values[i];
      },
  
      clear : function() {
          this.keys.length = 0;
          this.values.length = 0;
      },
  
      forEach : function(callback, ctx) {
          var keys = this.keys;
          this.values.forEach(function(v, i) {
              callback.call(ctx, v, keys[i], this);
          }, this);
      },
  
      pointers : function() {
          return this.keys.length;
      }
  };
  
  var pointermap = new PointerMap();
  
  var dispatcher = {
      eventMap : {},
      eventSourceList : [],
  
      /**
       * Add a new event source that will generate pointer events
       */
      registerSource : function(name, source) {
          var newEvents = source.events;
          if(newEvents) {
              newEvents.forEach(function(e) {
                  source[e] && (this.eventMap[e] = function() { source[e].apply(source, arguments) });
              }, this);
              this.eventSourceList.push(source);
          }
      },
  
      register : function(element) {
          var len = this.eventSourceList.length;
          for(var i = 0, es; (i < len) && (es = this.eventSourceList[i]); i++) {
              // call eventsource register
              es.register.call(es, element);
          }
      },
  
      unregister : function(element) {
          var l = this.eventSourceList.length;
          for(var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
              // call eventsource register
              es.unregister.call(es, element);
          }
      },
  
      down : function(event) {
          event.bubbles = true;
          this.fireEvent('pointerdown', event);
      },
  
      move : function(event) {
          event.bubbles = true;
          this.fireEvent('pointermove', event);
      },
  
      up : function(event) {
          event.bubbles = true;
          this.fireEvent('pointerup', event);
      },
  
      enter : function(event) {
          event.bubbles = false;
          this.fireEvent('pointerenter', event);
      },
  
      leave : function(event) {
          event.bubbles = false;
          this.fireEvent('pointerleave', event);
      },
  
      over : function(event) {
          event.bubbles = true;
          this.fireEvent('pointerover', event);
      },
  
      out : function(event) {
          event.bubbles = true;
          this.fireEvent('pointerout', event);
      },
  
      cancel : function(event) {
          event.bubbles = true;
          this.fireEvent('pointercancel', event);
      },
  
      leaveOut : function(event) {
          this.out(event);
          if(!this.contains(event.target, event.relatedTarget)) {
              this.leave(event);
          }
      },
  
      enterOver : function(event) {
          this.over(event);
          if(!this.contains(event.target, event.relatedTarget)) {
              this.enter(event);
          }
      },
  
      contains : function(target, relatedTarget) {
          return target === relatedTarget || $.contains(target, relatedTarget);
      },
  
      // LISTENER LOGIC
      eventHandler : function(e) {
          // This is used to prevent multiple dispatch of pointerevents from
          // platform events. This can happen when two elements in different scopes
          // are set up to create pointer events, which is relevant to Shadow DOM.
          if(e._handledByPE) {
              return;
          }
  
          var type = e.type, fn;
          (fn = this.eventMap && this.eventMap[type]) && fn(e);
  
          e._handledByPE = true;
      },
  
      /**
       * Sets up event listeners
       */
      listen : function(target, events) {
          events.forEach(function(e) {
              this.addEvent(target, e);
          }, this);
      },
  
      /**
       * Removes event listeners
       */
      unlisten : function(target, events) {
          events.forEach(function(e) {
              this.removeEvent(target, e);
          }, this);
      },
  
      addEvent : function(target, eventName) {
          $(target).on(eventName, boundHandler);
      },
  
      removeEvent : function(target, eventName) {
          $(target).off(eventName, boundHandler);
      },
  
      getTarget : function(event) {
          return event._target;
      },
  
      /**
       * Creates a new Event of type `type`, based on the information in `event`
       */
      makeEvent : function(type, event) {
          var e = new PointerEvent(type, event);
          if(event.preventDefault) {
              e.preventDefault = event.preventDefault;
          }
  
          e._target = e._target || event.target;
  
          return e;
      },
  
      /**
       * Dispatches the event to its target
       */
      dispatchEvent : function(event) {
          var target = this.getTarget(event);
          if(target) {
              return $(target).trigger(event);
          }
      },
  
      /**
       * Makes and dispatch an event in one call
       */
      fireEvent : function(type, event) {
          var e = this.makeEvent(type, event);
          return this.dispatchEvent(e);
      }
  };
  
  function boundHandler() {
      dispatcher.eventHandler.apply(dispatcher, arguments);
  }
  
  var CLICK_COUNT_TIMEOUT = 200,
      // Radius around touchend that swallows mouse events
      MOUSE_DEDUP_DIST = 25,
      MOUSE_POINTER_ID = 1,
      // This should be long enough to ignore compat mouse events made by touch
      TOUCH_DEDUP_TIMEOUT = 2500,
      // A distance for which touchmove should fire pointercancel event
      TOUCHMOVE_HYSTERESIS = 20;
  
  // handler block for native mouse events
  var mouseEvents = {
      POINTER_TYPE : 'mouse',
      events : [
          'mousedown',
          'mousemove',
          'mouseup',
          'mouseover',
          'mouseout'
      ],
  
      register : function(target) {
          dispatcher.listen(target, this.events);
      },
  
      unregister : function(target) {
          dispatcher.unlisten(target, this.events);
      },
  
      lastTouches : [],
  
      // collide with the global mouse listener
      isEventSimulatedFromTouch : function(event) {
          var lts = this.lastTouches,
              x = event.clientX,
              y = event.clientY;
  
          for(var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
              // simulated mouse events will be swallowed near a primary touchend
              var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
              if(dx <= MOUSE_DEDUP_DIST && dy <= MOUSE_DEDUP_DIST) {
                  return true;
              }
          }
      },
  
      prepareEvent : function(event) {
          var e = cloneEvent(event);
          e.pointerId = MOUSE_POINTER_ID;
          e.isPrimary = true;
          e.pointerType = this.POINTER_TYPE;
          return e;
      },
  
      mousedown : function(event) {
          if(!this.isEventSimulatedFromTouch(event)) {
              if(pointermap.has(MOUSE_POINTER_ID)) {
                  // http://crbug/149091
                  this.cancel(event);
              }
  
              pointermap.set(MOUSE_POINTER_ID, event);
  
              var e = this.prepareEvent(event);
              dispatcher.down(e);
          }
      },
  
      mousemove : function(event) {
          if(!this.isEventSimulatedFromTouch(event)) {
              var e = this.prepareEvent(event);
              dispatcher.move(e);
          }
      },
  
      mouseup : function(event) {
          if(!this.isEventSimulatedFromTouch(event)) {
              var p = pointermap.get(MOUSE_POINTER_ID);
              if(p && p.button === event.button) {
                  var e = this.prepareEvent(event);
                  dispatcher.up(e);
                  this.cleanupMouse();
              }
          }
      },
  
      mouseover : function(event) {
          if(!this.isEventSimulatedFromTouch(event)) {
              var e = this.prepareEvent(event);
              dispatcher.enterOver(e);
          }
      },
  
      mouseout : function(event) {
          if(!this.isEventSimulatedFromTouch(event)) {
              var e = this.prepareEvent(event);
              dispatcher.leaveOut(e);
          }
      },
  
      cancel : function(inEvent) {
          var e = this.prepareEvent(inEvent);
          dispatcher.cancel(e);
          this.cleanupMouse();
      },
  
      cleanupMouse : function() {
          pointermap['delete'](MOUSE_POINTER_ID);
      }
  };
  
  var touchEvents = {
      events : [
          'touchstart',
          'touchmove',
          'touchend',
          'touchcancel'
      ],
  
      register : function(target) {
          dispatcher.listen(target, this.events);
      },
  
      unregister : function(target) {
          dispatcher.unlisten(target, this.events);
      },
  
      POINTER_TYPE : 'touch',
      clickCount : 0,
      resetId : null,
      firstTouch : null,
  
      isPrimaryTouch : function(touch) {
          return this.firstTouch === touch.identifier;
      },
  
      /**
       * Sets primary touch if there no pointers, or the only pointer is the mouse
       */
      setPrimaryTouch : function(touch) {
          if(pointermap.pointers() === 0 ||
                  (pointermap.pointers() === 1 && pointermap.has(MOUSE_POINTER_ID))) {
              this.firstTouch = touch.identifier;
              this.firstXY = { X : touch.clientX, Y : touch.clientY };
              this.scrolling = null;
  
              this.cancelResetClickCount();
          }
      },
  
      removePrimaryPointer : function(pointer) {
          if(pointer.isPrimary) {
              this.firstTouch = null;
              //this.firstXY = null;
              this.resetClickCount();
          }
      },
  
      resetClickCount : function() {
          var _this = this;
          this.resetId = setTimeout(function() {
              _this.clickCount = 0;
              _this.resetId = null;
          }, CLICK_COUNT_TIMEOUT);
      },
  
      cancelResetClickCount : function() {
          this.resetId && clearTimeout(this.resetId);
      },
  
      typeToButtons : function(type) {
          return type === 'touchstart' || type === 'touchmove'? 1 : 0;
      },
  
      findTarget : function(event) {
          // Currently we don't interested in shadow dom handling
          return doc.elementFromPoint(event.clientX, event.clientY);
      },
  
      touchToPointer : function(touch) {
          var cte = this.currentTouchEvent,
              e = cloneEvent(touch);
  
          // Spec specifies that pointerId 1 is reserved for Mouse.
          // Touch identifiers can start at 0.
          // Add 2 to the touch identifier for compatibility.
          e.pointerId = touch.identifier + 2;
          e.target = this.findTarget(e);
          e.bubbles = true;
          e.cancelable = true;
          e.detail = this.clickCount;
          e.button = 0;
          e.buttons = this.typeToButtons(cte.type);
          e.width = touch.webkitRadiusX || touch.radiusX || 0;
          e.height = touch.webkitRadiusY || touch.radiusY || 0;
          e.pressure = touch.mozPressure || touch.webkitForce || touch.force || 0.5;
          e.isPrimary = this.isPrimaryTouch(touch);
          e.pointerType = this.POINTER_TYPE;
  
          // forward touch preventDefaults
          var _this = this;
          e.preventDefault = function() {
              _this.scrolling = false;
              _this.firstXY = null;
              cte.preventDefault();
          };
  
          return e;
      },
  
      processTouches : function(event, fn) {
          var tl = event.originalEvent.changedTouches;
          this.currentTouchEvent = event;
          for(var i = 0, t; i < tl.length; i++) {
              t = tl[i];
              fn.call(this, this.touchToPointer(t));
          }
      },
  
      shouldScroll : function(touchEvent) {
          // return "true" for things to be much easier
          return true;
      },
      
      findTouch : function(touches, pointerId) {
          for(var i = 0, l = touches.length, t; i < l && (t = touches[i]); i++) {
              if(t.identifier === pointerId) {
                  return true;
              }
          }
      },
      
      /**
       * In some instances, a touchstart can happen without a touchend.
       * This leaves the pointermap in a broken state.
       * Therefore, on every touchstart, we remove the touches
       * that did not fire a touchend event.
       * 
       * To keep state globally consistent, we fire a pointercancel
       * for this "abandoned" touch
       */
      vacuumTouches : function(touchEvent) {
          var touches = touchEvent.touches;
          // pointermap.pointers() should be less than length of touches here, as the touchstart has not
          // been processed yet.
          if(pointermap.pointers() >= touches.length) {
              var d = [];
              
              pointermap.forEach(function(pointer, pointerId) {
                  // Never remove pointerId == 1, which is mouse.
                  // Touch identifiers are 2 smaller than their pointerId, which is the
                  // index in pointermap.
                  if(pointerId === MOUSE_POINTER_ID || this.findTouch(touches, pointerId - 2)) return;
                  d.push(pointer.outEvent);
              }, this);
              
              d.forEach(this.cancelOut, this);
          }
      },
  
      /**
       * Prevents synth mouse events from creating pointer events
       */
      dedupSynthMouse : function(touchEvent) {
          var lts = mouseEvents.lastTouches,
              t = touchEvent.changedTouches[0];
  
          // only the primary finger will synth mouse events
          if(this.isPrimaryTouch(t)) {
              // remember x/y of last touch
              var lt = { x : t.clientX, y : t.clientY };
              lts.push(lt);
  
              setTimeout(function() {
                  var i = lts.indexOf(lt);
                  i > -1 && lts.splice(i, 1);
              }, TOUCH_DEDUP_TIMEOUT);
          }
      },
      
      touchstart : function(event) {
          var touchEvent = event.originalEvent;
  
          this.vacuumTouches(touchEvent);
          this.setPrimaryTouch(touchEvent.changedTouches[0]);
          this.dedupSynthMouse(touchEvent);
          
          if(!this.scrolling) {
              this.clickCount++;
              this.processTouches(event, this.overDown);
          }
      },
      
      touchmove : function(event) {
          var touchEvent = event.originalEvent;
          if(!this.scrolling) {
              if(this.scrolling === null && this.shouldScroll(touchEvent)) {
                  this.scrolling = true;
              } else {
                  event.preventDefault();
                  this.processTouches(event, this.moveOverOut);
              }
          } else if(this.firstXY) {
              var firstXY = this.firstXY,
                  touch = touchEvent.changedTouches[0],
                  dx = touch.clientX - firstXY.X,
                  dy = touch.clientY - firstXY.Y,
                  dd = Math.sqrt(dx * dx + dy * dy);
              if(dd >= TOUCHMOVE_HYSTERESIS) {
                  this.touchcancel(event);
                  this.scrolling = true;
                  this.firstXY = null;
              }
          }
      },
      
      touchend : function(event) {
          var touchEvent = event.originalEvent;
          this.dedupSynthMouse(touchEvent);
          this.processTouches(event, this.upOut);
      },
      
      touchcancel : function(event) {
          this.processTouches(event, this.cancelOut);
      },
      
      overDown : function(pEvent) {
          var target = pEvent.target;
          pointermap.set(pEvent.pointerId, {
              target : target,
              outTarget : target,
              outEvent : pEvent
          });
          dispatcher.over(pEvent);
          dispatcher.enter(pEvent);
          dispatcher.down(pEvent);
      },
  
      moveOverOut : function(pEvent) {
          var pointer = pointermap.get(pEvent.pointerId);
  
          // a finger drifted off the screen, ignore it
          if(!pointer) {
              return;
          }
  
          dispatcher.move(pEvent);
  
          var outEvent = pointer.outEvent,
              outTarget = pointer.outTarget;
  
          if(outEvent && outTarget !== pEvent.target) {
              pEvent.relatedTarget = outTarget;
              outEvent.relatedTarget = pEvent.target;
              // recover from retargeting by shadow
              outEvent.target = outTarget;
  
              if(pEvent.target) {
                  dispatcher.leaveOut(outEvent);
                  dispatcher.enterOver(pEvent);
              } else {
                  // clean up case when finger leaves the screen
                  pEvent.target = outTarget;
                  pEvent.relatedTarget = null;
                  this.cancelOut(pEvent);
              }
          }
  
          pointer.outEvent = pEvent;
          pointer.outTarget = pEvent.target;
      },
  
      upOut : function(pEvent) {
          dispatcher.up(pEvent);
          dispatcher.out(pEvent);
          dispatcher.leave(pEvent);
  
          this.cleanUpPointer(pEvent);
      },
  
      cancelOut : function(pEvent) {
          dispatcher.cancel(pEvent);
          dispatcher.out(pEvent);
          dispatcher.leave(pEvent);
          this.cleanUpPointer(pEvent);
      },
  
      cleanUpPointer : function(pEvent) {
          pointermap['delete'](pEvent.pointerId);
          this.removePrimaryPointer(pEvent);
      }
  };
  
  var msEvents = {
      events : [
          'MSPointerDown',
          'MSPointerMove',
          'MSPointerUp',
          'MSPointerOut',
          'MSPointerOver',
          'MSPointerCancel'
      ],
      
      register : function(target) {
          dispatcher.listen(target, this.events);
      },
      
      unregister : function(target) {
          dispatcher.unlisten(target, this.events);
      },
      
      POINTER_TYPES : [
          '',
          'unavailable',
          'touch',
          'pen',
          'mouse'
      ],
      
      prepareEvent : function(event) {
          var e = cloneEvent(event);
          HAS_BITMAP_TYPE && (e.pointerType = this.POINTER_TYPES[event.pointerType]);
          return e;
      },
      
      MSPointerDown : function(event) {
          pointermap.set(event.pointerId, event);
          var e = this.prepareEvent(event);
          dispatcher.down(e);
      },
      
      MSPointerMove : function(event) {
          var e = this.prepareEvent(event);
          dispatcher.move(e);
      },
      
      MSPointerUp : function(event) {
          var e = this.prepareEvent(event);
          dispatcher.up(e);
          this.cleanup(event.pointerId);
      },
      
      MSPointerOut : function(event) {
          var e = this.prepareEvent(event);
          dispatcher.leaveOut(e);
      },
      
      MSPointerOver : function(event) {
          var e = this.prepareEvent(event);
          dispatcher.enterOver(e);
      },
      
      MSPointerCancel : function(event) {
          var e = this.prepareEvent(event);
          dispatcher.cancel(e);
          this.cleanup(event.pointerId);
      },
      
      cleanup : function(id) {
          pointermap['delete'](id);
      }
  };
  
  var navigator = window.navigator;
  if(navigator.msPointerEnabled) {
      dispatcher.registerSource('ms', msEvents);
  } else {
      dispatcher.registerSource('mouse', mouseEvents);
      if(typeof window.ontouchstart !== 'undefined') {
          dispatcher.registerSource('touch', touchEvents);
      }
  }
  
  dispatcher.register(doc);
  
  }));
  
  /* ../../libs/bem-core/common.blocks/jquery/__event/_type/jquery__event_type_pointernative.js end */
  ;
  /* ../../libs/bem-core/common.blocks/jquery/__event/_type/jquery__event_type_pointerpressrelease.js begin */
  modules.define('jquery', function(provide, $) {
  
  $.each({
      pointerpress : 'pointerdown',
      pointerrelease : 'pointerup pointercancel'
  }, function(spec, origEvent) {
      function eventHandler(e) {
          var res, origType = e.handleObj.origType;
  
          if(!e.button) {
              e.type = spec;
              res = $.event.dispatch.apply(this, arguments);
              e.type = origType;
          }
  
          return res;
      }
  
      $.event.special[spec] = {
          setup : function() {
              $(this).on(origEvent, eventHandler);
              return false;
          },
          teardown : function() {
              $(this).off(origEvent, eventHandler);
              return false;
          }
      };
  });
  
  provide($);
  
  });
  
  /* ../../libs/bem-core/common.blocks/jquery/__event/_type/jquery__event_type_pointerpressrelease.js end */
  ;
  /* ../../libs/bem-core/common.blocks/tick/tick.vanilla.js begin */
  /**
   * @module tick
   * @description Helpers for polling anything
   */
  
  modules.define('tick', ['inherit', 'events'], function(provide, inherit, events) {
  
  var TICK_INTERVAL = 50,
      global = this.global,
  
      /**
       * @class Tick
       * @augments events:Emitter
       */
      Tick = inherit(events.Emitter, /** @lends Tick.prototype */{
          /**
           * @constructor
           */
          __constructor : function() {
              this._timer = null;
              this._isStarted = false;
          },
  
          /**
           * Starts polling
           */
          start : function() {
              if(!this._isStarted) {
                  this._isStarted = true;
                  this._scheduleTick();
              }
          },
  
          /**
           * Stops polling
           */
          stop : function() {
              if(this._isStarted) {
                  this._isStarted = false;
                  global.clearTimeout(this._timer);
              }
          },
  
          _scheduleTick : function() {
              var _this = this;
              this._timer = global.setTimeout(
                  function() {
                      _this._onTick();
                  },
                  TICK_INTERVAL);
          },
  
          _onTick : function() {
              this
                  .emit('tick')
                  ._scheduleTick();
          }
      });
  
  provide(
      /**
       * @exports
       * @type Tick
       */
      new Tick());
  
  });
  
  /* ../../libs/bem-core/common.blocks/tick/tick.vanilla.js end */
  ;
  /* ../../libs/bem-core/common.blocks/idle/idle.js begin */
  /**
   * @module idle
   */
  
  modules.define('idle', ['inherit', 'events', 'jquery'], function(provide, inherit, events, $) {
  
  var IDLE_TIMEOUT = 3000,
      USER_EVENTS = 'mousemove keydown click',
      /**
       * @class Idle
       * @augments events:Emitter
       */
      Idle = inherit(events.Emitter, /** @lends Idle.prototype */{
          /**
           * @constructor
           */
          __constructor : function() {
              this._timer = null;
              this._isStarted = false;
              this._isIdle = false;
          },
  
          /**
           * Starts monitoring of idle state
           */
          start : function() {
              if(!this._isStarted) {
                  this._isStarted = true;
                  this._startTimer();
                  $(document).on(USER_EVENTS, $.proxy(this._onUserAction, this));
              }
          },
  
          /**
           * Stops monitoring of idle state
           */
          stop : function() {
              if(this._isStarted) {
                  this._isStarted = false;
                  this._stopTimer();
                  $(document).off(USER_EVENTS, this._onUserAction);
              }
          },
  
          /**
           * Returns whether state is idle
           * @returns {Boolean}
           */
          isIdle : function() {
              return this._isIdle;
          },
  
          _onUserAction : function() {
              if(this._isIdle) {
                  this._isIdle = false;
                  this.emit('wakeup');
              }
  
              this._stopTimer();
              this._startTimer();
          },
  
          _startTimer : function() {
              var _this = this;
              this._timer = setTimeout(
                  function() {
                      _this._onTimeout();
                  },
                  IDLE_TIMEOUT);
          },
  
          _stopTimer : function() {
              this._timer && clearTimeout(this._timer);
          },
  
          _onTimeout : function() {
              this._isIdle = true;
              this.emit('idle');
          }
      });
  
  provide(
      /**
       * @exports
       * @type Idle
       */
      new Idle());
  
  });
  
  /* ../../libs/bem-core/common.blocks/idle/idle.js end */
  ;
  /* ../../libs/bem-components/common.blocks/input/_has-clear/input_has-clear.js begin */
  modules.define('input', function(provide, Input) {
  
  provide(Input.decl({ modName : 'has-clear', modVal : true }, {
      onSetMod : {
          'js' : {
              'inited' : function() {
                  this.__base.apply(this, arguments);
  
                  this
                      .on('change', this._updateClear)
                      ._updateClear();
              }
          }
      },
  
      _onClearClick : function() {
          this
              .setVal('', { source : 'clear' })
              .setMod('focused');
      },
  
      _updateClear : function() {
          this.toggleMod(this.elem('clear'), 'visible', true, !!this._val);
      }
  }, {
      live : function() {
          this.liveBindTo('clear', 'pointerclick', function() {
              this._onClearClick();
          });
  
          return this.__base.apply(this, arguments);
      }
  }));
  
  });
  
  /* ../../libs/bem-components/common.blocks/input/_has-clear/input_has-clear.js end */
  ;
  /* ../../libs/bem-components/desktop.blocks/input/_has-clear/input_has-clear.js begin */
  modules.define('input', function(provide, Input) {
  
  provide(Input.decl({ modName : 'has-clear', modVal : true }, {
      _onBoxClick : function() {
          this.hasMod(this.elem('clear'), 'visible') || this.setMod('focused');
      }
  }, {
      live : function() {
          this.liveBindTo('box', 'pointerclick', function() {
              this._onBoxClick();
          });
  
          return this.__base.apply(this, arguments);
      }
  }));
  
  });
  
  /* ../../libs/bem-components/desktop.blocks/input/_has-clear/input_has-clear.js end */
  ;
  /* ../../libs/bem-components/common.blocks/button/button.js begin */
  /**
   * @module button
   */
  
  modules.define(
      'button',
      ['i-bem__dom', 'base-control', 'jquery', 'dom', 'functions', 'keyboard__codes'],
      function(provide, BEMDOM, BaseControl, $, dom, functions, keyCodes) {
  
  /**
   * @exports
   * @class button
   * @augments base-control
   * @bem
   */
  provide(BEMDOM.decl({ block : this.name, baseBlock : BaseControl }, /** @lends button.prototype */{
      beforeSetMod : {
          'pressed' : {
              'true' : function() {
                  return !this.hasMod('disabled') || this.hasMod('togglable');
              }
          }
      },
  
      onSetMod : {
          'js' : {
              'inited' : function() {
                  this.__base.apply(this, arguments);
                  this._refocusOnBlur = false;
              }
          },
  
          'focused' : {
              'true' : function() {
                  this.__base.apply(this, arguments); // should be called before binds
                  this
                      .bindToWin('unload', this._onUnload) // TODO: WTF???
                      .bindTo('control', 'keydown', this._onKeyDown);
              },
  
              '' : function() {
                  this
                      .unbindFromWin('unload', this._onUnload)
                      .unbindFrom('control', 'keydown', this._onKeyDown)
                      .__base.apply(this, arguments);
              }
          },
  
          'disabled' : {
              'true' : function() {
                  this.__base.apply(this, arguments);
                  this.hasMod('togglable') || this.delMod('pressed');
              }
          }
      },
  
      /**
       * Returns text of the button
       * @returns {String}
       */
      getText : function() {
          return this.elem('text').text();
      },
  
      /**
       * Sets text to the button
       * @param {String} text
       * @returns {this}
       */
      setText : function(text) {
          this.elem('text').text(text);
  
          return this;
      },
  
      _onUnload : function() {
          this.delMod('focused');
      },
  
      _onBlur : function() {
          this._focused = false;
          if(this._refocusOnBlur) {
              this._refocusOnBlur = false;
              this.nextTick(this._focus);
          } else {
              this.delMod('focused');
          }
      },
  
      _onPointerPress : function() {
          this._refocusOnBlur = true;
          this.nextTick(this._clearRefocusOnBlur);
  
          this.hasMod('disabled') ||
              this
                  .bindToDoc('pointerrelease', this._onPointerRelease)
                  .setMod('pressed')
                  .setMod('focused');
      },
  
      _clearRefocusOnBlur : function() {
          this._refocusOnBlur = false;
      },
  
      _onPointerRelease : function(e) {
          this.unbindFromDoc('pointerrelease', this._onPointerRelease);
  
          dom.contains(this.elem('control'), $(e.target)) &&
              this
                  ._updateChecked()
                  .emit('click');
  
          this.delMod('pressed');
      },
  
      _onKeyDown : function(e) {
          if(this.hasMod('disabled')) return;
  
          var keyCode = e.keyCode;
          if(keyCode === keyCodes.SPACE || keyCode === keyCodes.ENTER) {
              this
                  .unbindFrom('control', 'keydown', this._onKeyDown)
                  .bindTo('control', 'keyup', this._onKeyUp)
                  ._updateChecked()
                  .setMod('pressed');
          }
      },
  
      _onKeyUp : function(e) {
          this
              .unbindFrom('control', 'keyup', this._onKeyUp)
              .bindTo('control', 'keydown', this._onKeyDown)
              .delMod('pressed');
  
          e.keyCode === keyCodes.SPACE && this._doAction();
  
          this.emit('click');
      },
  
      _updateChecked : function() {
          this.hasMod('togglable') &&
              (this.hasMod('togglable', 'check')?
                  this.toggleMod('checked') :
                  this.setMod('checked'));
  
          return this;
      },
  
      _doAction : functions.noop
  }, /** @lends button */{
      live : function() {
          this.liveBindTo('control', 'pointerpress', this.prototype._onPointerPress);
          return this.__base.apply(this, arguments);
      }
  }));
  
  });
  
  /* ../../libs/bem-components/common.blocks/button/button.js end */
  ;
  /* ../../libs/bem-components/desktop.blocks/button/button.js begin */
  modules.define('button', function(provide, Button) {
  
  provide(Button.decl({
      beforeSetMod : {
          'hovered' : {
              'true' : function() {
                  return !this.hasMod('disabled');
              }
          }
      },
  
      onSetMod : {
          'disabled' : {
              'true' : function() {
                  this.__base.apply(this, arguments);
                  this.delMod('hovered');
              }
          },
  
          'hovered' : {
              'true' : function() {
                  this.bindTo('mouseleave', this._onMouseLeave);
              },
  
              '' : function() {
                  this.unbindFrom('mouseleave', this._onMouseLeave);
              }
          }
      },
  
      _onMouseOver : function() {
          this.setMod('hovered');
      },
  
      _onMouseLeave : function() {
          this.delMod('hovered');
      }
  }, {
      live : function() {
          return this
              .liveBindTo('mouseover', this.prototype._onMouseOver)
              .__base.apply(this, arguments);
      }
  }));
  
  });
  
  /* ../../libs/bem-components/desktop.blocks/button/button.js end */
  ;
  /* ../../libs/bem-core/common.blocks/keyboard/__codes/keyboard__codes.js begin */
  /**
   * @module keyboard__codes
   */
  modules.define('keyboard__codes', function(provide) {
  
  provide(/** @exports */{
      BACKSPACE : 8,
      TAB : 9,
      ENTER : 13,
      CAPS_LOCK : 20,
      ESC : 27,
      SPACE : 32,
      PAGE_UP : 33,
      PAGE_DOWN : 34,
      END : 35,
      HOME : 36,
      LEFT : 37,
      UP : 38,
      RIGHT : 39,
      DOWN : 40,
      INSERT : 41,
      DELETE : 42
  });
  
  });
  
  /* ../../libs/bem-core/common.blocks/keyboard/__codes/keyboard__codes.js end */
  ;
  