[
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [master, v*]\n  pull_request:\n    branches: [master, v5]\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 22\n          cache: npm\n      - run: npm ci\n      - run: npm run lint\n\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version: [20, 22]\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: npm\n      - run: npm ci\n      - run: npm test\n\n  test-browser:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 22\n          cache: npm\n      - run: npm ci\n      - run: npx playwright install --with-deps chromium\n      - run: npm run test:browser\n\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 22\n          cache: npm\n      - run: npm ci\n      - run: npm run build\n      - uses: actions/upload-artifact@v4\n        with:\n          name: dist\n          path: dist/\n\n  docs:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 22\n          cache: npm\n      - run: npm ci\n      - run: npm run docs\n      - uses: actions/upload-artifact@v4\n        with:\n          name: jsdoc\n          path: docs/jsdoc/\n"
  },
  {
    "path": ".gitignore",
    "content": ".project\n.settings\n\n.bem/cache\n.bem/snapshots\n.enb/tmp\n\nlibs\nnode_modules\n\ncoverage.json\n/coverage\n\n*.bundles/*/*\n!*.bundles/.bem/*\n!*.bundles/*/blocks\n!*.bundles/*/*.bemjson.js\n\n/dist\n/docs/jsdoc\n/test-results\n/*desktop.docs\n/*touch.docs\n/*.examples\n/*.specs\n/*.tmpl-specs\n/*.tests\n/*.sets\n\n_book\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "npx lint-staged\n"
  },
  {
    "path": ".nvmrc",
    "content": "24\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## 5.0.0\n\n### Breaking changes\n\n- **ESM-only**: The entire codebase has been migrated from the `ym` module system (`modules.define`/`modules.require`) to native ES modules (`import`/`export`). The `ym` runtime dependency has been removed.\n- **Vite build system**: ENB and all 17+ associated packages have been replaced with [Vite](https://vite.dev/). A custom `vite-plugin-bem-levels` plugin handles BEM level scanning, `bem:*` virtual module resolution, and redefinition chain barrel generation.\n- **jQuery 4.0**: The `jquery` peer dependency has been upgraded from `^3.x` to `^4.0.0`. Notable API change: `$.unique()` has been removed — use `$.uniqueSort()` instead.\n- **Node.js 20+**: The minimum supported Node.js version is now 20 (was 8).\n- **Native Promises**: `vow` has been removed. All code now uses native `Promise`.\n- **Native test runner**: Server-side tests now use `node:test` and `node:assert` instead of `mocha`/`chai`.\n- **Playwright**: Browser tests now use [Playwright](https://playwright.dev/) instead of `mocha-phantomjs` (PhantomJS is dead).\n- **ESLint 10**: Linting has been migrated from `jshint`/`jscs` to ESLint 10 with flat config (`eslint.config.js`).\n- **GitHub Actions CI**: Travis CI has been replaced with GitHub Actions.\n- **Husky + lint-staged**: `git-hooks` has been replaced with `husky` and `lint-staged`.\n\n### Notable changes\n\n- All `.vanilla.js` and `.js` source files have been converted to ES modules with `import`/`export` syntax.\n- Module redefinition chains (e.g., `jquery` with pointer event extensions) are now handled via auto-generated barrel files by `vite-plugin-bem-levels`.\n- The `i18n` block retains its API but uses native ES modules internally.\n- Browser spec tests (28 spec files, 500+ test cases) run via Vite dev server + Playwright.\n- Vite produces platform-specific builds (`desktop`, `touch`) with jQuery as an external dependency.\n\n### Removed packages\n\n- **Build**: `enb`, `enb-bem-techs`, `enb-magic-factory`, `enb-magic-platform`, `enb-bemxjst`, `enb-bemxjst-6x`, `enb-bemxjst-7x`, `enb-bemxjst-i18n`, `enb-bh`, `enb-bh-i18n`, `enb-borschik`, `enb-css`, `enb-js`, `enb-bem-docs`, `enb-bem-examples`, `enb-bem-specs`, `enb-bem-tmpl-specs`, `enb-bem-i18n`, `borschik`\n- **Linting**: `jscs`, `jscs-bem`, `jshint`, `jshint-groups`\n- **Testing**: `mocha-phantomjs`, `istanbul`, `chai-as-promised`\n- **Other**: `ym`, `vow`, `bower`, `git-hooks`, `gitbook-api`, `bem-naming`, `bem-walk`\n\n### Removed config files\n\n- `.enb/` directory (ENB build config)\n- `.jshintrc`, `.jscs.json`, `.jshint-groups.js` (linting configs)\n- `.bowerrc`, `bower.json` (Bower configs)\n- `.travis.yml` (Travis CI config)\n- `.githooks/` directory (git-hooks config)\n\n## 4.3.1\n\n### Bug fixes\n- Reverted change that led to error when `lazyInit` in `declMod` was used ([#1594](https://github.com/bem/bem-core/pull/1594)).\n\n## 4.3.0\n\n### Notable changes\n- `jQuery` was updated to 3.2.1 and 1.12.4 ([#1587](https://github.com/bem/bem-core/pull/1587)).\n\n### Bug fixes\n- Possibility to force `lazy` initialization from markup was fixed ([#1579](https://github.com/bem/bem-core/pull/1579)).\n- Possibility to declare entities with mixins was fixed ([#1550](https://github.com/bem/bem-core/pull/1550)).\n- Now `lazyInit` in `declMod` will throw an error ([#1579](https://github.com/bem/bem-core/pull/1579)).\n- With bug in `isFunction` method of `functions` block, which works wrong for special functions ([#1577](https://github.com/bem/bem-core/pull/1577)).\n\n### Other changes\n- `.bemrc` config was added ([#1568](https://github.com/bem/bem-core/pull/1568)).\n- `vow` was updated to 0.4.17 ([#1565](https://github.com/bem/bem-core/pull/1565)).\n- `inherit` was updated to 2.2.6 ([#1519](https://github.com/bem/bem-core/pull/1519)).\n- Private `_delInitedMod` method of `i-bem` block was removed ([#1523](https://github.com/bem/bem-core/pull/1523)).\n- Removed code for old `bem-tools` (https://github.com/bem/bem-core/commit/e57678b2d64a3b976a53af4a7fa09bf918685821).\n- npm dependencies were updated ([#1589](https://github.com/bem/bem-core/pull/1589)).\n- Now tests are executed also on Node.js 8 (https://github.com/bem/bem-core/commit/dd7e5344a64ad1595eab28febb2242134fcedbb3).\n- More specs for `i-bem-dom` were added ([#1517](https://github.com/bem/bem-core/pull/1517)).\n- `gitbook` was added ([#1569](https://github.com/bem/bem-core/pull/1569)).\n- JSDoc was fixed.\n- Documentation updates.\n\n## 4.2.1\n\n### Bug fixes\n\n- Fixed an issue with elems cache invalidation on DOM modifications ([#1487](https://github.com/bem/bem-core/issues/1487)).\n- Fixed an issue in `i-bem-dom__events` when event's data was not passed to handler ([#1509](https://github.com/bem/bem-core/pull/1509)).\n- Fixed method `isEditable` of `dom` module. Missing editable input types were added ([#1502](https://github.com/bem/bem-core/pull/1502)).\n\n### Other changes\n- Fixed syntax error in `i-bem-dom` JSDoc.\n- Minor documentation updates.\n- [CLA](https://github.com/bem/bem-core/blob/v4/CLA.md) introduced.\n\n## 4.2.0\n\n### Notable changes\n\n- `bem-xjst 8.x` support was introduced in BEMHTML templates ([#1486](https://github.com/bem/bem-core/issues/1485)).\n\n### Bug fixes\n\n- `concat()` method was fixed in `i-bem-dom__collection` ([#1488](https://github.com/bem/bem-core/issues/1488)).\n- An issue in `ua__dom` was fix ([#1479](https://github.com/bem/bem-core/issues/1478)).\n- dist: `i-bem-dom__init_auto` was removed from `no-autoinit` bundle ([#1482](https://github.com/bem/bem-core/issues/1481)).\n\n### Other changes\n\n- Now `findChildBlock`, `findChildBlocks`, `findParentBlock`, `findParentBlocks`, `findMixedBlock` and `findMixedBlocks` methods throw an error if block is given as String ([#1469](https://github.com/bem/bem-core/pull/1469/)).\n- `buildClassName` function was optimized ([#1404](https://github.com/bem/bem-core/pull/1404)).\n- Docs: English translations were added ([#1483](https://github.com/bem/bem-core/pull/1483), [#1476](https://github.com/bem/bem-core/pull/1476), [#1475](https://github.com/bem/bem-core/pull/1475)).\n- Migration: Added notes about bemTarget ([#1491](https://github.com/bem/bem-core/issues/1472)).\n- Migration: added info about template options ([#1467](https://github.com/bem/bem-core/issues/1467)).\n- Fixed issues in docs.\n\n## 4.1.1\n\n### Bug fixes\n\n— Fixed a bug in `ua` block on `touch.blocks` level ([#1460](https://github.com/bem/bem-core/pull/1460)).\n\n## 4.1.0\n\n### Bug fixes\n\n- Fixed a bug in `identify` returning different result on each call for `document` ([#1441](https://github.com/bem/bem-core/issues/1441)).\n- `modules.define` recursion problem was fixed ([#1446](https://github.com/bem/bem-core/issues/1446)).\n- Support for escaping in `ua` block was fixed ([#1435](https://github.com/bem/bem-core/issues/1435)).\n- Workaround for `Array.prototype.push` bug in `Opera 41` was implemented.\n- An issue with pointer events on iOS devices was fixed ([#1253](https://github.com/bem/bem-core/issues/1253)).\n- An issue in `i-bem-dom__events` was fixed. Method `once()` was broken in some cases ([#1452](https://github.com/bem/bem-core/issues/1452)).\n\n### Other changes\n\n- Ability to specify `html@lang` attribute was added to `page` block ([#751](https://github.com/bem/bem-core/issues/751)).\n\n## 4.0.0\n\n### Breaking changes\n\n- Changes in the `i-bem` block. See [MIGRATION.md](MIGRATION.md#changes-in-the-i-bem-block).\n- Changes in the `querystring`. See [MIGRATION.md](MIGRATION.md#changes-in-the-querystring-block).\n- The optional parameter `onlyGet` was removed from the `identify` module ([#1028](https://github.com/bem/bem-core/issues/1028)).\n- All static methods were removed from the `events` module ([#1024](https://github.com/bem/bem-core/issues/1024)).\n- The `result` field in `Event` class of `events` module was removed  ([#1023](https://github.com/bem/bem-core/issues/1023)).\n- The `css` element of the `page` block does not support auto insertion of conditional comments for IE ([#379](https://github.com/bem/bem-core/issues/379)).\n\n## 3.2.0\n\n### Bug fixes\n\n- `modules.define` recursion problem was fixed ([#1446](https://github.com/bem/bem-core/issues/1446)).\n- Support for escaping in `ua` block was fixed ([#1435](https://github.com/bem/bem-core/issues/1435)).\n- Workaround for `Array.prototype.push` bug in `Opera 41` was implemented.\n\n### Other changes\n\n- Ability to specify `html@lang` attribute was added to `page` block ([#751](https://github.com/bem/bem-core/issues/751)).\n\n## 3.1.0\n\n### Bug fixes\n\n- An issue in `getMods()` method of `i-bem` was fixed ([#1379](https://github.com/bem/bem-core/issues/1379)).\n\n### Notable changes\n\n- Templates: support for escaped mode was introduced ([#1406](https://github.com/bem/bem-core/issues/1406)).\n\n### Other changes\n\n- Minor documentation updates.\n\n## 3.0.1\n\n### Bug fixes\n\n- An issue with pointer events on iOS devices was fixed ([#1253](https://github.com/bem/bem-core/issues/1253)).\n\n## 3.0.0\n\n### Breaking changes\n\n- Base templates for `BEMHTML` and `BEMTREE` were removed ([#1258](https://github.com/bem/bem-core/issues/1258)). `bem-xjst` 6.3.0+ should be used instead.\n- File extentions of BEMHTML templates were renamed from `*.bemhtml` to `*.bemhtml.js` ([#984](https://github.com/bem/bem-core/issues/984)). Please check that new extention is supported in you build config.\n- `i-bem__i18n` element was removed ([#1304](https://github.com/bem/bem-core/issues/1304)). Please use `i18n` block for internationalization.\n- `jquery__events_type_pointerclick` is not using [FastClick](https://github.com/ftlabs/fastclick) anymore ([#1088](https://github.com/bem/bem-core/issues/1088)).\n\n### Notable changes\n\n- `jQuery` was updated to 2.2.3 and 1.12.3 ([#1260](https://github.com/bem/bem-core/issues/1260)).\n\n### Bug fixes\n\n- An issue in `page` was fixed. `<meta name=viewport>` had wrong `user-scalable` value on the touch level ([#1294](https://github.com/bem/bem-core/issues/1294)).\n- An issue in `jquery__event_type_pointernative` which led to JS error in IE8 was fixed ([1317](https://github.com/bem/bem-core/issues/1317)).\n\n### Other changes\n\n- dist: Autoinitialisation of blocks is optional now ([#1271](https://github.com/bem/bem-core/issues/1271)).\n\n## 2.9.0\n\n### Notable changes\n\n- `jQuery` was updated to 2.2.0 and 1.12.0 ([#1249](https://github.com/bem/bem-core/issues/1249)).\n\n### Bug fixes\n\n- Fixed bug in BEMHTML 1.x which leads to drop of `this.mods` in `reapply()` ([#97](https://github.com/bem/bem-xjst/issues/97)).\n\n### Other changes\n\n- `jquery__event_type_pointerpressrelease` now exposes `originalEvent` ([#1254](https://github.com/bem/bem-core/issues/1254)).\n- dist: Support for `i18n` was added to dist ([#1212](https://github.com/bem/bem-core/issues/1212)).\n- `page__css.bemhtml` template was updated to support new `bem-xjst` versions ([#1228](https://github.com/bem/bem-core/issues/1228)).\n\n## 2.8.0\n\n### Notable changes\n\n- New [i18n](https://github.com/bem/bem-core/tree/v2/common.blocks/i18n) block was introduced, providing support for internationalization ([#1074](https://github.com/bem/bem-core/issues/1074)).\n- Now jQuery is included via `https` by default ([#1202](https://github.com/bem/bem-core/issues/1202)).\n- Dependency on `bemhtml-compat` was dropped ([#1186](https://github.com/bem/bem-core/issues/1186)). Users of `bem-tools` need to run `npm i bemhtml-compat --save` to install it on their projects.\n\n### Bug fixes\n\n- Bug with undefined handler call in `loader_type_js` was fixed ([#1159](https://github.com/bem/bem-core/pull/1159)).\n\n### Other changes\n\n- BH bundles in `dist` now mimic to BEMHTML ([#1210](https://github.com/bem/bem-core/issues/1210)).\n- `bem create` templates for `bemhtml`, `bemtree`, `vanilla.js` and `browser.js` were improved ([#1183](https://github.com/bem/bem-core/issues/1183)).\n- `vow` was updated to `0.4.10` ([#1056](https://github.com/bem/bem-core/issues/1056)).\n\n## 2.7.0\n\n### Notable changes\n\n- New `detach` method was added to `i-bem__dom` ([#1102](https://github.com/bem/bem-core/issues/1102)).\n- `i-bem.bemhtml` now supports nested mixes as objects ([873](https://github.com/bem/bem-core/issues/873)).\n- Some minor attribute escaping optimizations were added to `i-bem.bemhtml` ([#961](https://github.com/bem/bem-core/issues/961)), ([#980](https://github.com/bem/bem-core/issues/980)) and ([#982](https://github.com/bem/bem-core/issues/982)).\n- Support for [bem-xjst](https://github.com/bem/bem-xjst) 2.x was added to BEMHTML templates ([#1021](https://github.com/bem/bem-core/issues/1021)).\n- `clearfix` was optimized to work properly in supported IE browsers ([#722](https://github.com/bem/bem-core/issues/722)).\n- `jquery` was updated to 2.1.4 and 1.11.3 ([#999](https://github.com/bem/bem-core/issues/999)).\n\n### Bug fixes\n\n- An issue in `i-bem__dom` was fixed. `findElem` didn't update cache of elements that\n  had been found previously ([#583](https://github.com/bem/bem-core/issues/583)).\n- An issue in `i-bem__dom` was fixed. `dropElemCache` worked incorrectly in some edge cases ([#1037](https://github.com/bem/bem-core/issues/1037)).\n- An issue in `i-bem__dom` was fixed. `setMod` didn't add CSS classes if blocks on the same DOM node had\n  overlapping end parts in their names ([#1090](https://github.com/bem/bem-core/issues/1090)).\n- An issue in `page` was fixed. `zoom` attribute of the block didn't work for touch levels ([#1020](https://github.com/bem/bem-core/issues/1020)).\n- An issue in `keyboard__codes` was fixed. `insert` and `delete` keys had wrong key codes ([#1002](https://github.com/bem/bem-core/issues/1002)).\n- An issue in `i-bem.bemhtml` was fixed. `applyNext` calls were skipped in nested templates ([b1dc50c](https://github.com/bem/bem-core/commit/b1dc50c621b5659cff33daa4dd3f210b67cf25e1)).\n- An issue in `jquery__events_type_pointernative` was fixed to work properly in IE 11/Edge ([#1066](https://github.com/bem/bem-core/issues/1066)).\n\n### Other changes\n\n- Russian documentation for every blocks was reworked. Please visit https://ru.bem.info/libs/bem-core/ for new documentation.\n- Other minor improvements of the documentation.\n\n## 2.6.0\n\n### Notable changes\n\n- Since now `i-bem__dom` provides module after DOM is ready ([#859](https://github.com/bem/bem-core/issues/859)).\n- Since now `setMod` and `hasMod` methods of `i-bem__dom` convert their `modVal` argument to string in case\n  it is not of type string or boolean ([#890](https://github.com/bem/bem-core/issues/890)).\n- An ability to pass `nonce` attribute was added to `page`, to support related parts of Content Security Policy\n  specification ([#882](https://github.com/bem/bem-core/issues/882)).\n- New `page__conditional-comment` template was added ([#551](https://github.com/bem/bem-core/issues/511)).\n- `vow` was updated to 0.4.8 ([#837](https://github.com/bem/bem-core/issues/837)).\n\n### Bug fixes\n\n- An issue in `i-bem.bemhtml` was fixed. Block CSS class repeated in case of mix with the same\n  block ([#792](https://github.com/bem/bem-core/issues/792)).\n- An issue in `loader_type_bundle` was fixed. Success callback might be applied after timeout\n  error ([67ff55f](https://github.com/bem/bem-core/commit/da5fdb9923e7e83e3ef9cd31aefc3967ff55fd3c)).\n- An issue in `i-bem__dom` was fixed. `append`, `prepend` and other similar methods won't properly work with strings\n  in some cases ([#852](https://github.com/bem/bem-core/issues/852)).\n- An issue in `jquery__event_type_winresize` was fixed. MSIE wasn't detected properly ([#862](https://github.com/bem/bem-core/issues/862)).\n- An issue in `object` was fixed to proper handle `null` value as `target` argument in `extend` method ([#910](https://github.com/bem/bem-core/issues/910)).\n- An issue in `page` was fixed. There was no way to disable `x-ua-compatible` meta tag from BEMJSON ([#794](https://github.com/bem/bem-core/issues/794)).\n\n### Other changes\n\n- Timeout in `loader_type_bundle` module was increased to 30000 ms ([4e27422](https://github.com/bem/bem-core/commit/000c6af02bfae4506fa460168de16d4e27422393)).\n- Russian documentation for several blocks was fixed.\n\n## 2.5.1\n\n### Bug fixes\n\n- An issue in `jquery__pointerpress` and `jquery__pointerrelease` was fixed. Events work now in\n  Internet Explorer 8 ([#792](https://github.com/bem/bem-core/issues/792)).\n- An issue in `jquery__pointernative` was fixed. `pointerenter` and `pointerleave` events have bubbled up\n  to the document root, while they shouldn't ([#801](https://github.com/bem/bem-core/issues/801)).\n- An issue in `loader_type_bundle` was fixed. CSS bundle has been always added to the top of the HTML `<head>`, so CSS rules\n  from the bundle might not work properly ([#808](https://github.com/bem/bem-core/issues/808)).\n- Issues in BH templates for `ua` were fixed. There was no possibility to pass the content of the block from\n  BEMJSON ([#734](https://github.com/bem/bem-core/pull/734)).\n- An issue in `page` was fixed. There was a problem with conditional comments for Internet Explorer in the BH template\n  of the block ([#781](https://github.com/bem/bem-core/pull/781)).\n\n### Other changes\n\n- `jquery` was updated to the 2.1.3 and 1.11.2 ([#778](https://github.com/bem/bem-core/pull/788)).\n- Russian documentation for modules: `clearfix`, `cookie`, `identify`, `idle`, `inherit`, `keyboard`, `loader`, `next-tick`,\n  `string` and `tick` was added.\n- Russian documentation for `i-bem.js` was updated.\n- English guides to BEMHTM and BEMJSON were updated.\n\n## 2.5.0\n\n### Notable changes\n\n- bem-core in now published under the [MPL 2.0](https://www.mozilla.org/MPL/2.0/) license ([#443](https://github.com/bem/bem-core/issues/443)).\n- An ability to specify error handler was added to `loader_type_js` ([#672](https://github.com/bem/bem-core/issues/672)).\n- `BEMContext` class was added to `oninit` export context in `i-bem.bemtree` ([#602](https://github.com/bem/bem-core/issues/602)).\n- `reapply` static method was added to BEMContext class of BEMTREE ([#706](https://github.com/bem/bem-core/pull/706)).\n- bh templates for block `page` were added to touch level ([#689](https://github.com/bem/bem-core/pull/689)).\n- [bem-xjst](https://github.com/bem/bem-xjst) was updated to 0.9.0 ([#709](https://github.com/bem/bem-core/pull/709)).\n\n### Bug fixes\n\n- An issue in `i-bem__dom` was fixed. `findBlocksInside` could return blocks which weren't inited  ([#699](https://github.com/bem/bem-core/issues/699)).\n- An issue in `tick` was fixed. Timer was not removed by `Tick#stop()` ([#694](https://github.com/bem/bem-core/issues/694)).\n- An issue in `i-bem.bemhtml` was fixed. `i-bem` CSS class was added to elements by mistake ([#633](https://github.com/bem/bem-core/issues/633)).\n- `html-from-bemtree` tech was fixed to expose `vow`, `console`, `setTimeout` inside BEMTREE template context  ([#438ebb8](https://github.com/bem/bem-core/commit/438ebb8f828e26977592e26511e8aad15176d7a4)).\n\n### Other changes\n\n- English guide to BEMJSON was added.\n- Russian documentation for `querystring` module was added.\n- Russian documentation for `i-bem.js` was fixed to satisfy current API.\n- Documentation for BEMHML/BEMTREE for both languages was updated.\n\n## 2.4.0\n\n### Notable changes\n\n- [bem-xjst](https://github.com/bem/bem-xjst) was updated to 0.8.0; [bemhtml-compat](https://github.com/bem/bemhtml-compat) was updated to 0.0.11.\n\n### Bug fixes\n\n- An issue in `jquery__event_type_pointerpressrelease` was fixed. `pointerpress`/`pointerrelease` events fired for any press/release\n  of mouse button ([#607](https://github.com/bem/bem-core/issues/607)).\n- An issue in `i-bem__dom.js` was fixed. Base `live` method was not properly called in some edge cases ([#608](https://github.com/bem/bem-core/issues/608)).\n\n### Other changes\n\n- English documentation for JS-syntax of BEMHTML was added.\n\n## 2.3.0\n\n### Notable changes\n\n- New implementation of pointer events was added. Based on pointer events polyfills from [Polymer](http://www.polymer-project.org/) ([#567](https://github.com/bem/bem-core/pull/567)).\n- Ability to specify additional data for event was added to `bindTo*` methods of `i-bem__dom.js` ([#568](https://github.com/bem/bem-core/issues/568)).\n\n### Other changes\n\n- An issue in `i-bem.bemhtml` was fixed. There was an error when mix was used as an object (not an array) in BEMJSON and BEMHTML simultaneously ([#555](https://github.com/bem/bem-core/issues/555)).\n- An issue in `page` was fixed. There was no possibility to apply standard modes to `page` in BEMHTML template and touch template was broken ([516](https://github.com/bem/bem-core/issues/516)).\n\n## 2.2.4\n\n### Bug fixes\n\n- An issue in `i-bem.js` was fixed. Modifier change event has been emitted even if `beforeSetMod` handler\n  had prevented change ([#546](https://github.com/bem/bem-core/pull/546)).\n- String decoding process of `querystring__uri` module was fixed to return original string\n  if decode failed ([#554](https://github.com/bem/bem-core/pull/554)).\n\n## 2.2.3\n\n### Bug fixes\n\n- Destruction process of blocks was fixed to prevent unexpected block reinitialization ([#540](https://github.com/bem/bem-core/issues/540)).\n- An issue in `jquery__event_type_pointer` was fixed. Native mouse events were replaced with pointer events\n  in unexpected cases ([#534](https://github.com/bem/bem-core/issues/534)).\n- `unbindFrom*` methods of `i-bem__dom` now support multiple events to be passed in arguments ([#533](https://github.com/bem/bem-core/issues/533)).\n- Lost `functions` dependency in `events` module was restored ([#532](https://github.com/bem/bem-core/issues/532)).\n\n## 2.2.2\n\n### Bug fixes\n\n- An issue with block reinitialization on the DOM node, that has been processed with destructor, was fixed\n  in `i-bem__dom` ([#518](https://github.com/bem/bem-core/issues/518)).\n- An issue in mod events subscription was fixed in `i-bem`. `false` could be used as `modVal` ([#529](https://github.com/bem/bem-core/issues/529)).\n- `jquery` was updated to the latest minor releases 2.1.1 and 1.11.1 ([#515](https://github.com/bem/bem-core/issues/515)).\n\n## 2.2.1\n\n- An issue in `jquery__event_type_pointerpressrelease` was fixed. `pointerpress` event has been triggered twice on each mousedown\n  in IE10 ([#505](https://github.com/bem/bem-core/issues/505)).\n\n## 2.2.0\n\n### Notable changes\n\n- New `keyboard__codes` module has been added ([#431](https://github.com/bem/bem-core/issues/431)).\n- `BEMContext` class was added to oninit export context in `i-bem.bemhtml` ([#485](https://github.com/bem/bem-core/pull/485)).\n- Ability to declare elements with block class has been added ([#481](https://github.com/bem/bem-core/issues/481)).\n- Behaviour of `isSimple` method of `BEMContext` was fixed in `i-bem.bemhtml` ([#432](https://github.com/bem/bem-core/pull/432)).\n- An issue with `liveUnbindFrom` method of `BEMDOM` was fixed in `i-bem__dom` ([#476](https://github.com/bem/bem-core/pull/476)).\n- An issue with `isFocusable` method of `dom` module was fixed for cases where `domElem` is a link with `tabindex` attribute,\n  but without `href` ([#501](https://github.com/bem/bem-core/issues/501)).\n- Short way of module declaration was fixed for `i-bem__dom_elem-instances` ([#479](https://github.com/bem/bem-core/issues/479)).\n- A workaround for rendering performance of blocks initialisation in Chrome-based browsers was added\n  to `i-bem__dom_init_auto` ([#486](https://github.com/bem/bem-core/issues/486)).\n- `vow.js` module has been moved to `vow.vanilla.js` ([#412](https://github.com/bem/bem-core/issues/412)).\n\n### Other changes\n\n- `vow` module has been updated to 0.4.3 ([#504](https://github.com/bem/bem-core/pull/504)).\n- Russian documentation about BEMTREE technology was added ([#500](https://github.com/bem/bem-core/pull/500)).\n- Russian documentation for JS-syntax of BEMHTML was updated ([#471](https://github.com/bem/bem-core/pull/471)).\n- API references for JS-modules has been added as a separate branch `v2-jsdoc` ([#478](https://github.com/bem/bem-core/pull/478)).\n\n## 2.1.0\n\n### Notable changes\n\n- An issue in `i-bem.js` when modifiers change event had been emitted before `onSetMod` handlers have been called was fixed ([#454](https://github.com/bem/bem-core/issues/454)).\n- An issue in `i-bem.bemhtml` was fixed. Since now `this.mods` and `this.ctx.mods` use the same object ([#441](https://github.com/bem/bem-core/issues/441)).\n- Error in modular declaration of element modifiers was fixed in `i-bem__dom_elem-instances` ([#447](https://github.com/bem/bem-core/issues/447)).\n- [inherit](https://github.com/dfilatov/inherit) module was updated to 2.2.1 ([#466](https://github.com/bem/bem-core/issues/466)).\n- An order of tags in `head` section of `page.bemhtml` was fixed ([#465](https://github.com/bem/bem-core/pull/465)).\n\n### Other changes\n\n- `baseMix` field description of `i-bem.js` was added to russian docs ([#461](https://github.com/bem/bem-core/pull/461)).\n- CDN host was changed to `yastatic.net` ([#444](https://github.com/bem/bem-core/issues/444)).\n  Previous CDN host `yandex.st` is still accessible. Physically they both are the same web servers. DNS records is the only difference.\n- BEMHTML template for `bem create` command was added ([#277](https://github.com/bem/bem-core/issues/277)).\n- We do not support autobuilding of our tests with Node.js 0.8 in [Travis CI](http://travis-ci.com) any longer ([#455](https://github.com/bem/bem-core/issues/455)).\n- Travis's build status badge [was changed to SVG version](http://blog.travis-ci.com/2014-03-20-build-status-badges-support-svg/) :)\n\n## 2.0.0\n\n### Breaking changes\n\n- All deprecated methods have been removed from `i-bem.js` and `i-bem__dom.js` ([#318](https://github.com/bem/bem-core/issues/318)).\n  The following methods were removed:\n\n  * `destruct`, use `onSetMod js ''`;\n  * `extractParams`, use `elemParams`;\n  * `trigger`, use `emit`;\n  * `afterCurrentEvent`, use `next-tick` module;\n  * `channel`, use `events__channels` module;\n  * `changeThis`, use native `Function.prototype.bind`.\n\n- `init` and `destruct` events have been removed from `i-bem.js` in favor of modifiers changes events (see \"Notable changes\" section below).\n- `ecma` was moved to [separate repo](http://github.com/bem/es5-shims); ES5-shims should be used\n  for IE < 9 ([#230](https://github.com/bem/bem-core/issues/230)).\n- `vow` module has been updated to 0.4.1 ([#350](https://github.com/bem/bem-core/issues/350)).\n  See [Vow's changelog](https://github.com/dfilatov/vow/blob/0.4.1/CHANGELOG.md) for changes.\n- Support for vow@0.4 has been added to `i-bem.bemhtml` ([#385](https://github.com/bem/bem-core/issues/385)).\n\n### Notable changes\n\n- Support for defining BEMDOM-blocks as [ym](https://github.com/ymaps/modules) modules has been added ([#382](https://github.com/bem/bem-core/issues/382)).\n- Events for modifiers changes have been added to `i-bem.js` ([#357](https://github.com/bem/bem-core/issues/357)).\n- Support for passing string values has been added to `BEMDOM.init`\n  ([#419](https://github.com/bem/bem-core/issues/419)).\n  and `BEMDOM.update` methods ([#420](https://github.com/bem/bem-core/issues/420)).\n- DOM helpers from `i-bem__dom.js` `replace`, `append`, `prepend`, `before`, `after` now return new context and `update` returns\n  updated context as a jQuery object ([#410](https://github.com/bem/bem-core/issues/410)).\n- New `loader_type_bundle` has been added ([#358](https://github.com/bem/bem-core/issues/358)).\n- Default jQuery versions were updated to 2.1.0 and to 1.11.0, for IE < 9 ([#356](https://github.com/bem/bem-core/issues/356)).\n\n### Other changes\n\n- `i-bem.bemhtml` now uses strings concatination instead of pushing to buffer in it's internals ([#401](https://github.com/bem/bem-core/issues/401)).\n- jQuery no longer removes itself from global scope if it exists ([#349](https://github.com/bem/bem-core/issues/349)).\n- `jquery__event_type_pointerclick.js` has been moved from touch level to common ([#393](https://github.com/bem/bem-core/issues/393)).\n- Modifiers `i-bem_elem-instances_yes` and `i-bem__dom_elem-instances_yes` were renamed to boolean style ([#352](https://github.com/bem/bem-core/issues/352)).\n- Runtime error in `page` template in development mode has been fixed ([#417](https://github.com/bem/bem-core/issues/417)).\n- Usage of `Function.prototype.bind` has been droped from `i-bem.js` internals in favor of support\n  for Android 2.3 ([#404](https://github.com/bem/bem-core/issues/404)).\n- Some bugs in `browser-js+bemhtml` tech have been fixed ([#392](https://github.com/bem/bem-core/issues/392)).\n- Up to [ym@0.0.15](https://github.com/ymaps/modules/releases) ([#414](https://github.com/bem/bem-core/issues/414)).\n\n## 1.2.0\n\n### Notable changes\n\n- BEM-blocks are emit `destruct` event on destructing ([#370](https://github.com/bem/bem-core/issues/370)).\n- Improvements of `pointerevents` polyfills ([#354](https://github.com/bem/bem-core/pull/354)).\n\n### Other changes\n\n- All JSDocs were fixed so [bem-jsd](github.com/bem/bem-jsd) could parse them ([#335](https://github.com/bem/bem-core/issues/335)).\n- Russian version of BEMHTML reference was actualized to JavaScript syntax ([#355](https://github.com/bem/bem-core/pull/355)).\n- Use [bower](http://bower.io) for dependency management ([#367](https://github.com/bem/bem-core/issues/367)).\n\n## 1.1.0\n\n### Notable changes\n\n- `jquery__config` uses jQuery 2.x by default for modern browsers ([#319](https://github.com/bem/bem-core/issues/319)).\n- Add ability to use any BEMJSON as value of attributes in BEMHTML templates ([#290](https://github.com/bem/bem-core/issues/290)).\n- Fix dependencies in `i-bem__collection` ([#292](https://github.com/bem/bem-core/issues/292)).\n- Remove `page` block touch styles ([#306](https://github.com/bem/bem-core/issues/306)).\n- Fix `page` BEMHTML wrapping in production mode ([#309](https://github.com/bem/bem-core/issues/309)).\n- Fix possible JavaScript error in script injection in IE<9 in `next-tick` ([#324](https://github.com/bem/bem-core/issues/324)).\n- Fix `FastClick` initialisation in `jquery__event_type_pointerclick` of `touch.blocks` ([#332](https://github.com/bem/bem-core/issues/332)).\n- Fix `node.js` tech bug on Windows systems ([#274](https://github.com/bem/bem-core/issues/274)).\n- Fix `i-bem__dom_elem-instances` bug with `onElemSetMod` ([#340](https://github.com/bem/bem-core/issues/340)).\n- Use bemhtml from [bem-xjst](https://github.com/bem/bem-xjst) ([#329](https://github.com/bem/bem-core/issues/329)).\n\n### Other changes\n\n- [ym](https://github.com/ymaps/modules) was updated to 0.0.12 ([#326](https://github.com/bem/bem-core/issues/326)).\n- Do not flood `console` with messages if `i-bem__i18n` is not in debug mode ([#285](https://github.com/bem/bem-core/issues/285)).\n- Fix jsdoc for `dropElemCache()` method of `i-bem__dom` module ([#296](https://github.com/bem/bem-core/issues/296)).\n- Development infrastructure was updated to\n  [bem-pr@v0.5.x](https://github.com/narqo/bem-pr/blob/0.5.3/HISTORY.md) ([#323](https://github.com/bem/bem-core/issues/323)).\n- Russian documentation for `i-bem.js` was updated.\n- [List of supported browsers](https://github.com/bem/bem-core/blob/v1/README.md#supported-browsers)\n  was specified in project README.\n\n## 1.0.0\n\n### Breaking changes\n\n- Starts using modular system [ym](https://github.com/ymaps/modules).\n- Removes all deprecated methods from `i-bem` and `i-bem__dom`.\n- `i-bem` now has no dependency on jQuery. `i-bem__dom` still depends on jQuery.\n- BEMHTML-template can be written with [JS-syntax](https://gist.github.com/veged/6150760).\n- Introduces new tech `bemtree` (based on [bem-xjst](https://github.com/bem/bem-xjst))\n  for describing dynamic generation of BEM-tree.\n- Introduces new tech `vanilla.js` for JS-implementations that does not depend on particular JS-engine.\n- Introduces new techs `browser.js` and `node.js` for JS-implementations targeted corresponding engines.\n  For backward compatibility we assume that `.js`-files contains `browser.js` implementation.\n- Introduces polyfill (`jquery__event_type_pointer` and `jquery__event_type_pointerclick` as a jQuery-plugins)\n  for universalize desktop and touch pointer events.\n- Introduces system for unit testing and blocks examples generation.\n- Introduces \"simple\" modifiers (modifiers without value) support in `i-bem` and BEMHTML.\n\n### Other changes\n\n- Gets rid of prefixes in all block names (except `i-bem`).\n- Block `i-bem__dom` becomes a module (in terms of [ym](https://github.com/ymaps/modules))\n  and all `BEM.DOM`-block must define additions to `i-bem__dom` ([example](https://github.com/bem/bem-core/blob/v1/common.bundles/index/blocks/b-square/b-square.js)).\n- Method for blocks declaration (`.decl()`) does not accept object with `name` field as first parameter.\n  Required form with `block` field: `BEM.decl({ block: 'b1', modName: 'm', modVal: 'v' }, ...)`.\n- Introduces `nextTick` method as replacement for `afterCurrentEvent` method\n  for ensure of block existence in callback invocation time.\n  `BEM.afterCurrentEvent` is **deprecated**.\n- Introduces new `channels` module instead of `BEM.channel`. `BEM.channel` is **deprecated**.\n- `changeThis` is **deprecated**. Use native `bind` instead.\n- Removes `del` method from `i-bem` block.\n- Removes `getWindowSize` method from `i-bem__dom` block. Use `BEMDOM.win.width()` and `BEMDOM.win.height()`.\n- Introduces `jquery` module-wrapper for providing jQuery.\n  If jQuery already included into the page module-wrapper provides it. Otherwise it loads jQuery (version 1.10.1) on its own.\n- `$.observable` becomes `events` module and not longer depends on jQuery.\n- `$.inherit` becomes `inherit` module and not longer depends on jQuery.\n- `$.identify` becomes `identify` module and not longer depends on jQuery.\n- `$.throttle` splits into two modules: `functions__throttle` and `functions__debounce`, they both not longer depend on jQuery.\n- `$.decodeURI`, `$.decodeURIComponent` moves to `querystring__uri` module and not longer depends on jQuery.\n- `$.cookie` becomes `cookie` module and not longer depends on jQuery.\n- Introduces `ua` module instead of `$.browser` (with same interface).\n- Use `pointerclick` instead of `leftclick`. It provides by `jquery__event_type_pointerclick` polyfill.\n- `i-system` block splits into two modules: `idle` and `tick`.\n- Triggers for modifiers changes now splitted into two groups:\n  before setting new value (`beforeSetMod` and `beforeElemSetMod`)\n  and after the value has been set (`onSetMod` and `onElemSetMod`).\n  Cancellation of modifiers change is possible only from `before*`-triggers.\n- Using of `{ onSetMod : { js : function() { ... } } }` is **deprecated**, use `onSetMod: { js : { inited : ... } } }`.\n- `destruct` method from `i-bem` block is **deprecated**.\n  Use supplementary trigger for `_js` modifiers:\n  `onSetMod: { js : { inited : ... } } }` — `{ onSetMod : { js : { '' : ... } } }`.\n- `exractParams` method from `i-bem__dom` block is **deprecated**.\n  Use `elemParams` method for access to elements params.\n- `trigger` method from `i-bem` block is **deprecated** in flavor of `emit` method.\n- `onFirst` method from `i-bem` block is **deprecated** in flavor of `once` method.\n- **Deprecated** field `e.block` that provided block-target of BEM-events was removed. Use `e.target` field instead.\n- Field `e.data.domElem` that provided DOM-element of block in DOM-events was removed. Use `$(e.currentTarget)` (provided by jQuery).\n- Introduces parameter for `findElem` method that allows to search elements\n  of particular block instance (in case of nested blocks with same name).\n- Introduces possibility to point particular function in `unbindFrom*` methods.\n- Introduces `objects` module for work with JS-objects. It contains methods: `extend`, `isEmpty`, `each`.\n- Introduces `functions` module for work with JS-functions. It contains methods: `isFunction`, `noop`.\n- Introduces `dom` module for work with DOM-tree.\n- Introduces `querystring` module for work with URL-based strings.\n- Introduces `loader_type_js` module for JS loading.\n- Introduces `vow` module for Promises/A+.\n- Introduces `next-tick` module as polyfill for `nextTick`, `setImmediate`, `setTimeout(0, ...` and etc.\n- Introduces `strings__escape` module for XML, HTML and attributes escaping.\n- `inherit` module now supports mixins.\n- Introduces `invokeAsap` parameter for `functions__throttle` module that allows to delay first invocation.\n"
  },
  {
    "path": "CHANGELOG.ru.md",
    "content": "# История изменений\n\n## 5.0.0\n\n### Несовместимые изменения\n\n- **Только ESM**: Вся кодовая база мигрирована с модульной системы `ym` (`modules.define`/`modules.require`) на нативные ES-модули (`import`/`export`). Зависимость `ym` удалена.\n- **Сборка через Vite**: ENB и все 17+ связанных пакетов заменены на [Vite](https://vite.dev/). Кастомный плагин `vite-plugin-bem-levels` обеспечивает сканирование BEM-уровней, разрешение виртуальных модулей `bem:*` и генерацию barrel-файлов для цепочек переопределений.\n- **jQuery 4.0**: Peer-зависимость `jquery` обновлена с `^3.x` до `^4.0.0`. Основное изменение API: `$.unique()` удалён — используйте `$.uniqueSort()`.\n- **Node.js 20+**: Минимальная поддерживаемая версия Node.js — 20 (было 8).\n- **Нативные промисы**: `vow` удалён. Весь код использует нативный `Promise`.\n- **Нативный тест-раннер**: Серверные тесты используют `node:test` и `node:assert` вместо `mocha`/`chai`.\n- **Playwright**: Браузерные тесты используют [Playwright](https://playwright.dev/) вместо `mocha-phantomjs` (PhantomJS мёртв).\n- **ESLint 10**: Линтинг мигрирован с `jshint`/`jscs` на ESLint 10 с flat-конфигурацией (`eslint.config.js`).\n- **GitHub Actions CI**: Travis CI заменён на GitHub Actions.\n- **Husky + lint-staged**: `git-hooks` заменён на `husky` и `lint-staged`.\n\n### Крупные изменения\n\n- Все `.vanilla.js` и `.js` файлы конвертированы в ES-модули с синтаксисом `import`/`export`.\n- Цепочки переопределений модулей (напр., `jquery` с расширениями pointer-событий) обрабатываются автоматически сгенерированными barrel-файлами через `vite-plugin-bem-levels`.\n- Блок `i18n` сохраняет свой API, но использует нативные ES-модули внутри.\n- Браузерные спек-тесты (28 файлов, 500+ тестов) запускаются через Vite dev server + Playwright.\n- Vite генерирует платформенно-специфичные сборки (`desktop`, `touch`) с jQuery как внешней зависимостью.\n\n### Удалённые пакеты\n\n- **Сборка**: `enb`, `enb-bem-techs`, `enb-magic-factory`, `enb-magic-platform`, `enb-bemxjst`, `enb-bemxjst-6x`, `enb-bemxjst-7x`, `enb-bemxjst-i18n`, `enb-bh`, `enb-bh-i18n`, `enb-borschik`, `enb-css`, `enb-js`, `enb-bem-docs`, `enb-bem-examples`, `enb-bem-specs`, `enb-bem-tmpl-specs`, `enb-bem-i18n`, `borschik`\n- **Линтинг**: `jscs`, `jscs-bem`, `jshint`, `jshint-groups`\n- **Тестирование**: `mocha-phantomjs`, `istanbul`, `chai-as-promised`\n- **Прочие**: `ym`, `vow`, `bower`, `git-hooks`, `gitbook-api`, `bem-naming`, `bem-walk`\n\n### Удалённые конфигурационные файлы\n\n- `.enb/` (конфигурация сборки ENB)\n- `.jshintrc`, `.jscs.json`, `.jshint-groups.js` (конфигурация линтинга)\n- `.bowerrc`, `bower.json` (конфигурация Bower)\n- `.travis.yml` (конфигурация Travis CI)\n- `.githooks/` (конфигурация git-hooks)\n\n## 4.3.1\n\n### В релиз вошли следующие исправления ошибок\n- Откатили изменение, из-за которого при попытке использования `lazyInit` при деклации модификатора выбрасывалось исключение ([#1594](https://github.com/bem/bem-core/pull/1594)).\n\n## 4.3.0\n\n### Крупные изменения\n\n- `jQuery` была обновлена до 3.2.1 и 1.12.4 ([#1587](https://github.com/bem/bem-core/pull/1587)).\n\n### В релиз вошли следующие исправления ошибок\n- Исправлена возможность форсировать lazy-инициализацию из разметки ([#1579](https://github.com/bem/bem-core/pull/1579)).\n- Исправлена возможность декларировать сущности с миксинами ([#1550](https://github.com/bem/bem-core/pull/1550)).\n- Теперь при попытке использования `lazyInit` при деклации модификатора будет выбрасываться исключение ([#1579](https://github.com/bem/bem-core/pull/1579)).\n- Исправлен баг в методе `isFunction` блока `functions`, который неправильно определял сложные функции ([#1577](https://github.com/bem/bem-core/pull/1577)).\n\n### Также в релиз вошли следующие изменения\n- Добавлен конфиг `.bemrc` ([#1568](https://github.com/bem/bem-core/pull/1568)).\n- Блок `vow` обновлен до версии 0.4.17 ([#1565](https://github.com/bem/bem-core/pull/1565)).\n- Блок `inherit` обновлен до версии 2.2.6 ([#1519](https://github.com/bem/bem-core/pull/1519)).\n- Удален приватный метод `_delInitedMod` блока `i-bem` ([#1523](https://github.com/bem/bem-core/pull/1523)).\n- Удален код, относящийся к старой версии `bem-tools` (https://github.com/bem/bem-core/commit/e57678b2d64a3b976a53af4a7fa09bf918685821).\n- Обновлены npm-зависимости ([#1589](https://github.com/bem/bem-core/pull/1589)).\n- Теперь тесты в CI запускаются и под Node.js 8 (https://github.com/bem/bem-core/commit/dd7e5344a64ad1595eab28febb2242134fcedbb3).\n- Добавлены недостающие тесты для блока `i-bem-dom` ([#1517](https://github.com/bem/bem-core/pull/1517)).\n- Добавлен gitbook ([#1569](https://github.com/bem/bem-core/pull/1569)).\n- Исправления JSDoc.\n- Исправления документации.\n\n\n## 4.2.1\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка с инвалидацией кеша элементов при изменении DOM ([#1487](https://github.com/bem/bem-core/issues/1487)).\n- Исправлена ошибка в `i-bem-dom__events`, приводившая к тому, что данные события не передавались в обработчик ([#1509](https://github.com/bem/bem-core/pull/1509)).\n- Исправлен метод `isEditable` модуля `dom`. Добавлены недостающие типы ([#1502](https://github.com/bem/bem-core/pull/1502)).\n\n### Также в релиз вошли следующие изменения\n- Исправлены синтаксические ошибки в JSDoc блока `i-bem-dom`.\n- Незначительные исправления документации.\n- Добавлено [CLA](https://github.com/bem/bem-core/blob/v4/CLA.md).\n\n## 4.2.0\n\n### Крупные изменения\n\n- В BEMHTML-шаблонах реализована поддержка `bem-xjst 8.x` ([#1486](https://github.com/bem/bem-core/issues/1485)).\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена работа метода `concat()` в i-bem-dom__collection ([#1488](https://github.com/bem/bem-core/issues/1488)).\n- Исправлена опечатка в `ua__dom`, приводящая к runtime-ошибке при использовании touch-уровня ([#1479](https://github.com/bem/bem-core/issues/1478)).\n- dist: Исправлена ошибка, при которой `i-bem-dom__init_auto` подключался в `no-autoinit`-бандл ([#1482](https://github.com/bem/bem-core/issues/1481)).\n\n### Также в релиз вошли следующие изменения\n\n- Теперь методы `findChildBlock`, `findChildBlocks`, `findParentBlock`, `findParentBlocks`, `findMixedBlock` и `findMixedBlocks` выбрасывают исключение, если блок передан строкой ([#1469](https://github.com/bem/bem-core/pull/1469/)).\n- Оптимизирована функция построения имен классов ([#1404](https://github.com/bem/bem-core/pull/1404)).\n- Добавлены переводы на английский ([#1483](https://github.com/bem/bem-core/pull/1483), [#1476](https://github.com/bem/bem-core/pull/1476), [#1475](https://github.com/bem/bem-core/pull/1475)).\n- Миграционный гайд: добавлено примечание о `bemTarget` ([#1491](https://github.com/bem/bem-core/issues/1472)).\n- Миграционный гайд: добавлено примечание о необходимых опциях для шаблонизаторов ([#1467](https://github.com/bem/bem-core/issues/1467)).\n- Исправлены ошибки в документации.\n\n## 4.1.1\n\n### В релиз вошли следующие исправления ошибок\n\n— Исправлена ошибка в блоке `ua` на уровне `touch.blocks`, которая приводила к невозможности предоставить ym-зависимости ([#1460](https://github.com/bem/bem-core/pull/1460)).\n\n## 4.1.0\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена проблема в `identify` с возвращением разного результата при вызове для `document` ([#1441](https://github.com/bem/bem-core/issues/1441)).\n- Исправлена проблема с переполнением глубины стека вызовов `modules.define` ([#1446](https://github.com/bem/bem-core/issues/1446)).\n- Исправлена поддержка эскейпинга в блоке `ua` ([#1435](https://github.com/bem/bem-core/issues/1435)).\n- Реализован обход бага в `Array.prototype.push` в `Opera 41`.\n- Исправлены pointer-события на iOS-устройствах ([#1253](https://github.com/bem/bem-core/issues/1253)).\n- Исправлена ошибка в `i-bem-dom__events`, приводящая к неправильной работе метода `once()` в некоторых случаях ([#1452](https://github.com/bem/bem-core/issues/1452)).\n\n### Также в релиз вошли следующие изменения\n\n- Реализована возможность задавать атрибут `lang` для `<html>` в блоке `page` ([#751](https://github.com/bem/bem-core/issues/751)).\n\n\n## 4.0.0\n\n### Изменения, ломающие обратную совместимость\n\n- Изменения в блоке `i-bem`. Подробнее в [MIGRATION.ru.md](MIGRATION.ru.md#Изменения-в-блоке-i-bem).\n- Изменения в блоке `querystring`. Подробнее в [MIGRATION.ru.md](MIGRATION.ru.md#Изменения-в-блоке-querystring).\n- Из модуля `identify` удален опциональный параметр `onlyGet` ([#1028](https://github.com/bem/bem-core/issues/1028)).\n- Из модуля `events` удалены все статические методы ([#1024](https://github.com/bem/bem-core/issues/1024)).\n- В классе `Event` модуля `events` удалено поле `result` ([#1023](https://github.com/bem/bem-core/issues/1023)).\n- Элемент `css` блока `page` больше не поддерживает автоматическое добавление условных комментариев для IE ([#379](https://github.com/bem/bem-core/issues/379)).\n\n## 3.2.0\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена проблема с переполнением глубины стека вызовов `modules.define` ([#1446](https://github.com/bem/bem-core/issues/1446)).\n- Исправлена поддержка эскейпинга в блоке `ua` ([#1435](https://github.com/bem/bem-core/issues/1435)).\n- Реализован обход бага в `Array.prototype.push` в `Opera 41`.\n\n### Также в релиз вошли следующие изменения\n\n- Реализована возможность задавать атрибут `lang` для `<html>` в блоке `page` ([#751](https://github.com/bem/bem-core/issues/751)).\n\n## 3.1.0\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлен баг в методе `getMods()` блока `i-bem` ([#1379](https://github.com/bem/bem-core/issues/1379)).\n\n### Крупные изменения\n\n- Шаблоны: реализована поддержка режима экранирования ([#1406](https://github.com/bem/bem-core/issues/1406)).\n\n### Также в релиз вошли следующие изменения\n\n- Мелкие исправления документации.\n\n## 3.0.1\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлены pointer-события на iOS-устройствах ([#1253](https://github.com/bem/bem-core/issues/1253)).\n\n## 3.0.0\n\n### Изменения, ломающие обратную совместимость\n\n- Удалены базовые шаблоны и документация для `BEMHTML` и `BEMTREE` ([#1258](https://github.com/bem/bem-core/issues/1258)). Следует использовать версию `bem-xjst` не ниже 6.3.0.\n- Расширения файлов BEMHTML-шаблонов переименованы с `*.bemhtml` на `*.bemhtml.js` ([#984](https://github.com/bem/bem-core/issues/984)). Необходимо убедиться, что в конфиге сборки поддерживается новое расширение.\n- Удален элемент `i-bem__i18n` ([#1304](https://github.com/bem/bem-core/issues/1304)). Для интернационализации следует использовать блок `i18n`.\n- `jquery__events_type_pointerclick` больше не использует библиотеку [FastClick](https://github.com/ftlabs/fastclick) ([#1088](https://github.com/bem/bem-core/issues/1088)).\n\n### Крупные изменения\n\n- `jQuery` была обновлена до 2.2.3 и 1.12.3 ([#1260](https://github.com/bem/bem-core/issues/1260)).\n\n### В релиз вошли следующие исправления ошибок\n\n- В блоке `page` на уровне `blocks.touch` исправлена ошибка с невалидным значением `user-scalable=0` для `<meta name=viewport>` ([#1294](https://github.com/bem/bem-core/issues/1294)).\n- Исправлена ошибка в `jquery__event_type_pointernative`, которая приводила к возникновению JS ошибки в IE8 ([1317](https://github.com/bem/bem-core/issues/1317)).\n\n### Также в релиз вошли следующие изменения\n\n- dist: Автоматическая инициализация блоков теперь опциональна ([#1271](https://github.com/bem/bem-core/issues/1271)).\n\n## 2.9.0\n\n### Крупные изменения\n\n- `jQuery` была обновлена до 2.2.0 и 1.12.0 ([#1249](https://github.com/bem/bem-core/issues/1249)).\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в `BEMHTML 1.x`, которая приводила к потере `this.mods` в `reapply()` ([#97](https://github.com/bem/bem-xjst/issues/97)).\n\n### Также в релиз вошли следующие изменения\n\n- Теперь `jquery__event_type_pointerpressrelease` предоставляет `originalEvent` ([#1254](https://github.com/bem/bem-core/issues/1254)).\n- dist: Поддержка `i18n` добавлена в dist ([#1212](https://github.com/bem/bem-core/issues/1212)).\n- Шаблон `page__css.bemhtml` был обновлен для поддержки новых версий `bem-xjst` ([#1228](https://github.com/bem/bem-core/issues/1228)).\n\n## 2.8.0\n\n### Крупные изменения\n\n- Реализован новый блок [i18n](https://github.com/bem/bem-core/tree/v2/common.blocks/i18n), реализующий интернационализацию проектов на bem-core ([#1074](https://github.com/bem/bem-core/issues/1074)).\n- Теперь `jQuery` по умолчанию подключается через `https` ([#1202](https://github.com/bem/bem-core/issues/1202)).\n- Удалена зависимость от `bemhtml-compat` ([#1186](https://github.com/bem/bem-core/issues/1186)). Пользователям `bem-tools` необходимо выполнить `npm i bemhtml-compat --save` для установки пакета на уровне проекта.\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в `loader_type_js`, допускавшая вызовы неопределенного обработчика ([#1159](https://github.com/bem/bem-core/pull/1159)).\n\n### Также в релиз вошли следующие изменения\n\n- BH-бандлы в `dist` теперь мимикрируют под BEMHTML ([#1210](https://github.com/bem/bem-core/issues/1210)).\n- Улучшены шаблоны `bem create` для `bemhtml`, `bemtree`, `vanilla.js` и `browser.js` ([#1183](https://github.com/bem/bem-core/issues/1183)).\n- `vow` обновлена до `0.4.10` ([#1056](https://github.com/bem/bem-core/issues/1056)).\n\n## 2.7.0\n\n### Крупные изменения\n\n- В `i-bem__dom` добавлен новый метод `detach` ([#1102](https://github.com/bem/bem-core/issues/1102)).\n- В `i-bem.bemhtml` добавлена поддержка вложенных миксов ([873](https://github.com/bem/bem-core/issues/873)).\n- В `i-bem.bemhtml` добавлены незначительные оптимизации, связанные с эскейпингом аттрибутов ([#961](https://github.com/bem/bem-core/issues/961)), ([#980](https://github.com/bem/bem-core/issues/980)) и ([#982](https://github.com/bem/bem-core/issues/982)).\n- В BEMHTML-шаблоны добавлена поддержка [bem-xjst](https://github.com/bem/bem-xjst) 2.x ([#1021](https://github.com/bem/bem-core/issues/1021)).\n- `clearfix` оптимизирован для работы в поддерживаемых библиотекой браузерах IE ([#722](https://github.com/bem/bem-core/issues/722)).\n- `jquery` обновлен до версий 2.1.4 и 1.11.3 ([#999](https://github.com/bem/bem-core/issues/999)).\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в `i-bem__dom`, из-за которой метод `findElem` не обновлял кэш ранее найденных\n  элементов ([#583](https://github.com/bem/bem-core/issues/583)).\n- Исправлена ошибка в `i-bem__dom`, приводящая к неправильной работе метода `dropElemCache` в некоторых граничных\n  случаях ([#1037](https://github.com/bem/bem-core/issues/1037)).\n- Исправлена ошибка в `i-bem__dom`, из-за которой вызов метода `setMod` не выставлял CSS-классы блоку в случае, если\n  на DOM-узеле был подмешан блок с пересекающимся окончанием в имени ([#1090](https://github.com/bem/bem-core/issues/1090)).\n- Исправлена ошибка в `page`, из-за которой на touch-уровнях не работало специализированное поле `zoom` ([#1020](https://github.com/bem/bem-core/issues/1020)).\n- Исправлена ошибка в `keyboard__codes`. Клавиши `insert` и `delete` были описаны неправильными\n  кодами ([#1002](https://github.com/bem/bem-core/issues/1002)).\n- Исправлена ошибка в `i-bem.bemhtml`, из-за которой неверно интерпретировались вложенные вызовы `applyNext` ([b1dc50c](https://github.com/bem/bem-core/commit/b1dc50c621b5659cff33daa4dd3f210b67cf25e1)).\n- Исправлена ошибка в `jquery__events_type_pointernative`, из-за которой события работали некорректно\n  в браузерах IE 11 и Edge ([#1066](https://github.com/bem/bem-core/issues/1066)).\n\n### Также в релиз вошли следующие изменения\n\n- Обновлена русская докуметация для всех блоков библиотеки. Документация доступна по адресу https://ru.bem.info/libs/bem-core/.\n- Прочие улучшения в документации к библиотеке.\n\n## 2.6.0\n\n### Крупные изменения\n\n- Предоставление модуля `i-bem__dom` теперь происходит после наступления события DOM ready ([#859](https://github.com/bem/bem-core/issues/859)).\n- Методы `setMod` и `hasMod` модуля `i-bem__dom` теперь явно преобразуют параметр `modVal` к строке,\n  если переданное значение не типа string или boolean ([#890](https://github.com/bem/bem-core/issues/890)).\n- В блок `page` добавлена возможность прокидывать атрибут `nonce`, для корректной работы инлайн-скриптов, в соответствии\n  со спецификацией Content Security Policy ([#882](https://github.com/bem/bem-core/issues/882)).\n- Добавлены шаблоны `page__conditional-comment` ([#551](https://github.com/bem/bem-core/issues/511)).\n- Модуль `vow` обновлен до версии 0.4.8 ([#837](https://github.com/bem/bem-core/issues/837)).\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в `i-bem.bemhtml`, из-за которой CSS-класс блока дублировался, в случае микса с этим же блоком\n  ([#792](https://github.com/bem/bem-core/issues/792)).\n- Исправлена ошибка в `loader_type_bundle`, из-за которой функция-обработчик успешного результата могла выполняться\n  после наступления таймаута ([67ff55f](https://github.com/bem/bem-core/commit/da5fdb9923e7e83e3ef9cd31aefc3967ff55fd3c)).\n- Исправлена ошибка в `i-bem__dom`, в некоторых случаях, приводящая к неправильной интерпретации строковых аргументов\n  в методах `append`, `prepend` и др. ([#852](https://github.com/bem/bem-core/issues/852)).\n- Исправлена ошибка в `jquery__event_type_winresize`, из-за которой неправильно определятся браузер MSIE ([#862](https://github.com/bem/bem-core/issues/862)).\n- Исправлена ошибка в `object`, из-за которой метод `extend` неправильно обрабатывал `null` в качестве значения\n  аргумента `target` ([#910](https://github.com/bem/bem-core/issues/910)).\n- Исправлена ошибка в `page`. Из BEMJSON было невозможно отключить добавление meta-тега `x-ua-compatible` ([#794](https://github.com/bem/bem-core/issues/794)).\n\n### Также в релиз вошли следующие изменения\n\n- Таймаут в `loader_type_bundle` увеличен до 30000 мс ([4e27422](https://github.com/bem/bem-core/commit/000c6af02bfae4506fa460168de16d4e27422393)).\n- Исправлены незначительные ошибки в русской документации блоков.\n\n## 2.5.1\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в `jquery__pointerpress` и `jquery__pointerrelease` из-за которой события не работали в браузере\n  Internet Explorer 8 ([#792](https://github.com/bem/bem-core/issues/792)).\n- Исправлена ошибка в `jquery__pointernative`. События `pointerenter` и `pointerleave` не должны всплывать\n  по DOM-дереву ([#801](https://github.com/bem/bem-core/issues/801)).\n- Исправлена ошибка в `loader_type_bundle`. После загрузки, CSS-бандл добавлялся в самый верх HTML-тега `<head>`, из-за чего\n  CSS-правила из содержимого бандла могли работать не корректно ([#808](https://github.com/bem/bem-core/issues/808)).\n- Исправлена ошибка в BH-шаблоне `ua`. Шаблон не позволял вставить содержимое блока из входного\n  BEMJSON ([#734](https://github.com/bem/bem-core/pull/734)).\n- Исправлена ошибка в `page`, приводящая к неработоспособности добавленных на страницу условных комментариев для браузера\n  Internet Explorer ([#781](https://github.com/bem/bem-core/pull/781)).\n\n### Также в релиз вошли следующие изменения\n\n- `jquery` обновлен до версий 2.1.3 и 1.11.2 ([#778](https://github.com/bem/bem-core/pull/788)).\n- Добавлена документация на русском языке для модулей: `clearfix`, `cookie`, `identify`, `idle`, `inherit`, `keyboard`,\n`loader`, `next-tick`, `string` and `tick`.\n- Исправлена документация на русском языке для `i-bem.js`.\n- Обновлено руководство на английском языке по технологиям BEMHTML и BEMJSON.\n\n## 2.5.0\n\n### Крупные изменения\n\n- Код библиотеки переведен на использование лицензии [MPL 2.0](https://www.mozilla.org/MPL/2.0/) ([#443](https://github.com/bem/bem-core/issues/443)).\n- В модуль `loader_type_js` добавлена возможность указывать функцию-обработчик ошибок ([#672](https://github.com/bem/bem-core/issues/672)).\n- Класс `BEMContext` добавлен в export-параметры функции `oninit` в базовых шаблонах `i-bem.bemtree` ([#602](https://github.com/bem/bem-core/issues/602)).\n- В `BEMContext` BEMTREE добавлен статический метод `reapply` по аналогии с BEMHTML ([#706](https://github.com/bem/bem-core/pull/706)).\n- Добавлены bh-шаблоны блока `page` для уровней touch ([#689](https://github.com/bem/bem-core/pull/689)).\n- npm-модуль [bem-xjst](https://github.com/bem/bem-xjst) обновлен до версии 0.9.0 ([#709](https://github.com/bem/bem-core/pull/709)).\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в `i-bem__dom`, из-за которой метод `findBlocksInside` мог возвращать блоки, которые еще не были\n  инициализированы ([#699](https://github.com/bem/bem-core/issues/699)).\n- Исправлена ошибка в `tick`, позволявшая вызвать метод `stop` без освобождения внутреннего таймера ([#694](https://github.com/bem/bem-core/issues/694)).\n- Исправлена ошибка в `i-bem.bemhtml`, из-за которой на элементы блока добавлялся CSS-класс `i-bem` ([#633](https://github.com/bem/bem-core/issues/633)).\n- Исправлена ошибка в технологии `html-from-bemtree`, из-за которой в контексте BEMTREE-шаблонов не было глобальных объектов\n  `vow`, `console`, `setTimeout` ([#438ebb8](https://github.com/bem/bem-core/commit/438ebb8f828e26977592e26511e8aad15176d7a4)).\n\n### Также в релиз вошли следующие изменения\n\n- Добавлено английское руководство по технологии BEMJSON.\n- Обновлена русская документация для i-bem.js. Теперь документация соответсвует текущему API библиотеки.\n- Обновлена документация для технологий BEMHTML/BEMTREE.\n\n## 2.4.0\n\n### Крупные изменения\n\n- npm-модуль [bem-xjst](https://github.com/bem/bem-xjst) обновлен до версии 0.8.0; [bemhtml-compat](https://github.com/bem/bemhtml-compat)\n  обновлен до 0.0.11.\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в `jquery__event_type_pointerpressrelease`, из-за которой события `pointerpress` / `pointerrelease` генерировались\n  на нажатие любой кнопки мыши ([#607](https://github.com/bem/bem-core/issues/607)).\n- Исправлена ошибка в `i-bem__dom.js`, из-за которой в некоторых случаях не происходил вызов базового метода\n  `live` ([#608](https://github.com/bem/bem-core/issues/608)).\n\n### Также в релиз вошли следующие изменения\n\n- Добавлена английская документация на JS-синтаксис BEMHTML.\n\n## 2.3.0\n\n### Крупные изменения\n\n- Добавлена новая реализация pointer-событий на основе полифилов из [Polymer](http://www.polymer-project.org/) ([#567](https://github.com/bem/bem-core/pull/567)).\n- Добавлена возможность в `i-bem__dom.js` указывать дополнительные данные о событии в методах `bindTo*` ([#568](https://github.com/bem/bem-core/issues/568)).\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в `i-bem.bemhtml`, из-за которой было невозможно использовать микс в виде одного объекта (не массива) одновременно в BEMJSON и BEMHTML ([#555](https://github.com/bem/bem-core/issues/555)).\n- Исправлена ошибка в BEMHTML-шаблоне блока `page`, из-за которой не выполнялись стандартные моды, и исправлена регрессия в шаблоне на touch-уровне ([516](https://github.com/bem/bem-core/issues/516)).\n\n## 2.2.4\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в `i-bem.js`, из-за которой событие об изменении модификатора генерировалось,\n  даже если обработчик `beforeSetMod` предотвращал изменение ([#546](https://github.com/bem/bem-core/pull/546)).\n- В случае возникновения ошибки в процессе декодирования строки, модуль `querystring__uri` теперь возвращает\n  оригинальную строку ([#554](https://github.com/bem/bem-core/pull/554)).\n\n## 2.2.3\n\n### В релиз вошли следующие исправления ошибок\n\n- В модуле `i-bem__dom` был исправлен процесс удаления блока для предотвращения нежелательной повторной\n  инициализации блока ([#540](https://github.com/bem/bem-core/issues/540)).\n- Исправлена ошибка в модуле `jquery__event_type_pointer`, из-за которой нативные события мыши ошибочно замещались\n  на pointer-события ([#534](https://github.com/bem/bem-core/issues/534)).\n- `unbindFrom*`-методы в модуле `i-bem__dom` теперь поддерживают отписывание от нескольких событий\n  за вызов ([#533](https://github.com/bem/bem-core/issues/533)).\n- Добавлена недостающая зависимость от модуля `functions` в модуле `events` ([#532](https://github.com/bem/bem-core/issues/532)).\n\n## 2.2.2\n\n### В релиз вошли следующие исправления ошибок\n\n- Исправлена ошибка в модуле `i-bem__dom` приводящая к повторной инициализации блока на DOM-узле, отмеченном как\n  удаленный ([#518](https://github.com/bem/bem-core/issues/518)).\n- Исправлена ошибка в модуле `i-bem`, из-за которой невозможно было подписаться на событие о выставлении модификатора в\n  значение `false` ([#529](https://github.com/bem/bem-core/issues/529)).\n- Модуль `jquery` обновлен до версий 2.1.1 и 1.11.1 ([#515](https://github.com/bem/bem-core/issues/515)).\n\n## 2.2.1\n\n- Исправлена ошибка в модуле `jquery__event_type_pointerpressrelease`, из-за которой событие `pointerpress` генерировалось\n  дважды на каждое событие `mousedown` в IE10 ([#505](https://github.com/bem/bem-core/issues/505)).\n\n## 2.2.0\n\n### Крупные изменения\n\n- Добавлен новый модуль `keyboard__codes` ([#431](https://github.com/bem/bem-core/issues/431)).\n- Класс `BEMContext` добавлен в export-параметры функции `oninit` в базовых шаблонах `i-bem.bemhtml` ([#485](https://github.com/bem/bem-core/pull/485)).\n- Добавлена возможность декларировать инстанс элемента используя класс блока ([#481](https://github.com/bem/bem-core/issues/481)).\n- Исправлено поведение метода `isSimple` класса BEMContext в  в базовых шаблонах `i-bem.bemhtml` ([#432](https://github.com/bem/bem-core/pull/432)).\n- Исправлена ошибка в методе `liveUnbindFrom` модуля `BEMDOM` ([#476](https://github.com/bem/bem-core/pull/476)).\n- Исправлена ошибка в методе `isFocusable` модуля `dom`, возникающая если переданный `domElem` является ссылкой\n  с атрибутом `tabindex`, но без атрибута `href` ([#501](https://github.com/bem/bem-core/issues/501)).\n- Исправлена ошибка возникающая в процессе декларации БЭМ-блока как модуля, если был подключен\n  модуль `i-bem__dom_elem-instances` ([#479](https://github.com/bem/bem-core/issues/479)).\n- В модуле `i-bem__dom_init_auto` добавлено временное решение для проблем с производительностью ренедеринга при инициализации блоков\n  в Chrome-браузерах ([#486](https://github.com/bem/bem-core/issues/486)).\n- Модуль `vow.js` перенесен в `vow.vanilla.js` ([#412](https://github.com/bem/bem-core/issues/412)).\n\n### Также в релиз вошли следующие изменения\n\n- Модуль `vow` обновлен до версии 0.4.3 ([#504](https://github.com/bem/bem-core/pull/504)).\n- Добавлена русская документация на технологию BEMTREE ([#500](https://github.com/bem/bem-core/pull/500)).\n- Обновлена русская документация на JavaScript-синтаксис BEMHTML ([#471](https://github.com/bem/bem-core/pull/471)).\n- Добавлен референс на API JavaScript-модулей. См. ветку `v2-jsdoc` ([#478](https://github.com/bem/bem-core/pull/478)).\n\n## 2.1.0\n\n### Крупные изменения\n\n- Исправлена ошибка в `i-bem.js`, из-за которой событие об изменении модификатора происходило до того, как будет\n  вызван обработчик реакции на изменение этого модификатора в `onSetMod` ([#454](https://github.com/bem/bem-core/issues/454)).\n- Свойства `this.mods` и `this.ctx.mods` базового шаблона `i-bem.bemhtml` теперь используют\n  один и тот же объект ([#441](https://github.com/bem/bem-core/issues/441)).\n- Модуль [inherit](https://github.com/dfilatov/inherit) обновлен до версии 2.2.1 ([#466](https://github.com/bem/bem-core/issues/466)).\n- Исправлен порядок тегов секции `head` в шаблоне `page.bemhtml` ([#465](https://github.com/bem/bem-core/pull/465)).\n\n### Также в релиз вошли следующие изменения\n\n- В русскую документацию к `i-bem.js` добавлено описание поля `baseMix` ([#461](https://github.com/bem/bem-core/pull/461)).\n- CDN-хост внешних ресурсов изменен на `yastatic.net` ([#444](https://github.com/bem/bem-core/issues/444)).\n  Все ресурсы все так же доступны с хоста `yandex.st`. Физически `yandex.st` и `yastatic.net` находятся на\n  одних и тех же серверах. Различие только в DNS-записях.\n- Добавлен базовый BEMHTML-шаблон для команды `bem create` технологии `bemhtml` ([#277](https://github.com/bem/bem-core/issues/277)).\n- Прекращен автоматический запуск тестов под Node.js 0.8 в [Travis CI](http://travis-ci.com) ([#455](https://github.com/bem/bem-core/issues/455)).\n- Иконка статуса автосборки Travis [заменена на SVG](http://blog.travis-ci.com/2014-03-20-build-status-badges-support-svg/) :)\n\n## 2.0.0\n\n### Изменения, ломающие обратную совместимость\n\n- Из `i-bem.js` и `i-bem__dom.js` удалены все **deprecated** методы ([#318](https://github.com/bem/bem-core/issues/318)):\n\n  * `destruct`, используйте `onSetMod js ''`;\n  * `extractParams`, используйте `elemParams`;\n  * `trigger`, используйте `emit`;\n  * `afterCurrentEvent`, используйте модуль `next-tick`;\n  * `channel`, используйте модуль `events__channels`;\n  * `changeThis`, используйте нативный `Function.prototype.bind`.\n\n- Из `i-bem.js` убраны события `init` и `destruct`. Вместо них следует использовать события об изменении модификатора\n  (см. «Крупные изменения»).\n- Блок `ecma` перенесен [в отдельный репозиторий](http://github.com/bem/es5-shims); ES5-shims следует использовать\n  для IE < 9 ([#230](https://github.com/bem/bem-core/issues/230)).\n- Модуль `vow` обновлен до мажорной версии 0.4.1 ([#350](https://github.com/bem/bem-core/issues/350)).\n  См. [изменения в Vow](https://github.com/dfilatov/vow/blob/0.4.1/CHANGELOG.md).\n- В `i-bem.bemhtml` добавлена поддержка vow@0.4 ([#385](https://github.com/bem/bem-core/issues/385)).\n\n### Крупные изменения\n\n- Добавлена возможность декларировать BEMDOM-блоки как модули [ym](https://github.com/ymaps/modules) ([#382](https://github.com/bem/bem-core/issues/382)).\n- В `i-bem.js` добавлены события об изменении модификатора ([#357](https://github.com/bem/bem-core/issues/357)).\n- Добавлена поддержка использования строковых значений в качестве аргумента в методах `BEMDOM.init` ([#419](https://github.com/bem/bem-core/issues/419))\n  и `BEMDOM.update` ([#420](https://github.com/bem/bem-core/issues/420)).\n- Методы `i-bem__dom.js` `replace`, `append`, `prepend`, `before`, `after` теперь возвращают новый контекст,\n  а `update` – изменённый ([#410](https://github.com/bem/bem-core/issues/410)).\n- В `loader` добавлен модификатор `_type_bundle` ([#358](https://github.com/bem/bem-core/issues/358)).\n- jQuery обновлен до версии 2.1.0. Для IE < 9 — до версии 1.11.0 ([#356](https://github.com/bem/bem-core/issues/356)).\n\n### Также в релиз вошли следующие изменения\n\n- Базовые шаблоны в `i-bem.bemhtml` используют конкатенацию строк вместо наполнения внутреннего буфера ([#401](https://github.com/bem/bem-core/issues/401)).\n- jQuery больше не удаляет себя из глобальной области видимости, если присутствует на странице ([#349](https://github.com/bem/bem-core/issues/349)).\n- `jquery__event_type_pointerclick.js` перемещен с уровня `touch.blocks` на уровень `common.blocks` ([#393](https://github.com/bem/bem-core/issues/393)).\n- Модификаторы `i-bem_elem-instances_yes` и `i-bem__dom_elem-instances_yes` приведены к булевому стилю ([#352](https://github.com/bem/bem-core/issues/352)).\n- Исправлена ошибка в шаблоне блока `page`, возникающая при использовании development-режима BEMHTML ([#417](https://github.com/bem/bem-core/issues/417)).\n- Для поддержки Android 2.3 внутри `i-bem.js` отказались от использований `Function.prototype.bind` ([#404](https://github.com/bem/bem-core/issues/404)).\n- Исправлены ошибки в модуле технологии `browser-js+bemhtml` ([#392](https://github.com/bem/bem-core/issues/392)).\n- NPM-модуль `ym` обновлен до версии [0.0.15](https://github.com/ymaps/modules/releases) ([#414](https://github.com/bem/bem-core/issues/414)).\n\n## 1.2.0\n\n### Крупные изменения\n\n- BEM-блоки инициируют событие `destruct` в процессе удаления ([#370](https://github.com/bem/bem-core/issues/370)).\n- Исправлены полифилы для `pointerevents` ([#354](https://github.com/bem/bem-core/pull/354)).\n\n### Также в релиз вошли следующие изменения\n\n- JSDoc блоков исправлен в соответствии с поддержкой [bem-jsd](github.com/bem/bem-jsd) ([#335](https://github.com/bem/bem-core/issues/335)).\n- Референс на BEMHTML обновлен для соответствия JavaScript-синтаксису шаблонизатора ([#355](https://github.com/bem/bem-core/pull/355)).\n- Переход на менеджер зависимостей [bower](http://bower.io) ([#367](https://github.com/bem/bem-core/issues/367)).\n\n## 1.1.0\n\n### Крупные изменения\n\n- Для современных браузеров `jquery__config` подключает jQuery 2.x ([#319](https://github.com/bem/bem-core/issues/319)).\n- Добавлена возможность использовать произвольный BEMJSON в качестве значения атрибутов в BEMHTML ([#290](https://github.com/bem/bem-core/issues/290)).\n- Исправлены зависимости в `i-bem__collection` ([#292](https://github.com/bem/bem-core/issues/292)).\n- Удалены CSS-стили блока `page` из уровня `touch.blocks` ([#306](https://github.com/bem/bem-core/issues/306)).\n- Исправлена ошибка в BEMHTML-шаблоне блока `page`, приводящая к зацикливанию шаблонизатора\n  в production-режиме ([#309](https://github.com/bem/bem-core/issues/309)).\n- Исправлена возможная ошибка в `next-tick`, возникающая при вставке скрипта в DOM в IE<9 ([#324](https://github.com/bem/bem-core/issues/324)).\n- Исправлена ошибка в инициализации плагина `FastClick` в модуле `jquery__event_type_pointerclick`\n  на уровне `touch.blocks` ([#332](https://github.com/bem/bem-core/issues/332)).\n- Исправлена ошибка в технологии `node.js` в Windows ([#274](https://github.com/bem/bem-core/issues/274)).\n- Исправлена ошибка в `onElemSetMod` в `i-bem__dom_elem-instances` ([#340](https://github.com/bem/bem-core/issues/340)).\n- В технологии `bemhtml` используется [bem-xjst](https://github.com/bem/bem-xjst) ([#329](https://github.com/bem/bem-core/issues/329)).\n\n### Также в релиз вошли следующие изменения\n\n- Модуль [ym](https://github.com/ymaps/modules) обновлен до версии 0.0.12 ([#326](https://github.com/bem/bem-core/issues/326)).\n- В ядре локализации `i-bem__i18n` отключен вывод сообщений о неизвестных ключах, если не включен\n  debug-режим ([#285](https://github.com/bem/bem-core/issues/285)).\n- Инфраструктура сборки тестов и примеров переведена\n  на [bem-pr@v0.5.x](https://github.com/narqo/bem-pr/blob/0.5.3/HISTORY.md) ([#323](https://github.com/bem/bem-core/issues/323)).\n- Исправлен jsdoc для метода `dropElemCache()` в `i-bem__dom` ([#296](https://github.com/bem/bem-core/issues/296)).\n- Доработана документация для блока `i-bem.js` на русском языке.\n- В README проекта добавлен [список поддерживаемых браузеров](https://github.com/bem/bem-core/blob/v1/README.ru.md#%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC%D1%8B%D0%B5-%D0%B1%D1%80%D0%B0%D1%83%D0%B7%D0%B5%D1%80%D1%8B).\n\n## 1.0.0\n\n### Крупные изменения\n\n- Переход на модульную систему [ym](https://github.com/ymaps/modules).\n- Из `i-bem`, `i-bem__dom` убраны все deprecated-методы.\n- `i-bem` больше не зависит от jQuery. `i-bem__dom` продолжает зависеть от jQuery.\n- BEMHTML-шаблоны можно писать с использованием [JS-синтаксиса](https://gist.github.com/veged/6150760).\n- Новая технология `bemtree` (на базе [bem-xjst](https://github.com/bem/bem-xjst)) для описания процесса\n  динамического построения БЭМ-дерева.\n- Новая технология `vanilla.js` для описания JS-реализации модулей, не зависящей от конкретного JavaScript движка.\n- Новые технологии `browser.js` и `node.js` для описания JS-реализаций модулей (блоков) в соответствующих движках.\n  Для совместимости с существующим кодом считаем, что файлы с расширением `.js` содержат реализацию блоков\n  в технологии `browser.js`.\n- Система модульного тестирования и примеров для блоков в библиотеке.\n- Появились полифилы (`jquery__event_type_pointer` и `jquery__event_type_pointerclick` как jQuery-плагины),\n  позволяющие использовать универсальные события для десктопных и тач-интерфейсов.\n- Плагин для jQuery, позволяющий навешивать обработчик события на нажатие левой кнопки мыши, становится модулем `jquery__pointerclick`.\n- В `i-bem` и BEMHTML добавлена поддержка простых модификаторов (модификаторов без значений).\n\n### Также в релиз вошли следующие изменения\n\n- Все блоки-модули, кроме `i-bem`, избавились от префиксов.\n- Блок `i-bem__dom` становится модулем `i-bem__dom`. Все BEM.DOM-блоки должны теперь доопределять\n  этот модуль ([пример](https://github.com/bem/bem-core/blob/v1/common.bundles/index/blocks/b-square/b-square.js)).\n- Метод для декларации блоков (`.decl()`) больше не принимает первым параметром объект с полем `name`.\n  Теперь обязательная форма записи с полем `block`: `BEM.decl({ block: 'b1', modName: 'm', modVal: 'v' }, ...)`.\n- Вместо метода `afterCurrentEvent` у блоков появился метод `nextTick`, который проверят существование блока в момент исполнения колбэка. `BEM.afterCurrentEvent` теперь **deprecated**.\n- Вместо `BEM.channel` появился отдельный модуль `channels`. `BEM.channel` теперь **deprecated**.\n- Метод `changeThis` помечен как **deprecated**. Используйте нативный `bind`.\n- Метод `del` удален из блока `i-bem`.\n- Метод `getWindowSize` удален из блока `i-bem__dom`. Используйте `BEMDOM.win.width()` и `BEMDOM.win.height()`.\n- Добавлен модуль-обертка `jquery`, предоставляющий jQuery. Модуль либо предоставляет jQuery, уже присутствующий на странице, либо сам его загружает (версию 1.10.1).\n- `$.observable` становится модулем `events` и больше не зависит от jQuery.\n- `$.inherit` становится модулем `inherit` и больше не зависит от jQuery.\n- `$.identify` становится модулем `identify` и больше не зависит от jQuery.\n- `$.throttle` разбивается на два модуля: `functions__throttle` и `functions__debounce`, которые больше не зависят от jQuery.\n- `$.decodeURI`, `$.decodeURIComponent` переезжают в модуль `querystring__uri` и больше не зависят от jQuery.\n- `$.cookie` становится модулем `cookie` и больше не зависит от jQuery.\n- Вместо `$.browser` появился модуль `ua` с аналогичным интерфейсом.\n- Блок `i-system` разбит на 2 модуля: `idle` и `tick`.\n- Вместо события `leftclick` следует использовать `pointerclick` (предоставляемый полифилом `jquery__event_type_pointerclick`).\n- Триггеры на установку модификаторов теперь разделены на две группы: до установки модификатора (`beforeSetMod` и `beforeElemSetMod`) и после (`onSetMod` и `onElemSetMod`). Отмена установки модификатора теперь возможна только из триггеров первой группы.\n- Использовать конструкцию `{ onSetMod : { js : function() { ... } } }` в качестве конструктора теперь **deprecated**, необходимо использовать `onSetMod: { js : { inited : ... } } }`.\n- Вместо метода `destruct` в `i-bem` появился зеркальный метод\n  для `onSetMod: { js : { inited : ... } } }` — `{ onSetMod : { js : { '' : ... } } }`.\n  Метод `destruct` теперь **deprecated**.\n- Метод `exractParams` в `i-bem__dom` теперь **deprecated**, для доступа к параметрам элементов нужно использовать метод `elemParams`.\n- Метод `trigger` в `i-bem` теперь **deprecated**, нужно использовать `emit`.\n- Метод `onFirst` в `i-bem` теперь **deprecated**, нужно использовать `once`.\n- Удалено **deprecated** поле `e.block`, представляющее блок-источник события для BEM-событий. Вместо него следует использовать поле `e.target`.\n- Для доступа к DOM-элементу блока в обработчике DOM-событий теперь нужно использовать поле `currentTarget`, предоставляемое jQuery. Вместо `e.data.domElem`нужно писать `$(e.currentTarget)`.\n- В методе `findElem` добавлен параметр, позволяющий находить элемента блока с учетом вложенных блоков.\n- Добавлена возможность указывать конкретную функцию для отписки от событий в методах `unbindFrom*`.\n- Добавлен модуль `objects` для работы с JS-объектами (содержит методы `extend`, `isEmpty`, `each`).\n- Добавлен модуль `functions` для работы с JS-функциями (содержит методы `isFunction`, `noop`).\n- Добавлен модуль `dom` для хелперов при работе с DOM.\n- Добавлен модуль `querystring` для работы с урлами.\n- Добавлен модуль `loader_type_js` для загрузки JS.\n- Добавлен модуль `vow` для работы с промисами.\n- Добавлен модуль `next-tick` для полифила `nextTick`, `setImmediate`, `setTimeout(0, ...` и т.п..\n- Добавлен модуль `strings__escape`, содержащий методы для эскейпинга XML, HTML и атрибутов.\n- Модуль `inherit` теперь поддерживает миксины.\n- В модуле `functions__throttle` добавлен параметр `invokeAsap`, позволяющий отложить первое исполнение.\n"
  },
  {
    "path": "CLA.md",
    "content": "# Notice to external contributors\n\n\n## General info\n\nHello! In order for us (YANDEX LLC) to accept patches and other contributions from you, you will have to adopt our Yandex Contributor License Agreement (the “**CLA**”). The current version of the CLA you may find here:\n1) https://yandex.ru/legal/cla/?lang=en (in English) and \n2) https://yandex.ru/legal/cla/?lang=ru (in Russian).\n\nBy adopting the CLA, you state the following:\n\n* You obviously wish and are willingly licensing your contributions to us for our open source projects under the terms of the CLA,\n* You has read the terms and conditions of the CLA and agree with them in full,\n* You are legally able to provide and license your contributions as stated,\n* We may use your contributions for our open source projects and for any other our project too,\n* We rely on your assurances concerning the rights of third parties in relation to your contributes.\n\nIf you agree with these principles, please read and adopt our CLA. By providing us your contributions, you hereby declare that you has already read and adopt our CLA, and we may freely merge your contributions with our corresponding open source project and use it in further in accordance with terms and conditions of the CLA.\n\n## Provide contributions \n\nIf you have already adopted terms and conditions of the CLA, you are able to provide your contributes. When you submit your pull request, please add the following information into it:\n\n```\nI hereby agree to the terms of the CLA available at: [link].\n```\n\nReplace the bracketed text as follows:\n* `[link]` is the link at the current version of the CLA (you may add here a link https://yandex.ru/legal/cla/?lang=en (in English) or a link https://yandex.ru/legal/cla/?lang=ru (in Russian).\n\nIt is enough to provide us such notification at once. \n\n## Other questions\n\nIf you have any questions, please mail us at opensource@yandex-team.ru.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to contribute\n\n1. [Create an issue](https://github.com/bem/bem-core/issues/new) with a proper description.\n2. Decide which version needs your changes.\n3. Create a feature-branch with an issue number and a version (`issues/<issue_number>@v<version_number>`) based on a version branch.\n   For example, for an issue #42 and a version #1: `git checkout -b issues/42@v1 v1`.\n   If you need changes for several versions, each of them has to have a feature branch.\n4. Commit changes accordingly to [CLA](CLA.md) and `push`. Rebase your branch on a corresponding version branch if it's needed.\n5. Create a pull-request from your feature branch; or several pull-requests if you changed several versions.\n6. Link your pull request with an issue number any way you like. A comment will work perfectly.\n7. Wait for your pull request and the issue to be closed ;-)\n\n## Contributors\n\nThe list of contributors is available at https://github.com/bem/bem-core/graphs/contributors. You may also get it with `git log --pretty=format:\"%an <%ae>\" | sort -u`.\n"
  },
  {
    "path": "CONTRIBUTING.ru.md",
    "content": "# Внесение изменений\n\n1. [Создать issue](https://github.com/bem/bem-core/issues/new) с описанием сути изменений.\n2. Определить в какую версию необходимо внести изменения.\n3. Сделать feature-branch с указанием номера issue и версии (`issues/<номер_issue>@v<номер_версии>`) на основе ветки версии.\n   Например, для issue с номером 42 и версией 1: `git checkout -b issues/42@v1 v1`. Если изменения нужно внести в несколько версий,\n   то для каждой из версий создаётся отдельная ветка.\n4. Сделать изменения, закоммитить согласно с [CLA](CLA.md) и сделать push. Если это необходимо, то нужно сделать rebase от базовой ветки версии.\n5. Создать pull-request на основе созданной ветки (или несколько pull-request-ов для случая изменений в нескольких версиях).\n6. Любым способом связать pull-request и issue (например, c помощью комментария).\n7. Ждать закрытия pull-request и issue ;-)\n\n\n## Контрибьюторы\n\nСписок контрибьютеров данного проекта доступен по ссылке https://github.com/bem/bem-core/graphs/contributors. Вы так же можете получить его с помощью команды `git log --pretty=format:\"%an <%ae>\" | sort -u`.\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "© YANDEX LLC, 2012\n\nThe Source Code called `bem-core` available at https://github.com/bem/bem-core is subject to the terms of the Mozilla Public License, v. 2.0 (hereinafter - MPL). The text of MPL is the following:\n\nMozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. \"Contributor\"\n\n     means each individual or legal entity that creates, contributes to the\n     creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n\n     means the combination of the Contributions of others (if any) used by a\n     Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n\n     means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n\n     means Source Code Form to which the initial Contributor has attached the\n     notice in Exhibit A, the Executable Form of such Source Code Form, and\n     Modifications of such Source Code Form, in each case including portions\n     thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n     means\n\n     a. that the initial Contributor has attached the notice described in\n        Exhibit B to the Covered Software; or\n\n     b. that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the terms of\n        a Secondary License.\n\n1.6. \"Executable Form\"\n\n     means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n\n     means a work that combines Covered Software with other material, in a\n     separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n\n     means this document.\n\n1.9. \"Licensable\"\n\n     means having the right to grant, to the maximum extent possible, whether\n     at the time of the initial grant or subsequently, any and all of the\n     rights conveyed by this License.\n\n1.10. \"Modifications\"\n\n     means any of the following:\n\n     a. any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered Software; or\n\n     b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. \"Patent Claims\" of a Contributor\n\n      means any patent claim(s), including without limitation, method,\n      process, and apparatus claims, in any patent Licensable by such\n      Contributor that would be infringed, but for the grant of the License,\n      by the making, using, selling, offering for sale, having made, import,\n      or transfer of either its Contributions or its Contributor Version.\n\n1.12. \"Secondary License\"\n\n      means either the GNU General Public License, Version 2.0, the GNU Lesser\n      General Public License, Version 2.1, the GNU Affero General Public\n      License, Version 3.0, or any later versions of those licenses.\n\n1.13. \"Source Code Form\"\n\n      means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n\n      means an individual or a legal entity exercising rights under this\n      License. For legal entities, \"You\" includes any entity that controls, is\n      controlled by, or is under common control with You. For purposes of this\n      definition, \"control\" means (a) the power, direct or indirect, to cause\n      the direction or management of such entity, whether by contract or\n      otherwise, or (b) ownership of more than fifty percent (50%) of the\n      outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n     Each Contributor hereby grants You a world-wide, royalty-free,\n     non-exclusive license:\n\n     a. under intellectual property rights (other than patent or trademark)\n        Licensable by such Contributor to use, reproduce, make available,\n        modify, display, perform, distribute, and otherwise exploit its\n        Contributions, either on an unmodified basis, with Modifications, or\n        as part of a Larger Work; and\n\n     b. under Patent Claims of such Contributor to make, use, sell, offer for\n        sale, have made, import, and otherwise transfer either its\n        Contributions or its Contributor Version.\n\n2.2. Effective Date\n\n     The licenses granted in Section 2.1 with respect to any Contribution\n     become effective for each Contribution on the date the Contributor first\n     distributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\n     The licenses granted in this Section 2 are the only rights granted under\n     this License. No additional rights or licenses will be implied from the\n     distribution or licensing of Covered Software under this License.\n     Notwithstanding Section 2.1(b) above, no patent license is granted by a\n     Contributor:\n\n     a. for any code that a Contributor has removed from Covered Software; or\n\n     b. for infringements caused by: (i) Your and any other third party's\n        modifications of Covered Software, or (ii) the combination of its\n        Contributions with other software (except as part of its Contributor\n        Version); or\n\n     c. under Patent Claims infringed by Covered Software in the absence of\n        its Contributions.\n\n     This License does not grant any rights in the trademarks, service marks,\n     or logos of any Contributor (except as may be necessary to comply with\n     the notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\n     No Contributor makes additional grants as a result of Your choice to\n     distribute the Covered Software under a subsequent version of this\n     License (see Section 10.2) or under the terms of a Secondary License (if\n     permitted under the terms of Section 3.3).\n\n2.5. Representation\n\n     Each Contributor represents that the Contributor believes its\n     Contributions are its original creation(s) or it has sufficient rights to\n     grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\n     This License is not intended to limit any rights You have under\n     applicable copyright doctrines of fair use, fair dealing, or other\n     equivalents.\n\n2.7. Conditions\n\n     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in\n     Section 2.1.\n\n\n3. Responsibilities\n\n3.1. Distribution of Source Form\n\n     All distribution of Covered Software in Source Code Form, including any\n     Modifications that You create or to which You contribute, must be under\n     the terms of this License. You must inform recipients that the Source\n     Code Form of the Covered Software is governed by the terms of this\n     License, and how they can obtain a copy of this License. You may not\n     attempt to alter or restrict the recipients' rights in the Source Code\n     Form.\n\n3.2. Distribution of Executable Form\n\n     If You distribute Covered Software in Executable Form then:\n\n     a. such Covered Software must also be made available in Source Code Form,\n        as described in Section 3.1, and You must inform recipients of the\n        Executable Form how they can obtain a copy of such Source Code Form by\n        reasonable means in a timely manner, at a charge no more than the cost\n        of distribution to the recipient; and\n\n     b. You may distribute such Executable Form under the terms of this\n        License, or sublicense it under different terms, provided that the\n        license for the Executable Form does not attempt to limit or alter the\n        recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\n     You may create and distribute a Larger Work under terms of Your choice,\n     provided that You also comply with the requirements of this License for\n     the Covered Software. If the Larger Work is a combination of Covered\n     Software with a work governed by one or more Secondary Licenses, and the\n     Covered Software is not Incompatible With Secondary Licenses, this\n     License permits You to additionally distribute such Covered Software\n     under the terms of such Secondary License(s), so that the recipient of\n     the Larger Work may, at their option, further distribute the Covered\n     Software under the terms of either this License or such Secondary\n     License(s).\n\n3.4. Notices\n\n     You may not remove or alter the substance of any license notices\n     (including copyright notices, patent notices, disclaimers of warranty, or\n     limitations of liability) contained within the Source Code Form of the\n     Covered Software, except that You may alter any license notices to the\n     extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\n     You may choose to offer, and to charge a fee for, warranty, support,\n     indemnity or liability obligations to one or more recipients of Covered\n     Software. However, You may do so only on Your own behalf, and not on\n     behalf of any Contributor. You must make it absolutely clear that any\n     such warranty, support, indemnity, or liability obligation is offered by\n     You alone, and You hereby agree to indemnify every Contributor for any\n     liability incurred by such Contributor as a result of warranty, support,\n     indemnity or liability terms You offer. You may include additional\n     disclaimers of warranty and limitations of liability specific to any\n     jurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n\n   If it is impossible for You to comply with any of the terms of this License\n   with respect to some or all of the Covered Software due to statute,\n   judicial order, or regulation then You must: (a) comply with the terms of\n   this License to the maximum extent possible; and (b) describe the\n   limitations and the code they affect. Such description must be placed in a\n   text file included with all distributions of the Covered Software under\n   this License. Except to the extent prohibited by statute or regulation,\n   such description must be sufficiently detailed for a recipient of ordinary\n   skill to be able to understand it.\n\n5. Termination\n\n5.1. The rights granted under this License will terminate automatically if You\n     fail to comply with any of its terms. However, if You become compliant,\n     then the rights granted under this License from a particular Contributor\n     are reinstated (a) provisionally, unless and until such Contributor\n     explicitly and finally terminates Your grants, and (b) on an ongoing\n     basis, if such Contributor fails to notify You of the non-compliance by\n     some reasonable means prior to 60 days after You have come back into\n     compliance. Moreover, Your grants from a particular Contributor are\n     reinstated on an ongoing basis if such Contributor notifies You of the\n     non-compliance by some reasonable means, this is the first time You have\n     received notice of non-compliance with this License from such\n     Contributor, and You become compliant prior to 30 days after Your receipt\n     of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\n     infringement claim (excluding declaratory judgment actions,\n     counter-claims, and cross-claims) alleging that a Contributor Version\n     directly or indirectly infringes any patent, then the rights granted to\n     You by any and all Contributors for the Covered Software under Section\n     2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user\n     license agreements (excluding distributors and resellers) which have been\n     validly granted by You or Your distributors under this License prior to\n     termination shall survive termination.\n\n6. Disclaimer of Warranty\n\n   Covered Software is provided under this License on an \"as is\" basis,\n   without warranty of any kind, either expressed, implied, or statutory,\n   including, without limitation, warranties that the Covered Software is free\n   of defects, merchantable, fit for a particular purpose or non-infringing.\n   The entire risk as to the quality and performance of the Covered Software\n   is with You. Should any Covered Software prove defective in any respect,\n   You (not any Contributor) assume the cost of any necessary servicing,\n   repair, or correction. This disclaimer of warranty constitutes an essential\n   part of this License. No use of  any Covered Software is authorized under\n   this License except under this disclaimer.\n\n7. Limitation of Liability\n\n   Under no circumstances and under no legal theory, whether tort (including\n   negligence), contract, or otherwise, shall any Contributor, or anyone who\n   distributes Covered Software as permitted above, be liable to You for any\n   direct, indirect, special, incidental, or consequential damages of any\n   character including, without limitation, damages for lost profits, loss of\n   goodwill, work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses, even if such party shall have been\n   informed of the possibility of such damages. This limitation of liability\n   shall not apply to liability for death or personal injury resulting from\n   such party's negligence to the extent applicable law prohibits such\n   limitation. Some jurisdictions do not allow the exclusion or limitation of\n   incidental or consequential damages, so this exclusion and limitation may\n   not apply to You.\n\n8. Litigation\n\n   Any litigation relating to this License may be brought only in the courts\n   of a jurisdiction where the defendant maintains its principal place of\n   business and such litigation shall be governed by laws of that\n   jurisdiction, without reference to its conflict-of-law provisions. Nothing\n   in this Section shall prevent a party's ability to bring cross-claims or\n   counter-claims.\n\n9. Miscellaneous\n\n   This License represents the complete agreement concerning the subject\n   matter hereof. If any provision of this License is held to be\n   unenforceable, such provision shall be reformed only to the extent\n   necessary to make it enforceable. Any law or regulation which provides that\n   the language of a contract shall be construed against the drafter shall not\n   be used to construe this License against a Contributor.\n\n\n10. Versions of the License\n\n10.1. New Versions\n\n      Mozilla Foundation is the license steward. Except as provided in Section\n      10.3, no one other than the license steward has the right to modify or\n      publish new versions of this License. Each version will be given a\n      distinguishing version number.\n\n10.2. Effect of New Versions\n\n      You may distribute the Covered Software under the terms of the version\n      of the License under which You originally received the Covered Software,\n      or under the terms of any subsequent version published by the license\n      steward.\n\n10.3. Modified Versions\n\n      If you create software not governed by this License, and you want to\n      create a new license for such software, you may create and use a\n      modified version of this License if you rename the license and remove\n      any references to the name of the license steward (except to note that\n      such modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\n      Licenses If You choose to distribute Source Code Form that is\n      Incompatible With Secondary Licenses under the terms of this version of\n      the License, the notice described in Exhibit B of this License must be\n      attached.\n\nExhibit A - Source Code Form License Notice\n\n      This Source Code Form is subject to the\n      terms of the Mozilla Public License, v.\n      2.0. If a copy of the MPL was not\n      distributed with this file, You can\n      obtain one at\n      http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file,\nthen You may include the notice in a location (such as a LICENSE file in a\nrelevant directory) where a recipient would be likely to look for such a\nnotice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\n      This Source Code Form is \"Incompatible\n      With Secondary Licenses\", as defined by\n      the Mozilla Public License, v. 2.0.\n\n\nA copy of the MPL is also available at http://mozilla.org/MPL/2.0/.\n"
  },
  {
    "path": "MIGRATION.md",
    "content": "# Migration\n\n## 5.0.0\n\n### ym → ES modules\n\nThe `ym` module system (`modules.define`/`modules.require`) has been replaced with native ES modules.\n\nBefore:\n\n```js\nmodules.define('my-block', ['i-bem-dom', 'events'], function(provide, bemDom, events) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nimport bemDom from 'bem:i-bem-dom';\nimport events from 'bem:events';\n\nexport default bemDom.declBlock('my-block', { /* ... */ });\n```\n\nAll `bem:*` imports are resolved at build time by `vite-plugin-bem-levels` to the actual file paths, respecting platform-specific level priorities.\n\n### Module redefinitions\n\nModule redefinitions that previously used `modules.define` with a callback receiving the previous module value are now handled via barrel files generated by the Vite plugin.\n\nBefore (ym redefinition):\n\n```js\nmodules.define('jquery', function(provide, $) {\n    $.event.special.pointerclick = { /* ... */ };\n    provide($);\n});\n```\n\nAfter (side-effect import in barrel):\n\n```js\n// The barrel file auto-generated by vite-plugin-bem-levels:\nimport $ from '../../common.blocks/jquery/jquery.js';\nimport '../../common.blocks/jquery/__event/_type/jquery__event_type_pointerclick.js';\nexport default $;\n```\n\n### jQuery 3 → 4\n\nThe `jquery` peer dependency is now `^4.0.0`.\n\nKey breaking changes in jQuery 4:\n- `$.unique()` has been removed. Use `$.uniqueSort()`.\n- Several deprecated methods have been removed. See [jQuery 4.0 upgrade guide](https://jquery.com/upgrade-guide/4.0/).\n\n### vow → native Promise\n\nThe `vow` dependency has been removed. All asynchronous code now uses native `Promise`.\n\nBefore:\n\n```js\nvar vow = require('vow');\nvar promise = vow.resolve(value);\n```\n\nAfter:\n\n```js\nconst promise = Promise.resolve(value);\n```\n\n### Build system: ENB → Vite\n\nThe entire ENB toolchain has been replaced with Vite.\n\nBefore:\n\n```bash\n./node_modules/.bin/enb make\n```\n\nAfter:\n\n```bash\nnpm run build           # both platforms\nnpm run build:desktop   # desktop only\nnpm run build:touch     # touch only\n```\n\nThe Vite config is in `build/vite.config.js`. The custom `vite-plugin-bem-levels` plugin in `build/plugins/` handles BEM level scanning and module resolution.\n\n### Node.js 20+\n\nThe minimum supported Node.js version is now 20 (was 8). Update your `.nvmrc` or CI configuration accordingly.\n\n### Linting: jshint/jscs → ESLint 10\n\nReplace any custom `.jshintrc` or `.jscs.json` rules with ESLint flat config (`eslint.config.js`).\n\n### Testing\n\n- **Server-side tests**: `mocha`/`chai` have been replaced with `node:test`/`node:assert`.\n- **Browser tests**: `mocha-phantomjs` has been replaced with Playwright. Run with `npm run test:browser`.\n- **All tests**: `npm run test:all` runs both server-side and browser tests.\n\n### CI/CD: Travis → GitHub Actions\n\nReplace `.travis.yml` with `.github/workflows/ci.yml`. The new CI runs lint, test, test:browser, and build jobs.\n\n### Git hooks: git-hooks → husky\n\nReplace `.githooks/` with husky configuration. The `prepare` script in `package.json` sets up husky automatically on `npm install`.\n\n## 4.0.0\n\n### Changes in the `i-bem` block\n\n#### Separate `i-bem-dom` block\n\nThe `dom` element of the `i-bem` block was moved to a separate `i-bem-dom` block.\n\nBefore:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n    /* ... */\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n    /* ... */\n});\n```\n\nThe `i-bem` and `i-bem-dom` blocks are no longer classes. They are modules with methods for declaring BEM entities, links to classes of BEM entities, and some additional helpers. These methods are no longer class methods for the corresponding blocks.\n\nIssue: [#413](https://github.com/bem/bem-core/issues/413).\n\n#### Declaration\n\n#### Block declaration\n\nInstead of the `decl()` method, use the `declBlock()` method to declare a block.\n\nBefore:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }));\n\n});\n```\n\n#### Modifier declaration\n\nInstead of the static `decl()` method, use the static `declMod()` method to declare a modifier.\n\nBefore:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'my-val' }, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.declMod({ modName : 'my-mod', modVal : 'myVal' }, { /* ... */ }));\n\n});\n```\n\n#### Boolean modifier declaration\n\nBefore:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'true' }, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.declMod({ modName : 'my-mod' }, { /* ... */ }));\n\n});\n```\n\nIssue: [#1374](https://github.com/bem/bem-core/issues/1374).\n\n#### Declaration for a modifier with any value\n\nBefore:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.decl({ modName : 'my-mod' }, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.declMod({ modName : 'my-mod', modVal : '*' }, { /* ... */ }));\n\n});\n```\n\nIssue: [#1376](https://github.com/bem/bem-core/pull/1376).\n\n#### Block redefinition\n\nInstead of the `decl()` method for a block class, use the `declBlock()` method for the `i-bem-dom` module.\n\nBefore:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.decl({ /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom, MyDomBlock) {\n\nprovide(bemDom.declBlock(MyDomBlock, { /* ... */ }));\n\n});\n```\n\n#### Inherited block declaration\n\nBefore:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom', 'my-base-dom-block'], function(provide, BEMDOM, MyBaseDomBlock) {\n\nprovide(BEMDOM.decl({ block : this.name, baseBlock : MyBaseDomBlock }, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom', 'my-base-dom-block'], function(provide, bemDom, MyBaseDomBlock) {\n\nprovide(bemDom.declBlock(this.name, MyBaseDomBlock, { /* ... */ }));\n\n});\n```\n\n#### Mix declaration\n\nThe `declMix` method has been renamed to `declMixin`. This clarifies the concept of [mixes of multiple BEM entities on a single DOM node](https://en.bem.info/methodology/key-concepts/#Mix) as opposed to JS-level mixins.\n\nBefore:\n\n```js\nmodules.define('my-mix-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.declMix(this.name, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-mixin-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declMixin({ /* ... */ }));\n\n});\n```\n\n#### Mixing a mixin\n\nBefore:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom', 'my-mix-1', 'my-mix-2'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl({ block : this.name, baseMix : ['my-mix-1', 'my-mix-2']}, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom', 'my-mixin-1', 'my-mixin-2'], function(provide, bemDom, MyMixin1, MyMixin2) {\n\nprovide(bemDom.declBlock(this.name, [MyMixin1, MyMixin2], { /* ... */ }));\n\n});\n```\n\n#### Triggers for changing modifiers\n\nWhen declaring a specific modifier (for example, `_my-mod_my-val`), it wasn't possible to declare the behavior for deleting this modifier. We had to make two declarations.\n\nBefore:\n\n```js\n//\n\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nMyDomBlock.decl({\n    onSetMod : {\n        'my-mod' : {\n            '' : function() { /* ... */ } // declaration for deleting the _my-mod_my-val modifier\n        }\n    }\n\n});\n\nprovide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'my-val' }, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.declMod({ modName : 'my-mod', modVal : 'my-val' }, {\n    onSetMod : {\n        'mod1' : {\n            '' : function() { /* ... */ } // declaration for deleting the _my-mod_my-val modifier\n        }\n    }\n}));\n});\n```\n\nIssue: [#1025](https://github.com/bem/bem-core/issues/1025).\n\nShorthand syntax is now available for declaring behaviors for changing modifiers.\n\nBefore:\n\n```js\nonSetMod : {\n    'my-mod' : {\n        '*' : function(modName, modVal, prevModVal) {\n            if(prevModVal === 'my-val') {\n                /* ... */ // declaration for changing _my-mod_my-val to any other value\n            }\n        }\n    }\n}\n```\n\nAfter:\n\n```js\nonSetMod : {\n    'my-mod' : {\n        '~my-val' : function() { /* ... */ } // declaration for changing the my-mod value from my-val to any other value\n        }\n    }\n}\n```\n\nBefore:\n\n```js\nonSetMod : {\n    'my-mod' : {\n        '*' : function(modName, modVal) {\n            if(modVal !== 'my-val') {\n                /* ... */ // declaration for changing my-mod to any value other than my-val\n            }\n        }\n    }\n}\n```\n\nAfter:\n\n```js\nonSetMod : {\n    'my-mod' : {\n        '!my-val' : function() { /* ... */ } // declaration for changing my-mod to any value other than my-val\n        }\n    }\n}\n```\n\nIssue: [#1072](https://github.com/bem/bem-core/issues/1072).\n\n\n#### Lazy initialization\n\nThe functionality of the `live` field has been divided into two parts: the `lazyInit` field and the `onInit()` method.\n\nBefore:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }, {\n    live : true\n}));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }, {\n    lazyInit : true\n}));\n\n});\n```\n\nBefore:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }, {\n    live : function() {\n        /* ... */\n    }\n}));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }, {\n    lazyInit : true,\n\n    onInit : function() {\n        /* ... */\n    }\n}));\n\n});\n```\n\nBefore:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }, {\n    live : function() {\n        /* ... */\n        return false;\n    }\n}));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }, {\n    onInit : function() {\n        /* ... */\n    }\n}));\n\n});\n```\n\nBefore:\n\n```js\n{\n    block : 'b1',\n    js : { live : false }\n}\n```\n\nAfter:\n\n```js\n{\n    block : 'b1',\n    js : { lazyInit : false }\n}\n```\n\nIssue: [#877](https://github.com/bem/bem-core/issues/877).\n\n#### Instances for elements\n\nDeleted the `elem-instances` element of the `i-bem` block and the `elem-instances` modifier of the `dom` element in the `i-bem` block.\nNow the corresponding functionality is incorporated into `i-bem` and `i-bem-dom`.\n\nBefore:\n\n```js\nmodules.define('my-dom-block__my-elem', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl({ block : 'my-dom-block', elem : 'my-elem' }, { /* ... */ }));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-dom-block__my-elem', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declElem('my-dom-block', 'my-elem', { /* ... */ }));\n\n});\n```\n\nNow the `_elem(elemName)` method of the block instance (previously `elem(elemName)`) returns an instance of the element's class, instead of a jQuery object with all the elements named `elemName`.\n\nTo get a collection of instances of the element's class, use the `_elems()` method.\n\n\nNow the caches for elements with JS implementation found with `_elem()` and `_elems()` are invalidated automatically when the DOM is modified.\nIssue: [#1352](https://github.com/bem/bem-core/issues/1352).\n\nNote: When this methods are used for elements without JS implementation you still need to use `_dropElemCache()` in cases of dynamically DOM update.\n\nNote: don't forget to switch on support for elements instances in template engine.\nFor `bem-xjst` please refer to https://github.com/bem/bem-xjst/blob/master/docs/en/3-api.md#support-js-instances-for-elements-bem-core-v4 or for `BH` see https://github.com/bem/bh#jselem.\n\n##### Ways to work with elements\n\nBefore:\n\n```js\nthis.setMod(this.elem('my-elem'), 'my-mod', 'my-val');\n```\n\nAfter:\n\n```js\nthis._elem('my-elem').setMod('my-mod', 'my-val');\n```\n\nThe same is true for the methods `getMod()`, `hasMod()`, `toggleMod()`, and `delMod()`.\n\n##### Deleted methods and fields\n\nThe following methods were deleted from the block API: `elemify()`, `elemParams()`, and the `onElemSetMod` field. The corresponding functionality is provided in instances of elements.\n\nAlso see  the changes for [search methods](#Search-methods).\n\nIssue: [#581](https://github.com/bem/bem-core/issues/581).\n\n#### Search methods\n\nRenamed the following methods:\n\n- `findBlockInside()` to `findChildBlock()`\n- `findBlocksInside()` to `findChildBlocks()`\n- `findBlockOutside()` to `findParentBlock()`\n- `findBlocksOutside()` to `findParentBlocks()`\n- `findBlockOn()` to `findMixedBlock()`\n- `findBlocksOn()` to `findMixedBlocks()`\n\nThe optional first parameter about the element has been removed from these methods.\n\nAdded the following methods: `findChildElem()`, `findChildElems()`, `findParentElem()`, `findParentElems()`, `findMixedElem()`, `findMixedElems()`.\n\nBefore:\n\n```js\nthis.findBlockInside(this.elem('my-elem'), 'my-block-2');\n```\n\nAfter:\n\n```js\nthis.findChildElem('my-elem').findChildBlock(MyBlock2);\n```\n\nDeleted the following methods: `findElem()`, `closestElem()`. Use the `findChildElem()` and `findParentElem()` elements, instead.\n\nThe methods `findChildBlocks()`, `findParentBlocks()`, `findMixedBlocks()`, `findChildElems()`, `findParentElems()`, and `findMixedElems()` return [collections of BEM entities](#Collections).\n\nThe `findChildElem()` and `findChildElems()` methods (unlike the previous equivalent `findElem`) don't search on their own DOM nodes of the instance.\n\nBefore:\n\n```js\nthis.findElem('my-elem');\n```\n\nAfter:\n\n```js\nthis.findChildElems('my-elem').concat(this.findMixedElems('my-elem'));\n```\n\nHowever, consider whether you really need both searches. In most cases, you can just use either `this.findChildElems('my-elem')` or `this.findMixedElems('my-elem')`.\n\n##### Checking for nesting\n\nIn place of the deleted `containsDomElem()` method, use the `containsEntity()` method.\n\nBefore:\n\n```js\nthis.containsDomElem(someElem);\n```\n\nAfter:\n\n```js\nthis.containsEntity(someElem);\n```\n\n#### Collections\n\nThe functionality of the `collection` element of the `i-bem` block is no longer optional.\n\nAll methods that return an array of BEM entities now return collections.\n\nBefore:\n\n```js\nthis.findBlocksInside('my-block-2')[0].setMod('my-mod', 'my-val');\n```\n\nAfter:\n\n```js\nthis.findChildBlocks(MyBlock2).get(0).setMod('my-mod', 'my-val');\n```\n\nBefore:\n\n```js\nthis.findBlocksInside('my-block-2').forEach(function(myBlock2) {\n    return myBlock2.setMod('my-mod', 'my-val');\n});\n```\n\nAfter:\n\n```js\nthis.findChildBlocks(MyBlock2).setMod('my-mod', 'my-val');\n```\n\nIssue: [#582](https://github.com/bem/bem-core/issues/582).\n\n#### Events\n\nThe events API has been simplified. Deleted the following block instance methods: `on()`, `un()`, `once()`, `bindTo()`, `unbindFrom()`, `bindToDoc()`, `bindToWin()`, `unbindFromDoc()`, `unbindFromWin()`, and class methods: `liveBindTo()`, `liveUnbindFrom()`, `on()`, `un()`, `once()`, `liveInitOnBlockEvent()`, `liveInitOnBlockInsideEvent()`.\nThey have been replaced with the new `_domEvents()` and `_events()` methods, which return an instance of the events manager class with the `on()`, `un()` and `once()` methods.\n\n##### DOM events on instances\n\nBefore:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this.bindTo('click', this._onClick);\n            }\n        }\n    }\n});\n```\n\nAfter:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._domEvents().on('click', this._onClick);\n            }\n        }\n    }\n});\n```\n\nBefore:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this.bindToDoc('click', this._onDocClick);\n            }\n        }\n    }\n});\n```\n\nAfter:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._domEvents(bemDom.doc).on('click', this._onDocClick);\n            }\n        }\n    }\n});\n```\n\nBefore:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this.bindToWin('resize', this._onWinResize);\n            }\n        }\n    }\n});\n```\n\nAfter:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._domEvents(bemDom.win).on('resize', this._onWinResize);\n            }\n        }\n    }\n});\n```\n\n##### Link to instance\n\nIf an event was fired on BEM instance the event object will contain a link to an instance:\n\n```js\nthis._domEvents('my-elem').on('click', function(e) {\n    e.bemTarget // refers to `my-elem` instance\n});\n```\n\n##### BEM events on instances\n\nBefore:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this.findBlockOutside('my-block-2').on('my-event', this._onMyBlock2MyEvent, this);\n            },\n\n            '' : function() {\n                this.findBlockOutside('my-block-2').un('my-event', this._onMyBlock2MyEvent, this);\n            }\n        }\n    }\n});\n```\n\nAfter:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._events(this.findParentBlock('my-block-2')).on('my-event', this._onMyBlock2MyEvent);\n            }\n        }\n    }\n});\n```\n\nNote that unsubscribing from events is now automatic when the instance is destroyed.\n\n##### Delegated DOM events\n\nBefore:\n\n```js\nBEMDOM.decl('my-block', { /* ... */ }, {\n    live : function() {\n        this.liveBindTo('click', this.prototype._onClick);\n    }\n});\n```\n\nAfter:\n\n```js\nbemDom.declBlock('my-block', { /* ... */ }, {\n    onInit : function() {\n        this._domEvents().on('click', this.prototype._onClick);\n    }\n});\n```\n\nBefore:\n\n```js\nBEMDOM.decl('my-block', { /* ... */ }, {\n    live : function() {\n        this.liveBindTo('my-elem', 'click', this.prototype._onMyElemClick);\n    }\n});\n```\n\nAfter:\n\n```js\nbemDom.declBlock('my-block', { /* ... */ }, {\n    onInit : function() {\n        this._domEvents('my-elem').on('click', this.prototype._onMyElemClick);\n    }\n});\n```\n\n##### Delegated BEM events\n\nBefore:\n\n```js\nBEMDOM.decl('my-block', { /* ... */ }, {\n    live : function() {\n        this.liveInitOnBlockInsideEvent('my-event', 'my-block-2', this.prototype._onMyBlock2MyEvent);\n    }\n});\n```\n\nAfter:\n\n```js\nbemDom.declBlock('my-block', { /* ... */ }, {\n    onInit : function() {\n        this._events(MyBlock2).on('my-event', this.prototype._onMyBlock2MyEvent);\n    }\n});\n```\n\nNote that the parameter with the event handler function is now required.\n\nBefore:\n\n```js\nmodules.define('my-block', ['i-bem__dom', 'my-block-2'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }, {\n    live : function() {\n        this.liveInitOnBlockInsideEvent('my-event', 'my-block-2');\n    }\n}));\n\n});\n```\n\nAfter:\n\n```js\nmodules.define('my-block', ['i-bem-dom', 'my-block-2', 'functions'], function(provide, bemDom, MyBlock2, functions) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }, {\n    onInit : function() {\n        this._events(MyBlock2).on('my-event', functions.noop);\n    }\n}));\n\n});\n```\n\nBefore:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                MyBlock2.on(this.domElem, 'my-event', this._onMyBlock2MyEvent, this);\n            },\n\n            '' : function() {\n                MyBlock2.un(this.domElem, 'my-event', this._onMyBlock2MyEvent, this);\n            }\n        }\n    }\n});\n```\n\nAfter:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._events(MyBlock2).on('my-event', this._onMyBlock2MyEvent);\n            }\n        }\n    }\n});\n```\n\nNote that unsubscribing from events is now automatic when the instance is destroyed.\n\n#### External code accessing BEM blocks\n\n##### Getting an instance of a BEM block\n\nNow the `bem()` method of a jQuery object accepts a BEM class instead of a string.\n\nBefore:\n\n```js\nmodules.require(['jquery', 'i-bem__dom'], function($, BEMDOM) {\n\nvar myBlock = $('.my-block').bem('my-block');\n\n});\n```\n\nAfter:\n\n```js\nmodules.require(['jquery', 'my-block'], function($, MyBlock) {\n\nvar myBlock = $('.my-block').bem(MyBlock);\n\n});\n```\n\n##### Subscribing to BEM events from external code\n\nBefore:\n\n```js\nmodules.require(['jquery', 'i-bem__dom'], function($, BEMDOM) {\n\n$('.my-block').bem('my-block').on('my-event', function() { /* ... */ });\n\n});\n```\n\nAfter:\n\n```js\nmodules.require(['jquery', 'my-block', 'events__observable'], function($, MyBlock, observable) {\n\nobservable($('.my-block').bem(MyBlock))\n    .on('my-event', function() { /* ... */ });\n\n});\n```\n\nIn addition, you must add `{ block : 'events', elem : 'observable', mods : { type : 'bem-dom' } }` to the dependency.\n\nIssue: [#394](https://github.com/bem/bem-core/issues/394).\n\n#### Names of protected methods begin with `_`\n\nRenamed the protected methods:\n\n- `emit()` to `_emit()`\n- `elem()` to `_elem()`\n- `dropElemCache()` to `_dropElemCache()`\n- `buildClass()` to `_buildClassName()`\n- `buildSelector()` to `_buildSelector()`\n- `getDefaultParams()` to `_getDefaultParams()`\n\nIssues: [#586](https://github.com/bem/bem-core/issues/586) and [#1359](https://github.com/bem/bem-core/issues/1359).\n\n#### Deleted methods\n\nDeleted the `getMods()` method.\n\n### Changes in the `querystring` block\n\nThe `querystring__uri` element is now the `uri` block. The `querystring` block is now the `uri__querystring` element.\n\nIssue: [#967](https://github.com/bem/bem-core/issues/967).\n\n### Changes in the `page` block\n\nThe `page__css` element does not support `ie` field. Use the `page__conditional-comment` element instead.\n\nBefore:\n\n```\n{\n    block : 'page',\n    head : [\n        { elem : 'css', url : 'my-css.css', ie : false },\n        { elem : 'css', url : 'my-css', ie : true }\n    ],\n    content : 'Page content'\n}\n```\n\nAfter:\n\n```\n{\n    block : 'page',\n    head : [\n        {\n            elem : 'conditional-comment',\n            condition : '! IE',\n            content : { elem : 'css', url : 'my-css.css' }\n        },\n        {\n            elem : 'conditional-comment',\n            condition : '> IE 8',\n            content : { elem : 'css', url : 'my-css.ie.css' }\n        }\n        // and so on for needed IE versions\n    ],\n    content : 'Page content'\n}\n```\n\nIssue: [#379](https://github.com/bem/bem-core/issues/379).\n\n## 3.0.0\n\nTo migrate to version 3.0.0, review the [history of changes](https://en.bem.info/libs/bem-core/v3/changelog/#300).\n\n## 2.0.0\n\nTo migrate to version 2.0.0, review the [history of changes](https://en.bem.info/libs/bem-core/v2/changelog/#200).\n\n## 1.0.0\n\nFor version 1.0.0, migrating requires switching from using [bem-bl](https://github.com/bem/bem-bl/) to using [bem-core](https://github.com/bem/bem-core/).\n\n### Modules\n\nThe entire code is now written in terms of the modular system https://github.com/ymaps/modules.\nAll dependencies must be explicitly stated in the code. Minimize or eliminate use of global variables, if possible.\n\nExample:\n\n```js\nmodules.define(\n    'my-module', // module name\n    ['module-from-library', 'my-another-module'], // module dependencies\n    function(provide, moduleFromLibrary, myAnotherModule) { // module declaration, called when all dependencies are resolved\n\n// module representation\nprovide({\n    myModuleMethod : function() {}\n});\n\n});\n```\n\nTODO: Add information about the build process (usage of special technologies for JS and instructions for custom builders).\n\n### jQuery and jQuery plugins\n\njQuery is represented by a `jquery` wrapper module that uses the global jQuery object if it already exists on the page, or loads it otherwise.\njQuery is now used only for operations directly related to the DOM (searching for elements, binding listeners to events, setting and getting attribute values, and so on).\n\nAll other operations have corresponding modules that provide the same functionality without depending on jQuery:\n * The `objects` module for operating on objects (with the `extend`, `isEmpty`, and `each` methods).\n * The `functions` module for operating on functions (with the `isFunction` and `noop` methods).\n\nIn addition, all the jQuery plugins that aren't directly related to jQuery (`$.observable`, `$.inherit`, `$.cookie`, `$.identify`, `$.throttle`) are now modules:\n * The `events` module replaces `$.observable` for working with events. It provides the \"classes\" `EventsEmitter` and `Event`.\n * The `inherit` module instead of `$.inherit` for working with \"classes\" and inheritance.\n * The `cookie` module instead of `$.cookie`.\n * The `identify` module instead of `$.identify`.\n * The `functions__throttle` and `functions__debounce` modules instead of `$.throttle` and `$.debounce`.\n\nBefore:\n\n```js\n// block code\n$.throttle()\n// block code\n```\n\nAfter:\n\n```js\nmodule.define('my-module', ['functions__throttle'], function(provide, throttle) {\n// module code\nthrottle()\n// module code\n});\n```\n\n### BEM.DOM blocks\n\n#### Declaration\n\nInstead of a declaration via BEM.DOM.decl, you need to extend the `i-bem__dom` module.\n\nBefore:\n\n```js\nBEM.DOM.decl('block', /* ... */);\n```\n\nAfter:\n\n```js\nmodules.define('i-bem__dom', function(provide, BEMDOM) {\n\nBEMDOM.decl('block', /* ... */);\n\nprovide(BEMDOM);\n\n});\n```\n\n#### Constructor\n\nYou must use full notation for the handler for setting the `js` modifier to `inited`.\n\nBefore:\n\n```js\nonSetMod : {\n  js : function() {\n      // constructor code\n    }\n}\n```\n\nAfter:\n\n```js\nonSetMod : {\n    'js' : {\n        'inited' : function() {\n            // constructor code\n        }\n    }\n}\n```\n\n#### Desctructor\n\nInstead of the `destruct` method, you need to use the handler for setting the `js` modifier to an empty value (remove the modifier).\nYou no longer need to call `__base` in order to run the base destructor defined in `i-bem__dom` on blocks.\n\nBefore:\n\n```js\ndestruct : function() {\n    this.__base.apply(this, arguments);\n    // destructor code\n}\n```\n\nAfter:\n\n```js\nonSetMod : {\n    js : {\n        '' : function() {\n            // destructor code\n        }\n    }\n}\n```\n\n#### `changeThis` method\n\nInstead of the `changeThis` method, use either the corresponding parameter, or the native `bind` method if there isn't a parameter.\n\nBefore:\n\n```js\n// block code\nobj.on('event', this.changeThis(this._method));\n// block code\n```\n\nAfter:\n\n```js\nobj.on('event', this._method.bind(this));\n// or better\nobj.on('event', this._method, this);\n```\n\n#### `afterCurrentEvent` method\n\nInstead of the `afterCurrentEvent` method, use the `nextTick` method, which guarantees that the block still exists during the callback (if the block has already been destroyed by this time, the callback isn't executed).\n\nBefore:\n\n```js\nBEM.DOM.decl('block', {\n    method : function() {\n        this.afterCurrentEvent(function() {\n            /* ... */\n        })\n    }\n});\n```\n\nAfter:\n\n```js\nmodules.define('i-bem__dom', function(provide, BEMDOM) {\n\nBEMDOM.decl('block', {\n    method : function() {\n        this.nextTick(function() {\n                /* ... */\n            });\n        }\n    });\n});\n```\n\n#### `findElem` method\n\nThe context for finding an element is no longer set as a string. Instead, pass a jQuery object.\n\nBefore:\n\n```js\nvar nestedElem = this.findElem('parent-elem', 'nested-elem');\n```\n\nAfter:\n\n```js\nvar nestedElem = this.findElem(this.findElem('parent-elem'), 'nested-elem'),\n    oneMoreElem = this.findElem(this.elem('another-elem'), 'nested-elem');\n```\n\n#### `liveBindTo` method\n\nThe `liveBindTo` method no longer supports the `elemName` field for passing the element name. Use the `elem` field instead.\n\n#### Access to a DOM element in an event handler\n\nA DOM element that had an event handler bound to it is now accessed as `$(e.currentTarget)` instead of `e.data.domElem`.\n\nBefore:\n\n```js\nonClick : function(e) {\n    e.data.domElem.attr(/* ... */);\n}\n```\n\nAfter:\n\n```js\nonClick : function(e) {\n    $(e.currentTarget).attr(/* ... */);\n}\n```\n\n#### Channels\n\nChannels are no longer an integral part of BEM. Now they are separate `events__channels` modules.\n\nBefore:\n\n```js\nBEM.DOM.decl('block', {\n    method : function() {\n        BEM.channel('channel-name').on(/* ... */);\n    }\n});\n```\n\nAfter:\n\n```js\nmodules.define('i-bem__dom', ['events__channels'], function(provide, channels, BEMDOM) {\n\nBEMDOM.decl('block', {\n    method : function() {\n        channels('channel-name').on(/* ... */);\n\n        }\n    });\n});\n```\n\n#### The `i-system` block and the `sys` channel for the `tick`, `idle`, and `wakeup` events\n\nThis block and channel no longer exist. They have been replaced with separate modules: `tick` with the \"tick\" event, and `idle` with the \"idle\" and \"wakeup\" events.\n\nBefore:\n\n```js\nBEM.DOM.decl('block', {\n    method : function() {\n        BEM.channel('sys').on('tick', /* ... */);\n    }\n});\n```\n\nAfter:\n\n```js\nmodules.define('i-bem__dom', ['tick'], function(provide, tick, BEMDOM) {\n\nBEMDOM.decl('block', {\n    method : function() {\n        tick.on('tick', /* ... */);\n\n        }\n    });\n});\n```\n\nBefore:\n\n```js\nBEM.DOM.decl('block', {\n    method : function() {\n        BEM.channel('sys').on('wakeup', /* ... */);\n    }\n});\n```\n\nAfter:\n\n```js\nmodules.define('i-bem__dom', ['idle'], function(provide, idle, BEMDOM) {\n\nBEMDOM.decl('block', {\n    method : function() {\n        idle.on('wakeup', /* ... */);\n\n        }\n    });\n});\n```\n\n### BEM blocks\nBEM blocks that were used as storage for some methods but that didn't use the BEM methodology in any way can now be written as modules.\n\nBefore:\n\n```js\nBEM.decl('i-router', {\n    route : function() { /* ... */ }\n});\n```\n\nAfter:\n\n```js\nmodules.define('router', function(provide) {\n\nprovide({\n    route : function() { /* ... */ }\n});\n\n});\n```\n\nIf for some reason you need BEM blocks (not BEM.DOM blocks), you can declare them by extending the `i-bem` module.\n\nBefore:\n\n```js\nBEM.decl('my-block', { /* ... */ });\n```\n\nAfter:\n\n```js\nmodules.define('i-bem', function(provide, BEM) {\n\nBEM.decl('my-block', { /* ... */ });\n\nprovide(BEM);\n\n});\n```\n\n#### Refactoring using the `b-spin` block example\n\nBefore:\n\n```js\nBEM.DOM.decl('b-spin', {\n\n    onSetMod : {\n\n        'js' : function() {\n\n            this._size = this.getMod('size') || /[\\d]+/.exec(this.getMod('theme'))[0];\n\n            this._bgProp = 'background-position';\n            this._posPrefix = '0 -';\n\n            if (this.elem('icon').css('background-position-y')) { /* In IE, you can't get the background-position property. You can only get background-position-y, so use this workaround */\n                this._bgProp = 'background-position-y';\n                this._posPrefix = '-';\n            }\n\n            this._curFrame = 0;\n\n            this.hasMod('progress') && this.channel('sys').on('tick', this._onTick, this);\n\n        },\n\n        'progress' : {\n\n            'yes' : function() {\n\n                this.channel('sys').on('tick', this._onTick, this);\n\n            },\n\n            '' : function() {\n\n                this.channel('sys').un('tick', this._onTick, this);\n\n            }\n\n        }\n    },\n\n    _onTick: function(){\n\n        var y = ++this._curFrame * this._size;\n\n        (y >= this._size * 36) && (this._curFrame = y = 0);\n\n        this.elem('icon').css(this._bgProp, this._posPrefix + y +'px');\n\n    },\n\n    destruct : function() {\n\n        this.channel('sys').un('tick', this._onTick, this);\n        this.__base.apply(this, arguments);\n\n    }\n\n});\n```\n\nAfter:\n\n```js\nmodules.define(\n    'i-bem__dom',\n    ['tick'],\n    function(provide, tick, BEMDOM) {\n\nvar FRAME_COUNT = 36;\n\nBEMDOM.decl('b-spin', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() { // constructor\n                var hasBackgroundPositionY = !!this.elem('icon').css('background-position-y')); /* In IE we can't get the background-position property, only background-position-y */\n\n                this._bgProp = hasBackgroundPositionY? 'background-position-y' : 'background-position';\n                this._posPrefix = hasBackgroundPositionY? '-' : '0 -';\n                this._curFrame = 0;\n                this._size = Number(this.getMod('size') || /[\\d]+/.exec(this.getMod('theme'))[0]);\n\n                this.hasMod('progress') && this._bindToTick();\n            },\n\n            '' : function() { // destructor\n                this._unbindFromTick();\n            }\n        },\n\n        'progress' : {\n            'true' : function() {\n                this._bindToTick();\n            },\n\n            '' : function() {\n                this._unbindFromTick();\n            }\n        }\n    },\n\n    _bindToTick : function() {\n        tick.on('tick', this._onTick, this);\n    },\n\n    _unbindFromTick : function() {\n        tick.un('tick', this._onTick, this);\n    },\n\n    _onTick : function() {\n        var offset;\n        this._curFrame++ >= FRAME_COUNT?\n            offset = this._curFrame * this._size :\n            this._curFrame = offset = 0;\n\n        this.elem('icon').css(this._bgProp, this._posPrefix + offset + 'px');\n    }\n});\n\nprovide(BEMDOM);\n\n});\n```\n"
  },
  {
    "path": "MIGRATION.ru.md",
    "content": "# Миграция\n\n## 5.0.0\n\n### ym → ES-модули\n\nМодульная система `ym` (`modules.define`/`modules.require`) заменена на нативные ES-модули.\n\nБыло:\n\n```js\nmodules.define('my-block', ['i-bem-dom', 'events'], function(provide, bemDom, events) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nimport bemDom from 'bem:i-bem-dom';\nimport events from 'bem:events';\n\nexport default bemDom.declBlock('my-block', { /* ... */ });\n```\n\nВсе `bem:*` импорты разрешаются на этапе сборки плагином `vite-plugin-bem-levels` в реальные пути файлов с учётом приоритетов уровней для конкретной платформы.\n\n### Переопределения модулей\n\nПереопределения модулей, которые ранее использовали `modules.define` с колбэком, получающим предыдущее значение модуля, теперь обрабатываются через barrel-файлы, генерируемые Vite-плагином.\n\nБыло (переопределение ym):\n\n```js\nmodules.define('jquery', function(provide, $) {\n    $.event.special.pointerclick = { /* ... */ };\n    provide($);\n});\n```\n\nСтало (side-effect импорт в barrel-файле):\n\n```js\n// Barrel-файл автоматически сгенерирован vite-plugin-bem-levels:\nimport $ from '../../common.blocks/jquery/jquery.js';\nimport '../../common.blocks/jquery/__event/_type/jquery__event_type_pointerclick.js';\nexport default $;\n```\n\n### jQuery 3 → 4\n\nPeer-зависимость `jquery` теперь `^4.0.0`.\n\nОсновные несовместимые изменения в jQuery 4:\n- `$.unique()` удалён. Используйте `$.uniqueSort()`.\n- Ряд устаревших методов удалён. См. [руководство по обновлению jQuery 4.0](https://jquery.com/upgrade-guide/4.0/).\n\n### vow → нативный Promise\n\nЗависимость `vow` удалена. Весь асинхронный код теперь использует нативный `Promise`.\n\nБыло:\n\n```js\nvar vow = require('vow');\nvar promise = vow.resolve(value);\n```\n\nСтало:\n\n```js\nconst promise = Promise.resolve(value);\n```\n\n### Система сборки: ENB → Vite\n\nВесь инструментарий ENB заменён на Vite.\n\nБыло:\n\n```bash\n./node_modules/.bin/enb make\n```\n\nСтало:\n\n```bash\nnpm run build           # обе платформы\nnpm run build:desktop   # только desktop\nnpm run build:touch     # только touch\n```\n\nКонфигурация Vite находится в `build/vite.config.js`. Кастомный плагин `vite-plugin-bem-levels` в `build/plugins/` обеспечивает сканирование BEM-уровней и разрешение модулей.\n\n### Node.js 20+\n\nМинимальная поддерживаемая версия Node.js — 20 (было 8). Обновите `.nvmrc` или конфигурацию CI.\n\n### Линтинг: jshint/jscs → ESLint 10\n\nЗамените кастомные `.jshintrc` или `.jscs.json` правила на flat-конфигурацию ESLint (`eslint.config.js`).\n\n### Тестирование\n\n- **Серверные тесты**: `mocha`/`chai` заменены на `node:test`/`node:assert`.\n- **Браузерные тесты**: `mocha-phantomjs` заменён на Playwright. Запуск: `npm run test:browser`.\n- **Все тесты**: `npm run test:all` запускает серверные и браузерные тесты.\n\n### CI/CD: Travis → GitHub Actions\n\nЗамените `.travis.yml` на `.github/workflows/ci.yml`. Новый CI запускает задачи lint, test, test:browser и build.\n\n### Git-хуки: git-hooks → husky\n\nЗамените `.githooks/` на конфигурацию husky. Скрипт `prepare` в `package.json` автоматически настраивает husky при `npm install`.\n\n## 4.0.0\n\n### Изменения в блоке `i-bem`\n\n#### Отдельный блок `i-bem-dom`\n\nЭлемент `dom` блока `i-bem` был перенесён в отдельный блок `i-bem-dom`.\n\nБыло:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n    /* ... */\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n    /* ... */\n});\n```\n\nБлоки `i-bem` и `i-bem-dom` больше не являются классами, представляя собой модули с методами для декларации\nБЭМ-сущностей, ссылками на классы БЭМ-сущностей и некоторыми дополнительными хелперами. Эти методы больше не являются методами класса для соответсвующих блоков.\n\nЗадача: [#413](https://github.com/bem/bem-core/issues/413).\n\n#### Декларация\n\n#### Декларация блока\n\nДля декларации блока, вместо метода `decl()`, следует использовать метод `declBlock()`.\n\nБыло:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }));\n\n});\n```\n\n#### Декларация модификатора\n\nДля декларации модификатора, вместо статического метода `decl()`, следует использовать статический метод `declMod()`.\n\nБыло:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'my-val' }, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.declMod({ modName : 'my-mod', modVal : 'myVal' }, { /* ... */ }));\n\n});\n```\n\n#### Декларация булевого модификатора\n\nБыло:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'true' }, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.declMod({ modName : 'my-mod' }, { /* ... */ }));\n\n});\n```\n\nЗадача: [#1374](https://github.com/bem/bem-core/issues/1374).\n\n#### Декларация для модификатора с любым значением\n\nБыло:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.decl({ modName : 'my-mod' }, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.declMod({ modName : 'my-mod', modVal : '*' }, { /* ... */ }));\n\n});\n```\n\nЗадача: [#1376](https://github.com/bem/bem-core/pull/1376).\n\n#### Доопределение блока\n\nВместо метода `decl()` класса блока следует использовать метод `declBlock()` модуля `i-bem-dom`.\n\nБыло:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.decl({ /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom, MyDomBlock) {\n\nprovide(bemDom.declBlock(MyDomBlock, { /* ... */ }));\n\n});\n```\n\n#### Декларация наследуемого блока\n\nБыло:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom', 'my-base-dom-block'], function(provide, BEMDOM, MyBaseDomBlock) {\n\nprovide(BEMDOM.decl({ block : this.name, baseBlock : MyBaseDomBlock }, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom', 'my-base-dom-block'], function(provide, bemDom, MyBaseDomBlock) {\n\nprovide(bemDom.declBlock(this.name, MyBaseDomBlock, { /* ... */ }));\n\n});\n```\n\n#### Декларация миксина\n\nМетод `declMix` переименован в `declMixin`, чтобы отделить понятие\n[миксов нескольких БЭМ-сущностей на одном DOM-узле](https://ru.bem.info/methodology/key-concepts/#Микс) от миксинов на уровне JS.\n\nБыло:\n\n```js\nmodules.define('my-mix-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.declMix(this.name, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-mixin-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declMixin({ /* ... */ }));\n\n});\n```\n\n#### Примешивание миксина\n\nБыло:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom', 'my-mix-1', 'my-mix-2'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl({ block : this.name, baseMix : ['my-mix-1', 'my-mix-2']}, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom', 'my-mixin-1', 'my-mixin-2'], function(provide, bemDom, MyMixin1, MyMixin2) {\n\nprovide(bemDom.declBlock(this.name, [MyMixin1, MyMixin2], { /* ... */ }));\n\n});\n```\n\n#### Триггеры для изменения модификаторов\n\nПри декларации определённого модификатора (например, `_my-mod_my-val`) невозможно было задекларировать поведение\nна удаление этого модификатора. Приходилось делать две декларации.\n\nБыло:\n\n```js\n// my-dom-block_my-mod_my-val.js\n\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nMyDomBlock.decl({\n    onSetMod : {\n        'my-mod' : {\n            '' : function() { /* ... */ } // декларация для удаления модификатора _my-mod_my-val\n        }\n    }\n\n});\n\nprovide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'my-val' }, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', function(provide, MyDomBlock) {\n\nprovide(MyDomBlock.declMod({ modName : 'my-mod', modVal : 'my-val' }, {\n    onSetMod : {\n        'mod1' : {\n            '' : function() { /* ... */ } // декларация для удаления модификатора _my-mod_my-val\n        }\n    }\n}));\n\n});\n```\n\nЗадача: [#1025](https://github.com/bem/bem-core/issues/1025).\n\nПоявился сокращённый синтаксис для декларации поведения на изменение модификатора.\n\nБыло:\n\n```js\nonSetMod : {\n    'my-mod' : {\n        '*' : function(modName, modVal, prevModVal) {\n            if(prevModVal === 'my-val') {\n                /* ... */ // декларация для изменения _my-mod_my-val в любое другое значение\n            }\n        }\n    }\n}\n```\n\nСтало:\n\n```js\nonSetMod : {\n    'my-mod' : {\n        '~my-val' : function() { /* ... */ } // декларация для изменения значения my-mod из my-val в любое другое значение\n        }\n    }\n}\n```\n\nБыло:\n\n```js\nonSetMod : {\n    'my-mod' : {\n        '*' : function(modName, modVal) {\n            if(modVal !== 'my-val') {\n                /* ... */ // декларация для изменения my-mod в любое значение, кроме my-val\n            }\n        }\n    }\n}\n```\n\nСтало:\n\n```js\nonSetMod : {\n    'my-mod' : {\n        '!my-val' : function() { /* ... */ } // декларация для изменения my-mod в любое значение, кроме my-val\n        }\n    }\n}\n```\n\nЗадача: [#1072](https://github.com/bem/bem-core/issues/1072).\n\n\n#### Ленивая инициализация\n\nФункциональность поля `live` была разделена на две части: поле `lazyInit` и метод `onInit()`.\n\nБыло:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }, {\n    live : true\n}));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }, {\n    lazyInit : true\n}));\n\n});\n```\n\nБыло:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }, {\n    live : function() {\n        /* ... */\n    }\n}));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }, {\n    lazyInit : true,\n\n    onInit : function() {\n        /* ... */\n    }\n}));\n\n});\n```\n\nБыло:\n\n```js\nmodules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }, {\n    live : function() {\n        /* ... */\n        return false;\n    }\n}));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }, {\n    onInit : function() {\n        /* ... */\n    }\n}));\n\n});\n```\n\nБыло:\n\n```js\n{\n    block : 'b1',\n    js : { live : false }\n}\n```\n\nСтало:\n\n```js\n{\n    block : 'b1',\n    js : { lazyInit : false }\n}\n```\n\nЗадача: [#877](https://github.com/bem/bem-core/issues/877).\n\n#### Экземпляры для элементов\n\nУдалены элемент `elem-instances` блока `i-bem` и модификатор `elem-instances` элемента `dom` блока `i-bem`.\nТеперь соответствующая функциональность является частью `i-bem` и `i-bem-dom`.\n\nБыло:\n\n```js\nmodules.define('my-dom-block__my-elem', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl({ block : 'my-dom-block', elem : 'my-elem' }, { /* ... */ }));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-dom-block__my-elem', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declElem('my-dom-block', 'my-elem', { /* ... */ }));\n\n});\n```\n\nТеперь метод `_elem(elemName)` экземпляра блока (бывший `elem(elemName)`) возвращает не jQuery-объект со всеми элементами\nс именем `elemName`, а экземпляр класса элемента.\n\nДля того, чтобы получить коллекцию экземпляров класса элемента, используйте метод `_elems()`.\n\nТеперь кэш для элементов с JS-реализацией найденных через `_elem()` и `_elems()` инвалидируется автоматически при DOM модификациях.\nЗадача: [#1352](https://github.com/bem/bem-core/issues/1352).\n\nКогда эти методы используются для элементов без JS-реализации необходимо использовать `_dropElemCache()` при динамическом обновлении DOM.\n\nНе забудьте включить поддержку экземплятор для элементов в шаблонизаторе.\nОпция [elemJsInstances](https://github.com/bem/bem-xjst/blob/master/docs/ru/3-api.md#%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%BA%D0%B0-js-%D1%8D%D0%BA%D0%B7%D0%B5%D0%BC%D0%BF%D0%BB%D1%8F%D1%80%D0%BE%D0%B2-%D0%B4%D0%BB%D1%8F-%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2-bem-core-v4) для `bem-xjst` или [jsElem](https://github.com/bem/bh#jselem) для `BH`.\n\n##### Cпособы взаимодействия с элементами\n\nБыло:\n\n```js\nthis.setMod(this.elem('my-elem'), 'my-mod', 'my-val');\n```\n\nСтало:\n\n```js\nthis._elem('my-elem').setMod('my-mod', 'my-val');\n```\n\nАналогично для методов `getMod()`, `hasMod()`, `toggleMod()`, `delMod()`.\n\n##### Удалённые методы и поля\n\nИз API блока удалены методы: `elemify()`, `elemParams()` и поле `onElemSetMod`. Соответствующая\nим функциональность выражается через экземпляры элементов .\n\nСм. также изменения про [методы поиска](#Методы-поиска).\n\nЗадача: [#581](https://github.com/bem/bem-core/issues/581).\n\n#### Методы поиска\n\nПереименованы следующие методы:\n\n- `findBlockInside()` в `findChildBlock()`\n- `findBlocksInside()` в `findChildBlocks()`\n- `findBlockOutside()` в `findParentBlock()`\n- `findBlocksOutside()` в `findParentBlocks()`\n- `findBlockOn()` в `findMixedBlock()`\n- `findBlocksOn()` в `findMixedBlocks()`\n\nИз этих методов удален опциональный первый параметр про элемент.\n\nДобавлены методы: `findChildElem()`, `findChildElems()`, `findParentElem()`, `findParentElems()`, `findMixedElem()`, `findMixedElems()`.\n\nБыло:\n\n```js\nthis.findBlockInside(this.elem('my-elem'), 'my-block-2');\n```\n\nСтало:\n\n```js\nthis.findChildElem('my-elem').findChildBlock(MyBlock2);\n```\n\nУдалены методы: `findElem()`, `closestElem()`, вместо них следует использовать методы `findChildElem()`\nи `findParentElem()`, соответсвенно.\n\nМетоды `findChildBlocks()`, `findParentBlocks()`, `findMixedBlocks()`, `findChildElems()`, `findParentElems()`,\n`findMixedElems()` возвращают [коллекции БЭМ-сущностей](#Коллекции).\n\nМетоды `findChildElem()` и `findChildElems()` (в отличие от предыдущего аналога `findElem`) не выполняют поиск на собственных DOM-узлах экземпляра.\n\nБыло:\n\n```js\nthis.findElem('my-elem');\n```\n\nСтало:\n\n```js\nthis.findChildElems('my-elem').concat(this.findMixedElems('my-elem'));\n```\n\nНо рекомендуется обратить внимание, действительно ли необходимы оба поиска:\nв большинстве случаев достаточно использовать или `this.findChildElems('my-elem')` или `this.findMixedElems('my-elem')`.\n\n##### Проверка вложенности\n\nВместо удаленного метода `containsDomElem()`, следует использовать метод `containsEntity()`.\n\nБыло:\n\n```js\nthis.containsDomElem(someElem);\n```\n\nСтало:\n\n```js\nthis.containsEntity(someElem);\n```\n\n#### Коллекции\n\nФункциональность элемента `collection` блока `i-bem` перестала быть опциональной.\n\nВсе методы возвращавшие массив БЭМ-сущностей, теперь возвращают коллекции.\n\nБыло:\n\n```js\nthis.findBlocksInside('my-block-2')[0].setMod('my-mod', 'my-val');\n```\n\nСтало:\n\n```js\nthis.findChildBlocks(MyBlock2).get(0).setMod('my-mod', 'my-val');\n```\n\nБыло:\n\n```js\nthis.findBlocksInside('my-block-2').forEach(function(myBlock2) {\n    return myBlock2.setMod('my-mod', 'my-val');\n});\n```\n\nСтало:\n\n```js\nthis.findChildBlocks(MyBlock2).setMod('my-mod', 'my-val');\n```\n\nЗадача: [#582](https://github.com/bem/bem-core/issues/582).\n\n#### События\n\nAPI работы с событиями значильно упрощено. Удалены методы экземпляра блока: `on()`, `un()`, `once()`, `bindTo()`,\n`unbindFrom()`, `bindToDoc()`, `bindToWin()`, `unbindFromDoc()`, `unbindFromWin()` и методы класса: `liveBindTo()`,\n`liveUnbindFrom()`, `on()`, `un()`, `once()`, `liveInitOnBlockEvent()`, `liveInitOnBlockInsideEvent()`.\nВместо них добавлены методы `_domEvents()` и `_events()`, возвращающие экземпляр класса менеджера событий, с методами\n`on()`, `un()` и `once()`;\n\n##### DOM-события на экземплярах\n\nБыло:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this.bindTo('click', this._onClick);\n            }\n        }\n    }\n});\n```\n\nСтало:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._domEvents().on('click', this._onClick);\n            }\n        }\n    }\n});\n```\n\nБыло:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this.bindToDoc('click', this._onDocClick);\n            }\n        }\n    }\n});\n```\n\nСтало:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._domEvents(bemDom.doc).on('click', this._onDocClick);\n            }\n        }\n    }\n});\n```\n\nБыло:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this.bindToWin('resize', this._onWinResize);\n            }\n        }\n    }\n});\n```\n\nСтало:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._domEvents(bemDom.win).on('resize', this._onWinResize);\n            }\n        }\n    }\n});\n```\n\n##### Ссылка на экземпляр\n\nЕсли событие произошло на БЭМ-экземпляре, в объект события будет добавлено поле, ссылающееся на экземпляр:\n\n```js\nthis._domEvents('my-elem').on('click', function(e) {\n    e.bemTarget // ссылается на экземпляр `my-elem`\n});\n```\n\n##### БЭМ-события на экземплярах\n\nБыло:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this.findBlockOutside('my-block-2').on('my-event', this._onMyBlock2MyEvent, this);\n            },\n\n            '' : function() {\n                this.findBlockOutside('my-block-2').un('my-event', this._onMyBlock2MyEvent, this);\n            }\n        }\n    }\n});\n```\n\nСтало:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._events(this.findParentBlock('my-block-2')).on('my-event', this._onMyBlock2MyEvent);\n            }\n        }\n    }\n});\n```\n\nСледует обратить внимание, что теперь, отписка от событий происходит автоматически во время уничтожения экземпляра.\n\n##### Делегированные DOM-события\n\nБыло:\n\n```js\nBEMDOM.decl('my-block', { /* ... */ }, {\n    live : function() {\n        this.liveBindTo('click', this.prototype._onClick);\n    }\n});\n```\n\nСтало:\n\n```js\nbemDom.declBlock('my-block', { /* ... */ }, {\n    onInit : function() {\n        this._domEvents().on('click', this.prototype._onClick);\n    }\n});\n```\n\nБыло:\n\n```js\nBEMDOM.decl('my-block', { /* ... */ }, {\n    live : function() {\n        this.liveBindTo('my-elem', 'click', this.prototype._onMyElemClick);\n    }\n});\n```\n\nСтало:\n\n```js\nbemDom.declBlock('my-block', { /* ... */ }, {\n    onInit : function() {\n        this._domEvents('my-elem').on('click', this.prototype._onMyElemClick);\n    }\n});\n```\n\n##### Делегированные БЭМ-события\n\nБыло:\n\n```js\nBEMDOM.decl('my-block', { /* ... */ }, {\n    live : function() {\n        this.liveInitOnBlockInsideEvent('my-event', 'my-block-2', this.prototype._onMyBlock2MyEvent);\n    }\n});\n```\n\nСтало:\n\n```js\nbemDom.declBlock('my-block', { /* ... */ }, {\n    onInit : function() {\n        this._events(MyBlock2).on('my-event', this.prototype._onMyBlock2MyEvent);\n    }\n});\n```\n\nСледует обратить внимание, что параметр с функцией обработчиком события теперь обязательный.\n\nБыло:\n\n```js\nmodules.define('my-block', ['i-bem__dom', 'my-block-2'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, { /* ... */ }, {\n    live : function() {\n        this.liveInitOnBlockInsideEvent('my-event', 'my-block-2');\n    }\n}));\n\n});\n```\n\nСтало:\n\n```js\nmodules.define('my-block', ['i-bem-dom', 'my-block-2', 'functions'], function(provide, bemDom, MyBlock2, functions) {\n\nprovide(bemDom.declBlock(this.name, { /* ... */ }, {\n    onInit : function() {\n        this._events(MyBlock2).on('my-event', functions.noop);\n    }\n}));\n\n});\n```\n\nБыло:\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                MyBlock2.on(this.domElem, 'my-event', this._onMyBlock2MyEvent, this);\n            },\n\n            '' : function() {\n                MyBlock2.un(this.domElem, 'my-event', this._onMyBlock2MyEvent, this);\n            }\n        }\n    }\n});\n```\n\nСтало:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._events(MyBlock2).on('my-event', this._onMyBlock2MyEvent);\n            }\n        }\n    }\n});\n```\n\nСледует обратить внимание, что теперь, отписка от событий происходит автоматически во время уничтожения экземпляра.\n\n#### Взаимодействие стороннего кода с БЭМ-блоками\n\n##### Получение экземпляра БЭМ-блока\n\nТеперь, метод jQuery-объекта `bem()` принимает БЭМ-класс, вместо строки.\n\nБыло:\n\n```js\nmodules.require(['jquery', 'i-bem__dom'], function($, BEMDOM) {\n\nvar myBlock = $('.my-block').bem('my-block');\n\n});\n```\n\nСтало:\n\n```js\nmodules.require(['jquery', 'my-block'], function($, MyBlock) {\n\nvar myBlock = $('.my-block').bem(MyBlock);\n\n});\n```\n\n##### Подписка на БЭМ-события из стороннего кода\n\nБыло:\n\n```js\nmodules.require(['jquery', 'i-bem__dom'], function($, BEMDOM) {\n\n$('.my-block').bem('my-block').on('my-event', function() { /* ... */ });\n\n});\n```\n\nСтало:\n\n```js\nmodules.require(['jquery', 'my-block', 'events__observable'], function($, MyBlock, observable) {\n\nobservable($('.my-block').bem(MyBlock))\n    .on('my-event', function() { /* ... */ });\n\n});\n```\n\nПри этом в зависимости нужно добавить `{ block : 'events', elem : 'observable', mods : { type : 'bem-dom' } }`.\n\nЗадача: [#394](https://github.com/bem/bem-core/issues/394).\n\n#### Имена protected-методов начинаются с `_`\n\nПереименованы protected-методы:\n\n- `emit()` в `_emit()`\n- `elem()` в `_elem()`\n- `dropElemCache()` в `_dropElemCache()`\n- `buildClass()` в `_buildClassName()`\n- `buildSelector()` в `_buildSelector()`\n- `getDefaultParams()` в `_getDefaultParams()`\n\nЗадачи: [#586](https://github.com/bem/bem-core/issues/586), [#1359](https://github.com/bem/bem-core/issues/1359).\n\n#### Удалённые методы\n\nУдалён метод `getMods()`.\n\n### Изменения в блоке `querystring`\n\nЭлемент `querystring__uri` стал блоком `uri`. Блок `querystring` стал элементом `uri__querystring`.\n\nЗадача: [#967](https://github.com/bem/bem-core/issues/967).\n\n### Изменения в блоке `page`\n\nЭлемент `page__css` больше не поддреживает поле `ie`. Используйте элемент `page__conditional-comment`.\n\nБыло:\n\n```\n{\n    block : 'page',\n    head : [\n        { elem : 'css', url : 'my-css.css', ie : false },\n        { elem : 'css', url : 'my-css', ie : true }\n    ],\n    content : 'Page content'\n}\n```\n\nСтало:\n\n```\n{\n    block : 'page',\n    head : [\n        {\n            elem : 'conditional-comment',\n            condition : '! IE',\n            content : { elem : 'css', url : 'my-css.css' }\n        },\n        {\n            elem : 'conditional-comment',\n            condition : '> IE 8',\n            content : { elem : 'css', url : 'my-css.ie.css' }\n        }\n        // и т.д. для других нужных версий IE\n    ],\n    content : 'Page content'\n}\n```\n\nЗадача: [#379](https://github.com/bem/bem-core/issues/379).\n\n## 3.0.0\n\nДля миграции на версию 3.0.0 достаточно ознакомиться с [историей изменений](https://ru.bem.info/libs/bem-core/v3/changelog/#300).\n\n## 2.0.0\n\nДля миграции на версию 2.0.0 достаточно ознакомиться с [историей изменений](https://ru.bem.info/libs/bem-core/v2/changelog/#200).\n\n## 1.0.0\n\nДля версии 1.0.0 миграция подразумевается с использования [bem-bl](https://github.com/bem/bem-bl/) на использование [bem-core](https://github.com/bem/bem-core/).\n\n### Модули\n\nВесь код теперь пишется в терминах модульной системы https://github.com/ymaps/modules.\nВсе зависимости должны явно указываться в коде, обращения к глобальным объектам необходимо минимизировать, а, по возможности, и полностью исключить.\n\nПример:\n\n```js\nmodules.define(\n    'my-module', // имя модуля\n    ['module-from-library', 'my-another-module'], // зависимости модуля\n    function(provide, moduleFromLibrary, myAnotherModule) { // декларация модуля, вызывается когда все зависимости \"разрезолвлены\"\n\n// предоставление модуля\nprovide({\n    myModuleMethod : function() {}\n});\n\n});\n```\n\nTODO: дописать про изменение сборки (использование специальных технологий для js и как быть с кастомными сборщиками)\n\n### jQuery и jQuery-плагины\n\njQuery представлен модулем-оберткой `jquery`, который использует глобальный объект jQuery,\nв случае если он уже присутствует на странице, в противном случае загружая его самостоятельно.\njQuery теперь используется только для операций, связанных непосредственно с DOM\n(поиск элементов, подписка на события, установка/получение атрибутов элементов, и т.д.).\n\nДля всех остальных операций написаны соответствующие модули,\nпредоставляющие аналогичный функционал, но, при этом, не зависящие от jQuery:\n * модуль `objects` для работы с объектами (с методами `extend`, `isEmpty`, `each`)\n * модуль `functions` для работы с функциями (с методами `isFunction` и `noop`)\n\nТакже, все jQuery-плагины, не связанные непосредственно с jQuery\n(`$.observable`, `$.inherit`, `$.cookie`, `$.identify`, `$.throttle`) стали модулями:\n * модуль `events` вместо `$.observable` для работы с событиями, предоставляющий \"классы\" `EventsEmitter` и `Event`\n * модуль `inherit` вместо `$.inherit` для работы с \"классами\" и наследованием\n * модуль `cookie` вместо `$.cookie`\n * модуль `identify` вместо `$.identify`\n * модули `functions__throttle`, `functions__debounce` вместо `$.throttle` и `$.debounce`, соответственно\n\nБыло:\n\n```js\n// код блока\n$.throttle()\n// код блока\n\n```\n\nСтало:\n```js\nmodule.define('my-module', ['functions__throttle'], function(provide, throttle) {\n// код модуля\nthrottle()\n// код модуля\n});\n```\n\n### BEM.DOM-блоки\n\n#### Декларация\n\nВместо декларации через BEM.DOM.decl необходимо доопределять модуль `i-bem__dom`.\n\nБыло:\n```js\nBEM.DOM.decl('block', /* ... */);\n```\nСтало:\n```js\nmodules.define('i-bem__dom', function(provide, BEMDOM) {\n\nBEMDOM.decl('block', /* ... */);\n\nprovide(BEMDOM);\n\n});\n```\n\n#### Конструктор\n\nНеобходимо использовать полную нотацию для обработчика установки модификатора `js` в значение `inited`.\n\nБыло:\n\n```js\nonSetMod : {\n  js : function() {\n      // код конструктора\n    }\n}\n```\n\nСтало:\n\n```js\nonSetMod : {\n    'js' : {\n        'inited' : function() {\n            // код конструктора\n        }\n    }\n}\n```\n\n#### Деструктор\n\nВместо метода `destruct` необходимо использовать обработчик установки модификатора `js` в пустое значение (удаление модификатора).\nВызывать `__base` для того, чтобы у блоков работал базовый деструктор, определенный в `i-bem__dom`, больше не нужно.\n\nБыло:\n\n```js\ndestruct : function() {\n    this.__base.apply(this, arguments);\n    // код деструктора\n}\n```\n\nСтало:\n\n```js\nonSetMod : {\n    js : {\n        '' : function() {\n            // код деструктора\n        }\n    }\n}\n```\n\n#### Метод `changeThis`\n\nВместо метода `changeThis` необходимо использовать либо соответствующий параметр, либо нативный метод `bind`, если такой параметр отсутствует.\n\nБыло:\n\n```js\n// код блока\nobj.on('event', this.changeThis(this._method));\n// код блока\n```\n\nСтало:\n\n```js\nobj.on('event', this._method.bind(this));\n// или лучше\nobj.on('event', this._method, this);\n```\n\n#### Метод `afterCurrentEvent`\n\nВместо метода `afterCurrentEvent` необходимо использовать метод `nextTick`,\nкоторый гарантирует, что блок еще существует в момент исполнения callback'а\n(если блок уже уничтожен к этому моменту, то callback не исполняется).\n\nБыло:\n\n```js\nBEM.DOM.decl('block', {\n    method : function() {\n        this.afterCurrentEvent(function() {\n            /* ... */\n        });\n    }\n});\n```\n\nСтало:\n\n```js\nmodules.define('i-bem__dom', function(provide, BEMDOM) {\n\nBEMDOM.decl('block', {\n    method : function() {\n        this.nextTick(function() {\n                /* ... */\n            });\n        }\n    });\n});\n```\n\n#### Метод `findElem`\n\nКонтекст для поиска элемента больше не задается строкой, вместо нее следует передавать jQuery-объект.\n\nБыло:\n\n```js\nvar nestedElem = this.findElem('parent-elem', 'nested-elem');\n```\n\nСтало:\n\n```js\nvar nestedElem = this.findElem(this.findElem('parent-elem'), 'nested-elem'),\n    oneMoreElem = this.findElem(this.elem('another-elem'), 'nested-elem');\n```\n\n#### Метод `liveBindTo`\n\nМетод `liveBindTo` больше не поддерживает поле `elemName` для передачи имени элемента. Вместо него следует использовать поле `elem`.\n\n#### Доступ до DOM-элемента в обработчике события\n\nDOM-элемент, к которому был подвешен обработчик события теперь доступен\nкак `$(e.currentTarget)`вместо `e.data.domElem`.\n\nБыло:\n\n```js\nonClick : function(e) {\n    e.data.domElem.attr(/* ... */);\n}\n```\n\nСтало:\n\n```js\nonClick : function(e) {\n    $(e.currentTarget).attr(/* ... */);\n}\n```\n\n#### Каналы (channels)\n\nКаналы больше не являются встроенными в BEM, теперь они являются самостоятельным модулем `events__channels`.\n\nБыло:\n\n```js\nBEM.DOM.decl('block', {\n    method : function() {\n        BEM.channel('channel-name').on(/* ... */);\n    }\n});\n```\n\nСтало:\n\n```js\nmodules.define('i-bem__dom', ['events__channels'], function(provide, channels, BEMDOM) {\n\nBEMDOM.decl('block', {\n    method : function() {\n        channels('channel-name').on(/* ... */);\n\n        }\n    });\n});\n```\n\n#### Блок `i-system` и канал `sys` событий `tick`, `idle`, `wakeup`\n\nЭтот блок и канал перестали существовать, вместо них появились отдельные модули: `tick` с событием tick  и `idle` с событиями idle и wakeup.\n\nБыло:\n\n```js\nBEM.DOM.decl('block', {\n    method : function() {\n        BEM.channel('sys').on('tick', /* ... */);\n    }\n});\n```\n\nСтало:\n\n```js\nmodules.define('i-bem__dom', ['tick'], function(provide, tick, BEMDOM) {\n\nBEMDOM.decl('block', {\n    method : function() {\n        tick.on('tick', /* ... */);\n\n        }\n    });\n\n});\n```\n\nБыло:\n\n```js\nBEM.DOM.decl('block', {\n    method : function() {\n        BEM.channel('sys').on('wakeup', /* ... */);\n    }\n});\n```\n\nСтало:\n\n```js\nmodules.define('i-bem__dom', ['idle'], function(provide, idle, BEMDOM) {\n\nBEMDOM.decl('block', {\n    method : function() {\n        idle.on('wakeup', /* ... */);\n\n        }\n    });\n});\n```\n\n### BEM-блоки\n\nТе BEM-блоки, которые использовались как хранилище для каких-то методов, при этом никак не использующие BEM-методологию, теперь\nмогут быть написаны как модули.\n\nБыло:\n\n```js\nBEM.decl('i-router', {\n    route : function() { /* ... */ }\n});\n```\n\nСтало:\n\n```js\nmodules.define('router', function(provide) {\n\nprovide({\n    route : function() { /* ... */ }\n});\n\n});\n```\n\nЕсли же, по каким-то причинам, нужны именно BEM-блоки (не BEM.DOM-блоки), то их можно объявлять, доопределяя модуль `i-bem`.\n\nБыло:\n\n```js\nBEM.decl('my-block', { /* ... */ });\n```\n\nСтало:\n\n```js\nmodules.define('i-bem', function(provide, BEM) {\n\nBEM.decl('my-block', { /* ... */ });\n\nprovide(BEM);\n\n});\n```\n\n#### Рефакторинг на примере блока `b-spin`\n\nБыло:\n\n```js\nBEM.DOM.decl('b-spin', {\n\n    onSetMod : {\n\n        'js' : function() {\n\n            this._size = this.getMod('size') || /[\\d]+/.exec(this.getMod('theme'))[0];\n\n            this._bgProp = 'background-position';\n            this._posPrefix = '0 -';\n\n            if (this.elem('icon').css('background-position-y')) { /* В IE нельзя получить свойство background-position, только background-position-y, поэтому костыляем */\n                this._bgProp = 'background-position-y';\n                this._posPrefix = '-';\n            }\n\n            this._curFrame = 0;\n\n            this.hasMod('progress') && this.channel('sys').on('tick', this._onTick, this);\n\n        },\n\n        'progress' : {\n\n            'yes' : function() {\n\n                this.channel('sys').on('tick', this._onTick, this);\n\n            },\n\n            '' : function() {\n\n                this.channel('sys').un('tick', this._onTick, this);\n\n            }\n\n        }\n    },\n\n    _onTick: function(){\n\n        var y = ++this._curFrame * this._size;\n\n        (y >= this._size * 36) && (this._curFrame = y = 0);\n\n        this.elem('icon').css(this._bgProp, this._posPrefix + y +'px');\n\n    },\n\n    destruct : function() {\n\n        this.channel('sys').un('tick', this._onTick, this);\n        this.__base.apply(this, arguments);\n\n    }\n\n});\n```\n\nСтало:\n\n```js\nmodules.define(\n    'i-bem__dom',\n    ['tick'],\n    function(provide, tick, BEMDOM) {\n\nvar FRAME_COUNT = 36;\n\nBEMDOM.decl('b-spin', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() { // конструктор\n                var hasBackgroundPositionY = !!this.elem('icon').css('background-position-y')); /* В IE нельзя получить свойство background-position, только background-position-y */\n\n                this._bgProp = hasBackgroundPositionY? 'background-position-y' : 'background-position';\n                this._posPrefix = hasBackgroundPositionY? '-' : '0 -';\n                this._curFrame = 0;\n                this._size = Number(this.getMod('size') || /[\\d]+/.exec(this.getMod('theme'))[0]);\n\n                this.hasMod('progress') && this._bindToTick();\n            },\n\n            '' : function() { // деструктор\n                this._unbindFromTick();\n            }\n        },\n\n        'progress' : {\n            'true' : function() {\n                this._bindToTick();\n            },\n\n            '' : function() {\n                this._unbindFromTick();\n            }\n        }\n    },\n\n    _bindToTick : function() {\n        tick.on('tick', this._onTick, this);\n    },\n\n    _unbindFromTick : function() {\n        tick.un('tick', this._onTick, this);\n    },\n\n    _onTick : function() {\n        var offset;\n        this._curFrame++ >= FRAME_COUNT?\n            offset = this._curFrame * this._size :\n            this._curFrame = offset = 0;\n\n        this.elem('icon').css(this._bgProp, this._posPrefix + offset + 'px');\n    }\n});\n\nprovide(BEMDOM);\n\n});\n```\n"
  },
  {
    "path": "PLAN.md",
    "content": "# Plan: Modernization of bem-core Dependencies\n\n## Current State Analysis\n\n### Runtime Environment\n| Component | Current | Target | Status |\n|-----------|---------|--------|--------|\n| Node.js | 8 | 24 LTS (Krypton, v24.13.1) | 16 major versions behind |\n| npm | 5-6 (lockfile v1) | 11+ (lockfile v3) | Needs regeneration |\n\n### Dependencies — Current vs Latest\n\n#### Production (`dependencies`)\n| Package | Current | Latest | Last Published | Status |\n|---------|---------|--------|----------------|--------|\n| `ym` | ^0.1.2 | 0.1.2 | ancient | **Abandoned**. BEM module system. No updates for years. |\n\n#### Dev Dependencies (`devDependencies`)\n\n**Build System (ENB) — ALL ABANDONED:**\n| Package | Current | Latest | Last Published | Status |\n|---------|---------|--------|----------------|--------|\n| `enb` | ^1.2.0 | 1.5.1 | 2017-11 | **Abandoned** |\n| `enb-bem-techs` | ^2.2.2 | 2.2.2 | 2017-12 | **Abandoned** |\n| `enb-magic-factory` | ^0.6.0 | 0.6.0 | 2018-02 | **Abandoned** |\n| `enb-magic-platform` | 0.7.0 | 0.7.0 | 2016-04 | **Abandoned** |\n| `enb-bemxjst` | ^8.10.2 | 8.10.6 | ~2018 | **Abandoned** |\n| `enb-bemxjst-6x` | ^6.5.3 | — | — | **Abandoned** |\n| `enb-bemxjst-7x` | ^7.3.1 | — | — | **Abandoned** |\n| `enb-bemxjst-i18n` | 1.0.0-beta3 | — | — | **Abandoned** |\n| `enb-bh` | ^1.2.1 | — | — | **Abandoned** |\n| `enb-bh-i18n` | 1.0.0-beta2 | — | — | **Abandoned** |\n| `enb-borschik` | ^2.8.0 | — | — | **Abandoned** |\n| `enb-css` | ^1.2.2 | 1.2.2 | — | **Abandoned** |\n| `enb-js` | ^1.1.1 | 1.1.1 | — | **Abandoned** |\n| `enb-bem-docs` | 0.14.1 | 0.15.0 | 2019-02 | **Abandoned** |\n| `enb-bem-examples` | ^1.0.2 | 1.0.2 | 2016-04 | **Abandoned** |\n| `enb-bem-specs` | ^0.11.0 | 0.11.0 | 2016-12 | **Abandoned** |\n| `enb-bem-tmpl-specs` | ^1.3.3 | 1.3.3 | 2018-03 | **Abandoned** |\n| `enb-bem-i18n` | ^1.1.1 | — | — | **Abandoned** |\n\n**Linting — ABANDONED/OUTDATED:**\n| Package | Current | Latest | Last Published | Status |\n|---------|---------|--------|----------------|--------|\n| `jscs` | ^2.11.0 | 3.0.7 | 2016-07 | **Abandoned** (merged into ESLint) |\n| `jscs-bem` | ^0.2.0 | — | — | **Abandoned** |\n| `jshint` | ^2.9.1 | 2.13.6 | maintained | Functional but superseded by ESLint |\n| `jshint-groups` | ^0.8.0 | — | — | **Abandoned** |\n\n**Testing — PARTIALLY ABANDONED:**\n| Package | Current | Latest | Last Published | Status |\n|---------|---------|--------|----------------|--------|\n| `mocha` | ^3.3.0 | 11.7.5 | active | 8 major versions behind |\n| `mocha-phantomjs` | ^4.1.0 | 4.1.0 | 2016-06 | **Abandoned** (PhantomJS is dead) |\n| `chai` | ^3.2.0 | 6.2.2 | active | 3 major versions behind; v5+ is ESM-only |\n| `chai-as-promised` | ^5.1.0 | — | — | Outdated |\n| `istanbul` | ^0.4.3 | 0.4.5 | 2016-08 | **Abandoned** (replaced by nyc → c8) |\n\n**BEM Tools:**\n| Package | Current | Latest | Last Published | Status |\n|---------|---------|--------|----------------|--------|\n| `bem-naming` | ^1.0.1 | 1.0.1 | — | **Abandoned** |\n| `bem-walk` | 1.0.0-alpha1 | 1.0.0-1 | — | **Never left alpha** |\n\n**Other:**\n| Package | Current | Latest | Last Published | Status |\n|---------|---------|--------|----------------|--------|\n| `borschik` | ^1.5.3 | 3.0.0 | 2021-02 | Unmaintained |\n| `bower` | ^1.7.9 | 1.8.14 | 2022-03 | **Deprecated** since 2017 |\n| `git-hooks` | ^1.0.2 | 1.1.10 | — | Superseded by husky |\n| `gitbook-api` | ^3.0.2 | — | — | **Abandoned** |\n| `jsdoc` | ^3.5.5 | 4.0.5 | active | 1 major version behind |\n| `vow` | ^0.4.17 | 0.4.20 | 2019-07 | **Abandoned** (native Promises exist) |\n\n### Configuration Files to Replace\n| File | Purpose | Modern Replacement |\n|------|---------|-------------------|\n| `.jshintrc` | JSHint config | `eslint.config.js` (ESLint flat config) |\n| `.jscs.json` | JSCS style config | `eslint.config.js` (ESLint flat config) |\n| `.jshint-groups.js` | JSHint groups config | `eslint.config.js` (ESLint flat config) |\n| `.bowerrc` | Bower directory config | Remove (drop Bower) |\n| `bower.json` | Bower package manifest | Remove (drop Bower) |\n| `.travis.yml` | Travis CI config | `.github/workflows/ci.yml` (GitHub Actions) |\n| `.enb/` (entire dir) | ENB build config | New build system config |\n| `.githooks/pre-commit/lint` | Pre-commit hook | `.husky/pre-commit` |\n\n### CI/CD\n| Component | Current | Target |\n|-----------|---------|--------|\n| CI system | Travis CI | GitHub Actions |\n| Coverage | Istanbul + Coveralls | c8 + Coveralls (or Codecov) |\n| Node.js in CI | 8 | 24 |\n\n---\n\n## Implementation Plan\n\n### Phase 0: Preparation\n1. Create feature branch `claude/update-dependencies-fWO1e`\n2. Verify project builds and tests in current state (baseline)\n\n### Phase 1: Node.js & npm Modernization\n1. Add `.nvmrc` with `24`\n2. Add `engines` field to `package.json`: `\"node\": \">=24\"`, `\"npm\": \">=11\"`\n3. Delete `package-lock.json` (will regenerate with lockfile v3)\n\n### Phase 2: Remove Abandoned/Deprecated Tools\n1. **Remove Bower**: delete `bower.json`, `.bowerrc`, remove `bower i` from scripts\n2. **Remove JSCS**: delete `.jscs.json`, uninstall `jscs`, `jscs-bem`\n3. **Remove JSHint**: delete `.jshintrc`, `.jshint-groups.js`, uninstall `jshint`, `jshint-groups`\n4. **Remove Istanbul**: uninstall `istanbul`\n5. **Remove mocha-phantomjs**: uninstall `mocha-phantomjs`\n6. **Remove gitbook-api**: uninstall `gitbook-api`\n7. **Remove git-hooks**: delete `.githooks/` directory, uninstall `git-hooks`\n\n### Phase 3: Linting — Migrate to ESLint 10\n1. Install `eslint@^10.0.1`\n2. Create `eslint.config.js` (flat config, required for ESLint 10) migrating rules from:\n   - `.jshintrc` rules → ESLint equivalents\n   - `.jscs.json` BEM preset rules → ESLint equivalents\n   - `.jshint-groups.js` file-group-specific overrides → ESLint flat config overrides\n3. Support file extensions: `.js`, `.bemtree`, `.bemhtml`\n4. Update `package.json` `lint` script: `\"lint\": \"eslint .\"`\n5. Delete old config files: `.jshintrc`, `.jscs.json`, `.jshint-groups.js`\n\n### Phase 4: Testing Modernization\n1. **Upgrade Mocha**: `mocha@^11.7.5`\n2. **Upgrade Chai**: `chai@^6.2.2` (ESM-only — requires `\"type\": \"module\"` or `.mjs` for test files using it)\n   - Alternative: stay on `chai@^4.x` (last CJS version) if ESM migration is too invasive\n3. **Replace Istanbul with c8**: install `c8@^10.1.3`\n4. **Replace mocha-phantomjs with Playwright**: install `playwright@^1.58.2` and `@playwright/test`\n   - Browser spec tests (`.spec.js` files using `modules.define`) need adaptation for Playwright\n5. Update test scripts in `package.json`\n\n### Phase 5: Build System — ENB → Vite 6 + BEM Levels Plugin\n\n> **This is the highest-risk, highest-effort phase.** The entire ENB ecosystem (17+ packages) is abandoned.\n> **Chosen approach: Vite 6** with custom `vite-plugin-bem-levels` for BEM level resolution + barrel file generation, plus dedicated plugins for BEMHTML/BH template compilation and i18n.\n\n#### What ENB Currently Does (to be replaced)\n\n| ENB Function | Packages | Vite Replacement |\n|---|---|---|\n| BEM level scanning & file resolution | `enb-bem-techs` | `vite-plugin-bem-levels` (custom) |\n| JS bundling with `ym` module system | `enb-js` + `ym` | Vite native ES modules + barrel files |\n| Module redefinition (`modules.define` chains) | `ym` runtime | Platform barrel files (auto-generated) |\n| CSS concatenation | `enb-css` | Vite native CSS handling |\n| BEMHTML template compilation | `enb-bemxjst`, `enb-bemxjst-6x`, `enb-bemxjst-7x` | `vite-plugin-bemhtml` (custom, wraps `bem-xjst`) |\n| BH template compilation | `enb-bh` | `vite-plugin-bh` (custom, wraps `bh`) |\n| i18n keysets processing | `enb-bem-i18n`, `enb-bemxjst-i18n`, `enb-bh-i18n` | `vite-plugin-bem-i18n` (custom) |\n| Minification (borschik) | `enb-borschik`, `borschik` | Vite built-in (esbuild/terser for JS, lightningcss for CSS) |\n| HTML from BEMJSON | `enb-bemxjst`, `enb-bh` | Build script using compiled templates |\n| Examples/tests/specs building | `enb-bem-examples`, `enb-bem-specs`, `enb-bem-docs`, `enb-magic-*` | Vite dev server + Playwright |\n\n#### Phase 5.0: Preparation & Coexistence\n\n> ENB and Vite will coexist during migration. ENB stays functional until Vite fully replaces it.\n\n1. Install Vite 6 and core dependencies:\n   ```\n   npm i -D vite@^6 @anthropic-ai/vite-plugin-bem-levels\n   ```\n   (Initially `vite-plugin-bem-levels` will live in `build/plugins/` as a local module)\n2. Create build directory structure:\n   ```\n   build/\n   ├── plugins/\n   │   ├── vite-plugin-bem-levels.js    — BEM level resolution + barrel generation\n   │   ├── vite-plugin-bemhtml.js       — BEMHTML compilation\n   │   ├── vite-plugin-bh.js            — BH compilation\n   │   └── vite-plugin-bem-i18n.js      — i18n keysets\n   ├── platforms/\n   │   ├── desktop.js                   — entry point for desktop platform\n   │   └── touch.js                     — entry point for touch platform\n   └── vite.config.js                   — main Vite config\n   ```\n3. Keep `.enb/` directory intact for fallback\n\n#### Phase 5.1: `vite-plugin-bem-levels` — Core BEM Resolution Plugin\n\nThis is the central piece. The plugin:\n\n**5.1.1. Level scanning** — scans BEM levels like ENB does:\n```js\n// Configuration mirrors .enb/config/levels.js\nconst LEVELS = {\n    common: ['common.blocks'],\n    desktop: ['common.blocks', 'desktop.blocks'],\n    touch: ['common.blocks', 'touch.blocks']\n};\n```\n\n**5.1.2. Virtual module resolution** — resolves `bem:*` imports:\n```js\n// In source code:\nimport $ from 'bem:jquery';\nimport bemDom from 'bem:i-bem-dom';\n\n// Plugin resolves 'bem:jquery' → generated barrel file\n```\n\n**5.1.3. Barrel file generation for module redefinition chains**\n\nFor each module with redefinitions, generates a platform-specific barrel file.\nBased on analysis, there are exactly **5 modules with redefinitions** (8 total redefinition instances):\n\n| Module | Files in chain | Generated barrel |\n|---|---|---|\n| `jquery` | base + 4 redefinitions (3 common + 1 desktop) | `@bem/desktop/jquery.js`, `@bem/touch/jquery.js` |\n| `jquery__config` | base (common) + 1 redefinition (desktop) | `@bem/desktop/jquery__config.js` |\n| `ua` | alternative defs (desktop vs touch) + 1 redefinition (touch) | `@bem/desktop/ua.js`, `@bem/touch/ua.js` |\n| `events__observable` | base + 1 redefinition | `@bem/common/events__observable.js` |\n| `i-bem-dom__init` | base + dynamic redefinition | Special handling (see below) |\n\nExample generated barrel for `jquery` on desktop platform:\n```js\n// @generated by vite-plugin-bem-levels for platform: desktop\nimport { $ } from '../../common.blocks/jquery/jquery.js';\n\n// Redefinition: pointer events polyfill (mutates $.event.special)\nimport '../../common.blocks/jquery/__event/_type/jquery__event_type_pointernative.js';\n\n// Redefinition: pointerclick event\nimport '../../common.blocks/jquery/__event/_type/jquery__event_type_pointerclick.js';\n\n// Redefinition: pointerpress/pointerrelease events\nimport '../../common.blocks/jquery/__event/_type/jquery__event_type_pointerpressrelease.js';\n\n// Redefinition: IE8 window resize fix (desktop only)\nimport '../../desktop.blocks/jquery/__event/_type/jquery__event_type_winresize.js';\n\nexport { $ };\n```\n\n**5.1.4. Automatic redefinition detection**\n\nThe plugin scans all `.js` files in BEM levels and detects `modules.define('name', ...)` calls:\n- If a module name appears in multiple files → it's a redefinition chain\n- Files are ordered by level priority (common < desktop/touch)\n- Within a level, element/modifier files redefine block files\n\n**5.1.5. deps.js → import graph**\n\nParse existing `.deps.js` files and generate import statements:\n```js\n// From: { shouldDeps: [{ block: 'events' }] }\n// To:   import 'bem:events';\n```\n\nThis runs as a build-time code generation step, not at runtime.\n\n#### Phase 5.2: ym → ES Modules Migration\n\n**5.2.1. Source file transformation** — Each `modules.define` file gets an ES module equivalent:\n\nBefore (ym):\n```js\nmodules.define('jquery', ['loader_type_js', 'jquery__config'],\n    function(provide, loader, cfg) {\n        // ...\n        provide(jQuery);\n    });\n```\n\nAfter (ES module):\n```js\nimport loader from 'bem:loader_type_js';\nimport cfg from 'bem:jquery__config';\n\nlet jQuery;\n// ... loading logic ...\nexport default jQuery;\n```\n\n**5.2.2. Redefinition files** — become side-effect imports or wrapper modules:\n\nBefore (ym redefinition):\n```js\nmodules.define('jquery', function(provide, $) {\n    $.event.special.pointerclick = { /* ... */ };\n    provide($);\n});\n```\n\nAfter (ES module side-effect):\n```js\nimport $ from 'bem:jquery';  // gets the base jquery\n$.event.special.pointerclick = { /* ... */ };\n// No export needed — this is a side-effect module imported by the barrel\n```\n\n**5.2.3. Scope of ym migration**\n\n| Category | Count | Migration complexity |\n|---|---|---|\n| `.vanilla.js` files (never redefined) | ~13 | Low — straightforward `export default` |\n| `.js` files (base definitions, no redefinition) | ~30 | Low — `import` deps + `export default` |\n| `.js` files (redefinition participants) | 14 | Medium — need barrel coordination |\n| `i-bem-dom__init` dynamic redefinition | 1 | High — needs architectural redesign |\n\n**5.2.4. `i-bem-dom__init` special case**\n\nThe dynamic `modules.define` monkey-patching in `i-bem-dom.js` (lines 1141-1158) cannot be directly expressed in ES modules. Solution:\n- The Vite plugin generates the `i-bem-dom__init` barrel by scanning which blocks depend on `i-bem-dom`\n- This replaces the runtime monkey-patching with build-time dependency collection\n- The barrel imports all BEM DOM blocks, then calls `bemDom.init()`\n\n#### Phase 5.3: Template Engine Plugins\n\n**5.3.1. `vite-plugin-bemhtml`**\n- Handles `.bemhtml` and `.bemhtml.js` files\n- Wraps `bem-xjst` compiler (keep as dependency)\n- Produces compiled JS that can be imported as ES module\n- Supports HMR in dev mode\n\n**5.3.2. `vite-plugin-bh`**\n- Handles `.bh.js` files\n- Wraps `bh` runtime\n- Produces CommonJS-compatible bundle (BH uses `module.exports`)\n\n#### Phase 5.4: i18n Plugin\n\n**`vite-plugin-bem-i18n`**\n- Scans `*.i18n/` directories for keysets\n- Generates per-language JS modules\n- Supports `{lang}` placeholder pattern from current ENB config\n- Integrates with BEMHTML i18n via `bem-xjst`\n\n#### Phase 5.5: Vite Configuration\n\n```js\n// build/vite.config.js\nimport { defineConfig } from 'vite';\nimport bemLevels from './plugins/vite-plugin-bem-levels.js';\nimport bemhtml from './plugins/vite-plugin-bemhtml.js';\nimport bh from './plugins/vite-plugin-bh.js';\nimport bemI18n from './plugins/vite-plugin-bem-i18n.js';\n\nexport default defineConfig(({ mode }) => {\n    const platform = process.env.BEM_PLATFORM || 'desktop';\n\n    return {\n        plugins: [\n            bemLevels({\n                platform,\n                levels: {\n                    common: ['common.blocks'],\n                    desktop: ['common.blocks', 'desktop.blocks'],\n                    touch: ['common.blocks', 'touch.blocks']\n                }\n            }),\n            bemhtml(),\n            bh({ jsAttrName: 'data-bem', jsAttrScheme: 'json' }),\n            bemI18n({ langs: ['ru', 'en'] })\n        ],\n        build: {\n            lib: {\n                entry: `./build/platforms/${platform}.js`,\n                name: 'bemCore',\n                formats: ['es', 'umd']\n            },\n            outDir: `dist/${platform}`,\n            rollupOptions: {\n                output: {\n                    // Reproduce ENB dist structure:\n                    // bem-core.js, bem-core.css, bem-core.bemhtml.js, etc.\n                }\n            }\n        }\n    };\n});\n```\n\n#### Phase 5.6: dist Task Replacement\n\nENB `dist` task currently produces these artifacts per platform:\n\n| Artifact | dev | min | Vite equivalent |\n|---|---|---|---|\n| `bem-core.css` | `.dev.css` | `.css` | Vite CSS output (dev: unminified, build: minified) |\n| `bem-core.js` | `.dev.js` | `.js` | Vite JS bundle (with autoinit) |\n| `bem-core.no-autoinit.js` | `.dev.no-autoinit.js` | `.no-autoinit.js` | Separate entry point without `i-bem-dom__init_auto` |\n| `bem-core.bemhtml.js` | `.dev.bemhtml.js` | `.bemhtml.js` | BEMHTML-only bundle via separate entry |\n| `bem-core.bh.js` | `.dev.bh.js` | `.bh.js` | BH-only bundle via separate entry |\n| `bem-core.js+bemhtml.js` | `.dev.js+bemhtml.js` | `.js+bemhtml.js` | Combined bundle (JS + BEMHTML) |\n| `bem-core.js+bh.js` | `.dev.js+bh.js` | `.js+bh.js` | Combined bundle (JS + BH) |\n\nVite handles dev/production modes natively (no borschik needed).\n\nnpm scripts:\n```json\n{\n    \"build\": \"npm run build:desktop && npm run build:touch\",\n    \"build:desktop\": \"BEM_PLATFORM=desktop vite build -c build/vite.config.js\",\n    \"build:touch\": \"BEM_PLATFORM=touch vite build -c build/vite.config.js\",\n    \"dev\": \"BEM_PLATFORM=desktop vite -c build/vite.config.js\"\n}\n```\n\n#### Phase 5.7: specs/tests/examples Migration\n\n1. **Browser specs** (`*.spec.js`) — Currently use `enb-bem-specs` + mocha-phantomjs:\n   - Migrate to Playwright (already planned in Phase 4)\n   - Vite dev server serves spec pages instead of ENB magic nodes\n   - `vite-plugin-bem-levels` resolves spec level: `libs/bem-pr/spec.blocks`\n\n2. **Examples** (`*.examples/`) — Currently use `enb-bem-examples`:\n   - Vite dev server with HTML plugin serves example pages\n   - BEMJSON → HTML conversion done via imported compiled templates\n\n3. **Template specs** (`tmpl-specs`) — Currently use `enb-bem-tmpl-specs`:\n   - Run as Node.js tests with Mocha (import compiled templates, compare output)\n\n#### Phase 5.8: Cleanup\n\n1. Delete `.enb/` directory entirely (17 files)\n2. Uninstall all ENB packages (17 packages):\n   ```\n   npm rm enb enb-bem-techs enb-magic-factory enb-magic-platform \\\n     enb-bemxjst enb-bemxjst-6x enb-bemxjst-7x enb-bemxjst-i18n \\\n     enb-bh enb-bh-i18n enb-borschik enb-css enb-js \\\n     enb-bem-docs enb-bem-examples enb-bem-specs enb-bem-tmpl-specs \\\n     enb-bem-i18n\n   ```\n3. Uninstall `borschik` (replaced by Vite built-in minification)\n4. Remove `ym` from production dependencies (replaced by ES modules)\n5. Remove `bem-walk` and `bem-naming` if no longer used outside ENB\n6. Update `package.json` scripts to use Vite commands\n\n#### Phase 5 Sub-Phase Execution Order\n\n```\n5.0 Preparation & coexistence setup\n │\n ├── 5.1 vite-plugin-bem-levels (CRITICAL PATH — everything depends on this)\n │    ├── 5.1.1 Level scanning\n │    ├── 5.1.2 Virtual module resolution (bem:* imports)\n │    ├── 5.1.3 Barrel file generation\n │    ├── 5.1.4 Redefinition detection\n │    └── 5.1.5 deps.js parsing\n │\n ├── 5.2 ym → ES modules migration (can start after 5.1 is functional)\n │    ├── 5.2.1 .vanilla.js files (easiest, start here)\n │    ├── 5.2.2 .js base definitions\n │    ├── 5.2.3 .js redefinition files\n │    └── 5.2.4 i-bem-dom__init special case\n │\n ├── 5.3 Template plugins (parallel with 5.2)\n │    ├── 5.3.1 vite-plugin-bemhtml\n │    └── 5.3.2 vite-plugin-bh\n │\n ├── 5.4 i18n plugin (parallel with 5.2, 5.3)\n │\n ├── 5.5 Vite config (after 5.1-5.4 plugins exist)\n │\n ├── 5.6 dist replacement (after 5.5)\n │    └── Verify output matches ENB dist artifacts\n │\n ├── 5.7 specs/tests/examples (after 5.6)\n │\n └── 5.8 Cleanup (LAST — only after full verification)\n```\n\n#### Phase 5 Risk Mitigation\n\n| Risk | Impact | Mitigation |\n|---|---|---|\n| Barrel file import order matters for side-effects | High | Plugin sorts by level priority; test thoroughly |\n| `i-bem-dom__init` dynamic dep collection hard to replicate | High | Build-time scanning replaces runtime monkey-patching |\n| `bem-xjst` may not work as Vite plugin | Medium | Keep as Node.js pre-compilation step if needed |\n| Circular dependencies between BEM blocks | Medium | Vite handles circular ESM; add cycle detection to plugin |\n| Output bundle size differs from ENB | Low | Compare sizes; adjust Rollup chunking |\n| Dev server HMR with BEM redefinitions | Low | Regenerate barrels on file change; full reload as fallback |\n\n### Phase 6: Git Hooks Modernization\n1. Install `husky@^9.1.7` + `lint-staged@^16.2.7`\n2. Configure `.husky/pre-commit` to run `lint-staged`\n3. Configure `lint-staged` in `package.json` to run ESLint on staged files\n4. Delete `.githooks/` directory\n\n### Phase 7: CI/CD — Travis CI → GitHub Actions\n1. Create `.github/workflows/ci.yml`:\n   - Matrix: Node.js 24\n   - Steps: install, lint, test\n   - Coverage: c8 + upload to Coveralls\n2. Delete `.travis.yml`\n\n### Phase 8: Update Remaining Packages\n1. `mocha` → ^11.7.5\n2. `jsdoc` → ^4.0.5\n3. `vow` → replace with native `Promise` where possible; remove if fully replaced\n4. `borschik` → remove (replaced by Vite built-in minification in Phase 5.8)\n5. `bem-naming` → remove if only used by ENB plugins; keep if used by `vite-plugin-bem-levels`\n6. `bem-walk` → remove (replaced by custom level scanning in `vite-plugin-bem-levels`)\n7. `ym` → remove from production deps (replaced by ES modules in Phase 5.2)\n\n### Phase 9: Regenerate Lock File & Validate\n1. Run `npm install` to generate new `package-lock.json` (lockfile v3)\n2. Run `npm run lint` — fix any ESLint issues\n3. Run `npm run build` — verify Vite builds succeed for both platforms\n4. Run `npm run test` — verify all tests pass\n5. Compare Vite dist output with archived ENB dist output (size, functionality)\n6. Run examples in Vite dev server, verify they work\n\n---\n\n## Risk Assessment\n\n| Risk | Impact | Mitigation |\n|------|--------|------------|\n| `vite-plugin-bem-levels` barrel generation order incorrect | **CRITICAL** | Comprehensive tests comparing ENB and Vite output |\n| `i-bem-dom__init` dynamic monkey-patching hard to replicate statically | **HIGH** | Build-time BEM block scanning replaces runtime logic |\n| `bem-xjst` (BEMHTML compiler) integration with Vite plugin | High | Keep as pre-compilation step if direct plugin fails |\n| Chai 6.x ESM-only breaks test imports | Medium | Use Chai 4.x (last CJS) or add ESM wrapper |\n| mocha-phantomjs removal breaks browser tests | High | Playwright migration for spec tests |\n| Circular dependencies between BEM blocks in ES modules | Medium | Vite handles circular ESM natively; add cycle detection |\n| BEM-specific spec files (`modules.define`) won't work without `ym` | High | Migrate spec files to ES imports as part of Phase 5.2 |\n| Output bundle size/behavior differs from ENB | Medium | Comparison testing: run both builds, diff output |\n| ym → ES modules migration introduces regressions | High | Phased migration with coexistence; ENB stays as reference |\n\n## Suggested Execution Order (by priority/safety)\n\n1. **Phase 0** — Baseline\n2. **Phase 1** — Node.js 24 (needed for Vite 6 and modern tooling)\n3. **Phase 2** — Remove dead packages (safe, reduces surface)\n4. **Phase 3** — ESLint migration (independent, high value)\n5. **Phase 6** — Git hooks (small, independent)\n6. **Phase 7** — GitHub Actions (independent)\n7. **Phase 8** — Update remaining packages\n8. **Phase 4** — Testing modernization (Playwright needed for Phase 5.7)\n9. **Phase 5** — Build system migration to Vite (largest effort):\n   - 5.0 → 5.1 → 5.2 + 5.3 + 5.4 (parallel) → 5.5 → 5.6 → 5.7 → 5.8\n10. **Phase 9** — Final validation (verify all Vite builds match ENB output)\n"
  },
  {
    "path": "README.md",
    "content": "# bem-core library [![CI](https://github.com/bem/bem-core/actions/workflows/ci.yml/badge.svg?branch=v5)](https://github.com/bem/bem-core/actions/workflows/ci.yml) [![GitHub Release](https://img.shields.io/github/release/bem/bem-core.svg)](https://github.com/bem/bem-core/releases)\n\nDocumentation on `bem-core` is also available at [bem.info](https://en.bem.info/libs/bem-core/). It is also available [in Russian](https://ru.bem.info/libs/bem-core/).\n\n## What is this?\n\n`bem-core` is a base library for web interface development.\nIt provides the minimal stack for coding client-side JavaScript and templating.\n\n## Use\n\nInstall as an npm dependency:\n\n```shell\nnpm install bem-core@5\n```\n\njQuery 4 is a peer dependency — install it alongside:\n\n```shell\nnpm install jquery@^4.0.0\n```\n\n## Inside\n\n### Levels\n\n  - `common.blocks` — suited for any devices and browsers\n  - `desktop.blocks` — should be used for desktop browsers\n  - `touch.blocks` — implement some touch-platforms specifics\n\n### Blocks\n\n  - `i-bem` — base block with helpers for JS and HTML\n  - `strings` — helpers for JS-strings\n  - `objects` — helpers for JS-objects\n  - `functions` — helpers for JS-functions\n  - `events` — JS-events\n  - `uri` — helpers for work with URIs and querystrings\n  - `tick` — global timer\n  - `idle` — IDLE event\n  - `next-tick` — polyfill for `nextTick`/`setTimeout(0, ...)`\n  - `inherit` — OOP helpers\n  - `jquery` — jQuery\n  - `clearfix` — CSS clearfix trick\n  - `identify` — identify JS-objects\n  - `cookie` — helpers for work with browser cookies\n  - `dom` — helpers for work with DOM\n  - `loader` — loader for JS files\n  - `ua` — browser features detection\n  - `keyboard` — keyboard helpers\n  - `page` — html/head/body scaffold\n\n### Technologies\n\n  - vanilla.js + browser.js\n  - bemhtml\n  - bemtree\n\n## API\n\nThe autogenerated JSDoc API can be found on bem.info. E.g. JSDoc for `i-bem` is here https://en.bem.info/platform/libs/bem-core/current/desktop/i-bem/#jsdoc\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md).\n\n## Migration\n\nIf you are upgrading from v4, see [MIGRATION.md](MIGRATION.md).\n\n## Development\n\n### Working copy\n\n1. Get the source code:\n   ```shell\n   git clone -b v5 git://github.com/bem/bem-core.git\n   cd bem-core\n   ```\n\n2. Install the dependencies (requires Node.js 20+):\n   ```shell\n   npm install\n   ```\n\n3. Run linting:\n   ```shell\n   npm run lint\n   ```\n\n4. Run tests:\n   ```shell\n   npm test              # server-side tests (node:test)\n   npm run test:browser  # browser tests (Playwright)\n   npm run test:all      # both\n   ```\n\n5. Build:\n   ```shell\n   npm run build         # desktop + touch platforms\n   ```\n\n### How to contribute\n\nPlease refer to [How to contribute](/CONTRIBUTING.md) guide.\n\n## Supported browsers\n\n  - Google Chrome (latest)\n  - Firefox (latest)\n  - Safari (latest)\n  - Edge (latest)\n\n## License\nCode and documentation copyright 2012 YANDEX LLC. Code released under the [Mozilla Public License 2.0](LICENSE.txt).\n"
  },
  {
    "path": "README.ru.md",
    "content": "# Библиотека BEM Core\n\n`bem-core` — это библиотека с открытым кодом, которая предоставляет набор блоков для разработки веб-интерфейсов. Содержит необходимый минимум для разработки клиентского JS и HTML-шаблонов.\n\n[![CI](https://github.com/bem/bem-core/actions/workflows/ci.yml/badge.svg?branch=v5)](https://github.com/bem/bem-core/actions/workflows/ci.yml) [![GitHub Release](https://img.shields.io/github/release/bem/bem-core.svg)](https://github.com/bem/bem-core/releases)\n\n> **Примечание.** Информация о библиотеке в более информативном виде доступна на [bem.info](https://ru.bem.info/libs/bem-core/). This README is also available [in English](https://en.bem.info/libs/bem-core/).\n\n## Содержание\n\n* [Уровни](#Уровни-переопределения)\n* [Блоки](#Блоки)\n* [Использование](#Использование)\n* [Поддерживаемые браузеры](#Поддерживаемые-браузеры)\n* [Технологии](#Технологии)\n* [API](#api)\n* [Разработка](#Разработка)\n\n**Дополнительная информация**\n\n* [История изменений](CHANGELOG.ru.md)\n* [Миграция на последующие версии](MIGRATION.ru.md)\n\n## Уровни переопределения\n\n* `common.blocks` — поддержка всех устройств и браузеров;\n* `desktop.blocks` — поддержка всех десктопных браузеров;\n* `touch.blocks` — реализация специфических особенностей для touch-платформ.\n\n## Блоки\n\n* [i-bem](common.blocks/i-bem/i-bem.ru.md) — базовый блок с хелперами для JS и HTML;\n* [i-bem-dom](common.blocks/i-bem-dom/i-bem-dom.ru.md) — базовый блок с хелперами для HTML;\n* [strings](common.blocks/strings/strings.ru.md) — хелперы для JS-строк;\n* [objects](common.blocks/objects/objects.ru.md) — хелперы для JS-объектов;\n* [functions](common.blocks/functions/functions.ru.md) — хелперы для JS-функций;\n* [events](common.blocks/events/events.ru.md) — JS-события;\n* [uri](common.blocks/uri/uri.ru.md) — работа с URI и строкой запроса;\n* [tick](common.blocks/tick/tick.ru.md) — глобальный таймер;\n* [idle](common.blocks/idle/idle.ru.md) — IDLE-событие;\n* [next-tick](common.blocks/next-tick/next-tick.ru.md) — полифил для `nextTick`/`setTimeout(0, ...)`;\n* [inherit](common.blocks/inherit/inherit.ru.md) — ООП-хелперы;\n* [jquery](common.blocks/jquery/jquery.ru.md) — jQuery;\n* [clearfix](common.blocks/clearfix/clearfix.ru.md) — CSS-трюк clearfix;\n* [identify](common.blocks/identify/identify.ru.md) — идентификация JS-объектов;\n* [cookie](common.blocks/cookie/cookie.ru.md) — хелперы для работы с браузерными куками;\n* [dom](common.blocks/dom/dom.ru.md) — хелперы для работы с DOM;\n* [loader](common.blocks/loader/loader.ru.md) — загрузчик для JS-файлов;\n* [ua](common.blocks/ua/ua.ru.md) — определение возможностей браузера;\n* [uri](common.blocks/uri/uri.ru.md) — декодирование строки из формата URI;\n* [keyboard](common.blocks/keyboard/keyboard.ru.md) — хелперы для работы с клавиатурой;\n* [page](common.blocks/page/page.ru.md) — скелет для html/head/body.\n\n## Использование\n\nУстановите как npm-зависимость:\n\n```shell\nnpm install bem-core@5\n```\n\njQuery 4 — peer-зависимость, установите рядом:\n\n```shell\nnpm install jquery@^4.0.0\n```\n\n## Поддерживаемые браузеры\n\n* Google Chrome (последняя версия)\n* Firefox (последняя версия)\n* Safari (последняя версия)\n* Edge (последняя версия)\n\n## Технологии\n\n* vanilla.js + browser.js;\n* DEPS;\n* bemhtml;\n* bemtree.\n\n## API\n\nАвтосгенерированную документацию на JavaScript API блоков (JSDoc) можно посмотреть на bem.info. Например, для блока `i-bem` она доступна по ссылке https://ru.bem.info/platform/libs/bem-core/current/desktop/i-bem/#jsdoc\n\n## Разработка\n\n### Рабочая копия\n\n1. Получаем исходники:\n\n   ```bash\n   git clone -b v5 git://github.com/bem/bem-core.git\n   cd bem-core\n   ```\n\n2. Устанавливаем зависимости (требуется Node.js 20+):\n\n   ```bash\n   npm install\n   ```\n\n3. Запускаем линтер:\n\n   ```bash\n   npm run lint\n   ```\n\n4. Запускаем тесты:\n\n   ```bash\n   npm test              # серверные тесты (node:test)\n   npm run test:browser  # браузерные тесты (Playwright)\n   npm run test:all      # все тесты\n   ```\n\n5. Собираем:\n\n   ```bash\n   npm run build         # desktop + touch платформы\n   ```\n\n## Команда основной разработки\n\n* [veged](https://github.com/veged)\n* [dfilatov](https://github.com/dfilatov)\n* [tadatuta](https://github.com/tadatuta)\n\n## Лицензия\n\n© 2012 YANDEX LLC. Код лицензирован [Mozilla Public License 2.0](LICENSE.txt).\n"
  },
  {
    "path": "SUMMARY.md",
    "content": "# Summary\n\n* [Введение](README.ru.md)\n    * [Общие сведения](common.docs/i-bem-js/i-bem-js-common.ru.md)\n    * [Привязка JS-блоков к HTML](common.docs/i-bem-js/i-bem-js-html-binding.ru.md)\n    * [Декларация блока](common.docs/i-bem-js/i-bem-js-decl.ru.md)\n    * [Передача параметров](common.docs/i-bem-js/i-bem-js-params.ru.md)\n    * [Работа с DOM-деревом](common.docs/i-bem-js/i-bem-js-dom.ru.md)\n    * [Состояния блока](common.docs/i-bem-js/i-bem-js-states.ru.md)\n    * [Коллекции](common.docs/i-bem-js/i-bem-js-collections.ru.md)\n    * [События](common.docs/i-bem-js/i-bem-js-events.ru.md)\n    * [Инициализация](common.docs/i-bem-js/i-bem-js-init.ru.md)\n    * [Взаимодействие блоков](common.docs/i-bem-js/i-bem-js-interact.ru.md)\n    * [Контекст](common.docs/i-bem-js/i-bem-js-context.ru.md)\n    * [Что дальше?](common.docs/i-bem-js/i-bem-js-extras.ru.md)\n"
  },
  {
    "path": "build/platforms/desktop.js",
    "content": "/**\n * Entry point for the desktop platform build.\n *\n * This file imports all BEM blocks in the correct order for the desktop platform.\n * The vite-plugin-bem-levels plugin resolves `bem:*` imports to actual files\n * from the level chain: common.blocks → desktop.blocks\n */\n\n// Core utilities (no dependencies)\nimport 'bem:identify';\nimport 'bem:inherit';\nimport 'bem:objects';\nimport 'bem:functions';\nimport 'bem:functions__throttle';\nimport 'bem:functions__debounce';\nimport 'bem:next-tick';\nimport 'bem:strings__escape';\n\n// Events system\nimport 'bem:events';\nimport 'bem:events__channels';\n\n// i-bem core\nimport 'bem:i-bem__internal';\nimport 'bem:i-bem';\nimport 'bem:i-bem__collection';\n\n// DOM utilities\nimport 'bem:jquery__config';\nimport 'bem:jquery';\nimport 'bem:jquery__event_type_winresize';\nimport 'bem:dom';\n\n// i-bem-dom and its subsystems\nimport 'bem:i-bem-dom__events';\nimport 'bem:i-bem-dom__events_type_dom';\nimport 'bem:i-bem-dom__events_type_bem';\nimport 'bem:i-bem-dom__collection';\nimport 'bem:i-bem-dom';\nimport 'bem:i-bem-dom__init';\nimport 'bem:i-bem-dom__init_auto';\n\n// Observable events (with BEM DOM support)\nimport 'bem:events__observable';\nimport 'bem:events__observable_type_bem-dom';\n\n// Browser APIs\nimport 'bem:cookie';\nimport 'bem:idle';\nimport 'bem:idle_start_auto';\nimport 'bem:tick';\nimport 'bem:tick_start_auto';\nimport 'bem:keyboard__codes';\nimport 'bem:ua';\n\n// URI utilities\nimport 'bem:uri';\nimport 'bem:uri__querystring';\n\n// Loaders\nimport 'bem:loader_type_js';\nimport 'bem:loader_type_bundle';\n"
  },
  {
    "path": "build/platforms/touch.js",
    "content": "/**\n * Entry point for the touch platform build.\n *\n * This file imports all BEM blocks in the correct order for the touch platform.\n * The vite-plugin-bem-levels plugin resolves `bem:*` imports to actual files\n * from the level chain: common.blocks → touch.blocks\n */\n\n// Core utilities (no dependencies)\nimport 'bem:identify';\nimport 'bem:inherit';\nimport 'bem:objects';\nimport 'bem:functions';\nimport 'bem:functions__throttle';\nimport 'bem:functions__debounce';\nimport 'bem:next-tick';\nimport 'bem:strings__escape';\n\n// Events system\nimport 'bem:events';\nimport 'bem:events__channels';\n\n// i-bem core\nimport 'bem:i-bem__internal';\nimport 'bem:i-bem';\nimport 'bem:i-bem__collection';\n\n// DOM utilities\nimport 'bem:jquery__config';\nimport 'bem:jquery';\nimport 'bem:dom';\n\n// i-bem-dom and its subsystems\nimport 'bem:i-bem-dom__events';\nimport 'bem:i-bem-dom__events_type_dom';\nimport 'bem:i-bem-dom__events_type_bem';\nimport 'bem:i-bem-dom__collection';\nimport 'bem:i-bem-dom';\nimport 'bem:i-bem-dom__init';\nimport 'bem:i-bem-dom__init_auto';\n\n// Observable events (with BEM DOM support)\nimport 'bem:events__observable';\nimport 'bem:events__observable_type_bem-dom';\n\n// Browser APIs\nimport 'bem:cookie';\nimport 'bem:idle';\nimport 'bem:idle_start_auto';\nimport 'bem:tick';\nimport 'bem:tick_start_auto';\nimport 'bem:keyboard__codes';\nimport 'bem:ua';\nimport 'bem:ua__dom';\n\n// URI utilities\nimport 'bem:uri';\nimport 'bem:uri__querystring';\n\n// Loaders\nimport 'bem:loader_type_js';\nimport 'bem:loader_type_bundle';\n"
  },
  {
    "path": "build/plugins/vite-plugin-bem-levels.js",
    "content": "import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs'\nimport { join, resolve, relative, dirname, basename } from 'node:path'\nconst BEM_PREFIX = 'bem:'\nconst VIRTUAL_PREFIX = '\\0bem:'\n/**\n * Scans a BEM level directory and returns all BEM entities found.\n *\n * BEM nested file structure:\n *   block/\n *     block.js\n *     block.deps.js\n *     __elem/\n *       block__elem.js\n *     _mod/\n *       block_mod.js\n *       block_mod_val.js\n *\n * @param {string} levelDir - absolute path to a BEM level directory\n * @returns {Map<string, object[]>} moduleName → array of file entries\n */\nfunction scanLevel(levelDir) {\n    const modules = new Map()\n    if (!existsSync(levelDir)) return modules\n    const blocks = readdirSync(levelDir).filter(name => {\n        const fullPath = join(levelDir, name)\n        return statSync(fullPath).isDirectory() && !name.startsWith('.')\n    })\n    for (const block of blocks) {\n        const blockDir = join(levelDir, block)\n        scanDirectory(blockDir, modules, levelDir)\n    }\n\n    return modules\n}\n\n/**\n * Recursively scans a BEM entity directory for JS source files.\n * Module names are derived from file names per BEM naming convention —\n * file contents are never read during scanning.\n *\n * Only recurses into BEM-named subdirectories:\n *   __elemName (element) or _modName (modifier).\n * This automatically excludes .tests/, .examples/, .tmpl-specs/, etc.\n */\nfunction scanDirectory(dir, modules, levelDir) {\n    let entries\n    try {\n        entries = readdirSync(dir)\n    } catch {\n        return\n    }\n\n    for (const entry of entries) {\n        const fullPath = join(dir, entry)\n        let stat\n        try {\n            stat = statSync(fullPath)\n        } catch {\n            continue\n        }\n\n        if (stat.isDirectory()) {\n            // Only recurse into BEM-named subdirectories: __elem or _mod\n            if (entry.startsWith('__') || entry.startsWith('_')) {\n                scanDirectory(fullPath, modules, levelDir)\n            }\n            continue\n        }\n\n        if (!stat.isFile()) continue\n        // Consider JS source files and .post.css files\n        const isPostCss = entry.endsWith('.post.css')\n        const isVanillaJs = entry.endsWith('.vanilla.js')\n        const isPlainJs = !isVanillaJs && entry.endsWith('.js')\n            && !entry.endsWith('.deps.js')\n            && !entry.endsWith('.spec.js')\n            && !entry.endsWith('.bemhtml.js')\n            && !entry.endsWith('.bh.js')\n            && !entry.endsWith('.bemjson.js')\n            && !entry.endsWith('.test.js')\n            && !entry.endsWith('.i18n.js')\n        if (!isVanillaJs && !isPlainJs && !isPostCss) continue\n        // Module name is derived from the filename per BEM naming convention\n        const name = filePathToModuleName(fullPath, levelDir)\n        if (!name) continue\n        const suffix = isPostCss ? '.post.css' : (isVanillaJs ? '.vanilla.js' : '.js')\n        const existing = modules.get(name) || []\n        existing.push({\n            name,\n            filePath: fullPath,\n            suffix,\n            levelDir,\n        })\n        modules.set(name, existing)\n    }\n}\n\n/**\n * Parses a modules.define() call from source code.\n * Returns { name, deps, callbackParamCount, isRedefinition } or null.\n *\n * In ym, a redefinition is detected by the callback having one extra parameter\n * beyond provide + deps. That extra parameter receives the previous module value.\n *\n * Base:         modules.define('name', ['dep1'], function(provide, dep1) { ... })\n *                  → callbackParams = 2, isRedefinition = false\n *\n * Redefinition: modules.define('name', ['dep1'], function(provide, dep1, prev) { ... })\n *                  → callbackParams = 3, isRedefinition = true\n */\nfunction parseModulesDefine(source) {\n    // Match modules.define('name'  or  modules.define(\"name\"\n    const defineMatch = source.match(\n        /modules\\.define\\s*\\(\\s*(['\"])([^'\"]+)\\1/\n    )\n    if (!defineMatch) return null\n    const name = defineMatch[2]\n    // Try to extract dependency array\n    // Look for the pattern after the name: , ['dep1', 'dep2']\n    const afterName = source.slice(defineMatch.index + defineMatch[0].length)\n    const depsMatch = afterName.match(\n        /^\\s*,\\s*\\[([^\\]]*)\\]/\n    )\n    const deps = []\n    if (depsMatch) {\n        const depsStr = depsMatch[1]\n        const depPattern = /['\"]([^'\"]+)['\"]/g\n        let m\n        while ((m = depPattern.exec(depsStr)) !== null) {\n            deps.push(m[1])\n        }\n    }\n\n    // Extract callback parameter count to detect redefinitions.\n    // Look for `function(` after the deps array (or after the name if no deps).\n    const afterDeps = depsMatch\n        ? afterName.slice(depsMatch[0].length)\n        : afterName\n    const callbackMatch = afterDeps.match(\n        /,\\s*function\\s*\\(([^)]*)\\)/\n    )\n    let callbackParamCount = 0\n    if (callbackMatch) {\n        const params = callbackMatch[1].trim()\n        callbackParamCount = params ? params.split(/\\s*,\\s*/).length : 0\n    }\n\n    // A redefinition has more callback params than provide (1) + deps count.\n    // The extra parameter receives the previous module value.\n    const isRedefinition = callbackParamCount > 1 + deps.length\n    return { name, deps, callbackParamCount, isRedefinition }\n}\n\n/**\n * Parses a migrated ES module file.\n * Detects `export default` and derives the module name from the file path.\n *\n * For redefinitions (transformer functions), detects:\n *   export default function(prev) { ... }\n *\n * Extracts `import ... from 'bem:...'` as dependencies.\n *\n * @param {string} source - file content\n * @param {string} filePath - absolute path to the file\n * @param {string} levelDir - absolute path to the level directory\n * @returns {{ name: string, deps: string[], isRedefinition: boolean } | null}\n */\nfunction parseEsModule(source, filePath, levelDir) {\n    // Must have export default\n    if (!/export\\s+default\\b/.test(source)) return null\n    // Derive module name from file path using BEM naming\n    const name = filePathToModuleName(filePath, levelDir)\n    if (!name) return null\n    // Extract bem: imports as dependencies\n    const deps = []\n    const importPattern = /import\\s+\\w+\\s+from\\s+['\"]bem:([^'\"]+)['\"]/g\n    let m\n    while ((m = importPattern.exec(source)) !== null) {\n        deps.push(m[1])\n    }\n\n    // Detect if this is a transformer (redefinition):\n    // export default function(prev) { ... }\n    // The pattern is: export default function with exactly one parameter\n    const isRedefinition = /export\\s+default\\s+function\\s*\\([^)]+\\)\\s*\\{/.test(source)\n    return { name, deps, isRedefinition }\n}\n\n/**\n * Derives a BEM module name from a file path.\n *\n * common.blocks/objects/objects.vanilla.js → 'objects'\n * common.blocks/i-bem/__internal/i-bem__internal.vanilla.js → 'i-bem__internal'\n * common.blocks/functions/__debounce/functions__debounce.vanilla.js → 'functions__debounce'\n * common.blocks/loader/_type/loader_type_js.js → 'loader_type_js'\n */\nfunction filePathToModuleName(filePath, levelDir) {\n    const rel = relative(levelDir, filePath)\n    // Get the filename without extensions\n    const fileName = basename(rel)\n    // Strip .vanilla.js, .js, or .post.css\n    const name = fileName.replace(/\\.(vanilla\\.js|js|post\\.css)$/, '')\n    return name || null\n}\n\n/**\n * Parses a .deps.js file and returns the dependency declarations.\n *\n * deps.js format:\n *   ({ shouldDeps: [...], mustDeps: [...], noDeps: [...] })\n *   or\n *   ([{ shouldDeps: ... }, { ... }])\n */\nfunction parseDepsFile(filePath) {\n    if (!existsSync(filePath)) return null\n    const content = readFileSync(filePath, 'utf8')\n    try {\n        // deps.js files are wrapped in parentheses: ({ ... }) or ([...])\n        // Use Function constructor to evaluate (safer than eval, no access to scope)\n        const fn = new Function('return ' + content)\n        const result = fn()\n        return normalizeDeps(result)\n    } catch {\n        return null\n    }\n}\n\n/**\n * Normalizes deps.js result into { mustDeps: [], shouldDeps: [], noDeps: [] }\n */\nfunction normalizeDeps(raw) {\n    if (Array.isArray(raw)) {\n        // Array of dep declarations — merge them\n        const merged = { mustDeps: [], shouldDeps: [], noDeps: [] }\n        for (const item of raw) {\n            const norm = normalizeDeps(item)\n            if (norm) {\n                merged.mustDeps.push(...norm.mustDeps)\n                merged.shouldDeps.push(...norm.shouldDeps)\n                merged.noDeps.push(...norm.noDeps)\n            }\n        }\n        return merged\n    }\n\n    if (raw && typeof raw === 'object') {\n        return {\n            mustDeps: normalizeDep(raw.mustDeps || []),\n            shouldDeps: normalizeDep(raw.shouldDeps || []),\n            noDeps: normalizeDep(raw.noDeps || []),\n        }\n    }\n\n    return null\n}\n\n/**\n * Normalizes a single deps declaration value to an array of BEM entity references.\n * Input can be: string | object | array\n */\nfunction normalizeDep(dep) {\n    if (!dep) return []\n    if (typeof dep === 'string') return [{ block: dep }]\n    if (Array.isArray(dep)) return dep.flatMap(d => normalizeDep(d))\n    if (typeof dep === 'object') {\n        // Could be { block: 'name' } or { elem: 'name' } or { mods: {...} } etc.\n        return [dep]\n    }\n    return []\n}\n\n/**\n * Converts a BEM entity reference to a module name.\n * { block: 'jquery', elem: 'event', mods: { type: 'pointer' } }\n * → 'jquery__event_type_pointer'\n *\n * This is a simplified conversion — real BEM naming is more complex,\n * but for the modules actually defined in bem-core, this covers all cases.\n */\nfunction bemEntityToModuleName(entity, contextBlock) {\n    if (typeof entity === 'string') return entity\n    const block = entity.block || contextBlock\n    if (!block) return null\n    let name = block\n    // Handle elem / elems\n    if (entity.elem) {\n        name += '__' + entity.elem\n    }\n\n    // Handle mod / mods\n    if (entity.mod) {\n        name += '_' + entity.mod\n        if (entity.val && entity.val !== true) {\n            name += '_' + entity.val\n        }\n    }\n    if (entity.mods) {\n        for (const [mod, vals] of Object.entries(entity.mods)) {\n            if (Array.isArray(vals)) {\n                // Multiple values → multiple modules (e.g., type: ['dom', 'bem'])\n                // Return only the first for now; caller should handle arrays\n                name += '_' + mod\n            } else if (vals === true) {\n                name += '_' + mod\n            } else {\n                name += '_' + mod + '_' + vals\n            }\n        }\n    }\n\n    return name\n}\n\n/**\n * Expands a BEM dependency entity into one or more module names.\n * Handles elems (array) and mods (array values).\n */\nfunction expandBemEntity(entity, contextBlock) {\n    if (typeof entity === 'string') return [entity]\n    const block = entity.block || contextBlock\n    if (!block) return []\n    const results = []\n    // If entity has elems (array of elements), expand each\n    if (entity.elems) {\n        const elems = Array.isArray(entity.elems) ? entity.elems : [entity.elems]\n        for (const elem of elems) {\n            if (typeof elem === 'string') {\n                results.push(block + '__' + elem)\n            } else if (elem && typeof elem === 'object') {\n                // { elem: 'init', mods: { auto: true } }\n                const elemName = elem.elem\n                results.push(block + '__' + elemName)\n                if (elem.mods) {\n                    for (const [mod, vals] of Object.entries(elem.mods)) {\n                        const modVals = Array.isArray(vals) ? vals : [vals]\n                        for (const val of modVals) {\n                            if (val === true) {\n                                results.push(block + '__' + elemName + '_' + mod)\n                            } else {\n                                results.push(block + '__' + elemName + '_' + mod + '_' + val)\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return results\n    }\n\n    // If entity has elem (single)\n    if (entity.elem) {\n        const base = block + '__' + entity.elem\n        if (entity.mods) {\n            for (const [mod, vals] of Object.entries(entity.mods)) {\n                const modVals = Array.isArray(vals) ? vals : [vals]\n                for (const val of modVals) {\n                    if (val === true) {\n                        results.push(base + '_' + mod)\n                    } else {\n                        results.push(base + '_' + mod + '_' + val)\n                    }\n                }\n            }\n            return results\n        }\n        return [base]\n    }\n\n    // Block with mods\n    if (entity.mods) {\n        for (const [mod, vals] of Object.entries(entity.mods)) {\n            const modVals = Array.isArray(vals) ? vals : [vals]\n            for (const val of modVals) {\n                if (val === true) {\n                    results.push(block + '_' + mod)\n                } else {\n                    results.push(block + '_' + mod + '_' + val)\n                }\n            }\n        }\n        return results\n    }\n\n    // Simple block reference\n    return [block]\n}\n\n/**\n * Build a complete module registry from BEM levels.\n *\n * @param {string[]} levels - ordered list of level directories (e.g., ['common.blocks', 'desktop.blocks'])\n * @param {string} rootDir - project root directory\n * @returns {{ modules: Map, deps: Map, redefinitions: Map }}\n */\nfunction buildRegistry(levels, rootDir) {\n    // moduleName → [{ name, deps, filePath, suffix, levelDir, levelIndex }]\n    const allModules = new Map()\n    for (let i = 0; i < levels.length; i++) {\n        const levelDir = resolve(rootDir, levels[i])\n        const levelModules = scanLevel(levelDir)\n        for (const [name, entries] of levelModules) {\n            const existing = allModules.get(name) || []\n            for (const entry of entries) {\n                entry.levelIndex = i\n            }\n            existing.push(...entries)\n            allModules.set(name, existing)\n        }\n    }\n\n    // Detect cross-level redefinitions: same module name from different levels.\n    // First entry (lowest level index) is the base, subsequent entries are redefinitions.\n    const redefinitions = new Map()\n    for (const [name, entries] of allModules) {\n        entries.sort((a, b) => a.levelIndex - b.levelIndex)\n        if (entries.length > 1) {\n            redefinitions.set(name, entries)\n        }\n    }\n\n    // Collect deps.js files\n    const depsMap = new Map()\n    for (const [name, entries] of allModules) {\n        for (const entry of entries) {\n            // Find corresponding .deps.js file\n            // e.g., common.blocks/jquery/jquery.js → common.blocks/jquery/jquery.deps.js\n            const dir = dirname(entry.filePath)\n            const possibleDepsFiles = [\n                // Same directory, same base name\n                entry.filePath.replace(/\\.(vanilla\\.)?js$/, '.deps.js'),\n            ]\n            // Also look for block-level deps.js\n            const blockDir = dirname(dir) === entry.levelDir ? dir : dirname(dir)\n            const blockName = basename(blockDir)\n            const blockDeps = join(blockDir, blockName + '.deps.js')\n            if (!possibleDepsFiles.includes(blockDeps)) {\n                possibleDepsFiles.push(blockDeps)\n            }\n\n            for (const depsFile of possibleDepsFiles) {\n                if (existsSync(depsFile) && !depsMap.has(depsFile)) {\n                    const parsed = parseDepsFile(depsFile)\n                    if (parsed) {\n                        depsMap.set(depsFile, { ...parsed, forModule: name })\n                    }\n                }\n            }\n        }\n    }\n\n    // Separate CSS entries from JS entries.\n    // CSS files are side-effect imports, not module redefinitions.\n    const cssModules = new Map()\n    for (const [name, entries] of allModules) {\n        const cssEntries = entries.filter(e => e.suffix === '.post.css')\n        const jsEntries = entries.filter(e => e.suffix !== '.post.css')\n        if (cssEntries.length > 0) {\n            cssModules.set(name, cssEntries)\n        }\n        if (jsEntries.length > 0) {\n            allModules.set(name, jsEntries)\n        } else if (cssEntries.length > 0) {\n            // CSS-only module — keep in allModules so it can be resolved\n            allModules.set(name, [])\n        }\n    }\n\n    // Recompute redefinitions after removing CSS entries\n    redefinitions.clear()\n    for (const [name, entries] of allModules) {\n        if (entries.length > 1) {\n            redefinitions.set(name, entries)\n        }\n    }\n\n    return { modules: allModules, deps: depsMap, redefinitions, cssModules }\n}\n\n/**\n * Generate a barrel (re-export) module for a module with redefinitions.\n *\n * In ym, each redefinition receives the previous module value as its last\n * callback parameter and calls provide() with a new/modified value.\n *\n * In ES modules, we model this as:\n * - Base file: `export default value;`\n * - Redefinition file: `export default function(prev) { return newValue; }`\n *\n * The barrel chains them:\n *   import _base from './base.js'\n *   import _redef0 from './redef0.js'\n *   import _redef1 from './redef1.js'\n *   let _module = _base\n *   _module = _redef0(_module)\n *   _module = _redef1(_module)\n *   export default _module\n *\n * @param {string} name - module name\n * @param {object[]} entries - sorted array of file entries (base + redefinitions)\n * @param {string} rootDir - project root\n * @returns {string} generated ES module source code\n */\nfunction generateBarrel(name, entries, rootDir) {\n    const lines = [`// @generated by vite-plugin-bem-levels`]\n    const base = entries[0]\n    const basePath = './' + relative(rootDir, base.filePath).replace(/\\\\/g, '/')\n    const baseId = safeIdentifier(name) + '_base'\n    lines.push(`import ${baseId} from '${basePath}';`)\n    // Import each redefinition as a named transformer\n    const redefIds = []\n    for (let i = 1; i < entries.length; i++) {\n        const redef = entries[i]\n        const redefPath = './' + relative(rootDir, redef.filePath).replace(/\\\\/g, '/')\n        const redefId = safeIdentifier(name) + '_redef' + (i - 1)\n        lines.push(`import ${redefId} from '${redefPath}';`)\n        redefIds.push(redefId)\n    }\n\n    lines.push('')\n    lines.push(`let _module = ${baseId};`)\n    for (const redefId of redefIds) {\n        lines.push(`_module = ${redefId}(_module);`)\n    }\n    lines.push(`export default _module;`)\n    return lines.join('\\n')\n}\n\n/**\n * Generate a safe JavaScript identifier from a BEM module name.\n * 'jquery__config' → '_jquery__config'\n * 'i-bem-dom' → '_iBemDom'\n */\nfunction safeIdentifier(name) {\n    // Replace hyphens with camelCase, prefix with underscore\n    let id = name\n        .replace(/-([a-z])/g, (_, c) => c.toUpperCase())\n        .replace(/-/g, '_')\n    // Ensure starts with valid identifier char\n    if (/^[0-9]/.test(id)) id = '_' + id\n    return '_' + id\n}\n\n/**\n * Vite plugin for BEM level resolution and barrel file generation.\n *\n * @param {object} options\n * @param {string} options.platform - 'desktop' or 'touch'\n * @param {object} options.levels - platform → level directories mapping\n * @param {string} [options.rootDir] - project root (default: process.cwd())\n */\nexport default function bemLevels(options = {}) {\n    const {\n        platform = 'desktop',\n        levels = {\n            common: ['common.blocks'],\n            desktop: ['common.blocks', 'desktop.blocks'],\n            touch: ['common.blocks', 'touch.blocks'],\n        },\n        rootDir = process.cwd(),\n    } = options\n    const platformLevels = levels[platform]\n    if (!platformLevels) {\n        throw new Error(`Unknown platform: ${platform}. Available: ${Object.keys(levels).join(', ')}`)\n    }\n\n    let registry = null\n    function getRegistry() {\n        if (!registry) {\n            registry = buildRegistry(platformLevels, rootDir)\n        }\n        return registry\n    }\n\n    return {\n        name: 'vite-plugin-bem-levels',\n\n        resolveId(id) {\n            if (id.startsWith(BEM_PREFIX)) {\n                return VIRTUAL_PREFIX + id.slice(BEM_PREFIX.length)\n            }\n            return null\n        },\n\n        load(id) {\n            if (!id.startsWith(VIRTUAL_PREFIX)) return null\n            const moduleName = id.slice(VIRTUAL_PREFIX.length)\n            const reg = getRegistry()\n            const entries = reg.modules.get(moduleName)\n            const cssEntries = reg.cssModules ? reg.cssModules.get(moduleName) : null\n\n            if ((!entries || entries.length === 0) && !cssEntries) {\n                this.error(`BEM module not found: ${moduleName}`)\n                return null\n            }\n\n            // Generate CSS side-effect imports\n            const cssImports = []\n            if (cssEntries) {\n                for (const cssEntry of cssEntries) {\n                    const cssPath = './' + relative(rootDir, cssEntry.filePath).replace(/\\\\/g, '/')\n                    cssImports.push(`import '${cssPath}';`)\n                }\n            }\n\n            // CSS-only module (no JS)\n            if (!entries || entries.length === 0) {\n                return cssImports.join('\\n') + '\\n'\n            }\n\n            // If the module has redefinitions, generate a barrel\n            if (entries.length > 1) {\n                const barrel = generateBarrel(moduleName, entries, rootDir)\n                if (cssImports.length > 0) {\n                    return cssImports.join('\\n') + '\\n' + barrel\n                }\n                return barrel\n            }\n\n            // Single definition — re-export or side-effect import\n            const entry = entries[0]\n            const entryPath = './' + relative(rootDir, entry.filePath).replace(/\\\\/g, '/')\n            const source = readFileSync(entry.filePath, 'utf8')\n            const hasDefaultExport = /export\\s+default\\b/.test(source)\n            const jsCode = hasDefaultExport\n                ? `export { default } from '${entryPath}';\\n`\n                : `import '${entryPath}';\\n`\n            if (cssImports.length > 0) {\n                return cssImports.join('\\n') + '\\n' + jsCode\n            }\n            return jsCode\n        },\n\n        // Invalidate registry on file changes in BEM levels\n        configureServer(server) {\n            const levelDirs = platformLevels.map(l => resolve(rootDir, l))\n            server.watcher.on('all', (event, filePath) => {\n                for (const levelDir of levelDirs) {\n                    if (filePath.startsWith(levelDir)) {\n                        registry = null; // invalidate cache\n                        break\n                    }\n                }\n            })\n        },\n\n        // Expose registry for testing and introspection\n        api: {\n            getRegistry() { return getRegistry(); },\n            scanLevel,\n            parseModulesDefine,\n            parseDepsFile,\n            normalizeDeps,\n            expandBemEntity,\n            generateBarrel,\n            buildRegistry,\n        },\n    }\n}\n\n// Named exports for testing\nexport {\n    scanLevel,\n    parseModulesDefine,\n    parseEsModule,\n    filePathToModuleName,\n    parseDepsFile,\n    normalizeDeps,\n    normalizeDep,\n    bemEntityToModuleName,\n    expandBemEntity,\n    buildRegistry,\n    generateBarrel,\n    safeIdentifier,\n}"
  },
  {
    "path": "build/plugins/vite-plugin-bem-levels.test.js",
    "content": "import { describe, it } from 'node:test';\nimport { strict as assert } from 'node:assert';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport {\n    scanLevel,\n    parseModulesDefine,\n    parseDepsFile,\n    expandBemEntity,\n    buildRegistry,\n    generateBarrel,\n    safeIdentifier,\n} from './vite-plugin-bem-levels.js';\n\nconst ROOT = resolve(import.meta.dirname, '../..');\n\n// --- parseModulesDefine ---\n\ndescribe('parseModulesDefine', function() {\n    it('parses simple define without deps', function() {\n        const result = parseModulesDefine(\n            \"modules.define('cookie', function(provide) { provide({}); });\"\n        );\n        assert.strictEqual(result.name, 'cookie');\n        assert.deepStrictEqual(result.deps, []);\n        assert.strictEqual(result.callbackParamCount, 1);\n        assert.strictEqual(result.isRedefinition, false);\n    });\n\n    it('parses define with dependency array', function() {\n        const result = parseModulesDefine(\n            \"modules.define('i-bem', ['i-bem__internal', 'inherit'], function(provide, internal, inherit) {});\"\n        );\n        assert.strictEqual(result.name, 'i-bem');\n        assert.deepStrictEqual(result.deps, ['i-bem__internal', 'inherit']);\n        assert.strictEqual(result.callbackParamCount, 3);\n        assert.strictEqual(result.isRedefinition, false);\n    });\n\n    it('parses define with double quotes', function() {\n        const result = parseModulesDefine(\n            'modules.define(\"jquery\", [\"loader_type_js\"], function(provide, loader) {});'\n        );\n        assert.strictEqual(result.name, 'jquery');\n        assert.deepStrictEqual(result.deps, ['loader_type_js']);\n        assert.strictEqual(result.isRedefinition, false);\n    });\n\n    it('returns null for non-module files', function() {\n        const result = parseModulesDefine('var x = 1;');\n        assert.strictEqual(result, null);\n    });\n\n    it('handles multiline define', function() {\n        const result = parseModulesDefine(`\nmodules.define(\n    'i-bem-dom',\n    [\n        'i-bem', 'i-bem__internal', 'inherit',\n        'identify', 'objects', 'functions',\n        'jquery', 'dom'\n    ],\n    function(provide, BEM, BEMINTERNAL, inherit, identify, objects, functions, $, dom) {\n    });\n`);\n        assert.strictEqual(result.name, 'i-bem-dom');\n        assert.deepStrictEqual(result.deps, [\n            'i-bem', 'i-bem__internal', 'inherit',\n            'identify', 'objects', 'functions',\n            'jquery', 'dom',\n        ]);\n        assert.strictEqual(result.callbackParamCount, 9);\n        assert.strictEqual(result.isRedefinition, false);\n    });\n});\n\n// --- parseModulesDefine: redefinition detection ---\n\ndescribe('parseModulesDefine (redefinition detection)', function() {\n    it('detects redefinition: jquery__config on desktop (2 deps + prev)', function() {\n        const result = parseModulesDefine(\n            \"modules.define('jquery__config', ['ua', 'objects'], function(provide, ua, objects, base) {});\"\n        );\n        assert.strictEqual(result.name, 'jquery__config');\n        assert.deepStrictEqual(result.deps, ['ua', 'objects']);\n        assert.strictEqual(result.callbackParamCount, 4);\n        assert.strictEqual(result.isRedefinition, true);\n    });\n\n    it('detects redefinition: jquery extension (1 dep + prev)', function() {\n        const result = parseModulesDefine(\n            \"modules.define('jquery', ['next-tick'], function(provide, nextTick, $) {});\"\n        );\n        assert.strictEqual(result.isRedefinition, true);\n        assert.strictEqual(result.callbackParamCount, 3);\n    });\n\n    it('detects redefinition: jquery extension (0 deps + prev)', function() {\n        const result = parseModulesDefine(\n            \"modules.define('jquery', function(provide, $) {});\"\n        );\n        assert.strictEqual(result.isRedefinition, true);\n        assert.strictEqual(result.callbackParamCount, 2);\n    });\n\n    it('detects redefinition: events__observable type_bem-dom (1 dep + prev)', function() {\n        const result = parseModulesDefine(\n            \"modules.define('events__observable', ['i-bem-dom'], function(provide, bemDom, observable) {});\"\n        );\n        assert.strictEqual(result.isRedefinition, true);\n        assert.strictEqual(result.callbackParamCount, 3);\n    });\n\n    it('detects redefinition: ua__dom on touch (1 dep + prev)', function() {\n        const result = parseModulesDefine(\n            \"modules.define('ua', ['i-bem-dom'], function(provide, bemDom, ua) {});\"\n        );\n        assert.strictEqual(result.isRedefinition, true);\n    });\n\n    it('does NOT detect base as redefinition: cookie (0 deps, 1 param)', function() {\n        const result = parseModulesDefine(\n            \"modules.define('cookie', function(provide) { provide({}); });\"\n        );\n        assert.strictEqual(result.isRedefinition, false);\n    });\n\n    it('does NOT detect base as redefinition: events (3 deps, 4 params)', function() {\n        const result = parseModulesDefine(\n            \"modules.define('events', ['identify', 'inherit', 'functions'], function(provide, identify, inherit, functions) {});\"\n        );\n        assert.strictEqual(result.isRedefinition, false);\n        assert.strictEqual(result.callbackParamCount, 4);\n    });\n});\n\n// --- parseModulesDefine: real file detection ---\n\ndescribe('parseModulesDefine (real file cross-check)', function() {\n    it('real file: jquery base is now ESM (migrated)', function() {\n        const source = readFileSync(resolve(ROOT, 'common.blocks/jquery/jquery.js'), 'utf8');\n        const result = parseModulesDefine(source);\n        assert.strictEqual(result, null, 'migrated ESM file has no modules.define');\n        assert.ok(source.includes('export default'), 'should have export default');\n    });\n\n    it('real file: jquery__config base is now ESM (migrated)', function() {\n        const source = readFileSync(\n            resolve(ROOT, 'common.blocks/jquery/__config/jquery__config.js'), 'utf8'\n        );\n        const result = parseModulesDefine(source);\n        assert.strictEqual(result, null, 'migrated ESM file has no modules.define');\n        assert.ok(source.includes('export default'), 'should have export default');\n    });\n\n    it('real file: jquery__config desktop is now ESM (migrated)', function() {\n        const source = readFileSync(\n            resolve(ROOT, 'desktop.blocks/jquery/__config/jquery__config.js'), 'utf8'\n        );\n        const result = parseModulesDefine(source);\n        assert.strictEqual(result, null, 'migrated ESM file has no modules.define');\n        assert.ok(source.includes('export default'), 'should have export default');\n    });\n\n    it('real file: events__observable base is now ESM (migrated)', function() {\n        const source = readFileSync(\n            resolve(ROOT, 'common.blocks/events/__observable/events__observable.js'), 'utf8'\n        );\n        const result = parseModulesDefine(source);\n        assert.strictEqual(result, null, 'migrated ESM file has no modules.define');\n        assert.ok(source.includes('export default'), 'should have export default');\n    });\n\n    it('real file: events__observable type_bem-dom is now ESM (migrated)', function() {\n        const source = readFileSync(\n            resolve(ROOT, 'common.blocks/events/__observable/_type/events__observable_type_bem-dom.js'), 'utf8'\n        );\n        const result = parseModulesDefine(source);\n        assert.strictEqual(result, null, 'migrated ESM file has no modules.define');\n        assert.ok(source.includes('export default'), 'should have export default');\n    });\n\n    it('real file: ua touch base is now ESM (migrated)', function() {\n        const source = readFileSync(\n            resolve(ROOT, 'touch.blocks/ua/ua.js'), 'utf8'\n        );\n        const result = parseModulesDefine(source);\n        assert.strictEqual(result, null, 'migrated ESM file has no modules.define');\n        assert.ok(source.includes('export default'), 'should have export default');\n    });\n\n    it('real file: ua__dom touch is now ESM (migrated)', function() {\n        const source = readFileSync(\n            resolve(ROOT, 'touch.blocks/ua/__dom/ua__dom.js'), 'utf8'\n        );\n        const result = parseModulesDefine(source);\n        assert.strictEqual(result, null, 'migrated ESM file has no modules.define');\n        assert.ok(source.includes('export default'), 'should have export default');\n    });\n\n    it('real file: desktop winresize is now ESM (migrated)', function() {\n        const source = readFileSync(\n            resolve(ROOT, 'desktop.blocks/jquery/__event/_type/jquery__event_type_winresize.js'), 'utf8'\n        );\n        const result = parseModulesDefine(source);\n        assert.strictEqual(result, null, 'migrated ESM file has no modules.define');\n        assert.ok(source.includes('export default'), 'should have export default');\n    });\n});\n\n// --- scanLevel ---\n\ndescribe('scanLevel', function() {\n    it('scans common.blocks and finds modules', function() {\n        const modules = scanLevel(resolve(ROOT, 'common.blocks'));\n        assert.ok(modules.size > 0, 'Should find modules');\n        assert.ok(modules.has('jquery'), 'Should find jquery');\n        assert.ok(modules.has('i-bem'), 'Should find i-bem');\n        assert.ok(modules.has('i-bem-dom'), 'Should find i-bem-dom');\n        assert.ok(modules.has('events'), 'Should find events');\n        assert.ok(modules.has('cookie'), 'Should find cookie');\n        assert.ok(modules.has('objects'), 'Should find objects');\n        assert.ok(modules.has('inherit'), 'Should find inherit');\n    });\n\n    it('finds correct file suffixes', function() {\n        const modules = scanLevel(resolve(ROOT, 'common.blocks'));\n        const objects = modules.get('objects');\n        assert.ok(objects, 'objects should exist');\n        assert.strictEqual(objects[0].suffix, '.vanilla.js');\n\n        const cookie = modules.get('cookie');\n        assert.ok(cookie, 'cookie should exist');\n        assert.strictEqual(cookie[0].suffix, '.js');\n    });\n\n    it('scans desktop.blocks', function() {\n        const modules = scanLevel(resolve(ROOT, 'desktop.blocks'));\n        assert.ok(modules.has('ua'), 'Should find ua on desktop');\n        assert.ok(modules.has('jquery__config'), 'Should find jquery__config on desktop');\n    });\n\n    it('scans touch.blocks', function() {\n        const modules = scanLevel(resolve(ROOT, 'touch.blocks'));\n        assert.ok(modules.has('ua'), 'Should find ua on touch');\n    });\n\n    it('returns empty map for non-existent directory', function() {\n        const modules = scanLevel(resolve(ROOT, 'nonexistent.blocks'));\n        assert.strictEqual(modules.size, 0);\n    });\n\n    it('derives module name from filename, not file content', function() {\n        const modules = scanLevel(resolve(ROOT, 'common.blocks'));\n        assert.ok(modules.has('events__observable_type_bem-dom'),\n            'Should derive module name from filename');\n        const jqEntries = modules.get('jquery');\n        assert.ok(jqEntries, 'jquery base should still exist');\n        assert.strictEqual(jqEntries.length, 1,\n            'jquery should have exactly 1 entry (base only, no within-level redefinitions)');\n    });\n\n    it('finds new modules from BEM naming', function() {\n        const modules = scanLevel(resolve(ROOT, 'common.blocks'));\n        assert.ok(modules.has('events__observable_type_bem-dom'));\n        assert.ok(modules.has('tick_start_auto'));\n        assert.ok(modules.has('idle_start_auto'));\n        assert.ok(modules.has('i-bem-dom__init_auto'));\n    });\n});\n\n// --- buildRegistry ---\n\ndescribe('buildRegistry', function() {\n    it('builds registry for desktop platform', function() {\n        const reg = buildRegistry(['common.blocks', 'desktop.blocks'], ROOT);\n        assert.ok(reg.modules.size > 0);\n        assert.ok(reg.modules.has('jquery'));\n        assert.ok(reg.modules.has('ua'));\n        assert.ok(reg.modules.has('i-bem-dom'));\n    });\n\n    it('builds registry for touch platform', function() {\n        const reg = buildRegistry(['common.blocks', 'touch.blocks'], ROOT);\n        assert.ok(reg.modules.has('ua'));\n        const uaEntries = reg.modules.get('ua');\n        assert.strictEqual(uaEntries.length, 1, 'ua has single touch definition');\n        assert.ok(reg.modules.has('ua__dom'), 'ua__dom is a separate module');\n    });\n\n    it('jquery has single entry per level (BEM naming)', function() {\n        const reg = buildRegistry(['common.blocks', 'desktop.blocks'], ROOT);\n        const jquery = reg.modules.get('jquery');\n        assert.ok(jquery, 'jquery should exist');\n        assert.strictEqual(jquery.length, 1,\n            'jquery should have exactly 1 entry (only common.blocks/jquery/jquery.js)');\n    });\n\n    it('detects jquery__config cross-level redefinition on desktop', function() {\n        const reg = buildRegistry(['common.blocks', 'desktop.blocks'], ROOT);\n        assert.ok(reg.redefinitions.has('jquery__config'), 'jquery__config should have redefinitions');\n        const entries = reg.redefinitions.get('jquery__config');\n        assert.strictEqual(entries.length, 2);\n        assert.ok(entries[0].filePath.includes('common.blocks'));\n        assert.ok(entries[1].filePath.includes('desktop.blocks'));\n    });\n\n    it('events__observable has no cross-level redefinition (type_bem-dom is separate module)', function() {\n        const reg = buildRegistry(['common.blocks', 'desktop.blocks'], ROOT);\n        assert.ok(!reg.redefinitions.has('events__observable'),\n            'events__observable should NOT have redefinitions — type_bem-dom is a separate module');\n        assert.ok(reg.modules.has('events__observable_type_bem-dom'),\n            'events__observable_type_bem-dom should be its own module');\n    });\n\n    it('ua has single entry per platform (no common.blocks/ua/ua.js)', function() {\n        const regDesktop = buildRegistry(['common.blocks', 'desktop.blocks'], ROOT);\n        const uaDesktop = regDesktop.modules.get('ua');\n        assert.ok(uaDesktop, 'ua should exist on desktop');\n        assert.strictEqual(uaDesktop.length, 1, 'desktop ua has 1 entry');\n\n        const regTouch = buildRegistry(['common.blocks', 'touch.blocks'], ROOT);\n        const uaTouch = regTouch.modules.get('ua');\n        assert.ok(uaTouch, 'ua should exist on touch');\n        assert.strictEqual(uaTouch.length, 1, 'touch ua has 1 entry');\n\n        assert.ok(regTouch.modules.has('ua__dom'),\n            'ua__dom should be its own module on touch');\n    });\n\n    it('detects i-bem-dom__init definition', function() {\n        const reg = buildRegistry(['common.blocks', 'desktop.blocks'], ROOT);\n        const init = reg.modules.get('i-bem-dom__init');\n        assert.ok(init, 'i-bem-dom__init should exist');\n        assert.ok(init.length >= 1, 'i-bem-dom__init should have at least base definition');\n    });\n\n    it('finds all expected modules (complete inventory, BEM naming)', function() {\n        const reg = buildRegistry(['common.blocks', 'desktop.blocks'], ROOT);\n        const expectedModules = [\n            // common.blocks .vanilla.js\n            'objects', 'identify', 'functions', 'inherit', 'i-bem',\n            'tick', 'tick_start_auto', 'uri', 'next-tick',\n            'events', 'functions__throttle', 'functions__debounce',\n            'i-bem__internal', 'uri__querystring', 'strings__escape',\n            'events__channels',\n            // common.blocks .js\n            'cookie', 'dom', 'jquery', 'idle', 'idle_start_auto',\n            'keyboard__codes', 'loader_type_js', 'loader_type_bundle',\n            'events__observable', 'events__observable_type_bem-dom',\n            'i-bem__collection', 'i-bem-dom', 'i-bem-dom__collection',\n            'i-bem-dom__init', 'i-bem-dom__init_auto',\n            'i-bem-dom__events', 'i-bem-dom__events_type_bem',\n            'i-bem-dom__events_type_dom', 'jquery__config',\n            // desktop.blocks .js\n            'ua', 'jquery__event_type_winresize',\n        ];\n\n        const missing = expectedModules.filter(m => !reg.modules.has(m));\n        assert.deepStrictEqual(missing, [],\n            `Missing modules: ${missing.join(', ')}`);\n    });\n});\n\n// --- parseDepsFile ---\n\ndescribe('parseDepsFile', function() {\n    it('parses simple shouldDeps', function() {\n        const result = parseDepsFile(resolve(ROOT, 'common.blocks/dom/dom.deps.js'));\n        assert.ok(result);\n        assert.ok(result.shouldDeps.length > 0);\n    });\n\n    it('parses mustDeps', function() {\n        const result = parseDepsFile(\n            resolve(ROOT, 'touch.blocks/ua/__dom/ua__dom.deps.js')\n        );\n        assert.ok(result);\n        assert.ok(result.mustDeps.length > 0);\n    });\n\n    it('parses complex deps with elem and mods', function() {\n        const result = parseDepsFile(resolve(ROOT, 'common.blocks/i-bem-dom/i-bem-dom.deps.js'));\n        assert.ok(result);\n        assert.ok(result.shouldDeps.length > 0);\n    });\n\n    it('returns null for non-existent file', function() {\n        const result = parseDepsFile(resolve(ROOT, 'nonexistent.deps.js'));\n        assert.strictEqual(result, null);\n    });\n});\n\n// --- expandBemEntity ---\n\ndescribe('expandBemEntity', function() {\n    it('expands string to itself', function() {\n        assert.deepStrictEqual(expandBemEntity('jquery'), ['jquery']);\n    });\n\n    it('expands block with elem', function() {\n        assert.deepStrictEqual(\n            expandBemEntity({ block: 'i-bem', elem: 'internal' }),\n            ['i-bem__internal']\n        );\n    });\n\n    it('expands block with elems array', function() {\n        const result = expandBemEntity({ block: 'i-bem', elems: ['internal', 'collection'] });\n        assert.deepStrictEqual(result, ['i-bem__internal', 'i-bem__collection']);\n    });\n\n    it('expands elem with mods', function() {\n        const result = expandBemEntity({\n            block: 'i-bem-dom',\n            elem: 'events',\n            mods: { type: ['dom', 'bem'] },\n        });\n        assert.deepStrictEqual(result, [\n            'i-bem-dom__events_type_dom',\n            'i-bem-dom__events_type_bem',\n        ]);\n    });\n\n    it('expands block with bool mod', function() {\n        const result = expandBemEntity({ block: 'i-bem-dom', elem: 'init', mods: { auto: true } });\n        assert.deepStrictEqual(result, ['i-bem-dom__init_auto']);\n    });\n\n    it('expands block-level mods', function() {\n        const result = expandBemEntity({ block: 'loader', mods: { type: 'js' } });\n        assert.deepStrictEqual(result, ['loader_type_js']);\n    });\n\n    it('expands elems with nested mods', function() {\n        const result = expandBemEntity({\n            block: 'i-bem-dom',\n            elems: { elem: 'init', mods: { auto: true } },\n        });\n        assert.deepStrictEqual(result, ['i-bem-dom__init', 'i-bem-dom__init_auto']);\n    });\n});\n\n// --- generateBarrel ---\n\ndescribe('generateBarrel', function() {\n    it('generates chained barrel for module with redefinitions', function() {\n        const entries = [\n            { filePath: resolve(ROOT, 'common.blocks/jquery/__config/jquery__config.js'), isRedefinition: false },\n            { filePath: resolve(ROOT, 'desktop.blocks/jquery/__config/jquery__config.js'), isRedefinition: true },\n        ];\n\n        const barrel = generateBarrel('jquery__config', entries, ROOT);\n        assert.ok(barrel.includes('@generated'));\n        assert.ok(barrel.includes(\"import _jquery__config_base from './common.blocks/jquery/__config/jquery__config.js'\"));\n        assert.ok(barrel.includes(\"import _jquery__config_redef0 from './desktop.blocks/jquery/__config/jquery__config.js'\"));\n        assert.ok(barrel.includes('let _module = _jquery__config_base;'));\n        assert.ok(barrel.includes('_module = _jquery__config_redef0(_module);'));\n        assert.ok(barrel.includes('export default _module;'));\n    });\n\n    it('generates barrel for jquery__config with desktop redefinition', function() {\n        const entries = [\n            { filePath: resolve(ROOT, 'common.blocks/jquery/__config/jquery__config.js'), isRedefinition: false },\n            { filePath: resolve(ROOT, 'desktop.blocks/jquery/__config/jquery__config.js'), isRedefinition: true },\n        ];\n\n        const barrel = generateBarrel('jquery__config', entries, ROOT);\n        assert.ok(barrel.includes(\"import _jquery__config_base from './common.blocks/jquery/__config/jquery__config.js'\"));\n        assert.ok(barrel.includes(\"import _jquery__config_redef0 from './desktop.blocks/jquery/__config/jquery__config.js'\"));\n        assert.ok(barrel.includes('let _module = _jquery__config_base;'));\n        assert.ok(barrel.includes('_module = _jquery__config_redef0(_module);'));\n        assert.ok(barrel.includes('export default _module;'));\n    });\n\n    it('generates barrel for events__observable with type redefinition', function() {\n        const entries = [\n            { filePath: resolve(ROOT, 'common.blocks/events/__observable/events__observable.js'), isRedefinition: false },\n            { filePath: resolve(ROOT, 'common.blocks/events/__observable/_type/events__observable_type_bem-dom.js'), isRedefinition: true },\n        ];\n\n        const barrel = generateBarrel('events__observable', entries, ROOT);\n        assert.ok(barrel.includes('_events__observable_base'));\n        assert.ok(barrel.includes('_events__observable_redef0'));\n        assert.ok(barrel.includes('_module = _events__observable_redef0(_module);'));\n    });\n\n    it('barrel does NOT use side-effect imports', function() {\n        const entries = [\n            { filePath: resolve(ROOT, 'common.blocks/jquery/__config/jquery__config.js'), isRedefinition: false },\n            { filePath: resolve(ROOT, 'desktop.blocks/jquery/__config/jquery__config.js'), isRedefinition: true },\n        ];\n\n        const barrel = generateBarrel('jquery__config', entries, ROOT);\n        const sideEffectImport = /^import\\s+'/m;\n        assert.ok(!sideEffectImport.test(barrel),\n            'barrel should not contain side-effect imports');\n    });\n\n    it('barrel with single redefinition produces correct chain', function() {\n        const entries = [\n            { filePath: '/root/common.blocks/foo/foo.js', isRedefinition: false },\n            { filePath: '/root/desktop.blocks/foo/foo.js', isRedefinition: true },\n        ];\n\n        const barrel = generateBarrel('foo', entries, '/root');\n        const expectedLines = [\n            \"// @generated by vite-plugin-bem-levels\",\n            \"import _foo_base from './common.blocks/foo/foo.js';\",\n            \"import _foo_redef0 from './desktop.blocks/foo/foo.js';\",\n            \"\",\n            \"let _module = _foo_base;\",\n            \"_module = _foo_redef0(_module);\",\n            \"export default _module;\",\n        ];\n        assert.strictEqual(barrel, expectedLines.join('\\n'));\n    });\n\n    it('barrel with 4 redefinitions produces full chain', function() {\n        const entries = [\n            { filePath: '/root/a/m.js' },\n            { filePath: '/root/b/m.js' },\n            { filePath: '/root/c/m.js' },\n            { filePath: '/root/d/m.js' },\n            { filePath: '/root/e/m.js' },\n        ];\n\n        const barrel = generateBarrel('m', entries, '/root');\n        assert.ok(barrel.includes('_m_redef0'));\n        assert.ok(barrel.includes('_m_redef1'));\n        assert.ok(barrel.includes('_m_redef2'));\n        assert.ok(barrel.includes('_m_redef3'));\n        assert.strictEqual((barrel.match(/_module = _m_redef/g) || []).length, 4);\n    });\n});\n\n// --- safeIdentifier ---\n\ndescribe('safeIdentifier', function() {\n    it('converts simple name', function() {\n        assert.strictEqual(safeIdentifier('jquery'), '_jquery');\n    });\n\n    it('converts hyphenated name', function() {\n        assert.strictEqual(safeIdentifier('i-bem-dom'), '_iBemDom');\n    });\n\n    it('converts name with double underscore', function() {\n        assert.strictEqual(safeIdentifier('jquery__config'), '_jquery__config');\n    });\n});\n"
  },
  {
    "path": "build/vite.config.js",
    "content": "import { defineConfig } from 'vite';\nimport { resolve } from 'node:path';\nimport bemLevels from './plugins/vite-plugin-bem-levels.js';\n\nconst rootDir = resolve(import.meta.dirname, '..');\n\nexport default defineConfig(({ mode }) => {\n    const platform = process.env.BEM_PLATFORM || 'desktop';\n\n    return {\n        root: rootDir,\n\n        plugins: [\n            bemLevels({\n                platform,\n                levels: {\n                    common: ['common.blocks'],\n                    desktop: ['common.blocks', 'desktop.blocks'],\n                    touch: ['common.blocks', 'touch.blocks'],\n                },\n                rootDir,\n            }),\n        ],\n\n        build: {\n            lib: {\n                entry: resolve(import.meta.dirname, 'platforms', `${platform}.js`),\n                name: 'bemCore',\n                formats: ['es', 'umd'],\n                fileName: (format) => `bem-core.${format === 'es' ? 'mjs' : 'js'}`,\n            },\n            outDir: resolve(rootDir, 'dist', platform),\n            emptyOutDir: true,\n            sourcemap: true,\n            minify: mode === 'production',\n            rolldownOptions: {\n                external: ['jquery'],\n                output: {\n                    globals: {\n                        jquery: 'jQuery',\n                    },\n                },\n            },\n        },\n\n        server: {\n            open: false,\n        },\n    };\n});\n"
  },
  {
    "path": "build/vite.test.config.js",
    "content": "import { defineConfig } from 'vite';\nimport { resolve } from 'node:path';\nimport bemLevels from './plugins/vite-plugin-bem-levels.js';\n\nconst rootDir = resolve(import.meta.dirname, '..');\n\nexport default defineConfig({\n    root: rootDir,\n\n    plugins: [\n        bemLevels({\n            // Use desktop platform for browser tests\n            platform: 'desktop',\n            levels: {\n                common: ['common.blocks'],\n                desktop: ['common.blocks', 'desktop.blocks'],\n                touch: ['common.blocks', 'touch.blocks'],\n            },\n            rootDir,\n        }),\n    ],\n\n    // NOTE: jQuery is NOT external here — it must be bundled into the test page.\n    // (In the production build it's a peerDependency / external.)\n    resolve: {\n        alias: {},\n    },\n\n    server: {\n        port: 5174,\n        open: false,\n    },\n\n    // Polyfill Node.js globals used by mocha's browser-entry.js\n    define: {\n        'process.env.NODE_ENV': JSON.stringify('test'),\n        'process.env': JSON.stringify({ NODE_ENV: 'test' }),\n        'process.stdout': 'null',\n        'process.version': JSON.stringify('v22.0.0'),\n        global: 'globalThis',\n    },\n\n    optimizeDeps: {\n        include: ['chai', 'sinon', 'sinon-chai', 'jquery'],\n    },\n});\n"
  },
  {
    "path": "common.blocks/clearfix/clearfix.css",
    "content": ".clearfix:after\n{\n    display: table;\n    clear: both;\n\n    content: '';\n}\n"
  },
  {
    "path": "common.blocks/clearfix/clearfix.en.md",
    "content": "# clearfix\n\nThis block provides a CSS class that implements the **clearfix** layout hack, also known as the [Easy Clearing Method](http://www.456bereastreet.com/archive/200603/new_clearing_method_needed_for_ie7/). The hack allows you to clear wrapping for elements with the CSS `float` property, without making any changes to the document's original HTML structure.\n\nThis block can be used as a container for elements with the `float` property, or mixed with such a container.\n\nExample when used as a container:\n\n```bemjson\n[{\n    block : 'header',\n    attrs : { style : 'border : 2px solid blue;' },\n    content : 'Top element'\n},\n{\n    block : 'clearfix',\n    attrs : { style : 'border : 2px dotted yellow;' },\n    content : [\n    {\n        block : 'float',\n        attrs : { style : 'float : left; border : 1px solid green;' },\n        content : 'Floating item 1'\n    },\n    {\n        block : 'float',\n        attrs : { style : 'float : left; border : 1px solid green;' },\n        content : 'Floating item 2'\n    }]\n},\n{\n    block : 'footer',\n    attrs : { style : 'border : 2px solid red' },\n    content : 'Footer'\n}]\n```\n\nMixed with a container block:\n\n```bemjson\n[{\n    block : 'header',\n    attrs : { style : 'border : 2px solid blue;' },\n    content : 'Top element'\n},\n{\n    block : 'some-container',\n    mix : [{ block : 'clearfix' }],\n    attrs : { style : 'border : 2px dotted yellow;' },\n    content : [\n    {\n        block : 'float',\n        attrs : { style : 'float : left; border : 1px solid green;' },\n        content : 'Floating item 1'\n    },\n    {\n        block : 'float',\n        attrs : { style : 'float : left; border : 1px solid green;' },\n        content : 'Floating item 2'\n    }]\n},\n{\n    block : 'footer',\n    attrs : { style : 'border : 2px solid red' },\n    content : 'Footer'\n}]\n```\n\n## Public block technologies\n\nThe block is implemented in:\n\n* `css`.\n"
  },
  {
    "path": "common.blocks/clearfix/clearfix.en.title.txt",
    "content": "Block with floats inside\n"
  },
  {
    "path": "common.blocks/clearfix/clearfix.ru.md",
    "content": "# clearfix\n\nБлок предоставляет СSS-класс, реализующий прием верстки **clearfix**, также известный как [Easy Clearing Hack](http://www.456bereastreet.com/archive/200603/new_clearing_method_needed_for_ie7/). Прием позволяет отменить обтекание для элементов с CSS-свойством `float`, без внесения изменений в исходную HTML-структуру документа.\n\nБлок можно использовать в качестве контейнера для элементов со свойством `float`, или примешивая его к такому контейнеру.\n\nПример использования в качестве контейнера:\n\n```bemjson\n[{\n    block : 'header',\n    attrs : { style : 'border : 2px solid blue;' },\n    content : 'Top element'\n},\n{\n    block : 'clearfix',\n    attrs : { style : 'border : 2px dotted yellow;' },\n    content : [\n    {\n        block : 'float',\n        attrs : { style : 'float : left; border : 1px solid green;' },\n        content : 'Floating item 1'\n    },\n    {\n        block : 'float',\n        attrs : { style : 'float : left; border : 1px solid green;' },\n        content : 'Floating item 2'\n    }]\n},\n{\n    block : 'footer',\n    attrs : { style : 'border : 2px solid red' },\n    content : 'Footer'\n}]\n```\n\nПримешивание к блоку-контейнеру:\n\n```bemjson\n[{\n    block : 'header',\n    attrs : { style : 'border : 2px solid blue;' },\n    content : 'Top element'\n},\n{\n    block : 'some-container',\n    mix : [{ block : 'clearfix' }],\n    attrs : { style : 'border : 2px dotted yellow;' },\n    content : [\n    {\n        block : 'float',\n        attrs : { style : 'float : left; border : 1px solid green;' },\n        content : 'Floating item 1'\n    },\n    {\n        block : 'float',\n        attrs : { style : 'float : left; border : 1px solid green;' },\n        content : 'Floating item 2'\n    }]\n},\n{\n    block : 'footer',\n    attrs : { style : 'border : 2px solid red' },\n    content : 'Footer'\n}]\n```\n\n## Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `css`.\n"
  },
  {
    "path": "common.blocks/clearfix/clearfix.ru.title.txt",
    "content": "Блок с float элементами внутри\n"
  },
  {
    "path": "common.blocks/cookie/cookie.en.md",
    "content": "# cookie\n\nThis block provides an object with a set of methods for working with browser cookies (the JS `document.cookie` property).\n\n## Overview\n\n### Object properties and methods\n\n| Name | Return type | Description |\n| -------- | --- | -------- |\n| <a href=\"#fields-get\">get</a>(`name`) | `String` &#124; `null` | Gets the value stored in a browser cookie. |\n| <a href=\"#fields-set\">set</a>(`name`, `val`, `[options]`) | `String` | Sets the cookie with the specified name.|\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `js`\n\n## Description\n\n<a name=\"fields\"></a>\n\n### Object properties and methods\n\n<a name=\"fields-get\"></a>\n\n#### `get` method\n\nUse this method to get the value stored in a cookie for the name passed in the argument.\n\n**Accepted arguments:**\n\n| Argument | Type | Description |\n| ------- | --- | -------- |\n| `name`&#42; | `String` | The name of the cookie. |\n\n&#42; Required argument.\n\n**Returns:**\n\n* `String` — If a cookie with the specified name was set. The value is automatically decoded using [decodeURIComponent](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent).\n* `null` — If a cookie with the specified name doesn't exist.\n\nExample:\n\n```js\nmodules.require('cookie', function(cookie) {\n\n    cookie.set('mycookie', 'foobar');\n    console.log(cookie.get('mycookie')); // 'foobar'\n    console.log(cookie.get('foo')); // null\n\n});\n```\n\n<a name=\"fields-set\"></a>\n\n#### `set` method\n\nUse this method to set the cookie with the specified name. In addition to the name and value, you can pass the method a hash with additional cookie parameters.\n\n**Accepted arguments:**\n\n| Argument | Type | Description |\n| ------- | --- | -------- |\n| `name`&#42; | `String` | The name of the cookie. |\n| `val`&#42; | `String` &#124; `null` | The value of the cookie. If the value is set to `null`, the cookie is deleted.|\n| [`options`] | `Object` | Options. </br></br> Object properties</br></br> &#8226; `expires` (`Number`) – The cookie's time to live, in days. If the value is negative, the cookie is deleted. Alternatively, you can pass a generated date object (`new Date()`) for the value. </br> &#8226; `path` (`String`) – The path from the domain root where the cookie will be available. </br> &#8226; `domain` (`String`) – The domain. By default, this is the current domain. </br> &#8226; `secure` (`Boolean`) – Flag indicating that an encrypted SSL connection must be used with the cookie. By default, it is `false`. |\n\n&#42; Required argument.\n\n**Returns:** the `this` object.\n\nExample:\n\n```js\nmodules.require('cookie', function(cookie) {\n\n    cookie.set('mycookie', 'foobar', {\n        expires : 1, // lifetime is one day\n        path : '/', // available for all pages secure\n        secure : true // only send the cookie over SSL\n    });\n\n    console.log(cookie.get('mycookie')); // 'foobar'\n\n    cookie.set('mycookie', null); // deleting the cookie\n    console.log(cookie.get('mycookie')); // null\n\n});\n```\n"
  },
  {
    "path": "common.blocks/cookie/cookie.js",
    "content": "/**\n * @module cookie\n * @description Inspired from $.cookie plugin by Klaus Hartl (stilbuero.de)\n */\n\nexport default {\n    /**\n     * Returns cookie by given name\n     * @param {String} name\n     * @returns {String|null}\n     */\n    get(name) {\n        let res = null\n        if(document.cookie && document.cookie !== '') {\n            const cookies = document.cookie.split(';')\n            for(let i = 0; i < cookies.length; i++) {\n                const cookie = cookies[i].trim()\n                // Does this cookie string begin with the name we want?\n                if(cookie.substring(0, name.length + 1) === (name + '=')) {\n                    res = decodeURIComponent(cookie.substring(name.length + 1))\n                    break\n                }\n            }\n        }\n        return res\n    },\n\n    /**\n     * Sets cookie by given name\n     * @param {String} name\n     * @param {String} val\n     * @param {Object} options\n     * @returns {cookie} this\n     */\n    set(name, val, options) {\n        options = options || {}\n        if(val === null) {\n            val = ''\n            options.expires = -1\n        }\n        let expires = ''\n        if(options.expires && (typeof options.expires === 'number' || options.expires.toUTCString)) {\n            let date\n            if(typeof options.expires === 'number') {\n                date = new Date()\n                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000))\n            } else {\n                date = options.expires\n            }\n            expires = '; expires=' + date.toUTCString() // use expires attribute, max-age is not supported by IE\n        }\n        // CAUTION: Needed to parenthesize options.path and options.domain\n        // in the following expressions, otherwise they evaluate to undefined\n        // in the packed version for some reason...\n        const path = options.path? '; path=' + (options.path) : '',\n            domain = options.domain? '; domain=' + (options.domain) : '',\n            secure = options.secure? '; secure' : ''\n        document.cookie = [name, '=', encodeURIComponent(val), expires, path, domain, secure].join('')\n\n        return this\n    }\n}\n"
  },
  {
    "path": "common.blocks/cookie/cookie.ru.md",
    "content": "# cookie\n\nБлок предоставляет объект, содержащий набор методов для работы с cookie браузера (JS-свойство `document.cookie`).\n\n## Обзор\n\n### Свойства и методы объекта\n\n| Имя | Тип возвращаемого значения | Описание |\n| -------- | --- | -------- |\n| <a href=\"#fields-get\">get</a>(`name`) | `String` &#124; `null` | Служит для получения значения, хранящегося в cookie браузера. |\n| <a href=\"#fields-set\">set</a>(`name`, `val`, `[options]`) | `String` | Cлужит для записи cookie с заданным именем.|\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `js`\n\n## Описание\n\n<a name=\"fields\"></a>\n\n### Свойства и методы объекта\n\n<a name=\"fields-get\"></a>\n\n#### Метод `get`\n\nМетод служит для получения значения, хранящегося в cookie, для имени переданного аргументом.\n\n**Принимаемые аргументы:**\n\n| Аргумент | Тип | Описание |\n| ------- | --- | -------- |\n| `name`&#42; | `String` | Имя cookie. |\n\n&#42; Обязательный аргумент.\n\n**Возвращает:**\n\n* `String` — если cookie с заданным именем было установлено. Значение автоматически декодируется с помощью [decodeURIComponent](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent).\n* `null` — если cookie с заданным именем отсутствует.\n\nПример:\n\n```js\nmodules.require('cookie', function(cookie) {\n\n    cookie.set('mycookie', 'foobar');\n    console.log(cookie.get('mycookie')); // 'foobar'\n    console.log(cookie.get('foo')); // null\n\n});\n```\n\n<a name=\"fields-set\"></a>\n\n#### Метод `set`\n\nМетод служит для записи cookie с заданным именем. Помимо имени и значения, методу можно передать хеш с дополнительными параметрами cookie.\n\n**Принимаемые аргументы:**\n\n| Аргумент | Тип | Описание |\n| ------- | --- | -------- |\n| `name`&#42; | `String` | Имя cookie. |\n| `val`&#42; | `String` &#124; `null` | Значение cookie. При установке в качестве значения `null` cookie удаляется.|\n| [`options`] | `Object` | Опции. </br></br> Свойства объекта: </br></br> &#8226; `expires` (`Number`) – срок жизни cookie в сутках. При отрицательном значении cookie будет удалено. Альтернативно, можно передать в качестве значения сформированный объект даты (`new Date()`). </br> &#8226; `path` (`String`) – путь от корня домена внутри которого будет доступно cookie. </br> &#8226; `domain` (`String`) – домен. По умолчанию текущий домен. </br> &#8226; `secure` (`Boolean`) – флаг, указывающий на необходимость использования с cookie шифрованного соединения SSL. По умолчанию `false`. |\n\n&#42; Обязательный аргумент.\n\n**Возвращает:** объект `this`.\n\nПример:\n\n```js\nmodules.require('cookie', function(cookie) {\n\n    cookie.set('mycookie', 'foobar', {\n        expires : 1, // срок жизни одни сутки\n        path : '/', // доступно для всех страниц\n        secure : true // передавать cookie только по SSL\n    });\n\n    console.log(cookie.get('mycookie')); // 'foobar'\n\n    cookie.set('mycookie', null); // удаляем cookie\n    console.log(cookie.get('mycookie')); // null\n\n});\n```\n"
  },
  {
    "path": "common.blocks/cookie/cookie.spec.js",
    "content": "modules.define('spec', [\n    'cookie',\n    'chai'\n], function(provide,\n    cookie,\n    chai\n) {\n\nvar expect = chai.expect;\n\ndescribe('cookie', function() {\n    describe('get', function() {\n        it('should return value of defined cookie', function() {\n            document.cookie = 'name1=val1';\n            document.cookie = 'name2=val2';\n            cookie.get('name1').should.be.equal('val1');\n            cookie.get('name2').should.be.equal('val2');\n        });\n\n        it('should return null if cookie is undefined', function() {\n            expect(cookie.get('name3')).to.be.null;\n        });\n    });\n\n    describe('set', function() {\n        it('should properly set cookie', function() {\n            cookie.set('name', 'val');\n            cookie.get('name').should.be.equal('val');\n        });\n\n        it('should properly remove cookie', function() {\n            cookie.set('name', 'val');\n            cookie.set('name', null);\n            expect(cookie.get('name')).to.be.null;\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/dom/dom.deps.js",
    "content": "({\n    shouldDeps : 'jquery'\n})\n"
  },
  {
    "path": "common.blocks/dom/dom.en.md",
    "content": "# dom\n\nThis block provides an object with a set of methods for working with the DOM tree.\n\n## Overview\n\n### Object properties and methods\n\n| Name | Return type | Description |\n| -------- | --- | -------- |\n| <a href=\"#fields-contains\">contains</a>(<br>`ctx {jQuery}`,<br>`domElem {jQuery}`) | `Boolean` | Checks whether a DOM element contains another DOM element. |\n| <a href=\"#fields-getFocused\">getFocused</a>(<br>`domElem {jQuery} `) | `jQuery` | Gets a reference to the DOM element that is in focus. |\n| <a href=\"#fields-containsFocus\">containsFocus</a>(<br>`domElem {jQuery} `) | `Boolean` | Checks whether a DOM element or its descendants contains the focus. |\n| <a href=\"#fields-isFocusable\">isFocusable</a>(<br>`domElem {jQuery} `) | `Boolean` | Checks whether the DOM element is in focus. |\n| <a href=\"#fields-isEditable\">isEditable</a>(<br>`domElem {jQuery}`) | `Boolean` | Checks whether text can be entered in the DOM element. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `js`\n\n## Description\n\n<a name=\"fields\"></a>\n\n### Object properties and methods\n\n<a name=\"fields-contains\"></a>\n\n#### `contains` method\n\nUse this method to check whether a `ctx` DOM element contains `domElem`.\n\n**Accepted arguments:**\n\n* `ctx {jQuery}` – The DOM element to search inside. Required argument.\n* `domElem {jQuery}` – The DOM element to search for. Required argument.\n\n**Return value:** `Boolean`. If found, then `true`.\n\nExample:\n\n```js\nmodules.require(['dom', 'jquery'], function(dom, $) {\n\n/*\n<div class=\"block1\">\n  <div class=\"block2\"></div>\n</div>\n*/\n\ndom.contains($('.block1'), $('.block2'));  // true\n\n});\n```\n\n<a name=\"fields-getFocused\"></a>\n\n#### `getFocused` method\n\nGets a reference to the DOM element that is in focus.\n\nDoesn't accept arguments.\n\n**Return value:** `jQuery` – The object in focus.\n\nExample:\n\n```js\nmodules.require(['dom'], function(dom) {\n\ndom.getFocused(); // a reference to the element in focus\n\n});\n```\n\n<a name=\"fields-containsFocus\"></a>\n\n#### `containsFocus` method\n\nThis method checks whether the focus is on the DOM element passed in the argument or one of its descendants.\n\n**Accepted arguments:**\n\n* `domElem {jQuery}` – The DOM element to check. Required argument.\n\n**Return value:** `Boolean`. If this element is in focus, then `true`.\n\nExample:\n\n```js\nmodules.require(['dom', 'jquery'], function(dom, $) {\n\n/*\n<div class=\"block1\">\n  <input class=\"block1__control\"></div>\n</div>\n*/\n\n$('.block1__control').focus();\ndom.containsFocus($('.block1'));  // true\n\n});\n```\n\n<a name=\"fields-isFocusable\"></a>\n\n#### `isFocusable` method\n\nThis method checks whether the user's browser can set the focus on the DOM element passed in the argument.   \n\n**Accepted arguments:**\n\n* `domElem {jQuery}` – The DOM element to check. Required argument. If there are mutiple DOM elements in the jQuery chain, the first one is checked.\n\n**Return value:** `Boolean`. If the focus can be set on this element, then `true`.\n\nExample:\n\n```js\nmodules.require(['dom', 'jquery'], function(dom, $) {\n\n/*\n<div class=\"menu\">\n  <a class=\"menu__item\" href=\"/\">Link 1</a>\n</div>\n*/\n\ndom.isFocusable($('.menu__item')); // true\n\n/*\n<div class=\"menu\">\n  <span class=\"menu__item menu__item_current\">Link 1</span>\n</div>\n*/\n\ndom.isFocusable($('.menu__item')); // false\n\n});\n```\n\n<a name=\"fields-isEditable\"></a>\n\n#### `isEditable` method\n\nThis method checks whether text can be entered in the DOM element passed in the argument. In other words, you can use this method to check whether the element is an input field, text field, and so on.\n\n**Accepted arguments:**\n\n* `domElem {jQuery}` – The DOM element to check. Required argument. If there are mutiple DOM elements in the jQuery chain, the first one is checked.\n\n**Return value:** `Boolean`. If text can be entered in the DOM element, then `true`.\n\nExample:\n\n```js\nmodules.require(['dom', 'jquery'], function(dom, $) {\n\ndom.isEditable($('input, textarea')); // true\n\n});\n```\n"
  },
  {
    "path": "common.blocks/dom/dom.js",
    "content": "/**\n * @module dom\n * @description some DOM utils\n */\n\nimport $ from 'bem:jquery'\n\nconst EDITABLE_INPUT_TYPES = new Set([\n    'datetime-local',\n    'date',\n    'month',\n    'number',\n    'password',\n    'search',\n    'tel',\n    'text',\n    'time',\n    'url',\n    'week'\n])\n\nexport default {\n    /**\n     * Checks whether a DOM elem is in a context\n     * @param {jQuery} ctx DOM elem where check is being performed\n     * @param {jQuery} domElem DOM elem to check\n     * @returns {Boolean}\n     */\n    contains(ctx, domElem) {\n        let res = false\n\n        domElem.each(function() {\n            let domNode = this\n            do {\n                if(~ctx.index(domNode)) return !(res = true)\n            } while(domNode = domNode.parentNode)\n\n            return res\n        })\n\n        return res\n    },\n\n    /**\n     * Returns current focused DOM elem in document\n     * @returns {jQuery}\n     */\n    getFocused() {\n        // \"Error: Unspecified error.\" in iframe in IE9\n        try { return $(document.activeElement) } catch(e) {}\n    },\n\n    /**\n     * Checks whether a DOM element contains focus\n     * @param {jQuery} domElem\n     * @returns {Boolean}\n     */\n    containsFocus(domElem) {\n        return this.contains(domElem, this.getFocused())\n    },\n\n    /**\n    * Checks whether a browser currently can set focus on DOM elem\n    * @param {jQuery} domElem\n    * @returns {Boolean}\n    */\n    isFocusable(domElem) {\n        const domNode = domElem[0]\n\n        if(!domNode) return false\n        if(domNode.hasAttribute('tabindex')) return true\n\n        switch(domNode.tagName.toLowerCase()) {\n            case 'iframe':\n                return true\n\n            case 'input':\n            case 'button':\n            case 'textarea':\n            case 'select':\n                return !domNode.disabled\n\n            case 'a':\n                return !!domNode.href\n        }\n\n        return false\n    },\n\n    /**\n    * Checks whether a domElem is intended to edit text\n    * @param {jQuery} domElem\n    * @returns {Boolean}\n    */\n    isEditable(domElem) {\n        const domNode = domElem[0]\n\n        if(!domNode) return false\n\n        switch(domNode.tagName.toLowerCase()) {\n            case 'input':\n                return EDITABLE_INPUT_TYPES.has(domNode.type) && !domNode.disabled && !domNode.readOnly\n\n            case 'textarea':\n                return !domNode.disabled && !domNode.readOnly\n\n            default:\n                return domNode.contentEditable === 'true'\n        }\n    }\n}\n"
  },
  {
    "path": "common.blocks/dom/dom.ru.md",
    "content": "# dom\n\nБлок предоставляет объект, содержащий набор методов для работы с DOM-деревом.\n\n## Обзор\n\n### Свойства и методы объекта\n\n| Имя | Тип возвращаемого значения | Описание |\n| -------- | --- | -------- |\n| <a href=\"#fields-contains\">contains</a>(<br>`ctx {jQuery}`,<br>`domElem {jQuery}`) | `Boolean` | Проверяет, содержит ли один DOM-элемент другой. |\n| <a href=\"#fields-getFocused\">getFocused</a>(<br>`domElem {jQuery} `) | `jQuery` | Служит для получения ссылки на DOM-элемент в фокусе. |\n| <a href=\"#fields-containsFocus\">containsFocus</a>(<br>`domElem {jQuery} `) | `Boolean` | Проверят, содержит ли DOM-элемент или его потомки фокус. |\n| <a href=\"#fields-isFocusable\">isFocusable</a>(<br>`domElem {jQuery} `) | `Boolean` | Проверят, может ли DOM-элемент находиться в фокусе. |\n| <a href=\"#fields-isEditable\">isEditable</a>(<br>`domElem {jQuery}`) | `Boolean` | Проверят, возможен ли в DOM-элементе ввод текста. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `js`\n\n## Описание\n\n<a name=\"fields\"></a>\n\n### Свойства и методы объекта\n\n<a name=\"fields-contains\"></a>\n\n#### Метод `contains`\n\nМетод позволяет проверить содержит ли некоторый DOM-элемент `ctx` элемент `domElem`.\n\n**Принимаемые аргументы:**\n\n* `ctx {jQuery}` – DOM-элемент внутри которого производится поиск. Обязательный аргумент.\n* `domElem {jQuery}` – искомый DOM-элемент. Обязательный аргумент.\n\n**Возвращаемое значение:** `Boolean`. Если искомый элемент найден – `true`.\n\nПример:\n\n```js\nmodules.require(['dom', 'jquery'], function(dom, $) {\n\n/*\n<div class=\"block1\">\n  <div class=\"block2\"></div>\n</div>\n*/\n\ndom.contains($('.block1'), $('.block2'));  // true\n\n});\n```\n\n<a name=\"fields-getFocused\"></a>\n\n#### Метод `getFocused`\n\nМетод служит для получения ссылки на DOM-элемент, находящийся в фокусе.\n\nНе принимает аргументов.\n\n**Возвращаемое значение:** `jQuery` – объект в фокусе.\n\nПример:\n\n```js\nmodules.require(['dom'], function(dom) {\n\ndom.getFocused(); // ссылка на элемент в фокусе\n\n});\n```\n\n<a name=\"fields-containsFocus\"></a>\n\n#### Метод `containsFocus`\n\nМетод проверяет находится ли в фокусе переданный аргументом DOM-элемент или один из его потомков.\n\n**Принимаемые аргументы:**\n\n* `domElem {jQuery}` – проверяемый DOM-элемент. Обязательный аргумент.\n\n**Возвращаемое значение:** `Boolean`. Если искомый элемент в фокусе – `true`.\n\nПример:\n\n```js\nmodules.require(['dom', 'jquery'], function(dom, $) {\n\n/*\n<div class=\"block1\">\n  <input class=\"block1__control\"></div>\n</div>\n*/\n\n$('.block1__control').focus();\ndom.containsFocus($('.block1'));  // true\n\n});\n```\n\n<a name=\"fields-isFocusable\"></a>\n\n#### Метод `isFocusable`\n\nМетод проверят может ли браузер пользователя установить фокус на переданный аргументом DOM-элемент.   \n\n**Принимаемые аргументы:**\n\n* `domElem {jQuery}` – проверяемый DOM-элемент. Обязательный аргумент. Если в jQuery-цепочке несколько DOM-элементов, то проверяется первый из них.\n\n**Возвращаемое значение:** `Boolean`. Если фокус может быть установлен – `true`.\n\nПример:\n\n```js\nmodules.require(['dom', 'jquery'], function(dom, $) {\n\n/*\n<div class=\"menu\">\n  <a class=\"menu__item\" href=\"/\">Link 1</a>\n</div>\n*/\n\ndom.isFocusable($('.menu__item')); // true\n\n/*\n<div class=\"menu\">\n  <span class=\"menu__item menu__item_current\">Link 1</span>\n</div>\n*/\n\ndom.isFocusable($('.menu__item')); // false\n\n});\n```\n\n<a name=\"fields-isEditable\"></a>\n\n#### Метод `isEditable`\n\nМетод проверят возможен ли в переданном аргументом DOM-элементе ввод текста. Другими словами, с помощью метода можно проверить является ли элемент полем ввода, текстовой областью и т.п.\n\n**Принимаемые аргументы:**\n\n* `domElem {jQuery}` – проверяемый DOM-элемент. Обязательный аргумент. Если в jQuery-цепочке несколько DOM-элементов, то проверяется первый из них.\n\n**Возвращаемое значение:** `Boolean`. Если ввод текста в элементе возможен – `true`.\n\nПример:\n\n```js\nmodules.require(['dom', 'jquery'], function(dom, $) {\n\ndom.isEditable($('input, textarea')); // true\n\n});\n```\n"
  },
  {
    "path": "common.blocks/dom/dom.spec.js",
    "content": "modules.define('spec', [\n    'dom',\n    'jquery'\n], function(provide,\n    dom,\n    $\n) {\n\ndescribe('dom', function() {\n    describe('contains', function() {\n        var domElem;\n        beforeEach(function() {\n            domElem = $(\n                '<div>' +\n                    '<div class=\"a\">' +\n                        '<div class=\"x\"/>' +\n                    '</div>' +\n                    '<div class=\"a\">' +\n                        '<div class=\"x\"/>' +\n                        '<div class=\"y\"/>' +\n                    '</div>' +\n                    '<div class=\"c\"/>' +\n                '</div>')\n                    .appendTo('body');\n        });\n\n        afterEach(function() {\n            domElem.remove();\n        });\n\n        it('should properly checks for nested dom elem', function() {\n            dom.contains(domElem.find('.a'), domElem.find('.x')).should.be.true;\n            dom.contains(domElem.find('.a'), domElem.find('.y')).should.be.true;\n            dom.contains(domElem.find('.c'), domElem.find('.x')).should.be.false;\n        });\n\n        it('should returns true for itself', function() {\n            dom.contains(domElem.find('.x'), domElem.find('.x')).should.be.true;\n        });\n\n        it('should returns false for empty DOM elem', function() {\n            dom.contains(domElem.find('.a'), domElem.find('.no-exist')).should.be.false;\n        });\n    });\n\n    describe('getFocused', function() {\n        it('should returns focused DOM elem', function() {\n            var elem = $('<input/>')\n                .appendTo('body')\n                .focus();\n            dom.getFocused()[0].should.be.eql(elem[0]);\n            elem.blur();\n            dom.getFocused()[0].should.not.be.eql(elem[0]);\n            elem.remove();\n        });\n    });\n\n    describe('isFocusable', function() {\n        it('should returns true if given DOM elem is iframe, input, button, textarea or select', function() {\n            dom.isFocusable($('<iframe/>')).should.be.true;\n            dom.isFocusable($('<input/>')).should.be.true;\n            dom.isFocusable($('<button/>')).should.be.true;\n            dom.isFocusable($('<textarea/>')).should.be.true;\n            dom.isFocusable($('<select/>')).should.be.true;\n        });\n\n        it('should returns false if given DOM elem is disabled', function() {\n            dom.isFocusable($('<input disabled=\"disabled\"/>')).should.be.false;\n            dom.isFocusable($('<button disabled=\"disabled\"/>')).should.be.false;\n            dom.isFocusable($('<textarea disabled=\"disabled\"/>')).should.be.false;\n            dom.isFocusable($('<select disabled=\"disabled\"/>')).should.be.false;\n        });\n\n        it('should returns true if given DOM elem is link with href', function() {\n            dom.isFocusable($('<a href=\"/\"/>')).should.be.true;\n            dom.isFocusable($('<a/>')).should.be.false;\n        });\n\n        it('should returns true if given DOM elem has tabindex', function() {\n            dom.isFocusable($('<span tabindex=\"4\"/>')).should.be.true;\n            dom.isFocusable($('<a tabindex=\"5\"/>')).should.be.true;\n            dom.isFocusable($('<span/>')).should.be.false;\n        });\n\n        it('should returns false if given DOM elem is empty', function() {\n            dom.isFocusable($('.__no-exist')).should.be.false;\n        });\n    });\n\n    describe('containsFocus', function() {\n        var domElem;\n        beforeEach(function() {\n            domElem = $(\n                '<div>' +\n                    '<div class=\"a\">' +\n                        '<input class=\"x\"/>' +\n                    '</div>' +\n                    '<div class=\"b\"/>' +\n                '</div>')\n                    .appendTo('body');\n            domElem.find('.x').focus();\n        });\n\n        afterEach(function() {\n            domElem.remove();\n        });\n\n        it('should returns true if context contains focused DOM elem', function() {\n            dom.containsFocus(domElem.find('.a')).should.be.true;\n        });\n\n        it('should returns true if context self-focused', function() {\n            dom.containsFocus(domElem.find('.x')).should.be.true;\n        });\n\n        it('should returns false if context not contains focused DOM elem', function() {\n            dom.containsFocus(domElem.find('.b')).should.be.false;\n        });\n\n        it('should returns false if context is empty', function() {\n            dom.containsFocus(domElem.find('.__no-exist')).should.be.false;\n        });\n    });\n\n    describe('isEditable', function() {\n        it('should returns true if given DOM elem is text or password input', function() {\n            dom.isEditable($('<input type=\"text\"/>')).should.be.true;\n            dom.isEditable($('<input type=\"password\"/>')).should.be.true;\n            dom.isEditable($('<textarea/>')).should.be.true;\n            dom.isEditable($('<input type=\"radio\"/>')).should.be.false;\n            dom.isEditable($('<input type=\"checkbox\"/>')).should.be.false;\n            dom.isEditable($('<div/>')).should.be.false;\n        });\n\n        it('should returns false if given input is readonly', function() {\n            dom.isEditable($('<input type=\"text\" readonly=\"readonly\"/>')).should.be.false;\n            dom.isEditable($('<texarea readonly=\"readonly\"/>')).should.be.false;\n        });\n\n        it('should returns false if given input is disabled', function() {\n            dom.isEditable($('<input type=\"text\" disabled=\"disabled\"/>')).should.be.false;\n            dom.isEditable($('<texarea disabled=\"disabled\"/>')).should.be.false;\n        });\n\n        it('should returns true for contenteditable DOM elems', function() {\n            dom.isEditable($('<div contenteditable=\"true\"/>')).should.be.true;\n            dom.isEditable($('<div contenteditable=\"false\"/>')).should.be.false;\n            dom.isEditable($('<div contenteditable=\"yet-another-val\"/>')).should.be.false;\n        });\n\n        it('should returns false if given DOM elem is empty', function() {\n            dom.isEditable($('.__no-exist')).should.be.false;\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/events/__channels/events__channels.deps.js",
    "content": "({\n   shouldDeps : 'events'\n})\n"
  },
  {
    "path": "common.blocks/events/__channels/events__channels.en.md",
    "content": "<a name=\"elems-channels\"></a>\n\n# `channels` element in the `events` block\n\nUse the `channels` element in the `events` block for working with named event channels. Named channels allow you to work with events using the observer pattern (also known as the publish-subscribe pattern).\n\nThis element implements a function to:\n\n* Get a reference to a named channel by its `id`.\n* Get a reference to a standard channel.\n* Remove a standard channel or a named channel with an `id`.\n\n**Accepted arguments:**\n\n* [`id {String}`] – Channel ID. If omitted, the default channel is used (`'default'`).\n* [`drop {Boolean}`] – A boolean flag to remove the channel (when `true`). By default, `false`.\n\n**Returned value:**\n\n* `Object`. Object of the `Emitter` \"class\" – a named channel.\n* `undefined`. If the function was called with the `drop` parameter set to `true`.\n\nExample:\n\n```js\nmodules.require(['events__channels'], function(channels) {\n\nvar myChannel = channels('my-channel');\nmyChannel.on('test', function(e, data) { console.log(data.foo) });\n\nmyChannel.emit('test', { foo : 'bar' }); // 'bar'\n\n});\n```\n"
  },
  {
    "path": "common.blocks/events/__channels/events__channels.ru.md",
    "content": "<a name=\"elems-channels\"></a>\n\n# Элемент `channels` блока `events`\n\nЭлемент `channels` блока `events` предназначен для работы с именованными каналами событий. Именные каналы позволяют организовать работу с событиями, используя шаблон проектирования «наблюдатель» (также известный как Publisher-subscriber).\n\nЭлемент реализует функцию, позволяющую:\n\n* получить ссылку на именной канал по `id`;\n* получить ссылку на стандартный канал;\n* удалить канал – стандартный или по `id`.\n\n**Принимаемые аргументы:**\n\n* [`id {String}`] – Идентификатор канала. Если не задан будет использоваться канал по умолчанию (`'default'`).\n* [`drop {Boolean}`] – Логический флаг, указывающий (в значении `true`) на необходимость удалить канал. По умолчанию `false`.\n\n**Возвращаемое значение:**\n\n* `Object`. Объект «класса» `Emitter` – именной канал.\n* `undefined`. В случае если функция была вызвана с параметром `drop` в значении `true`.\n\nПример:\n\n```js\nmodules.require(['events__channels'], function(channels) {\n\nvar myChannel = channels('my-channel');\nmyChannel.on('test', function(e, data) { console.log(data.foo) });\n\nmyChannel.emit('test', { foo : 'bar' }); // 'bar'\n\n});\n```\n"
  },
  {
    "path": "common.blocks/events/__channels/events__channels.vanilla.js",
    "content": "/**\n * @module events__channels\n */\n\nimport events from 'bem:events'\n\nconst channels = new Map()\n\nexport default\n  /**\n   * Returns/destroys a named communication channel\n   * @param {String} [id='default'] Channel ID\n   * @param {Boolean} [drop=false] Destroy the channel\n   * @returns {events:Emitter|undefined} Communication channel\n   */\n  function(id, drop) {\n    if(typeof id === 'boolean') {\n      drop = id\n      id = undefined\n    }\n\n    id || (id = 'default')\n\n    if(drop) {\n      if(channels.has(id)) {\n        channels.get(id).un()\n        channels.delete(id)\n      }\n      return\n    }\n\n    let channel = channels.get(id)\n    if(!channel) {\n      channel = new events.Emitter()\n      channels.set(id, channel)\n    }\n    return channel\n  }\n"
  },
  {
    "path": "common.blocks/events/__observable/_type/events__observable_type_bem-dom.deps.js",
    "content": "[{\n    mustDeps : { elem : 'observable' },\n    shouldDeps : 'i-bem-dom'\n},\n{\n    tech : 'spec.js',\n    mustDeps : [\n        {\n            block : 'i-bem-dom',\n            tech : 'js'\n        }\n    ]\n}]\n"
  },
  {
    "path": "common.blocks/events/__observable/_type/events__observable_type_bem-dom.js",
    "content": "/**\n * @module events__observable_type_bem-dom\n */\n\nimport bemDom from 'bem:i-bem-dom';\nimport observable from 'bem:events__observable';\n\nexport default\n    /**\n     * Creates new observable with BEM DOM entity support\n     * @param {i-bem-dom:Block|i-bem-dom:Elem|events:Emitter} bemEntity\n     * @returns {Observable}\n     */\n    function(bemEntity) {\n        return observable(bemDom.isEntity(bemEntity)?\n            bemEntity._events() :\n            bemEntity);\n    };\n"
  },
  {
    "path": "common.blocks/events/__observable/_type/events__observable_type_bem-dom.spec.js",
    "content": "modules.define('spec', [\n    'events__observable',\n    'sinon',\n    'i-bem-dom',\n    'BEMHTML'\n], function(provide,\n    observable,\n    sinon,\n    bemDom,\n    BEMHTML\n) {\n\ndescribe('events__observable_type_bem-dom', function() {\n    var spy1, spy2, spy3, block;\n\n    beforeEach(function() {\n        spy1 = sinon.spy();\n        spy2 = sinon.spy();\n        spy3 = sinon.spy();\n        block = bemDom.init(BEMHTML.apply({\n            block : 'obs-dom-block'\n        })).appendTo('body').bem(bemDom.declBlock('obs-dom-block'));\n    });\n\n    afterEach(function() {\n        bemDom.destruct(block.domElem);\n    });\n\n    describe('on', function() {\n        it('should properly bind handlers', function() {\n            var data = {},\n                ctx = {};\n\n            observable(block)\n                .on('myevent', spy1)\n                .on('myevent', spy2, ctx)\n                .on('myevent', data, spy3, ctx);\n\n            block._emit('myevent');\n\n            spy1.should.have.been.calledOn(block);\n            spy2.should.have.been.calledOn(ctx);\n            spy3.args[0][0].data.should.be.equal(data);\n        });\n    });\n\n    describe('once', function() {\n        it('should properly bind handlers', function() {\n            var data = {},\n                ctx = {};\n\n            observable(block)\n                .once('myevent', spy1)\n                .once('myevent', spy2, ctx)\n                .once('myevent', data, spy3, ctx);\n\n            block._emit('myevent');\n            block._emit('myevent');\n\n            spy1.should.have.been.calledOnce;\n            spy1.should.have.been.calledOn(block);\n            spy2.should.have.been.calledOnce;\n            spy2.should.have.been.calledOn(ctx);\n            spy3.should.have.been.calledOnce;\n            spy3.args[0][0].data.should.be.equal(data);\n        });\n    });\n\n    describe('un', function() {\n        it('should properly unbind handlers', function() {\n            var ctx = {},\n                blockObserver = observable(block)\n                    .on('myevent', spy1)\n                    .on('myevent', spy2, ctx)\n                    .un('myevent', spy1)\n                    .un('myevent', spy2);\n\n            block._emit('myevent');\n\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n\n            blockObserver.un('myevent', spy2, ctx);\n            block._emit('myevent');\n\n            spy2.should.have.been.calledOnce;\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/events/__observable/events__observable.deps.js",
    "content": "[\n    { shouldDeps : 'inherit' },\n    {\n        tech : 'spec.js',\n        shouldDeps : { block : 'events', tech : 'js' }\n    }\n]\n"
  },
  {
    "path": "common.blocks/events/__observable/events__observable.js",
    "content": "/**\n * @module events__observable\n */\n\nimport inherit from 'bem:inherit';\n\n/**\n * @class Observable\n */\nconst Observable = inherit(/** @lends Observable.prototype */{\n    /**\n     * @constructor\n     * @param {Object} emitter\n     */\n    __constructor : function(emitter) {\n        this._emitter = emitter;\n    },\n\n    /**\n     * Adds an event handler\n     * @param {String} e Event type\n     * @param {Object} [data] Additional data that the handler gets as e.data\n     * @param {Function} fn Handler\n     * @param {Object} [fnCtx] Context\n     * @returns {Observable} this\n     */\n    on : function(e, data, fn, fnCtx) {\n        this._emitter.on.apply(this._emitter, arguments);\n        return this;\n    },\n\n    /**\n     * Adds an event handler\n     * @param {String} e Event type\n     * @param {Object} [data] Additional data that the handler gets as e.data\n     * @param {Function} fn Handler\n     * @param {Object} [fnCtx] Context\n     * @returns {Observable} this\n     */\n    once : function(e, data, fn, fnCtx) {\n        this._emitter.once.apply(this._emitter, arguments);\n        return this;\n    },\n\n    /**\n     * Removes event handler\n     * @param {String} [e] Event type\n     * @param {Function} [fn] Handler\n     * @param {Object} [fnCtx] Context\n     * @returns {Observable} this\n     */\n    un : function(e, fn, fnCtx) {\n        this._emitter.un.apply(this._emitter, arguments);\n        return this;\n    }\n});\n\nexport default\n    /**\n     * Creates new observable\n     * @param {events:Emitter} emitter\n     * @returns {Observable}\n     */\n    function(emitter) {\n        return new Observable(emitter);\n    };\n"
  },
  {
    "path": "common.blocks/events/__observable/events__observable.spec.js",
    "content": "modules.define('spec', [\n    'events__observable',\n    'sinon',\n    'events'\n], function(provide,\n    observable,\n    sinon,\n    events\n) {\n\ndescribe('events__observable', function() {\n    var spy1, spy2, spy3, emitter;\n\n    beforeEach(function() {\n        spy1 = sinon.spy();\n        spy2 = sinon.spy();\n        spy3 = sinon.spy();\n        emitter = new events.Emitter();\n    });\n\n    describe('on', function() {\n        it('should properly bind handlers', function() {\n            var data = {},\n                ctx = {};\n\n            observable(emitter)\n                .on('myevent', spy1)\n                .on('myevent', spy2, ctx)\n                .on('myevent', data, spy3, ctx);\n\n            emitter.emit('myevent');\n\n            spy1.should.have.been.calledOn(emitter);\n            spy2.should.have.been.calledOn(ctx);\n            spy3.args[0][0].data.should.be.equal(data);\n        });\n    });\n\n    describe('once', function() {\n        it('should properly bind handlers', function() {\n            var data = {},\n                ctx = {};\n\n            observable(emitter)\n                .once('myevent', spy1)\n                .once('myevent', spy2, ctx)\n                .once('myevent', data, spy3, ctx);\n\n            emitter.emit('myevent');\n            emitter.emit('myevent');\n\n            spy1.should.have.been.calledOnce;\n            spy1.should.have.been.calledOn(emitter);\n            spy2.should.have.been.calledOnce;\n            spy2.should.have.been.calledOn(ctx);\n            spy3.should.have.been.calledOnce;\n            spy3.args[0][0].data.should.be.equal(data);\n        });\n    });\n\n    describe('un', function() {\n        it('should properly unbind handlers', function() {\n            var ctx = {},\n                blockObserver = observable(emitter)\n                    .on('myevent', spy1)\n                    .on('myevent', spy2, ctx)\n                    .un('myevent', spy1)\n                    .un('myevent', spy2);\n\n            emitter.emit('myevent');\n\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n\n            blockObserver.un('myevent', spy2, ctx);\n            emitter.emit('myevent');\n\n            spy2.should.have.been.calledOnce;\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/events/events.deps.js",
    "content": "({\n   shouldDeps : ['inherit', 'identify', 'functions']\n})\n"
  },
  {
    "path": "common.blocks/events/events.en.md",
    "content": "# events\n\nThis block provides a set of JS classes for working with events.\n\n## Overview\n\n### Classes provided by the block\n\n| Class | Constructor | Description |\n| ----- | ----------- | -------- |\n| <a href=\"#class-Event\">Event</a> | Event(<br>`type {String}`, <br>`target {Object}`) | Creates the event object and changes and checks its states. |\n| <a href=\"#class-Emitter\">Emitter</a> | - | Generates events and subscriptions to them. |\n\n### Properties and methods of the class object\n\n| Class | Name | Type or return value | Description |\n| ----- | --- | ----------------------------- | -------- |\n| <a href=\"#class-Event\">Event</a> | <a href=\"#fields-type\">type</a> | `String` | Type of event. |\n|  | <a href=\"#fields-result\">result</a> | `*` | The result returned by the event's last handler. |\n|  | <a href=\"#fields-target\">target</a> | `Object` | The object where the event occurred. |\n|  | <a href=\"#fields-data\">data</a> | `*` | Data to pass to the handler as an argument. |\n|  | <a href=\"#fields-preventDefault\">preventDefault</a>()| - | Allows you to prevent execution of the default action for the event. |\n|  | <a href=\"#fields-isDefaultPrevented\">isDefaultPrevented</a>()| `Boolean` | Checks whether the default action for the event was prevented from being executed. |\n|  | <a href=\"#fields-stopPropagation\">stopPropagation</a>()| - | Allows you to stop event propagation. |\n|  | <a href=\"#fields-isPropagationStopped\">isPropagationStopped</a>()| `Boolean` | Checks whether event propagation was stopped. |\n| <a href=\"#class-Emitter\">Emitter</a> | <a href=\"#fields-on\">on</a>(<br>`type {String}`, <br>`[data {Object}]`, <br>`fn {Function}`, <br>`[ {Object} ctx]`) | - | Subscribes to a specific type of event. |\n|  | <a href=\"#fields-once\">once</a>(<br>`type {String}`, <br>`[data {Object}]`, <br>`fn {Function}`, <br>`[ctx {Object}]`) | - | Subscribes to a specific type of event. The handler executes only once. |\n|  | <a href=\"#fields-un\">un</a>(<br>`type {String}`, <br>`fn {Function}`, <br>`[ctx {Object}]`) | - | Unsubscribes to a specific type of event. |\n|  | <a href=\"#fields-emit\">emit</a>(<br>`type {String`&#124;`events:Event}`, <br>`[data {Object}]`) | - | Generates an event. |\n\n### Elements of the block\n\n| Element | Usage | Description |\n| ------- | --------------------- | -------- |\n| <a href=\"#elems-channels\">channels</a> | `JS` | Used for working with named event channels. |\n\n### Functions provided by block elements\n\n| Element | Function | Return type | Description |\n| ------- | ------- | ----------------------------- | -------- |\n| <a href=\"#elems-channels\">channels</a> | channels(<br>`[id {String}]`, <br>`[drop {Boolean}]`) | `Object`&#124;`undefined` | Creates or deletes a named event channel. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `vanilla.js`\n\n## Description\n\n<a name=\"class-Event\"></a>\n\n### `Event` class\n\nYou can use this class to instantiate an event object by indicating its type and source. To do this, use the `Event` constructor function.\n\n**Accepted arguments:**\n\n* `type {String}` – Type of event. Required argument.\n* `target {Object}` – Object (source) where the event occurred. Required argument.\n\n**Return value:** `Event`. The event object.\n\n<a name=\"fields-Event\"></a>\n\n#### Properties and methods of the class object\n\n<a name=\"fields-type\"></a>\n\n##### `type` property\n\nType: `String`.\n\nType of event.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myevent = new events.Event('myevent', this);\n    console.log(myevent.type); // 'myevent'\n\n});\n```\n\n<a name=\"fields-type\"></a>\n\n##### `target` property\n\nType: `Object`.\n\nThe object where the event occurred.\n\n<a name=\"fields-result\"></a>\n\n##### `result` property\n\nType: `*`.\n\nContains the data returned by the event's last handler function.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n    myEmitter.on('myevent', function() { return 'hi-hi-hi'; });\n\n    var myEvent = new events.Event('myevent');\n    myEmitter.emit(myEvent)\n\n    console.log(myEvent.result);    // 'hi-hi-hi'\n});\n```\n\n<a name=\"fields-data\"></a>\n\n##### `data` property\n\nType: `*`.\n\nContains the data passed to the event's handler function as an argument.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n    myEmitter.on('myevent', 'my-data', function(e) { console.log(e.data); });\n\n    myEmitter.emit('myevent'); // my-data\n});\n```\n\n<a name=\"fields-preventDefault\"></a>\n\n##### `preventDefault` method\n\nAllows you to prevent execution of the default action for the event.\n\nDoesn't accept arguments.\n\nNo return value.\n\n<a name=\"fields-isDefaultPrevented\"></a>\n\n##### `isDefaultPrevented` method\n\nAllows you to check whether the default action for the event was prevented from being executed.\n\nDoesn't accept arguments.\n\n**Return value:** `Boolean`. If the default action for the event was prevented from being executed, it is `true`.\n\n<a name=\"fields-stopPropagation\"></a>\n\n##### `stopPropagation` method\n\nAllows you to stop event propagation.\n\nDoesn't accept arguments.\n\nNo return value.\n\n<a name=\"fields-isPropagationStopped\"></a>\n\n##### `isPropagationStopped` method\n\nAllows you to check whether event propagation was stopped.\n\nDoesn't accept arguments.\n\n**Return value:** `Boolean`. If event propagation was stopped, it is `true`.\n\n<a name=\"class-Emitter\"></a>\n\n### `Emitter` class\n\nThis class instantiates objects that you can use for generating events and subscribing to them.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n});\n```\n\n<a name=\"fields-Event\"></a>\n\n#### Properties and methods of the class object\n\n<a name=\"fields-on\"></a>\n\n##### `on` method\n\nSubscribes to a specific type of event.\n\n**Accepted arguments:**\n\n* `type {String}` – The type of event being subscribed to. Required argument.\n* [`data {Object}`] – Additional data available to the handler as the value of the `e.data` field in the event object.\n* `fn {Function}` – The handler function to call for the event. Required argument.\n* [`ctx {Object}`] – Context for the handler function.\n\nReturns the `this` object.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on('myevent', function() { console.log('foo'); });\n    myEmitter.emit('myevent'); // 'foo'\n});\n```\n\nIn addition, the value of the `type` argument may be:\n\n* Multiple event types separated by spaces, in order to set a single handler function for all of them.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on('myevent1 myevent2', function(e) { console.log(e.type) });\n\n    myEmitter.emit('myevent1'); // 'myevent1'\n    myEmitter.emit('myevent2'); // 'myevent2'\n});\n```\n\n* A hash of `{ 'event-1' : handler-1, ... , 'event-n' : handler-n }`, in order to set multiple handlers for different event types.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on({\n        myevent1 : function(e) { console.log(e.type) },\n        myevent2 : function(e) { console.log(e.type) }\n    });  \n\n    myEmitter.emit('myevent1'); // 'myevent1'\n    myEmitter.emit('myevent2'); // 'myevent2'\n});\n```\n\nThe same is true for the `once` and `un` methods.\n\n<a name=\"fields-once\"></a>\n\n##### `once` method\n\nIdentical to the `on` method, but it only executes once. After the first event, the subscription is removed.\n\n**Accepted arguments:**\n\n* `type {String}` – The type of event being subscribed to. Required argument.\n* [`data {Object}`] – Additional data available as the value of the `e.data` field in the event object.\n* `fn {Function}` – The handler function to call for the event. Required argument.\n* [`ctx {Object}`] – Context for the handler function.\n\nReturns the `this` object.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on('myevent', function() { console.log('foo') });\n\n    myEmitter.emit('myevent'); // 'foo'\n    myEmitter.emit('myevent'); //handler isn't called\n});\n```\n\n<a name=\"fields-un\"></a>\n\n##### `un` method\n\nRemoves a previously set subscription to a specific type of event.\n\n**Accepted arguments:**\n\n* `type {String}` – The type of event being unsubscribed from. Required argument.\n* [`fn {Function}`] – The handler to delete.\n* [`ctx {Object}`] – The handler context.\n\nThe method returns a reference to the `this` object.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter(),\n        shout = function() { console.log('foo') };\n\n    myEmitter.on('myevent', shout);\n    myEmitter.emit('myevent'); // 'foo'\n\n    myEmitter.un('myevent', shout);\n    myEmitter.emit('myevent'); //handler isn't called\n});\n```\n\n<a name=\"fields-emit\"></a>\n\n##### `emit` method\n\nGenerates an event.\n\nThis method calls all the handler functions set for the event.\n\n**Accepted arguments:**\n\n* `type {String|events:Event}` – The event to generate, in the form of a string or a prepared event object. Required argument.\n* [`data {Object}`] – Additional data available as the second argument of the handler function.\n\nReturns the `this` object.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on('myevent', function(e, data) { console.log(data) });\n    myEmitter.emit('myevent', 'ololo');  // 'ololo'\n});\n```\n\n#### Static methods of the class\n\nThe set of static methods and their signatures is exactly the same as for the methods of the object being instantiated by the class.\n"
  },
  {
    "path": "common.blocks/events/events.ru.md",
    "content": "# events\n\nБлок предоставляет набор JS-классов, реализующий механизмы работы с событиями.\n\n## Обзор\n\n### Классы, предоставляемые блоком\n\n| Класс | Конструктор | Описание |\n| ----- | ----------- | -------- |\n| <a href=\"#class-Event\">Event</a> | Event(<br>`type {String}`, <br>`target {Object}`) | Служит для создания объекта события, изменения и проверки его состояний. |\n| <a href=\"#class-Emitter\">Emitter</a> | - | Служит для генерации событий и подписки на них. |\n\n### Свойства и методы объекта класса\n\n| Класс | Имя | Тип или возвращаемое значение | Описание |\n| ----- | --- | ----------------------------- | -------- |\n| <a href=\"#class-Event\">Event</a> | <a href=\"#fields-type\">type</a> | `String` | Тип события. |\n|  | <a href=\"#fields-result\">result</a> | `*` | Результат, возвращенный последним обработчиком события. |\n|  | <a href=\"#fields-target\">target</a> | `Object` | Объект на котором возникло событие. |\n|  | <a href=\"#fields-data\">data</a> | `*` | Данные, передаваемые как аргумент обработчику. |\n|  | <a href=\"#fields-preventDefault\">preventDefault</a>()| - | Позволяет предотвратить выполнение стандартного действия предусмотренного для события. |\n|  | <a href=\"#fields-isDefaultPrevented\">isDefaultPrevented</a>()| `Boolean` | Проверяет, было ли предотвращено выполнение стандартного действия, предусмотренного для события. |\n|  | <a href=\"#fields-stopPropagation\">stopPropagation</a>()| - | Позволяет остановить всплывание события. |\n|  | <a href=\"#fields-isPropagationStopped\">isPropagationStopped</a>()| `Boolean` | Проверяет, было ли остановлено всплывание события. |\n| <a href=\"#class-Emitter\">Emitter</a> | <a href=\"#fields-on\">on</a>(<br>`type {String}`, <br>`[data {Object}]`, <br>`fn {Function}`, <br>`[ {Object} ctx]`) | - | Служит для подписки на событие определенного типа. |\n|  | <a href=\"#fields-once\">once</a>(<br>`type {String}`, <br>`[data {Object}]`, <br>`fn {Function}`, <br>`[ctx {Object}]`) | - | Служит для подписки на событие определенного типа. Обработчик выполняется единожды. |\n|  | <a href=\"#fields-un\">un</a>(<br>`type {String}`, <br>`fn {Function}`, <br>`[ctx {Object}]`) | - | Служит для удаления подписки на событие определенного типа. |\n|  | <a href=\"#fields-emit\">emit</a>(<br>`type {String`&#124;`events:Event}`, <br>`[data {Object}]`) | - | Служит для генерации события. |\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| ------- | --------------------- | -------- |\n| <a href=\"#elems-channels\">channels</a> | `JS` | Предназначен для работы с именованными каналами событий. |\n\n### Функции предоставляемые элементами блока\n\n| Элемент | Функция | Тип возвращаемого значения | Описание |\n| ------- | ------- | ----------------------------- | -------- |\n| <a href=\"#elems-channels\">channels</a> | channels(<br>`[id {String}]`, <br>`[drop {Boolean}]`) | `Object`&#124;`undefined` | Создает или удаляет именованный канал событий. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `vanilla.js`\n\n## Описание\n\n<a name=\"class-Event\"></a>\n\n### Класс `Event`\n\nС помощью класса можно инстанцировать объект события, указав его тип и источник. Для этого нужно воспользоваться функцией-конструктором `Event`.\n\n**Принимаемые аргументы:**\n\n* `type {String}` – тип события. Обязательный аргумент.\n* `target {Object}` – объект (источник) на котором событие возникло. Обязательный аргумент.\n\n**Возвращаемое значение:** `Event`. Объект события.\n\n<a name=\"fields-Event\"></a>\n\n#### Свойства и методы объекта класса\n\n<a name=\"fields-type\"></a>\n\n##### Свойство `type`\n\nТип: `String`.\n\nТип события.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myevent = new events.Event('myevent', this);\n    console.log(myevent.type); // 'myevent'\n\n});\n```\n\n<a name=\"fields-type\"></a>\n\n##### Свойство `target`\n\nТип: `Object`.\n\nОбъект, на котором возникло событие.\n\n<a name=\"fields-result\"></a>\n\n##### Свойство `result`\n\nТип: `*`.\n\nСодержит данные, возвращаемые последней функцией-обработчиком события.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n    myEmitter.on('myevent', function() { return 'hi-hi-hi'; });\n\n    var myEvent = new events.Event('myevent');\n    myEmitter.emit(myEvent)\n\n    console.log(myEvent.result);    // 'hi-hi-hi'\n});\n```\n\n<a name=\"fields-data\"></a>\n\n##### Свойство `data`\n\nТип: `*`.\n\nСодержит данные, передаваемые функции-обработчику события в качестве аргумента.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n    myEmitter.on('myevent', 'my-data', function(e) { console.log(e.data); });\n\n    myEmitter.emit('myevent'); // my-data\n});\n```\n\n<a name=\"fields-preventDefault\"></a>\n\n##### Метод `preventDefault`\n\nПозволяет предотвратить выполнение стандартного действия предусмотренного для события.\n\nНе принимает аргументов.\n\nНе имеет возвращаемого значения.\n\n<a name=\"fields-isDefaultPrevented\"></a>\n\n##### Метод `isDefaultPrevented`\n\nПозволяет проверить было ли предотвращено выполнение стандартного действия для события.\n\nНе принимает аргументов.\n\n**Возвращаемое значение:** `Boolean`. В случае, если выполнение стандартного действия было предотвращено – `true`.\n\n<a name=\"fields-stopPropagation\"></a>\n\n##### Метод `stopPropagation`\n\nПозволяет остановить всплывание события.\n\nНе принимает аргументов.\n\nНе имеет возвращаемого значения.\n\n<a name=\"fields-isPropagationStopped\"></a>\n\n##### Метод `isPropagationStopped`\n\nПозволяет проверить, было ли остановлено всплывание события.\n\nНе принимает аргументов.\n\n**Возвращаемое значение:** `Boolean`. В случае, если всплывание события было остановлено – `true`.\n\n<a name=\"class-Emitter\"></a>\n\n### Класс `Emitter`\n\nКласс позволяет инстанцировать объекты, с помощью которых можно генерировать события и осуществлять подписку на них.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n});\n```\n\n<a name=\"fields-Event\"></a>\n\n#### Свойства и методы объекта класса\n\n<a name=\"fields-on\"></a>\n\n##### Метод `on`\n\nСлужит для подписки на событие определенного типа.\n\n**Принимаемые аргументы:**\n\n* `type {String}` – тип события, на которое производится подписка. Обязательный аргумент.\n* [`data {Object}`] – дополнительные данные, доступные обработчику как значение поля `e.data` объекта события.\n* `fn {Function}` – функция-обработчик, вызываемая для события. Обязательный аргумент.\n* [`ctx {Object}`] – контекст функции-обработчика.\n\nВозвращает объект `this`.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on('myevent', function() { console.log('foo'); });\n    myEmitter.emit('myevent'); // 'foo'\n});\n```\n\nКроме того, значением аргумента `type` могут быть:\n\n* несколько типов событий, перечисленных через пробел – чтобы установить для них общую функцию-обработчик;\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on('myevent1 myevent2', function(e) { console.log(e.type) });\n\n    myEmitter.emit('myevent1'); // 'myevent1'\n    myEmitter.emit('myevent2'); // 'myevent2'\n});\n```\n\n* хеш вида `{ 'событие-1' : обработчик-1, ... , 'событие-n' : обработчик-n }` – чтобы установить сразу несколько обработчиков для разных типов событий;\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on({\n        myevent1 : function(e) { console.log(e.type) },\n        myevent2 : function(e) { console.log(e.type) }\n    });  \n\n    myEmitter.emit('myevent1'); // 'myevent1'\n    myEmitter.emit('myevent2'); // 'myevent2'\n});\n```\n\nСказанное выше верно и для методов `once` и `un`.\n\n<a name=\"fields-once\"></a>\n\n##### Метод `once`\n\nИдентичен методу `on`, но выполняется единожды – после первого события подписка удаляется.\n\n**Принимаемые аргументы:**\n\n* `type {String}` – тип события, на которое производится подписка. Обязательный аргумент.\n* [`data {Object}`] – дополнительные данные, доступные как значение поля `e.data` объекта события.\n* `fn {Function}` – функция-обработчик, вызываемая для события. Обязательный аргумент.\n* [`ctx {Object}`] – контекст функции-обработчика.\n\nВозвращает объект `this`.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on('myevent', function() { console.log('foo') });\n\n    myEmitter.emit('myevent'); // 'foo'\n    myEmitter.emit('myevent'); // обработчик не вызывается\n});\n```\n\n<a name=\"fields-un\"></a>\n\n##### Метод `un`\n\nСлужит для удаления установленной ранее подписки на событие определенного типа.\n\n**Принимаемые аргументы:**\n\n* `type {String}` – тип события, подписка на которое удаляется. Обязательный аргумент.\n* [`fn {Function}`] – удаляемый обработчик.\n* [`ctx {Object}`] – контекст обработчика.\n\nМетод возвращает ссылку на объект `this`.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter(),\n        shout = function() { console.log('foo') };\n\n    myEmitter.on('myevent', shout);\n    myEmitter.emit('myevent'); // 'foo'\n\n    myEmitter.un('myevent', shout);\n    myEmitter.emit('myevent'); // обработчик не вызывается\n});\n```\n\n<a name=\"fields-emit\"></a>\n\n##### Метод `emit`\n\nСлужит для генерации события.\n\nМетод вызывает все функции-обработчики, заданные для события.\n\n**Принимаемые аргументы:**\n\n* `type {String|events:Event}` – генерируемое событие в виде строки или готового объекта события. Обязательный аргумент.\n* [`data {Object}`] – дополнительные данные, доступные как второй аргумент функции-обработчика.\n\nВозвращает объект `this`.\n\n```js\nmodules.require(['events'], function(events) {\n\n    var myEmitter = new events.Emitter();\n\n    myEmitter.on('myevent', function(e, data) { console.log(data) });\n    myEmitter.emit('myevent', 'ololo');  // 'ololo'\n});\n```\n\n#### Статические методы класса\n\nНабор и сигнатуры статических методов идентичны набору и сигнатурам методов объекта, инстанцируемого классом.\n"
  },
  {
    "path": "common.blocks/events/events.spec.js",
    "content": "modules.define('spec', [\n    'events',\n    'sinon'\n], function(provide,\n    events,\n    sinon\n) {\n\ndescribe('events', function() {\n    describe('Emitter', function() {\n        var emitter;\n        beforeEach(function() {\n            emitter = new events.Emitter();\n        });\n\n        describe('on/emit', function() {\n            it('should call callbacks according to the type of event', function() {\n                var spy1 = sinon.spy(),\n                    spy1_1 = sinon.spy(),\n                    spy2 = sinon.spy();\n\n                emitter\n                    .on('event1', spy1)\n                    .on('event1', spy1_1)\n                    .on('event2', spy2)\n                    .emit('event1');\n\n                spy1.should.have.been.calledOnce;\n                spy1_1.should.have.been.calledOnce;\n                spy2.should.not.have.been.called;\n\n                emitter.emit('event2');\n                spy2.should.have.been.calledOnce;\n\n                emitter.emit('event1');\n                spy1.should.have.been.calledTwice;\n                spy1_1.should.have.been.calledTwice;\n            });\n\n            it('should call callbacks according to all types of event', function() {\n                var spy = sinon.spy();\n\n                emitter\n                    .on('event1 event2', spy)\n                    .emit('event1');\n                spy.should.have.been.calledOnce;\n\n                emitter.emit('event2');\n                spy.should.have.been.calledTwice;\n            });\n\n            it('should call callbacks for all types of event', function() {\n                var spy = sinon.spy();\n\n                emitter\n                    .on('*', spy)\n                    .emit('event1');\n                spy.should.have.been.calledOnce;\n\n                emitter.emit('event2');\n                spy.should.have.been.calledTwice;\n\n                emitter.emit('event3');\n                spy.should.have.been.calledThrice;\n            });\n\n            it('should call callback with given context', function() {\n                var spy = sinon.spy(),\n                    ctx = {};\n\n                emitter\n                    .on('event', spy, ctx)\n                    .emit('event');\n\n                spy.should.have.been.calledOn(ctx);\n            });\n\n            it('should pass event to callback', function() {\n                var spy = sinon.spy(),\n                    data = { data : 'ok' };\n\n                emitter\n                    .on('event', spy)\n                    .emit('event', data);\n\n                var event = spy.args[0][0];\n                event.should.be.instanceOf(events.Event);\n                event.type.should.be.equal('event');\n            });\n\n            it('should pass additional data to callback', function() {\n                var spy = sinon.spy(),\n                    data = { data : 'ok' };\n\n                emitter\n                    .on('event', spy)\n                    .emit('event', data);\n\n                spy.args[0][1].should.be.equal(data);\n            });\n\n            it('should allow to Event instance to be passed', function() {\n                var spy = sinon.spy(),\n                    e = new events.Event('event');\n\n                emitter\n                    .on('event', spy)\n                    .emit(e);\n\n                spy.args[0][0].should.be.equal(e);\n            });\n\n            it('should set default target', function() {\n                var spy = sinon.spy();\n\n                emitter\n                    .on('event', spy)\n                    .emit('event');\n\n                spy.args[0][0].target.should.be.equal(emitter);\n            });\n\n            it('should pass custom target', function() {\n                var spy = sinon.spy(),\n                    target = {},\n                    e = new events.Event('event', target);\n\n                emitter\n                    .on('event', spy)\n                    .emit(e);\n\n                spy.args[0][0].target.should.be.equal(target);\n            });\n\n            it('should call stopPropagation and preventDefault if callback returns false', function() {\n                var e = new events.Event('event');\n                emitter\n                    .on('event', function() {\n                        return false;\n                    })\n                    .emit(e);\n\n                e.isPropagationStopped().should.be.true;\n                e.isDefaultPrevented().should.be.true;\n            });\n\n            it('should not immediately call callback that was binded in callback', function() {\n                var spy = sinon.spy();\n\n                emitter\n                    .on('event', function() {\n                        emitter.on('event', spy);\n                    })\n                    .emit('event');\n\n                spy.should.not.have.been.called;\n\n                emitter.emit('event');\n                spy.should.have.been.called;\n            });\n        });\n\n        describe('once/emit', function() {\n            it('should call callback once', function() {\n                var spy = sinon.spy();\n\n                emitter\n                    .once('event', spy)\n                    .emit('event')\n                    .emit('event')\n                    .emit('event');\n\n                spy.should.have.been.calledOnce;\n            });\n        });\n\n        describe('un/emit', function() {\n            it('should unbind given callback according to the type of event', function() {\n                var spy1 = sinon.spy(),\n                    spy2 = sinon.spy();\n\n                emitter\n                    .on('event', spy1)\n                    .on('event2', spy1)\n                    .on('event', spy2)\n                    .un('event', spy1)\n                    .emit('event');\n\n                spy1.should.not.have.been.called;\n                spy2.should.have.been.called;\n\n                emitter.emit('event2');\n                spy1.should.have.been.called;\n            });\n\n            it('should unbind given callback according to the type of all given events', function() {\n                var spy = sinon.spy();\n\n                emitter\n                    .on('event', spy)\n                    .on('event2', spy)\n                    .un('event event2', spy)\n                    .emit('event')\n                    .emit('event2');\n\n                spy.should.not.have.been.called;\n            });\n\n            it('should unbind given callback according to the type of event and context', function() {\n                var spy = sinon.spy(),\n                    ctx1 = {},\n                    ctx2 = {};\n\n                emitter\n                    .on('event', spy, ctx1)\n                    .on('event', spy, ctx2)\n                    .on('event', spy)\n                    .un('event', spy, ctx1)\n                    .emit('event');\n\n                spy.should.have.been.calledTwice;\n            });\n\n            it('should unbind all callbacks according to the type of event', function() {\n                var spy1 = sinon.spy(),\n                    spy2 = sinon.spy();\n\n                emitter\n                    .on('event', spy1)\n                    .on('event2', spy1)\n                    .on('event', spy2)\n                    .un('event')\n                    .emit('event');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n\n                emitter.emit('event2');\n                spy1.should.have.been.called;\n            });\n\n            it('should unbind all callbacks', function() {\n                var spy1 = sinon.spy(),\n                    spy2 = sinon.spy();\n\n                emitter\n                    .on('event', spy1)\n                    .on('event2', spy1)\n                    .on('event', spy2)\n                    .un()\n                    .emit('event');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n\n                emitter.emit('event2');\n                spy1.should.not.have.been.called;\n            });\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/events/events.vanilla.js",
    "content": "/**\n * @module events\n */\n\nimport identify from 'bem:identify'\nimport inherit from 'bem:inherit'\nimport functions from 'bem:functions'\n\nconst storageExpando = '__' + (+new Date) + 'storage'\n\n/**\n * @class Event\n * @exports events:Event\n */\nconst Event = inherit(/** @lends Event.prototype */{\n    /**\n     * @constructor\n     * @param {String} type\n     * @param {Object} target\n     */\n    __constructor : function(type, target) {\n        /**\n         * Type\n         * @member {String}\n         */\n        this.type = type\n\n        /**\n         * Target\n         * @member {Object}\n         */\n        this.target = target\n\n        /**\n         * Data\n         * @member {*}\n         */\n        this.data = undefined\n\n        this._isDefaultPrevented = false\n        this._isPropagationStopped = false\n    },\n\n    /**\n     * Prevents default action\n     */\n    preventDefault : function() {\n        this._isDefaultPrevented = true\n    },\n\n    /**\n     * Returns whether is default action prevented\n     * @returns {Boolean}\n     */\n    isDefaultPrevented : function() {\n        return this._isDefaultPrevented\n    },\n\n    /**\n     * Stops propagation\n     */\n    stopPropagation : function() {\n        this._isPropagationStopped = true\n    },\n\n    /**\n     * Returns whether is propagation stopped\n     * @returns {Boolean}\n     */\n    isPropagationStopped : function() {\n        return this._isPropagationStopped\n    }\n})\n\n/**\n * @class Emitter\n * @exports events:Emitter\n */\nconst Emitter = inherit(/** @lends Emitter.prototype */{\n    /**\n     * Adds an event handler\n     * @param {String} e Event type\n     * @param {Object} [data] Additional data that the handler gets as e.data\n     * @param {Function} fn Handler\n     * @param {Object} [ctx] Handler context\n     * @returns {Emitter} this\n     */\n    on : function(e, data, fn, ctx, _special) {\n        if(typeof e === 'string') {\n            if(functions.isFunction(data)) {\n                ctx = fn\n                fn = data\n                data = undefined\n            }\n\n            const id = identify(fn, ctx)\n            const storage = this[storageExpando] || (this[storageExpando] = {})\n            const eventTypes = e.split(' ')\n\n            for(const eventType of eventTypes) {\n                const eventStorage = storage[eventType] || (storage[eventType] = { ids : new Map(), list : {} })\n                if(!eventStorage.ids.has(id)) {\n                    const list = eventStorage.list\n                    const item = { fn, data, ctx, special : _special }\n                    if(list.last) {\n                        list.last.next = item\n                        item.prev = list.last\n                    } else {\n                        list.first = item\n                    }\n                    eventStorage.ids.set(id, item)\n                    list.last = item\n                }\n            }\n        } else {\n            for(const [key, val] of Object.entries(e)) {\n                this.on(key, val, data, _special)\n            }\n        }\n\n        return this\n    },\n\n    /**\n     * Adds a one time handler for the event.\n     * Handler is executed only the next time the event is fired, after which it is removed.\n     * @param {String} e Event type\n     * @param {Object} [data] Additional data that the handler gets as e.data\n     * @param {Function} fn Handler\n     * @param {Object} [ctx] Handler context\n     * @returns {Emitter} this\n     */\n    once : function(e, data, fn, ctx) {\n        return this.on(e, data, fn, ctx, { once : true })\n    },\n\n    /**\n     * Removes event handler or handlers\n     * @param {String} [e] Event type\n     * @param {Function} [fn] Handler\n     * @param {Object} [ctx] Handler context\n     * @returns {Emitter} this\n     */\n    un : function(e, fn, ctx) {\n        if(typeof e === 'string' || typeof e === 'undefined') {\n            const storage = this[storageExpando]\n            if(storage) {\n                if(e) { // if event type was passed\n                    const eventTypes = e.split(' ')\n                    for(const eventType of eventTypes) {\n                        const eventStorage = storage[eventType]\n                        if(eventStorage) {\n                            if(fn) {  // if specific handler was passed\n                                const id = identify(fn, ctx)\n                                const ids = eventStorage.ids\n                                if(ids.has(id)) {\n                                    const list = eventStorage.list\n                                    const item = ids.get(id)\n                                    const prev = item.prev\n                                    const next = item.next\n\n                                    if(prev) {\n                                        prev.next = next\n                                    } else if(item === list.first) {\n                                        list.first = next\n                                    }\n\n                                    if(next) {\n                                        next.prev = prev\n                                    } else if(item === list.last) {\n                                        list.last = prev\n                                    }\n\n                                    ids.delete(id)\n                                }\n                            } else {\n                                delete this[storageExpando][eventType]\n                            }\n                        }\n                    }\n                } else {\n                    delete this[storageExpando]\n                }\n            }\n        } else {\n            for(const [key, val] of Object.entries(e)) {\n                this.un(key, val, fn)\n            }\n        }\n\n        return this\n    },\n\n    /**\n     * Fires event handlers\n     * @param {String|events:Event} e Event\n     * @param {Object} [data] Additional data\n     * @returns {Emitter} this\n     */\n    emit : function(e, data) {\n        const storage = this[storageExpando]\n        let eventInstantiated = false\n\n        if(storage) {\n            const eventTypes = [typeof e === 'string'? e : e.type, '*']\n            for(const eventType of eventTypes) {\n                const eventStorage = storage[eventType]\n                if(eventStorage) {\n                    let item = eventStorage.list.first\n                    const lastItem = eventStorage.list.last\n                    while(item) {\n                        if(!eventInstantiated) { // instantiate Event only on demand\n                            eventInstantiated = true\n                            typeof e === 'string' && (e = new Event(e))\n                            e.target || (e.target = this)\n                        }\n\n                        e.data = item.data\n                        const res = item.fn.call(item.ctx || this, e, data)\n                        if(res === false) {\n                            e.preventDefault()\n                            e.stopPropagation()\n                        }\n\n                        item.special && item.special.once &&\n                            this.un(e.type, item.fn, item.ctx)\n\n                        if(item === lastItem) {\n                            break\n                        }\n\n                        item = item.next\n                    }\n                }\n            }\n        }\n\n        return this\n    }\n})\n\nexport default { Emitter, Event }\n"
  },
  {
    "path": "common.blocks/functions/__debounce/functions__debounce.spec.js",
    "content": "modules.define('spec', ['functions__debounce'], function(provide, debounce) {\n\ndescribe('functions__debounce', function() {\n    it('should properly debounce given function', function(done) {\n        var res = [],\n            debouncedFn = debounce(\n                function(arg) {\n                    res.push(arg);\n                },\n                50);\n\n        debouncedFn(1);\n        debouncedFn(2);\n        debouncedFn(3);\n        setTimeout(function() {\n            debouncedFn(4);\n            debouncedFn(5);\n        }, 25);\n        setTimeout(function() {\n            debouncedFn(6);\n            debouncedFn(7);\n        }, 220);\n        setTimeout(function() {\n            res.should.be.eql([5, 7]);\n            done();\n        }, 400);\n    });\n\n    it('should properly debounce given function according \"invokeAsap\" param', function(done) {\n        var res = [],\n            debouncedFn = debounce(\n                function(arg) {\n                    res.push(arg);\n                },\n                50,\n                true);\n\n        debouncedFn(1);\n        debouncedFn(2);\n        debouncedFn(3);\n        setTimeout(function() {\n            debouncedFn(4);\n            debouncedFn(5);\n        }, 25);\n        setTimeout(function() {\n            debouncedFn(6);\n            debouncedFn(7);\n        }, 230);\n        setTimeout(function() {\n            res.should.be.eql([1, 6]);\n            done();\n        }, 400);\n    });\n\n    it('should call debounced function with given \"ctx\" param', function(done) {\n        var ctx = {},\n            debouncedFn = debounce(\n                function() {\n                    this.should.be.eql(ctx);\n                    done();\n                },\n                20,\n                ctx);\n\n        debouncedFn();\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/functions/__debounce/functions__debounce.vanilla.js",
    "content": "/**\n * @module functions__debounce\n */\n\nexport default\n    /**\n     * Debounces given function\n     * @param {Function} fn function to debounce\n     * @param {Number} timeout debounce interval\n     * @param {Boolean} [invokeAsap=false] invoke before first interval\n     * @param {Object} [ctx] context of function invocation\n     * @returns {Function} debounced function\n     */\n    function(fn, timeout, invokeAsap, ctx) {\n        if(arguments.length === 3 && typeof invokeAsap !== 'boolean') {\n            ctx = invokeAsap\n            invokeAsap = false\n        }\n\n        let timer\n        return function() {\n            const args = arguments\n            ctx || (ctx = this)\n\n            invokeAsap && !timer && fn.apply(ctx, args)\n\n            globalThis.clearTimeout(timer)\n\n            timer = globalThis.setTimeout(() => {\n                invokeAsap || fn.apply(ctx, args)\n                timer = null\n            }, timeout)\n        }\n    }\n"
  },
  {
    "path": "common.blocks/functions/__throttle/functions__throttle.spec.js",
    "content": "modules.define('spec', ['functions__throttle'], function(provide, throttle) {\n\ndescribe('functions__throttle', function() {\n    it('should properly throttle given function', function(done) {\n        var res = [],\n            throttledFn = throttle(\n                function(arg) {\n                    res.push(arg);\n                },\n                20);\n\n        throttledFn(1);\n        throttledFn(2);\n        throttledFn(3);\n        setTimeout(function() {\n            throttledFn(4);\n        }, 10);\n        setTimeout(function() {\n            throttledFn(5);\n            res.should.be.eql([1, 4]);\n            done();\n        }, 30);\n    });\n\n    it('should properly throttle given function according \"invokeAsap\" param', function(done) {\n        var res = [],\n            throttledFn = throttle(\n                function(arg) {\n                    res.push(arg);\n                },\n                20,\n                false);\n\n        throttledFn(1);\n        throttledFn(2);\n        throttledFn(3);\n        setTimeout(function() {\n            throttledFn(4);\n        }, 10);\n        setTimeout(function() {\n            throttledFn(5);\n            setTimeout(function() {\n                res.should.be.eql([4, 5]);\n                done();\n            }, 30);\n        }, 30);\n    });\n\n    it('should call throttled function with given \"ctx\" param', function(done) {\n        var ctx = {},\n            throttledFn = throttle(\n                function() {\n                    this.should.be.eql(ctx);\n                    done();\n                },\n                20,\n                ctx);\n\n        throttledFn();\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/functions/__throttle/functions__throttle.vanilla.js",
    "content": "/**\n * @module functions__throttle\n */\n\nexport default\n    /**\n     * Throttle given function\n     * @param {Function} fn function to throttle\n     * @param {Number} timeout throttle interval\n     * @param {Boolean} [invokeAsap=true] invoke before first interval\n     * @param {Object} [ctx] context of function invocation\n     * @returns {Function} throttled function\n     */\n    function(fn, timeout, invokeAsap, ctx) {\n        const typeofInvokeAsap = typeof invokeAsap\n        if(typeofInvokeAsap === 'undefined') {\n            invokeAsap = true\n        } else if(arguments.length === 3 && typeofInvokeAsap !== 'boolean') {\n            ctx = invokeAsap\n            invokeAsap = true\n        }\n\n        let timer, args, needInvoke\n        const wrapper = function() {\n                if(needInvoke) {\n                    fn.apply(ctx, args)\n                    needInvoke = false\n                    timer = globalThis.setTimeout(wrapper, timeout)\n                } else {\n                    timer = null\n                }\n            }\n\n        return function() {\n            args = arguments\n            ctx || (ctx = this)\n            needInvoke = true\n\n            if(!timer) {\n                invokeAsap?\n                    wrapper() :\n                    timer = globalThis.setTimeout(wrapper, timeout)\n            }\n        }\n    }\n"
  },
  {
    "path": "common.blocks/functions/functions.en.md",
    "content": "# functions\n\nThis block provides an object with a set of methods for working with JavaScript functions.\n\n## Overview\n\n### Properties and methods of the object\n\n| Name | Type or return value | Description |\n| -------- | --- | -------- |\n| <a href=\"#fields-isFunction\">isFunction</a>(`obj {*}`) |  `Boolean` | Checks whether a passed argument is a function. |\n| <a href=\"#fields-noop\">noop</a> | `Function` | Empty function. |\n\n### Elements of the block\n\n| Element |  Usage | Description |\n| --------| ---- | -------- |\n| <a href=\"#elems-debounce\">debounce</a> | `JS`  | Function decorator that combines multiple function calls within a specified time period into one call. |\n| <a href=\"#elems-throttle\">throttle</a> | `JS` | Function decorator that limits the frequency of function execution to once per specified period. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `vanilla.js`\n\n## Description\n\n<a name=\"fields\"></a>\n\n### Properties and methods of the object\n\n<a name=\"fields-isFunction\"></a>\n\n#### `isFunction` method\n\nChecks whether a passed argument is a function.  \n\n**Accepted arguments:**\n\n* `obj {*}` – The object being checked. Required argument.\n\n**Return value:** `Boolean`. If the argument is a function, then `true`.\n\n```js\nmodules.require('functions', function(func) {\n    var a = function(){},\n        b = {};\n    console.log(func.isFunction(a)); //true\n    console.log(func.isFunction(b)); //false\n});\n```\n\n<a name=\"fields-noop\"></a>\n\n#### `noop` property\n\nEmpty function (`function() {}`).\n\nNo arguments or return value.\n\nYou can use `noop` when you need a function but there isn't a reason to add the logic. For example, you can use it as a placeholder for base classes at the design stage when using OOP.\n\nExample:\n\n```js\nmodules.define('base-class', ['inherit', 'functions'], function(provide, inherit, functions) {\n\nprovide(inherit({\n    getData : function() {\n        this._sendRequest();\n    },\n\n    _sendRequest : functions.noop\n\n}));\n\n});\n```\n\n<a name=\"elems\"></a>\n\n### Elements of the block\n\nThe block elements implement a set of function decorators.\n\nThe decorators add logic to the function without changing its original signature.\n\n<a name=\"elems-debounce\"></a>\n\n#### `debounce` element\n\nA decorator that postpones function calls for the specified delay time. After each attempt to make a call, the delay starts over again.\n\n**Accepted arguments:**\n\n* `fn {Function}` — Original function. Required argument.\n* `timeout {Number}` — Time of delay, in milliseconds. Required argument.\n* [`invokeAsap {Boolean}`] — The `debounce` mode. By default, the first mode is used (corresponding to the `false` value).\n* [`context {Object}`] — The context for executing the original function.\n\nThere are two `debounce` modes, depending on the value of `invokeAsap`:\n\n1. The original function is called when the delay expires after the last call attempt.\n2. The original function is first called as soon as the decorated function is called. After this, the behavior is the same as in the first mode.\n\n**Return value:** `Function`. The decorated function.\n\nExample:\n\n```js\nmodules.require('functions__debounce', function(provide, debounce) {\n\n    function log() {\n        console.log('hello!');\n    }\n\n    var debouncedLog = debounce(log, 300);\n    setInterval(debouncedLog, 50);\n\n});\n```\n\n<a name=\"elems-throttle\"></a>\n\n#### `throttle` element\n\nThis decorator allows you to \"slow down\" the function. It won't be executed more than once during the specified period, no matter how many times it is called during this time. All calls in the meantime are ignored.\n\n**Accepted arguments:**\n\n* `fn {Function}` — Original function. Required argument.\n* `period {Number}` — The interval between calls, in milliseconds. Required argument.\n* [`context {Object}`] — The context for executing the original function.\n\n**Return value:** `Function`. The decorated function.\n\nThis method is convenient for setting resource-intensive handlers for frequently generated events, such as `resize`, `pointermove`, and so on.\n\nExample:\n\n```js\nmodules.require('functions__throttle', function(provide, throttle) {\n\n    function log() {\n        console.log('hello!');\n    }\n\n    var throttledLog = throttle(log, 300);\n    setInterval(throttledLog, 50);\n\n});\n```\n\nAs a result, the function is executed no more than once every 300 milliseconds.\n"
  },
  {
    "path": "common.blocks/functions/functions.ru.md",
    "content": "# functions\n\nБлок предоставляет объект, содержащий набор методов для работы с функциями JavaScript.\n\n## Обзор\n\n### Свойства и методы объекта\n\n| Имя | Тип или возвращаемое значение | Описание |\n| -------- | --- | -------- |\n| <a href=\"#fields-isFunction\">isFunction</a>(`obj {*}`) |  `Boolean` | Проверяет, является ли переданный аргумент функцией. |\n| <a href=\"#fields-noop\">noop</a> | `Function` | Пустая функция. |\n\n### Элементы блока\n\n| Элемент |  Способы использования | Описание |\n| --------| ---- | -------- |\n| <a href=\"#elems-debounce\">debounce</a> | `JS`  | Декоратор функции. Объединяет несколько вызовов функции, производимых в заданном временном интервале, в один. |\n| <a href=\"#elems-throttle\">throttle</a> | `JS` | Декоратор функции. Ограничивает частоту выполнения функции до одного раза в указанный период. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `vanilla.js`\n\n## Описание\n\n<a name=\"fields\"></a>\n\n### Свойства и методы объекта\n\n<a name=\"fields-isFunction\"></a>\n\n#### Метод `isFunction`\n\nМетод проверяет, является ли переданный аргумент функцией.  \n\n**Принимаемые аргументы:**\n\n* `obj {*}` – проверяемый объект. Обязательный аргумент.\n\n**Возвращаемое значение:** `Boolean`. В случае, если аргумент является функцией – `true`.\n\n```js\nmodules.require('functions', function(func) {\n    var a = function(){},\n        b = {};\n    console.log(func.isFunction(a)); // true\n    console.log(func.isFunction(b)); // false\n});\n```\n\n<a name=\"fields-noop\"></a>\n\n#### Свойство `noop`\n\nПустая функция (`function() {}`).\n\nНе имеет аргументов и возвращаемого значения.\n\n`noop` можно использовать в ситуациях, когда для работы требуется функция, но нет смысла добавлять логику. Например в качестве «заглушки» для базовых классов при проектировании в парадигме ООП.\n\nПример:\n\n```js\nmodules.define('base-class', ['inherit', 'functions'], function(provide, inherit, functions) {\n\nprovide(inherit({\n    getData : function() {\n        this._sendRequest();\n    },\n\n    _sendRequest : functions.noop\n\n}));\n\n});\n```\n\n<a name=\"elems\"></a>\n\n### Элементы блока\n\nЭлементы блока реализуют набор декораторов функций.\n\nДекораторы добавляют функции логику, не меняя ее оригинальной сигнатуры.\n\n<a name=\"elems-debounce\"></a>\n\n#### Элемент `debounce`\n\nДекоратор, откладывающий вызовов функции до истечения задержки. После каждой попытки вызова задержка начинает отсчитываться заново.\n\n**Принимаемые аргументы:**\n\n* `fn {Function}` — оригинальная функция. Обязательный аргумент.\n* `timeout {Number}` — время задержки в миллисекундах. Обязательный аргумент.\n* [`invokeAsap {Boolean}`] — режим работы `debounce`. По умолчанию используется первый режим (соответствует значению `false`).\n* [`context {Object}`] — контекст для выполнения оригинальной функции.\n\nВ зависимости от значения аргумента `invokeAsap` `debounce` может работать в двух режимах:\n\n1. Вызов оригинальной функции производится по истечению задержки после последней попытки вызова.\n2. Первый вызов оригинальной функции производится сразу же при вызове декорированной функции. Дальнейшее поведение аналогично режиму 1.\n\n**Возвращаемое значение:** `Function`. Декорированная функция.\n\nПример:\n\n```js\nmodules.require('functions__debounce', function(provide, debounce) {\n\n    function log() {\n        console.log('hello!');\n    }\n\n    var debouncedLog = debounce(log, 300);\n    setInterval(debouncedLog, 50);\n\n});\n```\n\n<a name=\"elems-throttle\"></a>\n\n#### Элемент `throttle`\n\nДекоратор позволяет «затормозить» функцию. Она будет выполняться не чаще одного раза в указанный период, сколько бы раз в течение этого периода ни была вызвана. Все промежуточные вызовы игнорируются.\n\n**Принимаемые аргументы:**\n\n* `fn {Function}` — оригинальная функция. Обязательный аргумент.\n* `period {Number}` — интервал между вызовами в миллисекундах. Обязательный аргумент.\n* [`context {Object}`] — контекст для выполнения оригинальной функции.\n\n**Возвращаемое значение:** `Function`. Декорированная функция.\n\nМетод удобно использовать, например, для установки ресурсоемких обработчиков для часто генерируемых событий – `resize`, `pointermove` и т.п.\n\nПример:\n\n```js\nmodules.require('functions__throttle', function(provide, throttle) {\n\n    function log() {\n        console.log('hello!');\n    }\n\n    var throttledLog = throttle(log, 300);\n    setInterval(throttledLog, 50);\n\n});\n```\n\nВ результате, функция будет выполняться не чаще чем раз в 300 миллисекунд.\n"
  },
  {
    "path": "common.blocks/functions/functions.spec.js",
    "content": "modules.define('spec', ['functions'], function(provide, functions) {\n\ndescribe('functions', function() {\n    describe('isFunction', function() {\n        it('should returns true only for function', function() {\n            functions.isFunction({}).should.be.false;\n            functions.isFunction(null).should.be.false;\n            functions.isFunction(5).should.be.false;\n            functions.isFunction().should.be.false;\n            functions.isFunction('').should.be.false;\n            functions.isFunction([]).should.be.false;\n            functions.isFunction(new function() {}).should.be.false;\n\n            functions.isFunction(function() {}).should.be.true;\n        });\n    });\n\n    describe('noop', function() {\n        it('should be a function', function() {\n            functions.isFunction(functions.noop).should.be.true;\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/functions/functions.vanilla.js",
    "content": "/**\n * @module functions\n * @description A set of helpers to work with JavaScript functions\n */\n\nexport default {\n  /**\n   * Checks whether a given object is function\n   * @param {*} obj\n   * @returns {Boolean}\n   */\n  isFunction(obj) {\n    // In some browsers, typeof returns \"function\" for HTML <object> elements\n    // (i.e., `typeof document.createElement( \"object\" ) === \"function\"`).\n    // We don't want to classify *any* DOM node as a function.\n    return typeof obj === 'function' && typeof obj.nodeType !== 'number'\n  },\n\n  /**\n   * Empty function\n   */\n  noop() {}\n}\n"
  },
  {
    "path": "common.blocks/i-bem/__collection/i-bem__collection.js",
    "content": "/**\n * @module i-bem__collection\n */\nimport inherit from 'bem:inherit'\n\n/**\n * @class BemCollection\n */\nconst BemCollection = inherit(/** @lends BemCollection.prototype */{\n    /**\n     * @constructor\n     * @param {Array} entities BEM entities\n     */\n    __constructor(entities) {\n        const _entities = this._entities = []\n        const uniq = {}\n        ;(Array.isArray(entities) ? entities : [...arguments]).forEach((entity) => {\n            if(!uniq[entity._uniqId]) {\n                uniq[entity._uniqId] = true\n                _entities.push(entity)\n            }\n        })\n    },\n\n    /**\n     * Sets the modifier for entities in Collection.\n     * @param {String} modName Modifier name\n     * @param {String|Boolean} [modVal=true] Modifier value. If not of type String or Boolean, it is casted to String\n     * @returns {Collection} this\n     */\n    setMod : buildForEachEntityMethodProxyFn('setMod'),\n\n    /**\n     * Sets multiple modifiers at once for entities in Collection.\n     * @param {Object} mods Hash of modifiers (modName: modVal)\n     * @returns {Collection} this\n     */\n    setMods : buildForEachEntityMethodProxyFn('setMods'),\n\n    /**\n     * Removes the modifier from entities in Collection.\n     * @param {String} modName Modifier name\n     * @returns {Collection} this\n     */\n    delMod : buildForEachEntityMethodProxyFn('delMod'),\n\n    /**\n     * Sets a modifier for entities in Collection, depending on conditions.\n     * If the condition parameter is passed: when true, modVal1 is set; when false, modVal2 is set.\n     * If the condition parameter is not passed: modVal1 is set if modVal2 was set, or vice versa.\n     * @param {String} modName Modifier name\n     * @param {String} modVal1 First modifier value\n     * @param {String} [modVal2] Second modifier value\n     * @param {Boolean} [condition] Condition\n     * @returns {Collection} this\n     */\n    toggleMod : buildForEachEntityMethodProxyFn('toggleMod'),\n\n    /**\n     * Checks whether every entity in Collection has a modifier.\n     * @param {String} modName Modifier name\n     * @param {String|Boolean} [modVal] Modifier value. If not of type String or Boolean, it is casted to String\n     * @returns {Boolean}\n     */\n    everyHasMod : buildComplexProxyFn('every', 'hasMod'),\n\n    /**\n     * Checks whether some entities in Collection has a modifier.\n     * @param {String} modName Modifier name\n     * @param {String|Boolean} [modVal] Modifier value. If not of type String or Boolean, it is casted to String\n     * @returns {Boolean}\n     */\n    someHasMod : buildComplexProxyFn('some', 'hasMod'),\n\n    /**\n     * Returns entity by index.\n     * @param {Number} i Index\n     * @returns {BemEntity}\n     */\n    get(i) {\n        return this._entities[i]\n    },\n\n    /**\n     * Calls callback once for each entity in collection.\n     * @param {Function} fn Callback\n     * @param {Object} ctx Callback context\n     */\n    forEach : buildEntitiesMethodProxyFn('forEach'),\n\n    /**\n     * Creates an array with the results of calling callback on every entity in collection.\n     * @param {Function} fn Callback\n     * @param {Object} ctx Callback context\n     * @returns {Array}\n     */\n    map : buildEntitiesMethodProxyFn('map'),\n\n    /**\n     * Applies callback against an accumulator and each entity in collection (from left-to-right)\n     * to reduce it to a single value.\n     * @param {Function} fn Callback\n     * @param {Object} [initial] Initial value\n     * @returns {Array}\n     */\n    reduce : buildEntitiesMethodProxyFn('reduce'),\n\n    /**\n     * Applies callback against an accumulator and each entity in collection (from right-to-left)\n     * to reduce it to a single value.\n     * @param {Function} fn Callback\n     * @param {Object} [initial] Initial value\n     * @returns {Array}\n     */\n    reduceRight : buildEntitiesMethodProxyFn('reduceRight'),\n\n    /**\n     * Creates a new collection with all entities that pass the test implemented by the provided callback.\n     * @param {Function} fn Callback\n     * @param {Object} ctx Callback context\n     * @returns {Collection}\n     */\n    filter(...args) {\n        return new this.__self(buildEntitiesMethodProxyFn('filter').apply(this, args))\n    },\n\n    /**\n     * Tests whether some entities in the collection passes the test implemented by the provided callback.\n     * @param {Function} fn Callback\n     * @param {Object} ctx Callback context\n     * @returns {Boolean}\n     */\n    some : buildEntitiesMethodProxyFn('some'),\n\n    /**\n     * Tests whether every entities in the collection passes the test implemented by the provided callback.\n     * @param {Function} fn Callback\n     * @param {Object} ctx Callback context\n     * @returns {Boolean}\n     */\n    every : buildEntitiesMethodProxyFn('every'),\n\n    /**\n     * Returns a boolean asserting whether an entity is present in the collection.\n     * @param {BemEntity} entity BEM entity\n     * @returns {Boolean}\n     */\n    has(entity) {\n        return this._entities.includes(entity)\n    },\n\n    /**\n     * Returns an entity, if it satisfies the provided testing callback.\n     * @param {Function} fn Callback\n     * @param {Object} ctx Callback context\n     * @returns {BemEntity}\n     */\n    find(fn, ctx) {\n        return this._entities.find((entity, i) => fn.call(ctx || this, entity, i, this)) || null\n    },\n\n    /**\n     * Returns a new collection comprised of collection on which it is called joined with\n     * the collection(s) and/or array(s) and/or entity(es) provided as arguments.\n     * @param {...(Collection|Array|BemEntity)} args\n     * @returns {Collection}\n     */\n    concat(...args) {\n        const argsForConcat = args.map((arg) =>\n            arg instanceof BemCollection ? arg._entities : arg)\n\n        return new this.__self(this._entities.concat(...argsForConcat))\n    },\n\n    /**\n     * Returns size of the collection.\n     * @returns {Number}\n     */\n    size() {\n        return this._entities.length\n    },\n\n    /**\n     * Converts the collection into array.\n     * @returns {Array}\n     */\n    toArray() {\n        return this._entities.slice()\n    }\n})\n\nfunction buildForEachEntityMethodProxyFn(methodName) {\n    return function(...args) {\n        this._entities.forEach((entity) => {\n            entity[methodName].apply(entity, args)\n        })\n        return this\n    }\n}\n\nfunction buildEntitiesMethodProxyFn(methodName) {\n    return function(...args) {\n        const entities = this._entities\n        return entities[methodName].apply(entities, args)\n    }\n}\n\nfunction buildComplexProxyFn(arrayMethodName, entityMethodName) {\n    return function(...args) {\n        return this._entities[arrayMethodName]((entity) =>\n            entity[entityMethodName].apply(entity, args))\n    }\n}\n\nexport default BemCollection\n"
  },
  {
    "path": "common.blocks/i-bem/__collection/i-bem__collection.spec.js",
    "content": "modules.define('spec', [\n    'i-bem',\n    'i-bem__collection',\n    'sinon',\n    'chai'\n], function(provide,\n    bem,\n    BemCollection,\n    sinon,\n    chai\n) {\n\nvar expect = chai.expect;\n\ndescribe('BEM collections', function() {\n    var Block = bem.declBlock('collection-block');\n\n    describe('constructor', function() {\n        it('should create collection of unique entities', function() {\n            var b1 = Block.create(),\n                collection = new BemCollection([b1, b1]);\n            collection.size().should.be.equal(1);\n            collection.get(0).should.be.equal(b1);\n        });\n\n        it('should create collection via arguments', function() {\n            var b1 = Block.create(),\n                b2 = Block.create(),\n                collection = new BemCollection(b1, b2);\n            collection.size().should.be.equal(2);\n            collection.get(0).should.be.equal(b1);\n            collection.get(1).should.be.equal(b2);\n        });\n    });\n\n    describe('common methods', function() {\n        it('get', function() {\n            var b1 = Block.create(),\n                b2 = Block.create(),\n                collection = new BemCollection([b1, b2]);\n            collection.get(1).should.be.equal(b2);\n        });\n\n        it('has', function() {\n            var b1 = Block.create(),\n                b2 = Block.create(),\n                collection = new BemCollection([b1]);\n            collection.has(b1).should.be.true;\n            collection.has(b2).should.be.false;\n        });\n\n        it('size', function() {\n            new BemCollection([]).size().should.be.equal(0);\n\n            var b1 = Block.create(),\n                b2 = Block.create();\n            new BemCollection([b1, b2]).size().should.be.equal(2);\n        });\n\n        it('toArray', function() {\n            var b1 = Block.create(),\n                b2 = Block.create(),\n                collection = new BemCollection([b1, b2]);\n            collection.toArray().should.be.eql([b1, b2]);\n        });\n\n        describe('forEach', function() {\n            it('should call callback for every entity', function() {\n                var collection = new BemCollection([Block.create(), Block.create()]),\n                    spy = sinon.spy();\n\n                collection.forEach(spy);\n                spy.should.be.calledTwice;\n            });\n\n            it('should call callback with proper arguments', function() {\n                var b1 = Block.create(),\n                    collection = new BemCollection([b1]);\n\n                collection.forEach(function(entity, i) {\n                    entity.should.be.equal(b1);\n                    i.should.be.equal(0);\n                });\n            });\n\n            it('should call callback with proper context', function() {\n                var collection = new BemCollection([Block.create()]),\n                    spy = sinon.spy(),\n                    ctx = {};\n\n                collection.forEach(spy, ctx);\n                spy.should.be.calledOn(ctx);\n            });\n        });\n\n        describe('map', function() {\n            it('should call callback for every entity', function() {\n                var collection = new BemCollection([Block.create(), Block.create()]),\n                    spy = sinon.spy();\n\n                collection.map(spy);\n                spy.should.be.calledTwice;\n            });\n\n            it('should call callback with proper arguments', function() {\n                var b1 = Block.create(),\n                    collection = new BemCollection([b1]);\n\n                collection.map(function(entity, i) {\n                    entity.should.be.equal(b1);\n                    i.should.be.equal(0);\n                });\n            });\n\n            it('should call callback with proper context', function() {\n                var collection = new BemCollection([Block.create()]),\n                    spy = sinon.spy(),\n                    ctx = {};\n\n                collection.map(spy, ctx);\n                spy.should.be.calledOn(ctx);\n            });\n\n            it('should return proper result', function() {\n                var b1 = Block.create({ m : 'v1' }),\n                    b2 = Block.create({ m : 'v2' }),\n                    collection = new BemCollection([b1, b2]);\n\n                collection.map(function(entity) {\n                    return entity.getMod('m');\n                }).should.be.eql(['v1', 'v2']);\n            });\n        });\n\n        describe('reduce', function() {\n            it('should call callback for every entity if no initial passed', function() {\n                var collection = new BemCollection([Block.create(), Block.create()]),\n                    spy = sinon.spy();\n\n                collection.reduce(spy);\n                spy.should.be.calledOnce;\n            });\n\n            it('should call callback for every entity and initial value if it is passed', function() {\n                var collection = new BemCollection([Block.create(), Block.create()]),\n                    spy = sinon.spy();\n\n                collection.reduce(spy, Block.create());\n                spy.should.be.calledTwice;\n            });\n\n            it('should call callback with proper arguments', function() {\n                var b1 = Block.create({ m : 'v1' }),\n                    collection = new BemCollection([b1]);\n\n                collection.reduce(function(res, entity) {\n                    res.should.be.equal('');\n                    entity.should.be.equal(b1);\n                }, '');\n            });\n\n            it('should return proper result', function() {\n                var b1 = Block.create({ m : 'v1' }),\n                    b2 = Block.create({ m : 'v2' }),\n                    collection = new BemCollection([b1, b2]);\n\n                collection.reduce(function(res, entity) {\n                    return res + entity.getMod('m');\n                }, '').should.be.equal('v1v2');\n            });\n        });\n\n        describe('reduceRight', function() {\n            it('should call callback for every entity if no initial passed', function() {\n                var collection = new BemCollection([Block.create(), Block.create()]),\n                    spy = sinon.spy();\n\n                collection.reduceRight(spy);\n                spy.should.be.calledOnce;\n            });\n\n            it('should call callback for every entity and initial value if it is passed', function() {\n                var collection = new BemCollection([Block.create(), Block.create()]),\n                    spy = sinon.spy();\n\n                collection.reduceRight(spy, Block.create());\n                spy.should.be.calledTwice;\n            });\n\n            it('should call callback with proper arguments', function() {\n                var b1 = Block.create({ m : 'v1' }),\n                    collection = new BemCollection([b1]);\n\n                collection.reduceRight(function(res, entity) {\n                    res.should.be.equal('');\n                    entity.should.be.equal(b1);\n                }, '');\n            });\n\n            it('should return proper result', function() {\n                var b1 = Block.create({ m : 'v1' }),\n                    b2 = Block.create({ m : 'v2' }),\n                    collection = new BemCollection([b1, b2]);\n\n                collection.reduceRight(function(res, entity) {\n                    return res + entity.getMod('m');\n                }, '').should.be.equal('v2v1');\n            });\n        });\n\n        describe('concat', function() {\n            it('should return proper value', function() {\n                var b1 = Block.create(),\n                    b2 = Block.create(),\n                    b3 = Block.create(),\n                    b4 = Block.create(),\n                    collection = new BemCollection([b1]).concat(b2, new BemCollection([b3]), [b4]);\n\n                collection.get(0).should.be.equal(b1);\n                collection.get(1).should.be.equal(b2);\n                collection.get(2).should.be.equal(b3);\n                collection.get(3).should.be.equal(b4);\n            });\n        });\n\n        describe('filter', function() {\n            it('should call callback for every entity', function() {\n                var collection = new BemCollection([Block.create(), Block.create()]),\n                    spy = sinon.spy();\n\n                collection.filter(spy);\n                spy.should.be.calledTwice;\n            });\n\n            it('should call callback with proper arguments', function() {\n                var b1 = Block.create(),\n                    collection = new BemCollection([b1]);\n\n                collection.filter(function(entity, i) {\n                    entity.should.be.equal(b1);\n                    i.should.be.equal(0);\n                });\n            });\n\n            it('should call callback with proper context', function() {\n                var collection = new BemCollection([Block.create()]),\n                    spy = sinon.spy(),\n                    ctx = {};\n\n                collection.filter(spy, ctx);\n                spy.should.be.calledOn(ctx);\n            });\n\n            it('should return proper result', function() {\n                var b1 = Block.create(),\n                    b2 = Block.create({ m : 'v1' }),\n                    b3 = Block.create(),\n                    collection = new BemCollection([b1, b2, b3]),\n                    res;\n\n                res = collection.filter(function(entity) {\n                    return entity.hasMod('m');\n                });\n\n                res.get(0).should.be.equal(b2);\n            });\n        });\n\n        describe('some', function() {\n            it('should return proper result', function() {\n                var collection = new BemCollection([Block.create()]);\n\n                collection.some(function() { return true; }).should.be.ok;\n                collection.some(function() { return false; }).should.not.be.ok;\n\n                new BemCollection([]).some(function() { return true; }).should.not.be.ok;\n            });\n\n            it('should not call callback for every item if valid item present', function() {\n                var collection = new BemCollection([\n                        Block.create(),\n                        Block.create()\n                    ]),\n                    stub = sinon.stub().returns(true);\n\n                collection.some(stub);\n                stub.should.be.calledOnce;\n            });\n\n            it('should call callback for every item if no valid item present', function() {\n                var collection = new BemCollection([\n                        Block.create(),\n                        Block.create()\n                    ]),\n                    stub = sinon.stub().returns(false);\n\n                collection.some(stub);\n                stub.should.be.calledTwice;\n            });\n\n            it('should call callback with proper arguments', function() {\n                var b1 = Block.create(),\n                    collection = new BemCollection([b1]);\n\n                collection.some(function(entity, i) {\n                    entity.should.be.equal(b1);\n                    i.should.be.equal(0);\n                });\n            });\n        });\n\n        describe('every', function() {\n            it('should return proper result', function() {\n                var collection1 = new BemCollection([Block.create()]);\n                collection1.every(function() { return true; }).should.be.ok;\n                collection1.every(function() { return false; }).should.not.be.ok;\n\n                var collection2 = new BemCollection([]);\n                collection2.every(function() { return true; }).should.be.ok;\n                collection2.every(function() { return false; }).should.be.ok;\n            });\n\n            it('should call callback for every item if all items valid', function() {\n                var collection = new BemCollection([\n                        Block.create(),\n                        Block.create()\n                    ]),\n                    stub = sinon.stub().returns(true);\n\n                collection.every(stub);\n                stub.should.be.calledTwice;\n            });\n\n            it('should not call callback for every item if invalid item present', function() {\n                var collection = new BemCollection([\n                        Block.create(),\n                        Block.create()\n                    ]),\n                    stub = sinon.stub().returns(true);\n\n                stub.onFirstCall().returns(false);\n\n                collection.every(stub);\n                stub.should.be.calledOnce;\n            });\n\n            it('should call callback with proper arguments', function() {\n                var b1 = Block.create(),\n                    collection = new BemCollection([b1]);\n\n                collection.every(function(entity, i) {\n                    entity.should.be.equal(b1);\n                    i.should.be.equal(0);\n                });\n            });\n        });\n\n        describe('find', function() {\n            it('should return proper result', function() {\n                var b1 = Block.create(),\n                    collection = new BemCollection([b1]);\n\n                collection.find(function() { return true; }).should.be.equal(b1);\n                expect(collection.find(function() { return false; })).to.be.null;\n            });\n\n            it('should not call callback for every item if valid item present', function() {\n                var collection = new BemCollection([\n                        Block.create(),\n                        Block.create()\n                    ]),\n                    stub = sinon.stub().returns(true);\n\n                collection.find(stub);\n                stub.should.be.calledOnce;\n            });\n\n            it('should call callback for every item if no valid item present', function() {\n                var collection = new BemCollection([\n                        Block.create(),\n                        Block.create()\n                    ]),\n                    stub = sinon.stub().returns(false);\n\n                collection.find(stub);\n                stub.should.be.calledTwice;\n            });\n\n            it('should call callback with proper arguments', function() {\n                var b1 = Block.create(),\n                    collection = new BemCollection([b1]);\n\n                collection.find(function(entity, i, thisCollection) {\n                    entity.should.be.equal(b1);\n                    i.should.be.equal(0);\n                    thisCollection.should.be.equal(collection);\n                });\n            });\n        });\n    });\n\n    describe('for each entity', function() {\n        var entities, collection;\n\n        beforeEach(function() {\n            entities = [\n                Block.create(),\n                Block.create({ m1 : 'v1' }),\n                Block.create({ m1 : 'v2' })\n            ];\n            collection = new BemCollection(entities);\n        });\n\n        it('setMod', function() {\n            collection.setMod('m1', 'v3');\n            entities.every(function(entity) {\n                return entity.hasMod('m1', 'v3');\n            }).should.be.true;\n        });\n\n        it('delMod', function() {\n            collection.delMod('m1');\n            entities.every(function(entity) {\n                return !entity.hasMod('m1');\n            }).should.be.true;\n        });\n\n        it('toggleMod', function() {\n            collection.toggleMod('m1', 'v1', 'v2');\n            entities[1].hasMod('m1', 'v2').should.be.true;\n            entities[2].hasMod('m1', 'v1').should.be.true;\n        });\n    });\n\n    describe('*HasMod', function() {\n        var collection;\n\n        beforeEach(function() {\n            collection = new BemCollection([\n                Block.create({ m1 : 'v1' }),\n                Block.create({ m1 : 'v1', m2 : 'v2' })\n            ]);\n        });\n\n        it('everyHasMod', function() {\n            collection.everyHasMod('m1', 'v1').should.be.true;\n            collection.everyHasMod('m2').should.be.false;\n        });\n\n        it('someHasMod', function() {\n            collection.someHasMod('m2', 'v2').should.be.true;\n            collection.someHasMod('m3').should.be.false;\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem/__internal/i-bem__internal.ru.title.txt",
    "content": "Модуль для внутренних хелперов\n\n"
  },
  {
    "path": "common.blocks/i-bem/__internal/i-bem__internal.spec.js",
    "content": "modules.define('spec', ['i-bem__internal'], function(provide, bemInternal) {\n\ndescribe('i-bem__internal', function() {\n    describe('buildClassName', function() {\n        [\n            {\n                title : 'block class name should be valid',\n                input : ['b-foo'],\n                output : 'b-foo'\n            },\n            {\n                title : 'elem class name should be valid',\n                input : ['b-foo', 'elem'],\n                output : 'b-foo__elem'\n            },\n            {\n                title : 'block with mod class name should be valid',\n                input : ['b-foo', 'mod1', 'val1'],\n                output : 'b-foo_mod1_val1'\n            },\n            {\n                title : 'block with number mod class name should be valid',\n                input : ['b-foo', 'mod1', 5],\n                output : 'b-foo_mod1_5'\n            },\n            {\n                title : 'block with zero number mod class name should be valid',\n                input : ['b-foo', 'mod1', 0],\n                output : 'b-foo_mod1_0'\n            },\n            {\n                title : 'block with undefined elem mod class name should be valid',\n                input : ['b-foo', undefined, 'mod1', 'val1'],\n                output : 'b-foo_mod1_val1'\n            },\n            {\n                title : 'block with truly boolean mod class name should be valid',\n                input : ['b-foo', 'mod1', true],\n                output : 'b-foo_mod1'\n            },\n            {\n                title : 'block with falsy boolean mod class name should be valid',\n                input : ['b-foo', 'mod1', false],\n                output : 'b-foo'\n            },\n            {\n                title : 'elem with mod class name should be valid',\n                input : ['b-foo', 'elem', 'mod1', 'val1'],\n                output : 'b-foo__elem_mod1_val1'\n            },\n            {\n                title : 'elem with number mod class name should be valid',\n                input : ['b-foo', 'elem', 'mod1', 3],\n                output : 'b-foo__elem_mod1_3'\n            },\n            {\n                title : 'elem with zero number mod class name should be valid',\n                input : ['b-foo', 'elem', 'mod1', 0],\n                output : 'b-foo__elem_mod1_0'\n            },\n            {\n                title : 'elem with truly boolean mod class name should be valid',\n                input : ['b-foo', 'elem', 'mod1', true],\n                output : 'b-foo__elem_mod1'\n            },\n            {\n                title : 'elem with falsy boolean mod class name should be valid',\n                input : ['b-foo', 'elem', 'mod1', false],\n                output : 'b-foo__elem'\n            }\n        ].forEach(function(spec) {\n            it(spec.title, function() {\n                bemInternal.buildClassName.apply(bemInternal, spec.input).should.to.equal(spec.output);\n            });\n        });\n    });\n\n    describe('buildClassNames', function() {\n        [\n            {\n                title : 'block class names should be valid',\n                input : ['b-foo'],\n                output : 'b-foo'\n            },\n            {\n                title : 'elem class names should be valid',\n                input : ['b-foo', 'elem'],\n                output : 'b-foo__elem'\n            },\n            {\n                title : 'block with mods class name should be valid',\n                input : ['b-foo', { mod1 : 'val1', mod2 : 'val2', mod3 : true, mod4 : false }],\n                output : 'b-foo b-foo_mod1_val1 b-foo_mod2_val2 b-foo_mod3'\n            },\n            {\n                title : 'block with undefined elem and mods class name should be valid',\n                input : ['b-foo', undefined, { mod1 : 'val1', mod2 : 'val2', mod3 : true, mod4 : false }],\n                output : 'b-foo b-foo_mod1_val1 b-foo_mod2_val2 b-foo_mod3'\n            },\n            {\n                title : 'elem with mods class name should be valid',\n                input : ['b-foo', 'elem', { mod1 : 'val1', mod2 : 'val2', mod3 : true, mod4 : false }],\n                output : 'b-foo__elem b-foo__elem_mod1_val1 b-foo__elem_mod2_val2 b-foo__elem_mod3'\n            }\n        ].forEach(function(spec) {\n            it(spec.title, function() {\n                bemInternal.buildClassNames.apply(bemInternal, spec.input).should.to.equal(spec.output);\n            });\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem/__internal/i-bem__internal.vanilla.js",
    "content": "/**\n * @module i-bem__internal\n */\n\n/**\n * Separator for modifiers and their values\n * @const\n * @type String\n */\nconst MOD_DELIM = '_'\n\n/**\n * Separator between names of a block and a nested element\n * @const\n * @type String\n */\nconst ELEM_DELIM = '__'\n\n/**\n * Pattern for acceptable element and modifier names\n * @const\n * @type String\n */\nconst NAME_PATTERN = '[a-zA-Z0-9-]+'\n\nfunction isSimple(obj) {\n    const typeOf = typeof obj\n    return typeOf === 'string' || typeOf === 'number' || typeOf === 'boolean'\n}\n\nfunction buildModPostfix(modName, modVal) {\n    let res = ''\n    if(modVal != null && modVal !== false) {\n        res += MOD_DELIM + modName\n        modVal !== true && (res += MOD_DELIM + modVal)\n    }\n    return res\n}\n\nfunction buildBlockClassName(name, modName, modVal) {\n    return name + buildModPostfix(modName, modVal)\n}\n\nfunction buildElemClassName(block, name, modName, modVal) {\n    return buildBlockClassName(block, undefined, undefined) +\n        ELEM_DELIM + name +\n        buildModPostfix(modName, modVal)\n}\n\nexport default {\n    NAME_PATTERN,\n\n    MOD_DELIM,\n    ELEM_DELIM,\n\n    buildModPostfix,\n\n    /**\n     * Builds the class name of a block or element with a modifier\n     * @param {String} block Block name\n     * @param {String} [elem] Element name\n     * @param {String} [modName] Modifier name\n     * @param {String|Number} [modVal] Modifier value\n     * @returns {String} Class name\n     */\n    buildClassName(block, elem, modName, modVal) {\n        if(isSimple(modName)) {\n            if(!isSimple(modVal)) {\n                modVal = modName\n                modName = elem\n                elem = undefined\n            }\n        } else if(typeof modName !== 'undefined') {\n            modName = undefined\n        } else if(elem && typeof elem !== 'string') {\n            elem = undefined\n        }\n\n        if(!(elem || modName)) { // optimization for simple case\n            return block\n        }\n\n        return elem?\n            buildElemClassName(block, elem, modName, modVal) :\n            buildBlockClassName(block, modName, modVal)\n    },\n\n    /**\n     * Builds full class names for a buffer or element with modifiers\n     * @param {String} block Block name\n     * @param {String} [elem] Element name\n     * @param {Object} [mods] Modifiers\n     * @returns {String} Class\n     */\n    buildClassNames(block, elem, mods) {\n        if(elem && typeof elem !== 'string') {\n            mods = elem\n            elem = undefined\n        }\n\n        let res = elem?\n            buildElemClassName(block, elem, undefined, undefined) :\n            buildBlockClassName(block, undefined, undefined)\n\n        if(mods) {\n            for(const [modName, modVal] of Object.entries(mods)) {\n                if(modVal) {\n                    res += ' ' + (elem?\n                        buildElemClassName(block, elem, modName, modVal) :\n                        buildBlockClassName(block, modName, modVal))\n                }\n            }\n        }\n\n        return res\n    }\n}\n"
  },
  {
    "path": "common.blocks/i-bem/i-bem.deps.js",
    "content": "({\n    shouldDeps : [\n        { elem : 'internal' },\n        'inherit',\n        'identify',\n        'next-tick',\n        'objects',\n        'functions'\n    ]\n})\n"
  },
  {
    "path": "common.blocks/i-bem/i-bem.en.md",
    "content": "# i-bem\n\nA helper block for creating other blocks.\n\nThe block is implemented as a specialized JavaScript framework for web development using the BEM methodology.\n\nThere is a separate document with a detailed [user's guide](https://en.bem.info/technology/i-bem/v4/i-bem-js/).\n"
  },
  {
    "path": "common.blocks/i-bem/i-bem.en.title.txt",
    "content": "Helper to create other blocks\n"
  },
  {
    "path": "common.blocks/i-bem/i-bem.ru.md",
    "content": "# i-bem\n\nБлок-хелпер, позволяющий создавать другие блоки.\n\nРеализация блока представляет собой специализированный JavaScript-фреймворк для веб-разработки в рамках методологии БЭМ.\n\nВ виде отдельного документа доступно [подробное руководство пользователя](https://ru.bem.info/technology/i-bem/v4/i-bem-js/).\n"
  },
  {
    "path": "common.blocks/i-bem/i-bem.ru.title.txt",
    "content": "Помощник для создания других блоков\n"
  },
  {
    "path": "common.blocks/i-bem/i-bem.spec.js",
    "content": "modules.define('spec', [\n    'i-bem',\n    'sinon',\n    'objects'\n], function(provide,\n    bem,\n    sinon,\n    objects\n) {\n\ndescribe('i-bem', function() {\n    afterEach(function() {\n        objects.each(bem.entities, function(_, entityName) {\n            delete bem.entities[entityName];\n        });\n    });\n\n    describe('decl', function() {\n        it('should enable to declare block', function() {\n            var Block = bem.declBlock('block', {});\n\n            Block.should.be.equal(bem.entities['block']);\n            Block.getEntityName().should.be.equal('block');\n            (new Block()).should.be.instanceOf(bem.Block);\n        });\n\n        it('should enable to declare element', function() {\n            var Elem = bem.declElem('block', 'elem', {});\n\n            Elem.should.be.equal(bem.entities['block__elem']);\n            Elem.getEntityName().should.be.equal('block__elem');\n            (new Elem()).should.be.instanceOf(bem.Elem);\n        });\n\n        it('should enable to inherit block', function() {\n            var Block = bem.declBlock('block', {}),\n                Block2 = bem.declBlock('block2', Block, {});\n\n            (new Block2()).should.be.instanceOf(Block);\n            (new Block2()).should.be.instanceOf(Block2);\n        });\n\n        it('should enable to inherit block to itself', function() {\n            var spy1 = sinon.spy(),\n                spy2 = sinon.spy(),\n                Block = bem.declBlock('block', {\n                    onSetMod : {\n                        js : {\n                            inited : spy1\n                        }\n                    }\n                }),\n                Block2 = bem.declBlock('block', {\n                    onSetMod : {\n                        js : {\n                            inited : spy2\n                        }\n                    }\n                });\n\n            Block.create();\n\n            Block2.should.be.equal(Block);\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n        });\n\n        it('should enable to inherit block to itself using entity class', function() {\n            var spy1 = sinon.spy(),\n                spy2 = sinon.spy(),\n                Block = bem.declBlock('block', {\n                    onSetMod : {\n                        js : {\n                            inited : spy1\n                        }\n                    }\n                }),\n                Block2 = bem.declBlock(Block, {\n                    onSetMod : {\n                        js : {\n                            inited : spy2\n                        }\n                    }\n                });\n\n            Block.create();\n\n            Block2.should.be.equal(Block);\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n        });\n\n        it('should enable to inherit elem to itself', function() {\n            var spy1 = sinon.spy(),\n                spy2 = sinon.spy(),\n                Elem = bem.declElem('block', 'elem', {\n                    onSetMod : {\n                        js : {\n                            inited : spy1\n                        }\n                    }\n                }),\n                Elem2 = bem.declElem('block', 'elem', {\n                    onSetMod : {\n                        js : {\n                            inited : spy2\n                        }\n                    }\n                });\n\n            Elem.create();\n\n            Elem2.should.be.equal(Elem);\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n        });\n\n        it('should enable to inherit elem to itself using entity', function() {\n            var spy1 = sinon.spy(),\n                spy2 = sinon.spy(),\n                Elem = bem.declElem('block', 'elem', {\n                    onSetMod : {\n                        js : {\n                            inited : spy1\n                        }\n                    }\n                }),\n                Elem2 = bem.declElem(Elem, {\n                    onSetMod : {\n                        js : {\n                            inited : spy2\n                        }\n                    }\n                });\n\n            Elem.create();\n\n            Elem2.should.be.equal(Elem);\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n        });\n\n        it('should enable to mix block', function() {\n            var MixBlock = bem.declMixin({}),\n                Block = bem.declBlock('block', MixBlock, {}),\n                block = Block.create();\n\n            (new Block()).should.be.instanceOf(bem.Block);\n        });\n\n        it('should enable to mix blocks', function() {\n            var MixBlock1 = bem.declMixin({}),\n                MixBlock2 = bem.declMixin({}),\n                Block = bem.declBlock('block', [MixBlock1, MixBlock2], {});\n\n            (new Block()).should.be.instanceOf(bem.Block);\n        });\n\n        it('should enable to inherit and mix blocks', function() {\n            var MixBlock = bem.declMixin({}),\n                BaseBlock = bem.declBlock('base-block', {}),\n                Block = bem.declBlock('block', [BaseBlock, MixBlock], {});\n\n            (new Block()).should.be.instanceOf(bem.Block);\n        });\n\n        it('should enable to declare modifier', function() {\n            var Block = bem.declBlock('block', {}),\n                Block2 = Block.declMod({ modName : 'm1', modVal : 'v1' }, {});\n\n            Block2.should.be.equal(Block);\n        });\n\n        it('should apply method only if block has mod', function() {\n            var baseMethodSpy = sinon.spy(),\n                modsMethodSpy = sinon.spy(),\n                Block = bem\n                    .declBlock('block', { method : baseMethodSpy })\n                    .declMod({ modName : 'mod1', modVal : 'val1' }, { method : modsMethodSpy }),\n                instance = new Block({ mod1 : 'val1' });\n\n            instance.method();\n\n            baseMethodSpy.should.not.have.been.called;\n            modsMethodSpy.should.have.been.calledOnce;\n\n            instance.setMod('mod1', 'val2');\n            instance.method();\n\n            baseMethodSpy.should.have.been.calledOnce;\n            modsMethodSpy.should.have.been.calledOnce;\n        });\n\n        it('should apply method only if block has boolean mod', function() {\n            var baseMethodSpy = sinon.spy(),\n                modsMethodSpy = sinon.spy(),\n                Block = bem\n                    .declBlock('block', { method : baseMethodSpy })\n                    .declMod({ modName : 'mod1', modVal : true }, { method : modsMethodSpy }),\n                instance = new Block({ mod1 : true });\n\n            instance.method();\n\n            baseMethodSpy.should.not.have.been.called;\n            modsMethodSpy.should.have.been.calledOnce;\n\n            instance.delMod('mod1');\n            instance.method();\n\n            baseMethodSpy.should.have.been.calledOnce;\n            modsMethodSpy.should.have.been.calledOnce;\n        });\n\n        it('should apply method if block has any mod', function() {\n            var baseMethodSpy = sinon.spy(),\n                modsMethodSpy = sinon.spy(),\n                Block = bem\n                    .declBlock('block', { method : baseMethodSpy })\n                    .declMod({ modName : 'mod1', modVal : '*' }, { method : modsMethodSpy }),\n                instance = new Block({ mod1 : 'val1' });\n\n            instance.method();\n\n            baseMethodSpy.should.not.have.been.called;\n            modsMethodSpy.should.have.been.calledOnce;\n\n            instance.setMod('mod1', 'val2');\n            instance.method();\n\n            baseMethodSpy.should.not.have.been.called;\n            modsMethodSpy.should.have.been.calledTwice;\n\n            instance.delMod('mod1');\n            instance.method();\n\n            baseMethodSpy.should.have.been.calledOnce;\n            modsMethodSpy.should.have.been.calledTwice;\n        });\n    });\n\n    describe('create', function() {\n        it('should return instance of block', function() {\n            var Block = bem.declBlock('block', {}),\n                instance = Block.create();\n\n            instance.should.be.instanceOf(Block);\n        });\n\n        it('should return instance of element with proper block', function() {\n            var Block = bem.declBlock('block', {}),\n                block = Block.create(),\n                Elem = bem.declElem('block', 'elem', {}),\n                elem = Elem.create(block);\n\n            elem.should.be.instanceOf(Elem);\n            elem._block().should.be.instanceOf(Block);\n        });\n    });\n\n    describe('mods', function() {\n        var block;\n        beforeEach(function() {\n            block = bem\n                .declBlock('block', {})\n                .create({ mod1 : 'val1', mod2 : true, mod3 : false });\n        });\n\n        describe('getMod', function() {\n            it('should return current mod\\'s value', function() {\n                block.getMod('mod1').should.be.equal('val1');\n            });\n\n            it('should return current boolean mod\\'s value', function() {\n                block.getMod('mod2').should.be.true;\n                block.getMod('mod3').should.be.equal('');\n            });\n\n            it('should return \\'\\' for undefined mod', function() {\n                block.getMod('mod4').should.be.equal('');\n            });\n        });\n\n        describe('setMod', function() {\n            it('should update mod value', function() {\n                block\n                    .setMod('mod1', 'val2')\n                    .getMod('mod1')\n                        .should.be.equal('val2');\n            });\n\n            it('should update boolean mod value', function() {\n                block\n                    .setMod('mod1', true)\n                    .getMod('mod1')\n                        .should.be.true;\n\n                block\n                    .setMod('mod1', false)\n                    .getMod('mod1')\n                        .should.be.equal('');\n\n                block\n                    .setMod('mod1')\n                    .getMod('mod1')\n                        .should.be.true;\n            });\n\n            it('should cast non-boolean mod value to string', function() {\n                block\n                    .setMod('mod1', 0)\n                    .getMod('mod1').should.be.equal('0');\n            });\n        });\n\n        describe('delMod', function() {\n            it('should set mod\\'s value to \\'\\'', function() {\n                block\n                    .delMod('mod1')\n                    .getMod('mod1')\n                        .should.be.equal('');\n            });\n        });\n\n        describe('hasMod', function() {\n            it('should return true for matching mod\\'s value', function() {\n                block.hasMod('mod1', 'val1').should.be.true;\n            });\n\n            it('should return false for non-matching mod\\'s value', function() {\n                block.hasMod('mod1', 'val2').should.be.false;\n            });\n\n            it('should return false for undefined mod\\'s value', function() {\n                block.hasMod('mod2', 'val2').should.be.false;\n            });\n\n            it('in short form should return true for non-empty mod\\'s value', function() {\n                block.hasMod('mod1').should.be.true;\n            });\n\n            it('in short form should return false for empty mod\\'s value', function() {\n                block\n                    .setMod('mod1', '')\n                    .hasMod('mod1')\n                        .should.be.false;\n            });\n\n            it('in short form should return false for undefined mod', function() {\n                block.hasMod('mod4').should.be.false;\n            });\n\n            it('should return true for matching boolean mod\\'s value', function() {\n                block\n                    .setMod('mod1', true)\n                    .hasMod('mod1').should.be.true;\n\n                block.hasMod('mod1', true).should.be.true;\n            });\n\n            it('should not treat passed but undefined mod value as a short form', function() {\n                var modVal;\n                block.hasMod('mod1', modVal).should.be.false;\n            });\n\n            it('should treat defined non-boolean mod value as a string', function() {\n                block\n                    .setMod('mod1', 0)\n                    .hasMod('mod1', 0)\n                        .should.be.true;\n\n                block.hasMod('mod1', '0')\n                    .should.be.true;\n\n                block\n                    .setMod('mod1', '1')\n                    .hasMod('mod1', 1)\n                        .should.be.true;\n            });\n        });\n\n        describe('toggleMod', function() {\n            it('should switch mod\\'s values', function() {\n                block\n                    .toggleMod('mod1', 'val1', 'val2')\n                    .hasMod('mod1', 'val2')\n                        .should.be.true;\n\n                block\n                    .toggleMod('mod1', 'val1', 'val2')\n                    .hasMod('mod1', 'val1')\n                        .should.be.true;\n            });\n\n            it('should switch mod\\'s value if \"modVal2\" param omited', function() {\n                block\n                    .toggleMod('mod1', 'val1')\n                    .hasMod('mod1')\n                        .should.be.false;\n\n                block\n                    .toggleMod('mod1', 'val1')\n                    .hasMod('mod1', 'val1')\n                        .should.be.true;\n            });\n\n            it('should switch boolean mod\\'s value', function() {\n                block\n                    .toggleMod('mod2')\n                    .hasMod('mod2')\n                        .should.be.false;\n\n                block\n                    .toggleMod('mod2')\n                    .hasMod('mod2')\n                        .should.be.true;\n            });\n\n            it('should switch mod\\'s values according to \"condition\" param', function() {\n                block\n                    .toggleMod('mod1', 'val1', 'val2', true)\n                    .hasMod('mod1', 'val1')\n                        .should.be.true;\n\n                block\n                    .toggleMod('mod1', 'val1', 'val2', false)\n                    .hasMod('mod1', 'val2')\n                        .should.be.true;\n            });\n\n            it('should switch mod\\'s value according to \"condition\" param if \"modVal2\" param omited', function() {\n                block\n                    .toggleMod('mod1', 'val1', true)\n                    .hasMod('mod1', 'val1')\n                        .should.be.true;\n\n                block\n                    .toggleMod('mod1', 'val1', false)\n                    .hasMod('mod1')\n                        .should.be.false;\n            });\n\n            it('should work with numeric modVal', function() {\n                block.delMod('mod1');\n\n                block\n                    .toggleMod('mod1', 1)\n                    .hasMod('mod1', '1')\n                        .should.be.true;\n\n                block\n                    .toggleMod('mod1', 1)\n                    .hasMod('mod1')\n                        .should.be.false;\n\n                block\n                    .toggleMod('mod1', 1)\n                    .hasMod('mod1', '1')\n                        .should.be.true;\n            });\n\n            it('should switch numeric modVal values', function() {\n                block.setMod('mod1', 1);\n\n                block\n                    .toggleMod('mod1', 1, 2)\n                    .hasMod('mod1', '2')\n                        .should.be.true;\n\n                block\n                    .toggleMod('mod1', 1, 2)\n                    .hasMod('mod1', '1')\n                        .should.be.true;\n            });\n        });\n    });\n\n    describe('beforeSetMod', function() {\n        it('should call properly matched callbacks by order', function() {\n            var order = [],\n                spyMod1Val2 = sinon.spy(),\n                spyMod2Val1 = sinon.spy(),\n                spyMod2Val2 = sinon.spy();\n\n            bem.declBlock('block', {\n                beforeSetMod : {\n                    'mod1' : {\n                        'val1' : function() {\n                            order.push(5);\n                        }\n                    }\n                }\n            });\n\n            bem.declBlock('block', {\n                beforeSetMod : {\n                    'mod1' : function() {\n                        order.push(3);\n                    },\n\n                    '*' : function(modName) {\n                        modName === 'mod1' && order.push(1);\n                    }\n                }\n            });\n\n            bem.declBlock('block', {\n                beforeSetMod : function(modName) {\n                    this.__base.apply(this, arguments);\n                    modName === 'mod1' && order.push(2);\n                }\n            });\n\n            bem.declBlock('block', {\n                beforeSetMod : {\n                   'mod1' : {\n                       '*' : function() {\n                           this.__base.apply(this, arguments);\n                           order.push(4);\n                       },\n                       'val1' : function() {\n                            this.__base.apply(this, arguments);\n                           order.push(6);\n                       },\n                       'val2' : spyMod1Val2\n                   },\n                   'mod2' : {\n                       'val1' : spyMod2Val1,\n                       'val2' : spyMod2Val2\n                   }\n                }\n            });\n\n            var block = bem.entities['block'].create({ mod1 : 'val0', mod2 : 'val0' });\n            block.setMod('mod1', 'val1');\n\n            order.should.be.eql([1, 2, 3, 4, 5, 6]);\n            spyMod1Val2.should.not.have.been.called;\n            spyMod2Val1.should.not.have.been.called;\n            spyMod2Val2.should.not.have.been.called;\n        });\n\n        it('should properly call callbacks for special modifier value `!`-syntax', function() {\n            var spyMod1ValStar = sinon.spy(),\n                spyMod1NotVal1 = sinon.spy(),\n                spyMod1NotVal2 = sinon.spy(),\n                Block = bem.declBlock('block', {\n                    beforeSetMod : {\n                        'mod1' : {\n                            '*' : spyMod1ValStar,\n                            '!val1' : spyMod1NotVal1,\n                            '!val2' : spyMod1NotVal2\n                        }\n                    }\n                }),\n                block = Block.create();\n\n            block.setMod('mod1', 'val1');\n\n            spyMod1ValStar.should.have.been.called;\n            spyMod1NotVal1.should.not.have.been.called;\n            spyMod1NotVal2.should.have.been.called;\n        });\n\n        it('should properly call callbacks for special modifier value `~`-syntax', function() {\n            var spyMod1ValStar = sinon.spy(),\n                spyMod1DelVal1 = sinon.spy(),\n                spyMod1DelVal2 = sinon.spy(),\n                Block = bem.declBlock('block', {\n                    beforeSetMod : {\n                        'mod1' : {\n                            '*' : spyMod1ValStar,\n                            '~val1' : spyMod1DelVal1,\n                            '~val2' : spyMod1DelVal2\n                        }\n                    }\n                }),\n                block = Block.create();\n\n            block.setMod('mod1', 'val1');\n\n            spyMod1ValStar.should.have.been.called;\n            spyMod1DelVal1.should.not.have.been.called;\n            spyMod1DelVal2.should.not.have.been.called;\n\n            block.setMod('mod1', 'val2');\n\n            spyMod1ValStar.should.have.been.calledTwice;\n            spyMod1DelVal1.should.have.been.called;\n            spyMod1DelVal2.should.not.have.been.called;\n        });\n\n        it('should call callbacks before set mod', function(done) {\n            bem\n                .declBlock('block', {\n                    beforeSetMod : {\n                       'mod1' : {\n                           'val1' : function() {\n                               this.hasMod('mod1', 'val1').should.be.false;\n                               done();\n                           }\n                       }\n                    }\n                })\n                .create({ mod1 : 'val0' })\n                .setMod('mod1', 'val1');\n        });\n\n        it('should set mod after callbacks', function() {\n             bem\n                 .declBlock('block', {\n                    beforeSetMod : {\n                       'mod1' : {\n                           'val1' : function() {}\n                       }\n                    }\n                })\n                .create({ mod1 : 'val0' })\n                .setMod('mod1', 'val1')\n                .hasMod('mod1', 'val1')\n                    .should.be.true;\n        });\n\n        it('shouldn\\'t set mod when callback returns false', function() {\n            bem\n                .declBlock('block', {\n                    beforeSetMod : {\n                       'mod1' : {\n                           'val1' : function() {\n                               return false;\n                           }\n                       }\n                    }\n                })\n                .create({ mod1 : 'val0' })\n                .setMod('mod1', 'val1')\n                .hasMod('mod1', 'val1')\n                    .should.be.false;\n        });\n\n        it('shouldn\\'t set mod when callback for special `!`-syntax value returns false', function() {\n            var block = bem.declBlock('block', {\n                    beforeSetMod : {\n                       'mod1' : {\n                           '*' : function() {\n                               return false;\n                           },\n                           '!val1' : function() {\n                               return false;\n                           }\n                       },\n                       'mod2' : {\n                           '*' : function() {\n                               return false;\n                           },\n                           '!val1' : function() {}\n                       },\n                       'mod3' : {\n                           '*' : function() {},\n                           '!val1' : function() {\n                               return false;\n                           }\n                       }\n                    }\n                }).create();\n\n            block.setMod('mod1', 'val2').hasMod('mod1', 'val2')\n                .should.be.false;\n\n            block.setMod('mod2', 'val2').hasMod('mod2', 'val2')\n                .should.be.false;\n\n            block.setMod('mod3', 'val2').hasMod('mod3', 'val2')\n                .should.be.false;\n\n            block.setMod('mod3', 'val1').hasMod('mod3', 'val1')\n                .should.be.true;\n        });\n\n        it('shouldn\\'t set mod when callback for special `~`-syntax value returns false', function() {\n            var block = bem.declBlock('block', {\n                    beforeSetMod : {\n                       'mod1' : {\n                           '*' : function() {\n                               return false;\n                           },\n                           '~val1' : function() {\n                               return false;\n                           }\n                       },\n                       'mod2' : {\n                           '*' : function() {\n                               return false;\n                           },\n                           '~val1' : function() {}\n                       },\n                       'mod3' : {\n                           '*' : function() {},\n                           '~val1' : function() {\n                               return false;\n                           }\n                       }\n                    }\n                }).create({ mod1 : 'val1', mod2 : 'val1', mod3 : 'val1' });\n\n            block.setMod('mod1', 'val2').hasMod('mod1', 'val2')\n                .should.be.false;\n\n            block.setMod('mod2', 'val2').hasMod('mod2', 'val2')\n                .should.be.false;\n\n            block.setMod('mod3', 'val2').hasMod('mod3', 'val2')\n                .should.be.false;\n        });\n    });\n\n    describe('onSetMod', function() {\n        it('should call properly matched callbacks by order', function() {\n            var order = [],\n                spyMod1Val2 = sinon.spy(),\n                spyMod2Val1 = sinon.spy(),\n                spyMod2Val2 = sinon.spy();\n\n            bem.declBlock('block', {\n                onSetMod : {\n                    'mod1' : {\n                        'val1' : function() {\n                            order.push(5);\n                        }\n                    }\n                }\n            });\n\n            bem.declBlock('block', {\n                onSetMod : {\n                    'mod1' : function() {\n                        order.push(3);\n                    },\n\n                    '*' : function(modName) {\n                        modName === 'mod1' && order.push(1);\n                    }\n                }\n            });\n\n            bem.declBlock('block', {\n                onSetMod : function(modName) {\n                    this.__base.apply(this, arguments);\n                    modName === 'mod1' && order.push(2);\n                }\n            });\n\n            bem.declBlock('block', {\n                onSetMod : {\n                   'mod1' : {\n                       '*' : function() {\n                           this.__base.apply(this, arguments);\n                           order.push(4);\n                       },\n                       'val1' : function() {\n                            this.__base.apply(this, arguments);\n                           order.push(6);\n                       },\n                       'val2' : spyMod1Val2\n                   },\n                   'mod2' : {\n                       'val1' : spyMod2Val1,\n                       'val2' : spyMod2Val2\n                   }\n                }\n            });\n\n            bem.entities['block']\n                .create({ mod1 : 'val0', mod2 : 'val0' })\n                .setMod('mod1', 'val1');\n\n            order.should.be.eql([1, 2, 3, 4, 5, 6]);\n            spyMod1Val2.should.not.have.been.called;\n            spyMod2Val1.should.not.have.been.called;\n            spyMod2Val2.should.not.have.been.called;\n        });\n\n        it('should properly call callbacks for special modifier value `!`-syntax', function() {\n            var spyMod1ValStar = sinon.spy(),\n                spyMod1NotVal1 = sinon.spy(),\n                spyMod1NotVal2 = sinon.spy(),\n                Block = bem.declBlock('block', {\n                    onSetMod : {\n                        'mod1' : {\n                            '*' : spyMod1ValStar,\n                            '!val1' : spyMod1NotVal1,\n                            '!val2' : spyMod1NotVal2\n                        }\n                    }\n                }),\n                block = Block.create();\n\n            block.setMod('mod1', 'val1');\n\n            spyMod1ValStar.should.have.been.called;\n            spyMod1NotVal1.should.not.have.been.called;\n            spyMod1NotVal2.should.have.been.called;\n        });\n\n        it('should properly call callbacks for special modifier value `~`-syntax', function() {\n            var spyMod1ValStar = sinon.spy(),\n                spyMod1DelVal1 = sinon.spy(),\n                spyMod1DelVal2 = sinon.spy(),\n                Block = bem.declBlock('block', {\n                    onSetMod : {\n                        'mod1' : {\n                            '*' : spyMod1ValStar,\n                            '~val1' : spyMod1DelVal1,\n                            '~val2' : spyMod1DelVal2\n                        }\n                    }\n                }),\n                block = Block.create();\n\n            block.setMod('mod1', 'val1');\n\n            spyMod1ValStar.should.have.been.called;\n            spyMod1DelVal1.should.not.have.been.called;\n            spyMod1DelVal2.should.not.have.been.called;\n\n            block.setMod('mod1', 'val2');\n\n            spyMod1ValStar.should.have.been.calledTwice;\n            spyMod1DelVal1.should.have.been.called;\n            spyMod1DelVal2.should.not.have.been.called;\n        });\n\n        it('should call callbacks after set mod', function(done) {\n            bem\n                .declBlock('block', {\n                    onSetMod : {\n                       'mod1' : {\n                           'val1' : function() {\n                               this.hasMod('mod1', 'val1').should.be.true;\n                               done();\n                           }\n                       }\n                    }\n                })\n                .create({ mod1 : 'val0' })\n                .setMod('mod1', 'val1');\n        });\n\n        it('shouldn\\'t call callbacks if beforeSetMod cancel set mod', function() {\n            var spy = sinon.spy();\n            bem\n                .declBlock('block', {\n                    beforeSetMod : {\n                       'mod1' : {\n                           'val1' : function() {\n                               return false;\n                           }\n                       }\n                    },\n\n                    onSetMod : {\n                       'mod1' : {\n                           'val1' : spy\n                       }\n                    }\n                })\n                .create({ mod1 : 'val0' })\n                .setMod('mod1', 'val1');\n\n            spy.should.not.have.been.called;\n        });\n\n        it('should properly call callbacks for declaration with mod', function() {\n            var spy1 = sinon.spy(),\n                spy2 = sinon.spy(),\n                spy3 = sinon.spy(),\n                spy4 = sinon.spy(),\n                Block = bem.declBlock('block');\n\n            Block.declMod({ modName : 'm1', modVal : 'v1' }, {\n                onSetMod : {\n                    'm1' : {\n                        'v1' : spy1,\n                        'v2' : spy2\n                    }\n                }\n            });\n            Block.declMod({ modName : 'm1', modVal : 'v2' }, {\n                onSetMod : {\n                    'm1' : {\n                        'v1' : function() {\n                            this.__base.apply(this, arguments);\n                            spy3.apply(this, arguments);\n                        },\n\n                        'v2' : function() {\n                            this.__base.apply(this, arguments);\n                            spy4.apply(this, arguments);\n                        }\n                    }\n                }\n            });\n\n            var block = Block.create();\n\n            block.setMod('m1', 'v1');\n            spy1.should.have.been.called;\n            spy2.should.not.have.been.called;\n            spy3.should.not.have.been.called;\n            spy4.should.not.have.been.called;\n\n            block.setMod('m1', 'v2');\n            spy1.should.have.been.calledOnce;\n            spy2.should.have.been.called;\n            spy3.should.not.have.been.called;\n            spy4.should.have.been.called;\n\n            block\n                .setMod('m1', 'v3')\n                .setMod('m1', 'v2');\n            spy1.should.have.been.calledOnce;\n            spy2.should.have.been.calledOnce;\n            spy3.should.not.have.been.called;\n            spy4.should.have.been.calledTwice;\n        });\n    });\n\n    describe('beforeSetMod/onSetMod for boolean mods', function() {\n        it('should call properly matched callbacks for boolean mods by order', function() {\n            var order = [],\n                spyMod1Val2 = sinon.spy(),\n                spyMod2ValFalse = sinon.spy(),\n                spyMod2Val2 = sinon.spy();\n\n            bem.declBlock('block', {\n                beforeSetMod : {\n                    'mod1' : {\n                        'true' : function(modName, modVal, oldModVal) {\n                            modVal.should.be.true;\n                            oldModVal.should.be.equal('');\n                            order.push(5);\n                        }\n                    }\n                },\n\n                onSetMod : {\n                    'mod1' : {\n                        'true' : function() {\n                            order.push(11);\n                        }\n                    }\n                }\n            });\n\n            bem.declBlock('block', {\n                beforeSetMod : {\n                    'mod1' : function() {\n                        order.push(3);\n                    },\n\n                    '*' : function(modName) {\n                        modName === 'mod1' && order.push(1);\n                    }\n                },\n\n                onSetMod : {\n                    'mod1' : function() {\n                        order.push(9);\n                    },\n\n                    '*' : function(modName) {\n                        modName === 'mod1' && order.push(7);\n                    }\n                }\n            });\n\n            bem.declBlock('block', {\n                beforeSetMod : function(modName) {\n                    this.__base.apply(this, arguments);\n                    modName === 'mod1' && order.push(2);\n                },\n\n                onSetMod : function(modName) {\n                    this.__base.apply(this, arguments);\n                    modName === 'mod1' && order.push(8);\n                }\n            });\n\n            bem.declBlock('block', {\n                beforeSetMod : {\n                    'mod1' : {\n                        '*' : function(modName, modVal, oldModVal) {\n                           this.__base.apply(this, arguments);\n                           order.push(4);\n                        },\n\n                        'true' : function() {\n                            this.__base.apply(this, arguments);\n                           order.push(6);\n\n                        },\n                        'val2' : function() {\n                           spyMod1Val2();\n                        }\n                    },\n\n                    'mod2' : {\n                        '' : function(modName, modVal, oldModVal) {\n                            modVal.should.be.equal('');\n                            oldModVal.should.be.true;\n                            spyMod2ValFalse();\n                        },\n\n                       'val2' : function() {\n                            spyMod2Val2();\n                       }\n                   }\n                },\n\n                onSetMod : {\n                    'mod1' : {\n                       '*' : function() {\n                           this.__base.apply(this, arguments);\n                           order.push(10);\n                       },\n\n                       'true' : function() {\n                            this.__base.apply(this, arguments);\n                           order.push(12);\n                       },\n\n                       'val2' : spyMod1Val2\n                    },\n\n                    'mod2' : {\n                        '' : spyMod2ValFalse,\n                        'val2' : spyMod2Val2\n                    }\n                }\n            });\n\n            var block = bem.entities['block'].create({ mod1 : false, mod2 : true });\n            block.setMod('mod1', true);\n\n            spyMod1Val2.should.not.have.been.called;\n            spyMod2ValFalse.should.not.have.been.called;\n            spyMod2Val2.should.not.have.been.called;\n\n            order.should.be.eql([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);\n\n            block.setMod('mod2', false);\n            spyMod2ValFalse.should.have.been.calledTwice;\n        });\n    });\n\n    describe('_nextTick', function() {\n        var block;\n        beforeEach(function() {\n            block = bem\n                .declBlock('block', {})\n                .create({ mod1 : 'val1' });\n        });\n\n        it('should call callback asynchronously', function(done) {\n            var isAsync = false;\n            block._nextTick(function() {\n                isAsync.should.be.true;\n                done();\n            });\n            isAsync = true;\n        });\n\n        it('should call callback with block\\'s context', function(done) {\n            block._nextTick(function() {\n                this.should.be.equal(block);\n                done();\n            });\n        });\n\n        it('should not call callback if block destructed', function(done) {\n            var spy = sinon.spy();\n            block._nextTick(spy);\n            block.delMod('js');\n            setTimeout(function() {\n                spy.called.should.be.false;\n                done();\n            }, 0);\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem/i-bem.vanilla.js",
    "content": "/**\n * @module i-bem\n */\n\nimport bemInternal from 'bem:i-bem__internal'\nimport inherit from 'bem:inherit'\nimport identify from 'bem:identify'\nimport nextTick from 'bem:next-tick'\nimport objects from 'bem:objects'\nimport functions from 'bem:functions'\n\nconst ELEM_DELIM = bemInternal.ELEM_DELIM\n\n/**\n * Storage for block init functions\n * @private\n * @type Array\n */\nlet initFns = []\n\n/**\n * Storage for block declarations (hash by block name)\n * @private\n * @type Object\n */\nconst entities = {}\n\n/**\n * Builds the name of the handler method for setting a modifier\n * @param {String} prefix\n * @param {String} modName Modifier name\n * @param {String} modVal Modifier value\n * @returns {String}\n */\nfunction buildModFnName(prefix, modName, modVal) {\n    return '__' + prefix +\n       '__mod' +\n       (modName? '_' + modName : '') +\n       (modVal? '_' + modVal : '')\n}\n\n/**\n * Builds the function for the handler method for setting a modifier\n * for special syntax\n * @param {String} modVal Declared modifier value\n * @param {Function} curModFn Declared modifier handler\n * @param {Function} [prevModFn] Previous handler\n * @param {Function} [condition] Condition function\n * (called with declared, set and previous modifier values)\n * @returns {Function}\n */\nfunction buildSpecialModFn(modVal, curModFn, prevModFn, condition) {\n    return prevModFn || condition?\n        function(_modName, _modVal, _prevModVal) {\n            let res1, res2\n            prevModFn &&\n                (res1 = prevModFn.apply(this, arguments) === false)\n            ;(condition? condition(modVal, _modVal, _prevModVal) : true) &&\n                (res2 = curModFn.apply(this, arguments) === false)\n            if(res1 || res2) return false\n        } :\n        curModFn\n}\n\nconst specialModConditions = {\n    '!' : function(modVal, _modVal, _prevModVal) {\n        return _modVal !== modVal\n    },\n    '~' : function(modVal, _modVal, _prevModVal) {\n        return _prevModVal === modVal\n    }\n}\n\n/**\n * Transforms a hash of modifier handlers to methods\n * @param {String} prefix\n * @param {Object} modFns\n * @param {Object} props\n */\nfunction modFnsToProps(prefix, modFns, props) {\n    if(functions.isFunction(modFns)) {\n        props[buildModFnName(prefix, '*', '*')] = modFns\n    } else {\n        for(const [modName, modFn] of Object.entries(modFns)) {\n            if(functions.isFunction(modFn)) {\n                props[buildModFnName(prefix, modName, '*')] = modFn\n            } else {\n                const starModFnName = buildModFnName(prefix, modName, '*')\n                for(const [modValKey, curModFn] of Object.entries(modFn)) {\n                    let modVal = modValKey\n                    const modValPrefix = modVal[0]\n\n                    if(modValPrefix === '!' || modValPrefix === '~' || modVal === '*') {\n                        modVal === '*' || (modVal = modVal.substr(1))\n                        props[starModFnName] = buildSpecialModFn(\n                            modVal,\n                            curModFn,\n                            props[starModFnName],\n                            specialModConditions[modValPrefix])\n                    } else {\n                        props[buildModFnName(prefix, modName, modVal)] = curModFn\n                    }\n                }\n            }\n        }\n    }\n}\n\nfunction buildCheckMod(modName, modVal) {\n    /* treat modVal: false as modVal: '' (modifier removal) — #1457 */\n    if(modVal === false) modVal = ''\n    return modVal != null?\n        Array.isArray(modVal)?\n            function(block) {\n                for(const val of modVal)\n                    if(checkMod(block, modName, val))\n                        return true\n                return false\n            } :\n            function(block) {\n                return checkMod(block, modName, modVal)\n            } :\n        function(block) {\n            return checkMod(block, modName, true)\n        }\n}\n\nfunction checkMod(block, modName, modVal) {\n    const prevModVal = block._processingMods[modName]\n\n    // check if a block has either current or previous modifier value equal to passed modVal\n    return modVal === '*'?\n        block.hasMod(modName) || prevModVal != null :\n        block.hasMod(modName, modVal) || prevModVal === modVal\n}\n\nfunction convertModHandlersToMethods(props) {\n    for(const [key, prefix] of [['beforeSetMod', 'before'], ['onSetMod', 'after']]) {\n        if(props[key]) {\n            modFnsToProps(prefix, props[key], props)\n            delete props[key]\n        }\n    }\n}\n\nfunction declEntity(baseCls, entityName, base, props, staticProps) {\n    base || (base = entities[entityName] || baseCls)\n\n    Array.isArray(base) || (base = [base])\n\n    if(!base[0].__bemEntity) {\n        base = base.slice()\n        base.unshift(entities[entityName] || baseCls)\n    }\n\n    props && convertModHandlersToMethods(props)\n\n    let entityCls\n\n    entityName === base[0].getEntityName()?\n        // makes a new \"init\" if the old one was already executed\n        (entityCls = inherit.self(base, props, staticProps))._processInit(true) :\n        (entityCls = entities[entityName] = inherit(base, props, staticProps))\n\n    return entityCls\n}\n\n/**\n * @class BemEntity\n * @description Base block for creating BEM blocks\n */\nconst BemEntity = inherit(/** @lends BemEntity.prototype */ {\n    /**\n     * @constructor\n     * @private\n     * @param {Object} mods BemEntity modifiers\n     * @param {Object} params BemEntity parameters\n     * @param {Boolean} [initImmediately=true]\n     */\n    __constructor : function(mods, params, initImmediately) {\n        /**\n         * Cache of modifiers\n         * @member {Object}\n         * @private\n         */\n        this._modCache = mods || {}\n\n        /**\n         * Current modifiers in the stack\n         * @member {Object}\n         * @private\n         */\n        this._processingMods = {}\n\n        /**\n         * BemEntity parameters, taking into account the defaults\n         * @member {Object}\n         * @readonly\n         */\n        this.params = objects.extend(this._getDefaultParams(), params)\n\n        /**\n         * @member {String} Unique entity ID\n         * @private\n         */\n        this._uniqId = this.params.uniqId || identify(this)\n\n        initImmediately !== false?\n            this._setInitedMod() :\n            initFns.push(this._setInitedMod, this)\n    },\n\n    /**\n     * Initializes a BEM entity\n     * @private\n     */\n    _setInitedMod : function() {\n        return this.setMod('js', 'inited')\n    },\n\n    /**\n     * Checks whether a BEM entity has a modifier\n     * @param {String} modName Modifier name\n     * @param {String|Boolean} [modVal] Modifier value. If not of type String or Boolean, it is casted to String\n     * @returns {Boolean}\n     */\n    hasMod : function(modName, modVal) {\n        const typeModVal = typeof modVal\n        typeModVal === 'undefined' || typeModVal === 'boolean' || (modVal = modVal.toString())\n\n        const res = this.getMod(modName) === (modVal || '')\n        return arguments.length === 1? !res : res\n    },\n\n    /**\n     * Returns the value of the modifier of the BEM entity\n     * @param {String} modName Modifier name\n     * @returns {String} Modifier value\n     */\n    getMod : function(modName) {\n        const modCache = this._modCache\n        return modName in modCache?\n            modCache[modName] || '' :\n            modCache[modName] = this._extractModVal(modName)\n    },\n\n    /**\n     * Sets the modifier for a BEM entity\n     * @param {String} modName Modifier name\n     * @param {String|Boolean} [modVal=true] Modifier value. If not of type String or Boolean, it is casted to String\n     * @returns {BemEntity} this\n     */\n    setMod : function(modName, modVal) {\n        const typeModVal = typeof modVal\n        if(typeModVal === 'undefined') {\n            modVal = true\n        } else if(typeModVal === 'boolean') {\n            modVal === false && (modVal = '')\n        } else {\n            modVal = modVal.toString()\n        }\n\n        if(this._processingMods[modName] != null) return this\n\n        const curModVal = this.getMod(modName)\n        if(curModVal === modVal) return this\n\n        this._processingMods[modName] = curModVal\n\n        let needSetMod = true\n        const modFnParams = [modName, modVal, curModVal]\n        const modVars = [['*', '*'], [modName, '*'], [modName, modVal]]\n        const prefixes = ['before', 'after']\n\n        for(const prefix of prefixes) {\n            for(const modVar of modVars) {\n                if(this._callModFn(prefix, modVar[0], modVar[1], modFnParams) === false) {\n                    needSetMod = false\n                    break\n                }\n            }\n\n            if(!needSetMod) break\n\n            if(prefix === 'before') {\n                this._modCache[modName] = modVal\n                this._onSetMod(modName, modVal, curModVal)\n            }\n        }\n\n        this._processingMods[modName] = null\n        needSetMod && this._afterSetMod(modName, modVal, curModVal)\n\n        return this\n    },\n\n    /**\n     * Sets multiple modifiers at once\n     * @param {Object} mods Hash of modifiers (modName: modVal)\n     * @returns {BemEntity} this\n     */\n    setMods : function(mods) {\n        for(const modName of Object.keys(mods))\n            this.setMod(modName, mods[modName])\n        return this\n    },\n\n    /**\n     * @protected\n     * @param {String} modName Modifier name\n     * @param {String} modVal Modifier value\n     * @param {String} oldModVal Old modifier value\n     */\n    _onSetMod : function(modName, modVal, oldModVal) {},\n\n    /**\n     * @protected\n     * @param {String} modName Modifier name\n     * @param {String} modVal Modifier value\n     * @param {String} oldModVal Old modifier value\n     */\n    _afterSetMod : function(modName, modVal, oldModVal) {},\n\n    /**\n     * Sets a modifier for a BEM entity, depending on conditions.\n     * If the condition parameter is passed: when true, modVal1 is set; when false, modVal2 is set.\n     * If the condition parameter is not passed: modVal1 is set if modVal2 was set, or vice versa.\n     * @param {String} modName Modifier name\n     * @param {String} [modVal1=true] First modifier value, optional for boolean modifiers\n     * @param {String} [modVal2] Second modifier value\n     * @param {Boolean} [condition] Condition\n     * @returns {BemEntity} this\n     */\n    toggleMod : function(modName, modVal1, modVal2, condition) {\n        if(typeof modVal1 === 'undefined') {\n            modVal1 = true // boolean mod\n        } else if(typeof modVal1 !== 'boolean') {\n            modVal1 = modVal1.toString()\n        }\n\n        if(typeof modVal2 === 'undefined') {\n            modVal2 = ''\n        } else if(typeof modVal2 === 'boolean') {\n            condition = modVal2\n            modVal2 = ''\n        } else {\n            modVal2 = modVal2.toString()\n        }\n\n        const modVal = this.getMod(modName)\n        ;(modVal === modVal1 || modVal === modVal2) &&\n            this.setMod(\n                modName,\n                typeof condition === 'boolean'?\n                    (condition? modVal1 : modVal2) :\n                    this.hasMod(modName, modVal1)? modVal2 : modVal1)\n\n        return this\n    },\n\n    /**\n     * Removes a modifier from a BEM entity\n     * @param {String} modName Modifier name\n     * @returns {BemEntity} this\n     */\n    delMod : function(modName) {\n        return this.setMod(modName, '')\n    },\n\n    /**\n     * Executes handlers for setting modifiers\n     * @private\n     * @param {String} prefix\n     * @param {String} modName Modifier name\n     * @param {String} modVal Modifier value\n     * @param {Array} modFnParams Handler parameters\n     */\n    _callModFn : function(prefix, modName, modVal, modFnParams) {\n        const modFnName = buildModFnName(prefix, modName, modVal)\n        return this[modFnName]?\n           this[modFnName].apply(this, modFnParams) :\n           undefined\n    },\n\n    _extractModVal : function(modName) {\n        return ''\n    },\n\n    /**\n     * Returns a BEM entity's default parameters\n     * @protected\n     * @returns {Object}\n     */\n    _getDefaultParams : function() {\n        return {}\n    },\n\n    /**\n     * Executes given callback on next turn eventloop in BEM entity's context\n     * @protected\n     * @param {Function} fn callback\n     * @returns {BemEntity} this\n     */\n    _nextTick : function(fn) {\n        nextTick(() => {\n            this.hasMod('js', 'inited') && fn.call(this)\n        })\n        return this\n    }\n}, /** @lends BemEntity */{\n    /**\n     * Factory method for creating an instance\n     * @param {Object} mods modifiers\n     * @param {Object} params params\n     * @returns {BemEntity}\n     */\n    create : function(mods, params) {\n        return new this(mods, params)\n    },\n\n    /**\n     * Declares modifier\n     * @param {Object} mod\n     * @param {String} mod.modName\n     * @param {String|Boolean|Array} [mod.modVal]\n     * @param {Object} props\n     * @param {Object} [staticProps]\n     * @returns {Function}\n     */\n    declMod : function(mod, props, staticProps) {\n        props && convertModHandlersToMethods(props)\n\n        const checkMod = buildCheckMod(mod.modName, mod.modVal)\n        const basePtp = this.prototype\n\n        objects.each(props, function(prop, name) {\n            functions.isFunction(prop) &&\n                (props[name] = function() {\n                    let method\n                    if(checkMod(this)) {\n                        method = prop\n                    } else {\n                        const baseMethod = basePtp[name]\n                        baseMethod && baseMethod !== prop &&\n                            (method = this.__base)\n                    }\n                    return method?\n                        method.apply(this, arguments) :\n                        undefined\n                })\n        })\n\n        return inherit.self(this, props, staticProps)\n    },\n\n    __bemEntity : true,\n\n    _name : null,\n\n    /**\n     * Processes a BEM entity's init\n     * @private\n     * @param {Boolean} [heedInit=false] Whether to take into account that the BEM entity already processed its init property\n     */\n    _processInit : function(heedInit) {\n        this._inited = true\n    },\n\n    /**\n     * Returns the name of the current BEM entity\n     * @returns {String}\n     */\n    getName : function() {\n        return this._name\n    },\n\n    /**\n     * Returns the name of the current BEM entity\n     * @returns {String}\n     */\n    getEntityName : function() {\n        return this._name\n    }\n})\n\n/**\n * @class Block\n * @description Class for creating BEM blocks\n * @augments BemEntity\n */\nconst Block = BemEntity\n\n/**\n * @class Elem\n * @description Class for creating BEM elems\n * @augments BemEntity\n */\nconst Elem = inherit(BemEntity, /** @lends Elem.prototype */ {\n    /**\n     * Returns the own block of current element\n     * @protected\n     * @returns {Block}\n     */\n    _block : function() {\n        return this._blockInstance\n    }\n}, /** @lends Elem */{\n    /**\n     * Factory method for creating an instance\n     * @param {Object} block block instance\n     * @param {Object} mods modifiers\n     * @param {Object} params params\n     * @returns {BemEntity}\n     */\n    create : function(block, mods, params) {\n        const res = new this(mods, params)\n        res._blockInstance = block\n        return res\n    },\n\n    /**\n     * Returns the name of the current BEM entity\n     * @returns {String}\n     */\n    getEntityName : function() {\n        return this._blockName + ELEM_DELIM + this._name\n    }\n})\n\nexport default {\n    /**\n     * Block class\n     * @type Function\n     */\n    Block,\n\n    /**\n     * Elem class\n     * @type Function\n     */\n    Elem,\n\n    /**\n     * Storage for block declarations (hash by block name)\n     * @type Object\n     */\n    entities,\n\n    /**\n     * Declares block and creates a block class\n     * @param {String|Function} blockName Block name or block class\n     * @param {Function|Array.<Function>} [base] base block + mixes\n     * @param {Object} [props] Methods\n     * @param {Object} [staticProps] Static methods\n     * @returns {Function} Block class\n     */\n    declBlock(blockName, base, props, staticProps) {\n        if(typeof base === 'object' && !Array.isArray(base)) {\n            staticProps = props\n            props = base\n            base = undefined\n        }\n\n        let baseCls = Block\n        if(typeof blockName !== 'string') {\n            baseCls = blockName\n            blockName = blockName.getEntityName()\n        }\n\n        const res = declEntity(baseCls, blockName, base, props, staticProps)\n        res._name = res._blockName = blockName\n        return res\n    },\n\n    /**\n     * Declares elem and creates an elem class\n     * @param {String} [blockName] Block name\n     * @param {String|Function} elemName Elem name or elem class\n     * @param {Function|Function[]} [base] base elem + mixes\n     * @param {Object} [props] Methods\n     * @param {Object} [staticProps] Static methods\n     * @returns {Function} Elem class\n     */\n    declElem(blockName, elemName, base, props, staticProps) {\n        let baseCls = Elem\n        let entityName\n\n        if(typeof blockName !== 'string') {\n            staticProps = props\n            props = base\n            base = elemName\n            elemName = blockName._name\n            baseCls = blockName\n            blockName = baseCls._blockName\n            entityName = baseCls.getEntityName()\n        } else {\n            entityName = blockName + ELEM_DELIM + elemName\n        }\n\n        if(typeof base === 'object' && !Array.isArray(base)) {\n            staticProps = props\n            props = base\n            base = undefined\n        }\n\n        const res = declEntity(baseCls, entityName, base, props, staticProps)\n        res._blockName = blockName\n        res._name = elemName\n        return res\n    },\n\n    /**\n     * Declares mixin\n     * @param {Object} [props] Methods\n     * @param {Object} [staticProps] Static methods\n     * @returns {Function} mix\n     */\n    declMixin(props, staticProps) {\n        convertModHandlersToMethods(props || (props = {}))\n        return inherit(props, staticProps)\n    },\n\n    /**\n     * Executes the block init functions\n     * @private\n     */\n    _runInitFns() {\n        if(initFns.length) {\n            const fns = initFns\n\n            initFns = []\n            for(let i = 0; i < fns.length; i += 2) {\n                fns[i].call(fns[i + 1])\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__collection/i-bem-dom__collection.deps.js",
    "content": "[{\n    shouldDeps : [\n        'inherit',\n        { block : 'i-bem', elem : 'collection' }\n    ]\n},\n{\n    tech : 'spec.js',\n    shouldDeps : [\n        { block : 'i-bem', tech : 'js' },\n        { block : 'objects', tech : 'js' },\n        { block : 'i-bem-dom', tech : 'js' }\n    ]\n}]\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__collection/i-bem-dom__collection.js",
    "content": "/**\n * @module i-bem-dom__collection\n */\nimport inherit from 'bem:inherit'\nimport BemCollection from 'bem:i-bem__collection'\n\n/**\n * @class BemDomCollection\n */\nconst BemDomCollection = inherit(BemCollection, /** @lends BemDomCollection.prototype */{\n    /**\n     * Finds the first child block for every entities in collection\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {BemDomCollection}\n     */\n    findChildBlock : buildProxyMethodForOne('findChildBlock'),\n\n    /**\n     * Finds child block for every entities in collections\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {BemDomCollection}\n     */\n    findChildBlocks : buildProxyMethodForMany('findChildBlocks'),\n\n    /**\n     * Finds the first parent block for every entities in collection\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {BemDomCollection}\n     */\n    findParentBlock : buildProxyMethodForOne('findParentBlock'),\n\n    /**\n     * Finds parent block for every entities in collections\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {BemDomCollection}\n     */\n    findParentBlocks : buildProxyMethodForMany('findParentBlocks'),\n\n    /**\n     * Finds first mixed bloc for every entities in collectionk\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {BemDomCollection}\n     */\n    findMixedBlock : buildProxyMethodForOne('findMixedBlock'),\n\n    /**\n     * Finds mixed block for every entities in collections\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {BemDomCollection}\n     */\n    findMixedBlocks : buildProxyMethodForMany('findMixedBlocks'),\n\n    /**\n     * Finds the first child elemen for every entities in collectiont\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @param {Boolean} [strictMode=false]\n     * @returns {BemDomCollection}\n     */\n    findChildElem : buildProxyMethodForOne('findChildElem'),\n\n    /**\n     * Finds child element for every entities in collections\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @param {Boolean} [strictMode=false]\n     * @returns {BemDomCollection}\n     */\n    findChildElems : buildProxyMethodForMany('findChildElems'),\n\n    /**\n     * Finds the first parent elemen for every entities in collectiont\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @param {Boolean} [strictMode=false]\n     * @returns {BemDomCollection}\n     */\n    findParentElem : buildProxyMethodForOne('findParentElem'),\n\n    /**\n     * Finds parent element for every entities in collections\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @param {Boolean} [strictMode=false]\n     * @returns {BemDomCollection}\n     */\n    findParentElems : buildProxyMethodForMany('findParentElems'),\n\n    /**\n     * Finds the first mixed elemen for every entities in collectiont\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @returns {BemDomCollection}\n     */\n    findMixedElem : buildProxyMethodForOne('findMixedElem'),\n\n    /**\n     * Finds mixed element for every entities in collections\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @returns {BemDomCollection}\n     */\n    findMixedElems : buildProxyMethodForMany('findMixedElems'),\n\n    /**\n     * Checks whether any entity in collection has a modifier.\n     * @param {String} modName Modifier name\n     * @param {String|Boolean} [modVal] Modifier value\n     * @returns {Boolean}\n     */\n    hasMod : buildAggregateProxyFn('some', 'hasMod'),\n\n    /**\n     * Returns an array of modifier values from all entities in collection.\n     * @param {String} modName Modifier name\n     * @returns {Array}\n     */\n    getMod : buildMapProxyFn('getMod'),\n\n    /**\n     * Checks whether any entity in collection contains the specified entity.\n     * @param {BemDomEntity} entity entity to check\n     * @returns {Boolean}\n     */\n    containsEntity : buildAggregateProxyFn('some', 'containsEntity')\n})\n\nfunction collectionMapMethod(collection, methodName, args) {\n    return collection.map((entity) => entity[methodName].apply(entity, args))\n}\n\nfunction buildProxyMethodForOne(methodName) {\n    return function() {\n        return new BemDomCollection(collectionMapMethod(this, methodName, arguments))\n    }\n}\n\nfunction buildAggregateProxyFn(arrayMethodName, entityMethodName) {\n    return function(...args) {\n        return this._entities[arrayMethodName]((entity) =>\n            entity[entityMethodName].apply(entity, args))\n    }\n}\n\nfunction buildMapProxyFn(entityMethodName) {\n    return function(...args) {\n        return this._entities.map((entity) =>\n            entity[entityMethodName].apply(entity, args))\n    }\n}\n\nfunction buildProxyMethodForMany(methodName) {\n    return function() {\n        const res = []\n\n        collectionMapMethod(this, methodName, arguments).forEach((collection) => {\n            collection.forEach((entity) => {\n                res.push(entity)\n            })\n        })\n\n        return new BemDomCollection(res)\n    }\n}\n\nexport default BemDomCollection\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__collection/i-bem-dom__collection.spec.js",
    "content": "modules.define('spec', [\n    'i-bem',\n    'i-bem-dom',\n    'i-bem-dom__collection',\n    'objects',\n    'BEMHTML'\n], function(provide,\n    bem,\n    bemDom,\n    BemDomCollection,\n    objects,\n    BEMHTML\n) {\n\ndescribe('BEM collections', function() {\n    var rootNode;\n\n    afterEach(function() {\n        if(rootNode) {\n            bemDom.destruct(rootNode);\n            rootNode = null;\n        }\n\n        objects.each(bem.entities, function(_, entityName) {\n            delete bem.entities[entityName];\n        });\n    });\n\n    describe('find', function() {\n        var rootBlock, B1Block, B2Block, B3Block;\n\n        beforeEach(function() {\n            var RootBlock = bemDom.declBlock('root');\n            B1Block = bemDom.declBlock('b1');\n            B2Block = bemDom.declBlock('b2');\n            B3Block = bemDom.declBlock('b3');\n\n            rootNode = bemDom.init(BEMHTML.apply({\n                block : 'root',\n                content : [\n                    {\n                        block : 'b1',\n                        js : { id : 'b1_1' },\n                        mix : { block : 'b3', js : { id : 'b3_1' } },\n                        content : [\n                            {\n                                block : 'b2',\n                                js : { id : 'b2_1' }\n                            },\n                            {\n                                block : 'b2',\n                                js : { id : 'b2_2' }\n                            },\n                        ]\n                    },\n                    {\n                        block : 'b1',\n                        js : { id : 'b1_2' },\n                        mix : { block : 'b3', js : { id : 'b3_2' } },\n                        content : [\n                            {\n                                block : 'b2',\n                                js : { id : 'b2_3' }\n                            },\n                            {\n                                block : 'b2',\n                                js : { id : 'b2_4' }\n                            },\n                        ]\n                    }\n                ]\n            }));\n\n            rootBlock = rootNode.bem(RootBlock);\n        });\n\n        describe('findChildBlock', function() {\n            it('should find the first child block for every entities in collection', function() {\n                var res = rootBlock.findChildBlocks(B1Block).findChildBlock(B2Block);\n\n                res.should.be.instanceOf(BemDomCollection);\n                getEntityIds(res).should.be.eql(['b2_1', 'b2_3']);\n            });\n        });\n\n        describe('findChildBlocks', function() {\n            it('should find child block for every entities in collection', function() {\n                var res = rootBlock.findChildBlocks(B1Block).findChildBlocks(B2Block);\n\n                res.should.be.instanceOf(BemDomCollection);\n                getEntityIds(res).should.be.eql(['b2_1', 'b2_2', 'b2_3', 'b2_4']);\n            });\n        });\n\n        describe('findParentBlock', function() {\n            it('should find the first parent block for every entities in collection', function() {\n                var res = rootBlock.findChildBlocks(B2Block).findParentBlock(B1Block);\n\n                res.should.be.instanceOf(BemDomCollection);\n                getEntityIds(res).should.be.eql(['b1_1', 'b1_2']);\n            });\n        });\n\n        describe('findParentBlocks', function() {\n            it('should find parent block for every entities in collection', function() {\n                var res = rootBlock.findChildBlocks(B2Block).findParentBlocks(B1Block);\n\n                res.should.be.instanceOf(BemDomCollection);\n                getEntityIds(res).should.be.eql(['b1_1', 'b1_2']);\n            });\n        });\n\n        describe('findMixedBlock', function() {\n            it('should find the first parent block for every entities in collection', function() {\n                var res = rootBlock.findChildBlocks(B1Block).findMixedBlock(B3Block);\n\n                res.should.be.instanceOf(BemDomCollection);\n                getEntityIds(res).should.be.eql(['b3_1', 'b3_2']);\n            });\n        });\n\n        describe('findMixedBlocks', function() {\n            it('should find parent block for every entities in collection', function() {\n                var res = rootBlock.findChildBlocks(B1Block).findMixedBlocks(B3Block);\n\n                res.should.be.instanceOf(BemDomCollection);\n                getEntityIds(res).should.be.eql(['b3_1', 'b3_2']);\n            });\n        });\n    });\n});\n\nfunction getEntityIds(entities) {\n    return entities.map(function(entity) {\n        return entity.params && entity.params.id;\n    });\n}\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_bem.deps.js",
    "content": "[{\n    shouldDeps : [\n        'inherit',\n        'jquery',\n        'events',\n        { block : 'i-bem', elem : 'internal' }\n    ]\n},\n{\n    tech : 'spec.js',\n    shouldDeps : [\n        {\n            block : 'i-bem',\n            tech : 'js'\n        },\n        {\n            block : 'i-bem-dom',\n            tech : 'js'\n        },\n        {\n            block : 'events',\n            tech : 'js'\n        }\n    ]\n}]\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_bem.js",
    "content": "/**\n * @module i-bem-dom__events_type_bem\n */\nimport bemDomEvents from 'bem:i-bem-dom__events';\nimport bemInternal from 'bem:i-bem__internal';\nimport inherit from 'bem:inherit';\nimport functions from 'bem:functions';\nimport $ from 'bem:jquery';\nimport events from 'bem:events';\n\nconst EVENT_PREFIX = '__bem__',\n    MOD_CHANGE_EVENT = 'modchange',\n\n    specialEvents = $.event.special,\n    specialEventsStorage = {},\n\n    createSpecialEvent = function(event) {\n        return {\n            setup : function() {\n                specialEventsStorage[event] || (specialEventsStorage[event] = true);\n            },\n            teardown : functions.noop\n        };\n    },\n\n    eventBuilder = function(e, params) {\n        const event = EVENT_PREFIX + params.bindEntityCls.getEntityName() +\n            (typeof e === 'object'?\n                e instanceof events.Event?\n                    e.type :\n                    /* treat modVal: false as modVal: '' (modifier removal) — #1457 */\n                    bemInternal.buildModPostfix(e.modName, e.modVal === false? '' : e.modVal) :\n                e);\n\n        specialEvents[event] ||\n            (specialEvents[event] = createSpecialEvent(event));\n\n        return event;\n    },\n\n    /**\n     * @class EventManagerFactory\n     * @augments i-bem-dom__events:EventManagerFactory\n     * @exports i-bem-dom__events_type_bem:EventManagerFactory\n     */\n    EventManagerFactory = inherit(bemDomEvents.EventManagerFactory,/** @lends EventManagerFactory.prototype */{\n        /** @override */\n        _createEventManager : function(ctx, params, isInstance) {\n            function wrapperFn(fn, fnCtx, fnId) {\n                return function(e, data, flags, originalEvent) {\n                    if(flags.fns[fnId]) return;\n\n                    let instance,\n                        instanceDomElem;\n\n                    if(isInstance) {\n                        instance = ctx;\n                        instanceDomElem = instance.domElem;\n                    } else {\n                        // TODO: we could optimize all these \"closest\" to a single traversing\n                        instanceDomElem = $(e.target).closest(params.ctxSelector);\n                        instanceDomElem.length && (instance = instanceDomElem.bem(ctx));\n                    }\n\n                    // Skip events that bubbled up from nested blocks of the same type (#1525)\n                    if(!params.bindSelector &&\n                        params.bindDomElem.index(e.target) < 0) return\n\n                    if(instance &&\n                        (!flags.propagationStoppedDomNode ||\n                            !$.contains(instanceDomElem[0], flags.propagationStoppedDomNode))) {\n                        originalEvent.data = e.data;\n                        // TODO: do we really need both target and bemTarget?\n                        originalEvent.bemTarget = originalEvent.target;\n                        flags.fns[fnId] = true;\n                        fn.call(fnCtx || instance, originalEvent, data);\n\n                        if(originalEvent.isPropagationStopped()) {\n                            e.stopPropagation();\n                            flags.propagationStoppedDomNode = instanceDomElem[0];\n                        }\n                    }\n                };\n            }\n\n            return new this._eventManagerCls(params, wrapperFn, eventBuilder);\n        }\n    });\n\nexport default {\n    /**\n     * Emits BEM event\n     * @augments i-bem-dom__events_type_bem\n     * @param {BemDomEntity} ctx\n     * @param {String|Object|events:Event} e Event name\n     * @param {Object} [data]\n     */\n    emit : function(ctx, e, data) {\n        let originalEvent;\n        if(typeof e === 'string') {\n            originalEvent = new events.Event(e, ctx);\n        } else if(e.modName) {\n            originalEvent = new events.Event(MOD_CHANGE_EVENT, ctx);\n        } else if(!e.target) {\n            e.target = ctx;\n            originalEvent = e;\n        }\n\n        const event = eventBuilder(e, { bindEntityCls : ctx.__self });\n\n        specialEventsStorage[event] &&\n            ctx.domElem.trigger(event, [data, { fns : {}, propagationStoppedDomNode : null }, originalEvent]);\n    },\n\n    EventManagerFactory : EventManagerFactory\n};\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_bem.spec.js",
    "content": "modules.define('spec', [\n    'i-bem',\n    'i-bem-dom',\n    'objects',\n    'events',\n    'jquery',\n    'chai',\n    'sinon',\n    'BEMHTML'\n], function(provide,\n    bem,\n    bemDom,\n    objects,\n    events,\n    $,\n    chai,\n    sinon,\n    BEMHTML\n) {\n\ndescribe('BEM events', function() {\n    var Block1, Block2, Block3, block1, spy1, spy2, spy3, spy4, spy5, spy6, spy7, spy8, spy9,\n        wrapSpy = function(spy) {\n            return function(e) {\n                // NOTE: we need to pass bemTarget and data explicitly, as `e` is being\n                // changed while event is propagating\n                spy.call(this, e, e.bemTarget, e.data);\n            };\n        },\n        data = { data : 'data' };\n\n    beforeEach(function() {\n        spy1 = sinon.spy();\n        spy2 = sinon.spy();\n        spy3 = sinon.spy();\n        spy4 = sinon.spy();\n        spy5 = sinon.spy();\n        spy6 = sinon.spy();\n        spy7 = sinon.spy();\n        spy8 = sinon.spy();\n        spy9 = sinon.spy();\n    });\n\n    afterEach(function() {\n        bemDom.destruct(bemDom.scope, true);\n\n        objects.each(bem.entities, function(_, entityName) {\n            delete bem.entities[entityName];\n        });\n    });\n\n    function initDom(bemjson) {\n        return createDomNode(bemjson).appendTo(bemDom.scope);\n    }\n\n    describe('on instance events', function() {\n        describe('block BEM events', function() {\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block1', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._events()\n                                    .on('click', spy1)\n                                    .on('click', spy2)\n                                    .on('click', data, wrapSpy(spy3))\n                                    .on({ modName : 'm1', modVal : 'v1' }, spy4)\n                                    .on({ modName : 'm1', modVal : '*' }, spy5)\n                                    .on({ modName : 'm2', modVal : true }, spy6)\n                                    .once('click', spy7);\n                            }\n                        }\n                    }\n                });\n\n                Block2 = bemDom.declBlock('block2', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._events()\n                                    .on('click', spy5);\n                            }\n                        }\n                    }\n                });\n\n                block1 = initDom({\n                    block : 'block1',\n                    mix : { block : 'block2', js : true }\n                }).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                block1._emit('click');\n\n                spy1.should.have.been.called;\n                spy2.should.have.been.called;\n\n                spy3.should.have.been.calledOn(block1);\n                var spy3args = spy3.args[0];\n                spy3args[0].should.be.instanceOf(events.Event);\n                spy3args[1].should.be.instanceOf(Block1);\n                spy3args[1].should.be.equal(block1);\n                spy3args[2].should.have.been.equal(data);\n            });\n\n            it('should properly bind once handler', function() {\n                block1._emit('click');\n                spy7.should.have.been.called;\n\n                block1._emit('click');\n                spy7.should.have.been.calledOnce;\n\n                block1._events().once('click', spy7);\n\n                block1._emit('click');\n                spy7.should.have.been.calledTwice;\n            });\n\n            it('should properly bind the same handler', function() {\n                block1._events()\n                    .on('click', spy8)\n                    .on('click', spy8);\n\n                block1._emit('click');\n                spy8.should.have.been.calledOnce;\n\n                block1._events().un('click', spy8);\n                block1._emit('click');\n                spy8.should.have.been.calledOnce;\n            });\n\n            it('should not handle homonymous dom event', function() {\n                block1.domElem.trigger('click');\n                spy1.should.not.have.been.called;\n            });\n\n            it('should not handle homonymous bem event', function() {\n                block1._domEvents().on('click', spy8);\n                block1._emit('click');\n                spy8.should.not.have.been.called;\n            });\n\n            it('should properly unbind all handlers', function() {\n                block1._events().un('click');\n                block1._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                block1._events().un('click', spy1);\n                block1._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy3.should.have.been.called;\n            });\n\n            it('should unbind only own handlers', function() {\n                block1._events().un('click');\n                block1._emit('click');\n                block1.findMixedBlock(Block2)._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n\n                spy5.should.have.been.called;\n            });\n\n            it('should properly unbind once handler', function() {\n                block1._events().un('click', spy7);\n                block1._emit('click');\n                spy7.should.not.have.been.called;\n            });\n\n            it('should properly emit event as instance (not string)', function() {\n                var e = new events.Event('click');\n                block1._emit(e);\n                spy1.args[0][0].should.be.equal(e);\n            });\n\n            it('should handle modifier change events', function() {\n                block1.setMod('m1', 'v1');\n\n                spy4.should.have.been.called;\n                spy4.args[0][0].should.be.instanceOf(events.Event);\n                var eventData = spy4.args[0][1];\n                eventData.modName.should.be.equal('m1');\n                eventData.modVal.should.be.equal('v1');\n                eventData.oldModVal.should.be.equal('');\n\n                spy5.should.have.been.called;\n\n                block1.delMod('m1');\n\n                spy4.should.have.been.calledOnce;\n                spy5.should.have.been.calledTwice;\n\n                spy6.should.not.have.been.called;\n\n                block1.setMod('m2');\n\n                spy6.should.have.been.called;\n            });\n\n            it('should emit only destructing event after destruction', function() {\n                block1._events().on({ modName : 'js', modVal : '' }, spy8);\n\n                bemDom.destruct(block1.domElem);\n                block1.setMod('m1', 'v1');\n\n                spy8.should.have.been.called;\n\n                spy4.should.not.been.called;\n                spy5.should.not.been.called;\n            });\n        });\n\n        describe('block instance events', function() {\n            var block2_1, block2_2;\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block1', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._events(block2_1 = this.findChildBlocks(Block2).get(0))\n                                    .on('click', spy1)\n                                    .on('click', spy2)\n                                    .on('click', data, wrapSpy(spy3));\n\n                                this._events(block2_2 = this.findChildBlocks(Block2).get(1))\n                                    .on('click', spy5);\n                            }\n                        }\n                    }\n                });\n\n                Block2 = bemDom.declBlock('block2');\n\n                block1 = initDom({\n                    block : 'block1',\n                    content : [\n                        { block : 'block2' },\n                        { block : 'block2' }\n                    ]\n                }).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                block2_1._emit('click');\n\n                spy1.should.have.been.called;\n                spy2.should.have.been.called;\n\n                spy3.should.have.been.calledOn(block1);\n                spy3.args[0][0].should.be.instanceOf(events.Event);\n                spy3.args[0][1].should.be.equal(block2_1);\n                spy3.args[0][2].should.be.equal(data);\n\n                spy5.should.not.have.been.called;\n            });\n\n            it('should properly unbind all handlers', function() {\n                block1._events(block2_1).un('click');\n                block2_1._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n\n                spy5.should.not.have.been.called;\n\n                block2_2._emit('click');\n\n                spy5.should.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                block1._events(block2_1).un('click', spy1);\n                block2_1._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy3.should.have.been.called;\n            });\n        });\n\n        describe('nested blocks events', function() {\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._events(Block2).on('click', wrapSpy(spy1));\n                            }\n                        }\n                    }\n                });\n\n                Block2 = bemDom.declBlock('block2');\n\n                block1 = initDom({\n                    block : 'block',\n                    content : [\n                        {\n                            block : 'block2',\n                            content : { block : 'block2' }\n                        }\n                    ]\n                }).bem(Block1);\n            });\n\n            it('should properly handle events (bound in class context) from nested block', function() {\n                var block2 = block1.findChildBlocks(Block2).get(1);\n                block2._emit('click');\n\n                spy1.should.have.been.calledOnce;\n                spy1.args[0][1].should.be.equal(block2);\n            });\n        });\n\n        describe('block elems events', function() {\n            ['string', 'Class'].forEach(function(elemType) {\n                var elem1, elem2;\n\n                describe('elem as ' + elemType, function() {\n                    var Elem1, Elem2;\n\n                    beforeEach(function() {\n                        elem1 = elemType === 'string'?\n                            'e1' :\n                            bemDom.declElem('block', 'e1');\n\n                        Block1 = bemDom.declBlock('block', {\n                            onSetMod : {\n                                'js' : {\n                                    'inited' : function() {\n                                         this._events(elem1)\n                                            .on('click', spy1)\n                                            .on('click', spy2)\n                                            .on('click', data, spy3);\n\n                                         this._events('e2').on('click', spy5);\n                                         this._events('e6').on('click', spy9);\n                                    }\n                                }\n                            }\n                        });\n\n                        Block2 = bemDom.declBlock('block2');\n\n                        Elem1 = elemType === 'string'?\n                            bemDom.declElem('block', 'e1') :\n                            elem1;\n                        Elem2 = bemDom.declElem('block', 'e2', {\n                            onSetMod : {\n                                'js' : {\n                                    'inited' : function() {\n                                        this._events(elem1)\n                                            .on('click', wrapSpy(spy6))\n                                            .on('click', spy7);\n                                    }\n                                }\n                            }\n                        });\n\n                        block1 = initDom({\n                            block : 'block',\n                            mix : { block : 'block', elem : 'e6' },\n                            content : [\n                                { elem : 'e1', content : { elem : 'e3' } },\n                                { elem : 'e2', content : { elem : 'e1' } },\n                                { elem : 'e4', js : { id : 'ie4' } },\n                                { elem : 'e4', js : { id : 'ie4' } },\n                                { elem : 'e5', content : { elem : 'e5' } }\n                            ]\n                        }).bem(Block1);\n\n                        elem2 = block1._elem('e2');\n                    });\n\n                    describe('block', function() {\n                        it('should properly handle events on elems with multiple DOM nodes', function() {\n                            block1._events('e4').on('click', spy8);\n\n                            block1._elem('e4')._emit('click');\n\n                            spy8.should.have.been.calledOnce;\n                        });\n\n                        it('should properly handle events (bound in class context) from nested elems', function() {\n                            block1._events('e5').on('click', wrapSpy(spy8));\n\n                            var nestedE5 = block1.findChildElems('e5').get(1);\n                            nestedE5._emit('click');\n\n                            spy8.should.have.been.calledOnce;\n                            spy8.args[0][0].bemTarget.domElem[0]\n                                .should.be.equal(nestedE5.domElem[0]);\n                        });\n\n                        it('should properly bind handlers', function() {\n                            block1._elem('e3')._emit('click');\n\n                            spy1.should.not.have.been.called;\n                        });\n\n                        it('should properly bind handler to mixed elem', function() {\n                            block1._elem('e6')._emit('click');\n                            spy9.should.have.been.called;\n                        });\n\n                        it('should properly unbind all handlers', function() {\n                            block1._events(elem1).un('click');\n                            block1._elem(elem1)._emit('click');\n\n                            spy1.should.not.have.been.called;\n                            spy2.should.not.have.been.called;\n                            spy3.should.not.have.been.called;\n\n                            elem2._emit('click');\n\n                            spy5.should.have.been.called;\n                        });\n\n                        it('should properly unbind specified handler', function() {\n                            block1._events(elem1).un('click', spy2);\n                            block1._elem(elem1)._emit('click');\n\n                            spy1.should.have.been.called;\n                            spy2.should.not.have.been.called;\n                            spy3.should.have.been.called;\n                        });\n\n                        it('should properly unbind handler from mixed elem', function() {\n                            block1._events('e6').un('click', spy9);\n                            block1._elem('e6')._emit('click');\n\n                            spy9.should.not.have.been.called;\n                        });\n                    });\n\n                    describe('elem instance', function() {\n                        it('should properly bind handlers', function() {\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1._emit('click');\n\n                            spy6.should.have.been.called;\n                            spy6.should.have.been.calledOn(elem2);\n                            spy6.args[0][1].should.be.instanceOf(Elem1);\n                            spy6.args[0][1].domElem[0]\n                                .should.be.equal(e2elem1.domElem[0]);\n\n                            spy7.should.have.been.called;\n                        });\n\n                        it('should properly unbind all handlers', function() {\n                            elem2._events(elem1).un('click');\n\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1._emit('click');\n\n                            spy6.should.not.have.been.called;\n                            spy7.should.not.have.been.called;\n                        });\n\n                        it('should properly unbind specified handler', function() {\n                            elem2._events(elem1).un('click', spy7);\n\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1._emit('click');\n\n                            spy6.should.have.been.called;\n                            spy7.should.not.have.been.called;\n                        });\n                    });\n                });\n\n                describe('elem as ' + elemType + ', modName, modVal', function() {\n                    beforeEach(function() {\n                        Block1 = bemDom.declBlock('block', {\n                            onSetMod : {\n                                'js' : {\n                                    'inited' : function() {\n                                        this._events({ elem : elem1 })\n                                            .on('click', spy1);\n                                        this._events({ elem : elem1, modName : 'm1', modVal : 'v1' })\n                                            .on('click', spy2)\n                                            .on('click', spy3);\n                                    }\n                                }\n                            }\n                        });\n\n                        block1 = createDomNode({\n                            block : 'block',\n                            content : [\n                                { elem : 'e1' },\n                                { elem : 'e1', elemMods : { m1 : 'v1' } }\n                            ]\n                        }).bem(Block1);\n                    });\n\n                    it('should properly bind handlers', function() {\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' })._emit('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.have.been.called;\n\n                        block1._elem('e1')._emit('click');\n\n                        spy1.should.have.been.calledTwice;\n                        spy2.should.have.been.calledOnce;\n                    });\n\n                    it('should properly unbind all handlers', function() {\n                        block1._events({ elem : elem1, modName : 'm1', modVal : 'v1' }).un('click');\n\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' })._emit('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.not.have.been.called;\n                    });\n\n                    it('should properly unbind specified handler', function() {\n                        block1._events({ elem : elem1, modName : 'm1', modVal : 'v1' }).un('click', spy2);\n\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' })._emit('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.not.have.been.called;\n                        spy3.should.have.been.called;\n                    });\n                });\n            });\n        });\n\n        describe('collection events', function() {\n            var collection, block2;\n\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block1', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                collection = this.findChildBlocks(Block2);\n                                block2 = collection.get(0);\n                                this._events(collection)\n                                    .on('click', spy1)\n                                    .on('click', spy2)\n                                    .on('click', data, wrapSpy(spy3));\n                            }\n                        }\n                    }\n                });\n\n                Block2 = bemDom.declBlock('block2');\n\n                block1 = initDom({\n                    block : 'block1',\n                    content : { block : 'block2' }\n                }).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                block2._emit('click');\n\n                spy1.should.have.been.called;\n                spy2.should.have.been.called;\n\n                spy3.should.have.been.calledOn(block1);\n                spy3.args[0][0].should.be.instanceOf(events.Event);\n                spy3.args[0][1].should.be.equal(block2);\n                spy3.args[0][2].should.be.equal(data);\n            });\n\n            it('should properly unbind all handlers', function() {\n                block1._events(collection).un('click');\n                block2._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                block1._events(collection).un('click', spy1);\n                block2._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy3.should.have.been.called;\n            });\n        });\n\n        describe('stop propagation', function() {\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._events(Block3).on('click', spy1);\n                            }\n                        }\n                    }\n                });\n\n                Block2 = bemDom.declBlock('block2', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._events(Block3).on('click', function(e) {\n                                    e.stopPropagation();\n                                    spy2();\n                                });\n                            }\n                        }\n                    }\n                });\n\n                Block3 = bemDom.declBlock('block3');\n\n                block1 = initDom({\n                    block : 'block',\n                    content : [\n                        {\n                            block : 'block2',\n                            js : true,\n                            content : { block : 'block3' }\n                        }\n                    ]\n                }).bem(Block1);\n            });\n\n            it('should properly stop propagation', function() {\n                var block3 = block1.findChildBlock(Block3);\n                block3._emit('click');\n\n                spy2.should.have.been.called;\n                spy1.should.not.have.been.called;\n            });\n        });\n    });\n\n    describe('delegated events', function() {\n        function initDom(bemjson) {\n            return createDomNode(bemjson).appendTo(bemDom.scope);\n        }\n\n        describe('block events', function() {\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block1', {}, {\n                    onInit : function() {\n                        this._events()\n                            .on('click', spy1)\n                            .on('click', spy2)\n                            .on('click', data, wrapSpy(spy3))\n                            .once('click', spy4)\n                            .on({ modName : 'm1', modVal : 'v1' }, spy6)\n                            .on({ modName : 'm1', modVal : '*' }, spy7)\n                            .on({ modName : 'm2', modVal : true }, spy8);\n                    }\n                });\n\n                Block2 = bemDom.declBlock('block2', {}, {\n                    onInit : function() {\n                        this._events().on('click', spy5);\n                    }\n                });\n\n                block1 = initDom({\n                    block : 'block1',\n                    mix : { block : 'block2', js : true }\n                }).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                block1._emit('click');\n\n                spy1.should.have.been.called;\n                spy2.should.have.been.called;\n\n                spy3.should.have.been.calledOn(block1);\n                spy3.args[0][1].should.be.instanceOf(Block1);\n                spy3.args[0][2].should.have.been.equal(data);\n\n                spy5.should.not.have.been.called;\n            });\n\n            it('should properly bind once handler', function() {\n                block1._emit('click');\n                spy4.should.have.been.called;\n\n                block1._emit('click');\n                spy4.should.have.been.calledOnce;\n\n                block1._events().once('click', spy4);\n                block1._emit('click');\n                spy4.should.have.been.calledTwice;\n            });\n\n            it('should properly unbind all handlers', function() {\n                Block1._events().un('click');\n                block1._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                Block1._events().un('click', spy1);\n                block1._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy3.should.have.been.called;\n            });\n\n            it('should unbind only own handlers', function() {\n                Block1._events().un('click');\n                block1._emit('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n\n                block1\n                    .findMixedBlock(Block2)\n                    ._emit('click');\n\n                spy5.should.have.been.called;\n            });\n\n            it('should properly unbind once handler', function() {\n                Block1._events().un('click', spy4);\n                block1._emit('click');\n                spy4.should.not.have.been.called;\n            });\n\n            it('should handle modifier change events', function() {\n                block1.setMod('m1', 'v1');\n\n                spy6.should.have.been.called;\n                var eventData = spy6.args[0][1];\n                eventData.modName.should.be.equal('m1');\n                eventData.modVal.should.be.equal('v1');\n                eventData.oldModVal.should.be.equal('');\n\n                spy7.should.have.been.called;\n\n                block1.delMod('m1');\n\n                spy6.should.have.been.calledOnce;\n                spy7.should.have.been.calledTwice;\n\n                spy8.should.not.have.been.called;\n\n                block1.setMod('m2');\n\n                spy8.should.have.been.called;\n            });\n        });\n\n        describe('block elems events', function() {\n            ['string', 'Class'].forEach(function(elemType) {\n                var elem1, elem2;\n\n                describe('elem as ' + elemType, function() {\n                    var Elem1, Elem2;\n\n                    beforeEach(function() {\n                        elem1 = elemType === 'string'?\n                            'e1' :\n                            bemDom.declElem('block', 'e1');\n\n                        Block1 = bemDom.declBlock('block', {}, {\n                            onInit : function() {\n                                this._events(elem1)\n                                    .on('click', spy1)\n                                    .on('click', spy2)\n                                    .on('click', data, wrapSpy(spy3));\n\n                                this._events('e2').on('click', spy5);\n                            }\n                        });\n\n                        Elem1 = elemType === 'string'?\n                            bemDom.declElem('block', 'e1') :\n                            elem1;\n                        Elem2 = bemDom.declElem('block', 'e2', {}, {\n                            onInit : function() {\n                                this._events(elem1)\n                                    .on('click', wrapSpy(spy6))\n                                    .on('click', spy7);\n                            }\n                        });\n\n                        block1 = initDom({\n                            block : 'block',\n                            content : [\n                                { elem : 'e1', content : { elem : 'e3' } },\n                                { elem : 'e2', content : { elem : 'e1' } }\n                            ]\n                        }).bem(Block1);\n\n                        elem2 = block1._elem('e2');\n                    });\n\n                    describe('block', function() {\n                        it('should properly bind handlers', function() {\n                            block1._elem('e1')._emit('click');\n\n                            spy1.should.have.been.called;\n                            spy1.should.have.been.calledOn(block1);\n\n                            spy2.should.have.been.called;\n\n                            spy3.should.have.been.called;\n                            spy3.args[0][0].data.should.have.been.equal(data);\n                            spy3.args[0][1].should.be.instanceOf(Elem1);\n                            spy3.args[0][1].domElem[0]\n                                .should.be.equal(block1._elem(elem1).domElem[0]);\n\n                            spy5.should.not.have.been.called;\n                        });\n\n                        it('should properly unbind all handlers', function() {\n                            Block1._events(elem1).un('click');\n                            block1._elem(elem1)._emit('click');\n\n                            spy1.should.not.have.been.called;\n                            spy2.should.not.have.been.called;\n                            spy3.should.not.have.been.called;\n                        });\n\n                        it('should properly unbind specified handler', function() {\n                            Block1._events(elem1).un('click', spy2);\n                            block1._elem(elem1)._emit('click');\n\n                            spy1.should.have.been.called;\n                            spy2.should.not.have.been.called;\n                            spy3.should.have.been.called;\n                        });\n                    });\n\n                    describe('elem instance', function() {\n                        it('should properly bind handlers', function() {\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1._emit('click');\n\n                            spy6.should.have.been.called;\n                            spy6.should.have.been.calledOn(elem2);\n                            spy6.args[0][1].should.be.instanceOf(Elem1);\n                            spy6.args[0][1].domElem[0]\n                                .should.be.equal(e2elem1.domElem[0]);\n\n                            spy7.should.have.been.called;\n                        });\n\n                        it('should properly unbind all handlers', function() {\n                            Elem2._events(elem1).un('click');\n\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1._emit('click');\n\n                            spy6.should.not.have.been.called;\n                            spy7.should.not.have.been.called;\n                        });\n\n                        it('should properly unbind specified handler', function() {\n                            Elem2._events(elem1).un('click', spy7);\n\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1._emit('click');\n\n                            spy6.should.have.been.called;\n                            spy7.should.not.have.been.called;\n                        });\n                    });\n                });\n\n                describe('elem as ' + elemType + ', modName, modVal', function() {\n                    beforeEach(function() {\n                        Block1 = bemDom.declBlock('block', {}, {\n                            onInit : function() {\n                                this._events({ elem : elem1 })\n                                    .on('click', spy1);\n                                this._events({ elem : elem1, modName : 'm1', modVal : 'v1' })\n                                    .on('click', spy2)\n                                    .on('click', spy3);\n                            }\n                        });\n\n                        block1 = initDom({\n                            block : 'block',\n                            content : [\n                                { elem : 'e1' },\n                                { elem : 'e1', elemMods : { m1 : 'v1' } }\n                            ]\n                        }).bem(Block1);\n                    });\n\n                    it('should properly bind handlers', function() {\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' })._emit('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.have.been.called;\n\n                        block1._elem('e1')._emit('click');\n\n                        spy1.should.have.been.calledTwice;\n                        spy2.should.have.been.calledOnce;\n                    });\n\n                    it('should properly unbind all handlers', function() {\n                        Block1._events({ elem : elem1, modName : 'm1', modVal : 'v1' }).un('click');\n\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' })._emit('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.not.have.been.called;\n                    });\n\n                    it('should properly unbind specified handler', function() {\n                        Block1._events({ elem : elem1, modName : 'm1', modVal : 'v1' }).un('click', spy2);\n\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' })._emit('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.not.have.been.called;\n                        spy3.should.have.been.called;\n                    });\n                });\n            });\n        });\n\n        describe('stop propagation', function() {\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block', {}, {\n                    onInit : function() {\n                        this._events(Block3).on('click', spy1);\n                    }\n                });\n\n                Block2 = bemDom.declBlock('block2', {}, {\n                    onInit : function() {\n                        this._events(Block3).on('click', function(e) {\n                            e.stopPropagation();\n                        });\n                    }\n                });\n\n                Block3 = bemDom.declBlock('block3');\n\n                block1 = initDom({\n                    block : 'block',\n                    content : [\n                        {\n                            block : 'block2',\n                            js : true,\n                            content : { block : 'block3' }\n                        }\n                    ]\n                }).bem(Block1);\n            });\n\n            it('should properly stop propagation', function() {\n                var block3 = block1.findChildBlock(Block3);\n                block3._emit('click');\n\n                spy1.should.not.have.been.called;\n            });\n        });\n    });\n});\n\ndescribe('same-type nested blocks destruct (#1525)', function() {\n    var ParentBlock, rootNode;\n\n    beforeEach(function() {\n        ParentBlock = bemDom.declBlock('nested-same', {\n            onInit : function() {\n                this._events().on({ modName : 'js', modVal : '' }, this._onDestruct);\n            },\n            _onDestruct : function() {}\n        });\n\n        rootNode = createDomNode({\n            block : 'nested-same',\n            js : true,\n            content : {\n                block : 'nested-same',\n                js : true\n            }\n        });\n    });\n\n    afterEach(function() {\n        bemDom.destruct(rootNode);\n    });\n\n    it('should not fire parent destruct handler when child of same type is destructed', function() {\n        var parent = rootNode.bem(ParentBlock),\n            child = parent.findChildBlock(ParentBlock),\n            spy = sinon.spy(parent, '_onDestruct');\n\n        bemDom.destruct(child.domElem);\n        spy.should.not.have.been.called;\n    });\n});\n\nprovide();\n\nfunction createDomNode(bemjson) {\n    return bemDom.init(BEMHTML.apply(bemjson));\n}\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_dom.deps.js",
    "content": "[{\n    shouldDeps : [\n        'identify',\n        'inherit',\n        'jquery'\n    ]\n},\n{\n    tech : 'spec.js',\n    shouldDeps : { block : 'i-bem-dom', tech : 'js' }\n}]\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_dom.js",
    "content": "/**\n * @module i-bem-dom__events_type_dom\n */\nimport bemDomEvents from 'bem:i-bem-dom__events';\nimport inherit from 'bem:inherit';\nimport $ from 'bem:jquery';\n\nconst eventBuilder = function(e) {\n        return e;\n    },\n    /**\n     * @class EventManagerFactory\n     * @augments i-bem-dom__events:EventManagerFactory\n     * @exports i-bem-dom__events_type_dom:EventManagerFactory\n     */\n    EventManagerFactory = inherit(bemDomEvents.EventManagerFactory,/** @lends EventManagerFactory.prototype */{\n        /** @override */\n        _createEventManager : function(ctx, params, isInstance) {\n            function wrapperFn(fn) {\n                return function(e) {\n                    let instance;\n\n                    if(isInstance) {\n                        instance = ctx;\n                    } else {\n                        // TODO: we could optimize all these \"closest\" to a single traversing\n                        const entityDomNode = $(e.target).closest(params.ctxSelector);\n                        entityDomNode.length && (instance = entityDomNode.bem(ctx));\n                    }\n\n                    if(instance) {\n                        params.bindEntityCls && (e.bemTarget = $(this).bem(params.bindEntityCls));\n                        fn.apply(instance, arguments);\n                    }\n                };\n            }\n\n            return new this._eventManagerCls(params, wrapperFn, eventBuilder);\n        }\n    });\n\nexport default { EventManagerFactory : EventManagerFactory };\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_dom.spec.js",
    "content": "modules.define('spec', [\n    'i-bem',\n    'i-bem-dom',\n    'objects',\n    'jquery',\n    'chai',\n    'sinon',\n    'BEMHTML'\n], function(provide,\n    bem,\n    bemDom,\n    objects,\n    $,\n    chai,\n    sinon,\n    BEMHTML\n) {\n\nvar undef,\n    expect = chai.expect;\n\ndescribe('DOM events', function() {\n    var Block1, Block2, Block3, block1, spy1, spy2, spy3, spy4, spy5, spy6, spy7, spy8,\n        wrapSpy = function(spy) {\n            return function(e) {\n                // NOTE: we need to pass bemTarget and data explicitly, as `e` is being\n                // changed while event is propagating\n                spy.call(this, e, e.bemTarget, e.data);\n            };\n        },\n        data = { data : 'data' };\n\n    beforeEach(function() {\n        spy1 = sinon.spy();\n        spy2 = sinon.spy();\n        spy3 = sinon.spy();\n        spy4 = sinon.spy();\n        spy5 = sinon.spy();\n        spy6 = sinon.spy();\n        spy7 = sinon.spy();\n        spy8 = sinon.spy();\n    });\n\n    afterEach(function() {\n        bemDom.destruct(bemDom.scope, true);\n\n        objects.each(bem.entities, function(_, entityName) {\n            delete bem.entities[entityName];\n        });\n    });\n\n    describe('on instance events', function() {\n        describe('block domElem events', function() {\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block1', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._domEvents()\n                                    .on('click', spy1)\n                                    .on('click', spy2)\n                                    .on('click', data, wrapSpy(spy3))\n                                    .once('click', spy4);\n                            }\n                        }\n                    }\n                });\n\n                Block2 = bemDom.declBlock('block2', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._domEvents()\n                                    .on('click', spy5);\n                            }\n                        }\n                    }\n                });\n\n                block1 = createDomNode({\n                    block : 'block1',\n                    mix : { block : 'block2', js : true }\n                }).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                block1.domElem.trigger('click');\n\n                spy1.should.have.been.called;\n                spy2.should.have.been.called;\n\n                spy3.should.have.been.calledOn(block1);\n                spy3.args[0][1].should.be.instanceOf(Block1);\n                spy3.args[0][2].should.have.been.equal(data);\n            });\n\n            it('should pass data to handler', function() {\n                var data = { test : 'data' };\n                block1.domElem.trigger('click', data);\n\n                spy1.args[0][1].should.have.been.equal(data);\n            });\n\n            it('should properly bind once handler', function() {\n                block1.domElem.trigger('click');\n                spy4.should.have.been.called;\n\n                block1.domElem.trigger('click');\n                spy4.should.have.been.calledOnce;\n\n                block1._domEvents().once('click', spy4);\n                block1.domElem.trigger('click');\n                spy4.should.have.been.calledTwice;\n            });\n\n            it('should properly bind the same handler', function() {\n                block1._domEvents()\n                    .on('click', spy6)\n                    .on('click', spy6);\n\n                block1.domElem.trigger('click');\n                spy6.should.have.been.calledOnce;\n\n                block1._domEvents().un('click', spy6);\n                block1.domElem.trigger('click');\n                spy6.should.have.been.calledOnce;\n            });\n\n            it('should properly unbind all handlers', function() {\n                block1._domEvents().un('click');\n                block1.domElem.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                block1._domEvents().un('click', spy1);\n                block1.domElem.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy3.should.have.been.called;\n            });\n\n            it('should unbind only own handlers', function() {\n                block1._domEvents().un('click');\n                block1.domElem.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n\n                spy5.should.have.been.called;\n            });\n\n            it('should properly unbind once handler', function() {\n                block1._domEvents().un('click', spy4);\n                block1.domElem.trigger('click');\n                spy4.should.not.have.been.called;\n            });\n\n            it('should unbind single event from multi-event subscription', function() {\n                var multiSpy = sinon.spy();\n                block1._domEvents().on('click keypress', multiSpy);\n\n                block1._domEvents().un('click', multiSpy);\n                block1.domElem.trigger('click');\n                multiSpy.should.not.have.been.called;\n\n                block1.domElem.trigger('keypress');\n                multiSpy.should.have.been.calledOnce;\n            });\n        });\n\n        describe('block elems events', function() {\n            ['string', 'Class'].forEach(function(elemType) {\n                var elem1, elem2;\n\n                describe('elem as ' + elemType, function() {\n                    var Elem1, Elem2;\n\n                    beforeEach(function() {\n                        elem1 = elemType === 'string'?\n                            'e1' :\n                            bemDom.declElem('block', 'e1');\n                        Block1 = bemDom.declBlock('block', {\n                            onSetMod : {\n                                'js' : {\n                                    'inited' : function() {\n                                        this._domEvents(elem1)\n                                            .on('click', spy1)\n                                            .on('click', spy2)\n                                            .on('click', data, wrapSpy(spy3));\n\n                                        this._domEvents('e2').on('click', spy5);\n                                        this._domEvents('e4').on('click', spy8);\n                                    }\n                                }\n                            }\n                        });\n\n                        Elem1 = elemType === 'string'?\n                            bemDom.declElem('block', 'e1') :\n                            elem1;\n                        Elem2 = bemDom.declElem('block', 'e2', {\n                            onSetMod : {\n                                'js' : {\n                                    'inited' : function() {\n                                        this._domEvents(elem1)\n                                            .on('click', wrapSpy(spy6))\n                                            .on('click', spy7);\n                                    }\n                                }\n                            }\n                        });\n\n                        block1 = createDomNode({\n                            block : 'block',\n                            mix : { block : 'block', elem : 'e4' },\n                            content : [\n                                { elem : 'e1', content : { elem : 'e3' } },\n                                { elem : 'e2', content : { elem : 'e1' } }\n                            ]\n                        }).bem(Block1);\n\n                        elem2 = block1._elem('e2');\n                    });\n\n                    describe('block', function() {\n                        it('should properly bind handlers', function() {\n                            block1._elem('e3').domElem.trigger('click');\n\n                            spy1.should.have.been.called;\n                            spy1.should.have.been.calledOn(block1);\n\n                            spy2.should.have.been.called;\n\n                            spy3.should.have.been.called;\n                            spy3.args[0][2].should.have.been.equal(data);\n                            spy3.args[0][1].should.be.instanceOf(Elem1);\n                            spy3.args[0][1].domElem[0]\n                                .should.be.equal(block1._elem(elem1).domElem[0]);\n\n                            spy5.should.not.have.been.called;\n                        });\n\n                        it('should properly bind handlers to mixed elem', function() {\n                            block1._elem('e4').domElem.trigger('click');\n\n                            spy8.should.have.been.called;\n                        });\n\n                        it('should properly unbind all handlers', function() {\n                            block1._domEvents(elem1).un('click');\n                            block1._elem(elem1).domElem.trigger('click');\n\n                            spy1.should.not.have.been.called;\n                            spy2.should.not.have.been.called;\n                            spy3.should.not.have.been.called;\n\n                            elem2.domElem.trigger('click');\n\n                            spy5.should.have.been.called;\n                        });\n\n                        it('should properly unbind specified handler', function() {\n                            block1._domEvents(elem1).un('click', spy2);\n                            block1._elem(elem1).domElem.trigger('click');\n\n                            spy1.should.have.been.called;\n                            spy2.should.not.have.been.called;\n                            spy3.should.have.been.called;\n                        });\n\n                        it('should properly unbind handlers from mixed elem', function() {\n                            block1._domEvents('e4').un('click', spy8);\n                            block1._elem('e4').domElem.trigger('click');\n\n                            spy8.should.not.have.been.called;\n                        });\n                    });\n\n                    describe('elem instance', function() {\n                        it('should properly bind handlers', function() {\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1.domElem.trigger('click');\n\n                            spy6.should.have.been.called;\n                            spy6.should.have.been.calledOn(elem2);\n                            spy6.args[0][1].should.be.instanceOf(Elem1);\n                            spy6.args[0][1].domElem[0]\n                                .should.be.equal(e2elem1.domElem[0]);\n\n                            spy7.should.have.been.called;\n                        });\n\n                        it('should properly unbind all handlers', function() {\n                            elem2._domEvents(elem1).un('click');\n\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1.domElem.trigger('click');\n\n                            spy6.should.not.have.been.called;\n                            spy7.should.not.have.been.called;\n                        });\n\n                        it('should properly unbind specified handler', function() {\n                            elem2._domEvents(elem1).un('click', spy7);\n\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1.domElem.trigger('click');\n\n                            spy6.should.have.been.called;\n                            spy7.should.not.have.been.called;\n                        });\n                    });\n                });\n\n                describe('elem as ' + elemType + ', modName, modVal', function() {\n                    beforeEach(function() {\n                        Block1 = bemDom.declBlock('block', {\n                            onSetMod : {\n                                'js' : {\n                                    'inited' : function() {\n                                        this._domEvents({ elem : elem1 })\n                                            .on('click', spy1);\n                                        this._domEvents({ elem : elem1, modName : 'm1', modVal : 'v1' })\n                                            .on('click', spy2)\n                                            .on('click', spy3);\n                                    }\n                                }\n                            }\n                        });\n\n                        block1 = createDomNode({\n                            block : 'block',\n                            content : [\n                                { elem : 'e1' },\n                                { elem : 'e1', elemMods : { m1 : 'v1' } }\n                            ]\n                        }).bem(Block1);\n                    });\n\n                    it('should properly bind handlers', function() {\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' }).domElem.trigger('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.have.been.called;\n\n                        block1._elem('e1').domElem.trigger('click');\n\n                        spy1.should.have.been.calledTwice;\n                        spy2.should.have.been.calledOnce;\n                    });\n\n                    it('should properly unbind all handlers', function() {\n                        block1._domEvents({ elem : elem1, modName : 'm1', modVal : 'v1' }).un('click');\n\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' }).domElem.trigger('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.not.have.been.called;\n                    });\n\n                    it('should properly unbind specified handler', function() {\n                        block1._domEvents({ elem : elem1, modName : 'm1', modVal : 'v1' }).un('click', spy2);\n\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' }).domElem.trigger('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.not.have.been.called;\n                        spy3.should.have.been.called;\n                    });\n                });\n            });\n\n            describe('elem as instance', function() {\n                var elem1, elem2;\n\n                beforeEach(function() {\n                    Block1 = bemDom.declBlock('block', {\n                        onSetMod : {\n                            'js' : {\n                                'inited' : function() {\n                                    this._domEvents(this._elem('e1'))\n                                        .on('click', spy1)\n                                        .on('click', spy2)\n                                        .on('click', data, wrapSpy(spy3));\n\n                                    this._domEvents(this._elem('e2')).on('click', spy5);\n\n                                    this._domEvents(this._elems('e1').get(1)).on('click', spy6);\n                                }\n                            }\n                        }\n                    });\n\n                    block1 = createDomNode({\n                        block : 'block',\n                        content : [\n                            { elem : 'e1', content : { elem : 'e3' } },\n                            { elem : 'e2', content : { elem : 'e1' } }\n                        ]\n                    }).bem(Block1);\n\n                    elem1 = block1._elem('e1');\n                    elem2 = block1._elem('e2');\n                });\n\n                describe('block', function() {\n                    it('should properly bind handlers', function() {\n                        block1._elem('e3').domElem.trigger('click');\n\n                        spy1.should.have.been.called;\n                        spy1.should.have.been.calledOn(block1);\n\n                        spy2.should.have.been.called;\n\n                        spy3.should.have.been.called;\n                        spy3.args[0][1].should.be.equal(elem1);\n                        spy3.args[0][2].should.be.equal(data);\n\n                        spy5.should.not.have.been.called;\n                    });\n\n                    it('should properly bind handlers on elem with the same name', function() {\n                        block1._elem('e1').domElem.trigger('click');\n\n                        spy1.should.have.been.called;\n                        spy6.should.not.have.been.called;\n                    });\n\n                    it('should properly unbind all handlers', function() {\n                        block1._domEvents(elem1).un('click');\n                        elem1.domElem.trigger('click');\n\n                        spy1.should.not.have.been.called;\n                        spy2.should.not.have.been.called;\n                        spy3.should.not.have.been.called;\n\n                        elem2.domElem.trigger('click');\n\n                        spy5.should.have.been.called;\n                    });\n\n                    it('should properly unbind specified handler', function() {\n                        block1._domEvents(elem1).un('click', spy2);\n                        elem1.domElem.trigger('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.not.have.been.called;\n                        spy3.should.have.been.called;\n                    });\n                });\n            });\n        });\n\n        describe('collection events', function() {\n            var Elem1, collection;\n\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block1', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._domEvents(collection = this.findChildElems(Elem1))\n                                    .on('click', spy1)\n                                    .on('click', spy2)\n                                    .on('click', data, wrapSpy(spy3))\n                                    .once('click', spy4);\n                            }\n                        }\n                    }\n                });\n\n                Elem1 = bemDom.declElem('block1', 'elem1');\n\n                block1 = createDomNode({\n                    block : 'block1',\n                    content : { elem : 'elem1' }\n                }).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                block1._elem('elem1').domElem.trigger('click');\n\n                spy1.should.have.been.called;\n                spy2.should.have.been.called;\n\n                spy3.should.have.been.calledOn(block1);\n                spy3.args[0][1].should.be.instanceOf(Elem1);\n                spy3.args[0][2].should.have.been.equal(data);\n            });\n\n            it('should properly bind once handler', function() {\n                block1._elem('elem1').domElem.trigger('click');\n                spy4.should.have.been.called;\n\n                block1._elem('elem1').domElem.trigger('click');\n                spy4.should.have.been.calledOnce;\n\n                block1._domEvents(collection).once('click', spy4);\n                block1._elem('elem1').domElem.trigger('click');\n                spy4.should.have.been.calledTwice;\n            });\n\n            it('should properly bind the same handler', function() {\n                block1._domEvents(collection)\n                    .on('click', spy6)\n                    .on('click', spy6);\n\n                block1._elem('elem1').domElem.trigger('click');\n                spy6.should.have.been.calledOnce;\n\n                block1._domEvents(collection).un('click', spy6);\n                block1._elem('elem1').domElem.trigger('click');\n                spy6.should.have.been.calledOnce;\n            });\n\n            it('should properly unbind all handlers', function() {\n                block1._domEvents(collection).un('click');\n                block1._elem('elem1').domElem.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                block1._domEvents(collection).un('click', spy1);\n                block1._elem('elem1').domElem.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy3.should.have.been.called;\n            });\n\n            it('should properly unbind once handler', function() {\n                block1._domEvents(collection).un('click', spy4);\n                block1._elem('elem1').domElem.trigger('click');\n                spy4.should.not.have.been.called;\n            });\n        });\n\n        describe('document events', function() {\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._domEvents(document)\n                                    .on('click', spy1)\n                                    .on('click', spy2);\n\n                                this._domEvents(bemDom.doc)\n                                    .on('click', data, wrapSpy(spy3))\n                                    .once('click', spy4);\n                            }\n                        }\n                    }\n                });\n                block1 = bemDom.init(BEMHTML.apply({ block : 'block' })).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                bemDom.doc.trigger('click');\n\n                spy1.should.have.been.called;\n                spy2.should.have.been.called;\n\n                spy3.should.have.been.calledOn(block1);\n                spy3.args[0][2].should.have.been.equal(data);\n            });\n\n            it('should properly bind once handler', function() {\n                bemDom.doc.trigger('click');\n                spy4.should.have.been.called;\n\n                bemDom.doc.trigger('click');\n                spy4.should.have.been.calledOnce;\n\n                block1._domEvents(bemDom.doc).once('click', spy4);\n                bemDom.doc.trigger('click');\n                spy4.should.have.been.calledTwice;\n            });\n\n            it('should properly unbind all handlers', function() {\n                block1._domEvents(document).un('click');\n                bemDom.doc.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                block1._domEvents($(document)).un('click', spy1);\n                bemDom.doc.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.have.been.called;\n            });\n\n            it('should properly unbind once handler', function() {\n                block1._domEvents($(document)).un('click', spy4);\n                bemDom.doc.trigger('click');\n                spy4.should.not.have.been.called;\n            });\n\n            it('should properly unbind all handlers on block destruct', function() {\n                bemDom.destruct(block1.domElem);\n                bemDom.doc.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n                spy4.should.not.have.been.called;\n            });\n        });\n\n        describe('window events', function() {\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._domEvents(window)\n                                    .on('resize', spy1)\n                                    .on('resize', spy2);\n\n                                this._domEvents($(window))\n                                    .on('resize', data, wrapSpy(spy3))\n                                    .once('resize', spy4);\n                            }\n                        }\n                    }\n                });\n                block1 = createDomNode({ block : 'block' }).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                bemDom.win.trigger('resize');\n\n                spy1.should.have.been.called;\n                spy2.should.have.been.called;\n\n                spy3.should.have.been.calledOn(block1);\n                spy3.args[0][2].should.have.been.equal(data);\n            });\n\n            it('should properly bind once handler', function() {\n                bemDom.win.trigger('resize');\n                spy4.should.have.been.called;\n\n                bemDom.win.trigger('resize');\n                spy4.should.have.been.calledOnce;\n\n                block1._domEvents(bemDom.win).once('click', spy4);\n                bemDom.win.trigger('click');\n                spy4.should.have.been.calledTwice;\n            });\n\n            it('should properly unbind all handlers', function() {\n                block1._domEvents(window).un('resize');\n                bemDom.win.trigger('resize');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                block1._domEvents($(window)).un('resize', spy1);\n                bemDom.win.trigger('resize');\n\n                spy1.should.not.have.been.called;\n                spy2.should.have.been.called;\n            });\n\n            it('should properly unbind once handler', function() {\n                block1._domEvents($(window)).un('resize', spy4);\n                bemDom.win.trigger('resize');\n                spy4.should.not.have.been.called;\n            });\n\n            it('should properly unbind all handlers on block destruct', function() {\n                bemDom.destruct(block1.domElem);\n                bemDom.win.trigger('resize');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n                spy4.should.not.have.been.called;\n            });\n        });\n\n        describe('arbitrary jQuery-chain or DOM-node events', function() {\n            var rootNode;\n\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : function() {\n                                this._domEvents(rootNode[0])\n                                    .on('click', spy1)\n                                    .on('click', spy2);\n\n                                this._domEvents(rootNode.find('div').addBack())\n                                    .on('dblclick', data, wrapSpy(spy3))\n                                    .once('dblclick', spy4);\n                            }\n                        }\n                    }\n                });\n                rootNode = createDomNode({\n                    content : {\n                        content : { block : 'block', tag : 'p' }\n                    }\n                });\n                block1 = rootNode.find(Block1._buildSelector()).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                rootNode.trigger('click');\n\n                spy1.should.have.been.calledOnce;\n                spy2.should.have.been.calledOnce;\n\n                rootNode.find('div').trigger('dblclick');\n\n                spy3.should.have.been.calledTwice;\n                spy3.should.have.been.calledOn(block1);\n                spy3.args[0][2].should.have.been.equal(data);\n            });\n\n            it('should properly bind once handler', function() {\n                rootNode.trigger('dblclick');\n                spy4.should.have.been.called;\n\n                rootNode.trigger('dblclick');\n                spy4.should.have.been.calledOnce;\n\n                block1._domEvents(rootNode.find('div').addBack()).once('dblclick', spy4);\n                rootNode.trigger('dblclick');\n                spy4.should.have.been.calledTwice;\n            });\n\n            it('should properly unbind all handlers', function() {\n                block1._domEvents(rootNode[0]).un('click');\n                rootNode.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n\n                block1._domEvents(rootNode.find('div').addBack()).un('dblclick');\n                rootNode.find('div').trigger('dblclick');\n\n                spy3.should.not.have.been.called;\n                spy4.should.not.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                block1._domEvents(rootNode[0]).un('click', spy1);\n                rootNode.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.have.been.called;\n            });\n\n            it('should properly unbind once handler', function() {\n                block1._domEvents(rootNode.find('div').addBack()).un('dblclick', spy4);\n                rootNode.find('div').trigger('dblclick');\n                spy4.should.not.have.been.called;\n            });\n\n            it('should properly unbind all handlers on block destruct', function() {\n                bemDom.destruct(block1.domElem);\n                rootNode.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n            });\n        });\n    });\n\n    describe('delegated events', function() {\n        function initDom(bemjson) {\n            return createDomNode(bemjson).appendTo(bemDom.scope);\n        }\n\n        describe('block domElem events', function() {\n            beforeEach(function() {\n                Block1 = bemDom.declBlock('block1', {}, {\n                    onInit : function() {\n                        this._domEvents()\n                            .on('click', spy1)\n                            .on('click', spy2)\n                            .on('click', data, wrapSpy(spy3))\n                            .once('click', spy4);\n                    }\n                });\n\n                Block2 = bemDom.declBlock('block2', {}, {\n                    onInit : function() {\n                        this._domEvents()\n                            .on('click', spy5);\n                    }\n                });\n\n                Block3 = bemDom\n                    .declBlock('block3')\n                    .declMod({ modName : 'm1', modVal : 'v1' }, {}, {\n                        onInit : function() {\n                            this._domEvents({ modName : 'm1', modVal : 'v1' }).on('click', spy6);\n                        }\n                    });\n\n                block1 = initDom({\n                    block : 'block1',\n                    mix : { block : 'block2', js : true },\n                    content : [\n                        { block : 'block3', js : true },\n                        { block : 'block3', mods : { m1 : 'v1' }, js : true }\n                    ]\n                }).bem(Block1);\n            });\n\n            it('should properly bind handlers', function() {\n                block1.domElem.trigger('click');\n\n                spy1.should.have.been.called;\n                spy2.should.have.been.called;\n\n                spy3.should.have.been.calledOn(block1);\n                spy3.args[0][1].should.be.instanceOf(Block1);\n                spy3.args[0][0].data.should.have.been.equal(data);\n            });\n\n            it('should properly bind once handler', function() {\n                block1.domElem.trigger('click');\n                spy4.should.have.been.called;\n\n                block1.domElem.trigger('click');\n                spy4.should.have.been.calledOnce;\n\n                block1._domEvents().once('click', spy4);\n                block1.domElem.trigger('click');\n                spy4.should.have.been.calledTwice;\n            });\n\n            it('should properly unbind all handlers', function() {\n                Block1._domEvents().un('click');\n                block1.domElem.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n            });\n\n            it('should properly unbind specified handler', function() {\n                Block1._domEvents().un('click', spy1);\n                block1.domElem.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy3.should.have.been.called;\n            });\n\n            it('should unbind only own handlers', function() {\n                Block1._domEvents().un('click');\n                block1.domElem.trigger('click');\n\n                spy1.should.not.have.been.called;\n                spy2.should.not.have.been.called;\n\n                spy5.should.have.been.called;\n            });\n\n            it('should properly unbind once handler', function() {\n                Block1._domEvents().un('click', spy4);\n                block1.domElem.trigger('click');\n                spy4.should.not.have.been.called;\n            });\n\n            it('should properly bind to self with modifier', function() {\n                var blocks = block1.findChildBlocks(Block3);\n\n                blocks.get(0).domElem.trigger('click');\n                spy6.should.not.have.been.called;\n\n                blocks.get(1).domElem.trigger('click');\n                spy6.should.have.been.called;\n            });\n        });\n\n        describe('block elems events', function() {\n            ['string', 'Class'].forEach(function(elemType) {\n                var elem1, elem2;\n\n                describe('elem as ' + elemType, function() {\n                    var Elem1, Elem2;\n\n                    beforeEach(function() {\n                        elem1 = elemType === 'string'?\n                            'e1' :\n                            bemDom.declElem('block', 'e1');\n\n                        Block1 = bemDom.declBlock('block', {}, {\n                            onInit : function() {\n                                this._domEvents(elem1)\n                                    .on('click', spy1)\n                                    .on('click', spy2)\n                                    .on('click', data, wrapSpy(spy3));\n\n                                this._domEvents('e2').on('click', spy5);\n                            }\n                        });\n\n                        Elem1 = elemType === 'string'?\n                            bemDom.declElem('block', 'e1') :\n                            elem1;\n                        Elem2 = bemDom.declElem('block', 'e2', {}, {\n                            onInit : function() {\n                                this._domEvents(elem1)\n                                    .on('click', wrapSpy(spy6))\n                                    .on('click', spy7);\n                            }\n                        });\n\n                        block1 = initDom({\n                            block : 'block',\n                            content : [\n                                { elem : 'e1', content : { elem : 'e3' } },\n                                { elem : 'e2', content : { elem : 'e1' } }\n                            ]\n                        }).bem(Block1);\n\n                        elem2 = block1._elem('e2');\n                    });\n\n                    describe('block', function() {\n                        it('should properly bind handlers', function() {\n                            block1._elem('e3').domElem.trigger('click');\n\n                            spy1.should.have.been.called;\n                            spy1.should.have.been.calledOn(block1);\n\n                            spy2.should.have.been.called;\n\n                            spy3.should.have.been.called;\n                            spy3.args[0][0].data.should.have.been.equal(data);\n                            spy3.args[0][1].should.be.instanceOf(Elem1);\n                            spy3.args[0][1].domElem[0]\n                                .should.be.equal(block1._elem(elem1).domElem[0]);\n\n                            spy5.should.not.have.been.called;\n                        });\n\n                        it('should properly unbind all handlers', function() {\n                            Block1._domEvents(elem1).un('click');\n                            block1._elem(elem1).domElem.trigger('click');\n\n                            spy1.should.not.have.been.called;\n                            spy2.should.not.have.been.called;\n                            spy3.should.not.have.been.called;\n                        });\n\n                        it('should properly unbind specified handler', function() {\n                            Block1._domEvents(elem1).un('click', spy2);\n                            block1._elem(elem1).domElem.trigger('click');\n\n                            spy1.should.have.been.called;\n                            spy2.should.not.have.been.called;\n                            spy3.should.have.been.called;\n                        });\n                    });\n\n                    describe('elem instance', function() {\n                        it('should properly bind handlers', function() {\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1.domElem.trigger('click');\n\n                            spy6.should.have.been.called;\n                            spy6.should.have.been.calledOn(elem2);\n                            spy6.args[0][1].should.be.instanceOf(Elem1);\n                            spy6.args[0][1].domElem[0]\n                                .should.be.equal(e2elem1.domElem[0]);\n\n                            spy7.should.have.been.called;\n                        });\n\n                        it('should properly unbind all handlers', function() {\n                            Elem2._domEvents(elem1).un('click');\n\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1.domElem.trigger('click');\n\n                            spy6.should.not.have.been.called;\n                            spy7.should.not.have.been.called;\n                        });\n\n                        it('should properly unbind specified handler', function() {\n                            Elem2._domEvents(elem1).un('click', spy7);\n\n                            var e2elem1 = elem2.findChildElem('e1');\n                            e2elem1.domElem.trigger('click');\n\n                            spy6.should.have.been.called;\n                            spy7.should.not.have.been.called;\n                        });\n                    });\n                });\n\n                describe('elem as ' + elemType + ', modName, modVal', function() {\n                    beforeEach(function() {\n                        Block1 = bemDom.declBlock('block', {}, {\n                            onInit : function() {\n                                this._domEvents({ elem : elem1 })\n                                    .on('click', spy1);\n                                this._domEvents({ elem : elem1, modName : 'm1', modVal : 'v1' })\n                                    .on('click', spy2)\n                                    .on('click', spy3);\n                            }\n                        });\n\n                        block1 = initDom({\n                            block : 'block',\n                            content : [\n                                { elem : 'e1' },\n                                { elem : 'e1', elemMods : { m1 : 'v1' } }\n                            ]\n                        }).bem(Block1);\n                    });\n\n                    it('should properly bind handlers', function() {\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' }).domElem.trigger('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.have.been.called;\n\n                        block1._elem('e1').domElem.trigger('click');\n\n                        spy1.should.have.been.calledTwice;\n                        spy2.should.have.been.calledOnce;\n                    });\n\n                    it('should properly unbind all handlers', function() {\n                        Block1._domEvents({ elem : elem1, modName : 'm1', modVal : 'v1' }).un('click');\n\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' }).domElem.trigger('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.not.have.been.called;\n                    });\n\n                    it('should properly unbind specified handler', function() {\n                        Block1._domEvents({ elem : elem1, modName : 'm1', modVal : 'v1' }).un('click', spy2);\n\n                        block1._elem({ elem : 'e1', modName : 'm1', modVal : 'v1' }).domElem.trigger('click');\n\n                        spy1.should.have.been.called;\n                        spy2.should.not.have.been.called;\n                        spy3.should.have.been.called;\n                    });\n                });\n            });\n        });\n    });\n});\n\nprovide();\n\nfunction createDomNode(bemjson) {\n    return bemDom.init(BEMHTML.apply(bemjson));\n}\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__events/i-bem-dom__events.deps.js",
    "content": "({\n    shouldDeps : [\n        'inherit',\n        'identify',\n        'objects',\n        'jquery',\n        'functions',\n        { block : 'i-bem', elem : 'internal' },\n        { elem : 'collection' }\n    ]\n})\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__events/i-bem-dom__events.js",
    "content": "/**\n * @module i-bem-dom__events\n */\nimport bemInternal from 'bem:i-bem__internal'\nimport BemDomCollection from 'bem:i-bem-dom__collection'\nimport inherit from 'bem:inherit'\nimport identify from 'bem:identify'\nimport $ from 'bem:jquery'\nimport functions from 'bem:functions'\n\nconst winNode = window\nconst docNode = document\nconst eventStorage = new Map()\n\n/**\n * @class EventManager\n */\nconst EventManager = inherit(/** @lends EventManager.prototype */{\n    /**\n     * @constructor\n     * @param {Object} params EventManager parameters\n     * @param {Function} fnWrapper Wrapper function to build event handler\n     * @param {Function} eventBuilder Function to build event\n     */\n    __constructor(params, fnWrapper, eventBuilder) {\n        this._params = params\n        this._fnWrapper = fnWrapper\n        this._eventBuilder = eventBuilder\n        this._storage = new Map()\n    },\n\n    /**\n     * Adds an event handler\n     * @param {String|Object|events:Event} e Event type\n     * @param {*} [data] Additional data that the handler gets as e.data\n     * @param {Function} fn Handler\n     * @returns {EventManager} this\n     */\n    on(e, data, fn, _fnCtx, _isOnce) {\n        const params = this._params\n        const event = this._eventBuilder(e, params)\n\n        if(functions.isFunction(data)) {\n            _isOnce = _fnCtx\n            _fnCtx = fn\n            fn = data\n            data = undefined\n        }\n\n        const events = typeof event === 'string' ? event.split(/\\s+/) : [event]\n        events.forEach(singleEvent => {\n            this._bindSingle(singleEvent, e, data, fn, _fnCtx, _isOnce, params)\n        })\n\n        return this\n    },\n\n    _bindSingle(event, origEvent, data, fn, _fnCtx, _isOnce, params) {\n        let fnStorage = this._storage.get(event)\n        if(!fnStorage) {\n            fnStorage = new Map()\n            this._storage.set(event, fnStorage)\n        }\n        const fnId = identify(fn, _fnCtx)\n\n        if(!fnStorage.get(fnId)) {\n            const bindDomElem = params.bindDomElem\n            const bindSelector = params.bindSelector\n            const _this = this\n            const handler = this._fnWrapper(\n                _isOnce?\n                    function() {\n                        _this.un(origEvent, fn, _fnCtx)\n                        fn.apply(this, arguments)\n                    } :\n                    fn,\n                _fnCtx,\n                fnId)\n            fnStorage.set(fnId, handler)\n\n            bindDomElem.on(event, bindSelector, data, handler)\n            bindSelector && bindDomElem.is(bindSelector) && bindDomElem.on(event, data, handler)\n            // FIXME: \"once\" won't properly work in case of nested and mixed elem with the same name\n        }\n    },\n\n    /**\n     * Adds an event handler\n     * @param {String} e Event type\n     * @param {*} [data] Additional data that the handler gets as e.data\n     * @param {Function} fn Handler\n     * @returns {EventManager} this\n     */\n    once(e, data, fn, _fnCtx) {\n        if(functions.isFunction(data)) {\n            _fnCtx = fn\n            fn = data\n            data = undefined\n        }\n\n        return this.on(e, data, fn, _fnCtx, true)\n    },\n\n    /**\n     * Removes event handler or handlers\n     * @param {String|Object|events:Event} [e] Event type\n     * @param {Function} [fn] Handler\n     * @returns {EventManager} this\n     */\n    un(e, fn, _fnCtx) {\n        const argsLen = arguments.length\n        if(argsLen) {\n            const params = this._params\n            const event = this._eventBuilder(e, params)\n            const events = typeof event === 'string' ? event.split(/\\s+/) : [event]\n\n            events.forEach(singleEvent => {\n                if(argsLen === 1) {\n                    this._unbindByEvent(this._storage.get(singleEvent), singleEvent)\n                } else {\n                    const fnId = identify(fn, _fnCtx)\n                    const fnStorage = this._storage.get(singleEvent)\n                    const bindDomElem = params.bindDomElem\n                    const bindSelector = params.bindSelector\n\n                    let wrappedFn\n                    if(wrappedFn = fnStorage && fnStorage.get(fnId))\n                        fnStorage.delete(fnId)\n\n                    const handler = wrappedFn || fn\n\n                    bindDomElem.off(singleEvent, params.bindSelector, handler)\n                    bindSelector && bindDomElem.is(bindSelector) && bindDomElem.off(singleEvent, handler)\n                }\n            })\n        } else {\n            this._storage.forEach((fnStorage, e) => this._unbindByEvent(fnStorage, e))\n        }\n\n        return this\n    },\n\n    _unbindByEvent(fnStorage, e) {\n        const params = this._params\n        const bindDomElem = params.bindDomElem\n        const bindSelector = params.bindSelector\n        const unbindWithoutSelector = bindSelector && bindDomElem.is(bindSelector)\n\n        fnStorage && fnStorage.forEach((fn) => {\n            bindDomElem.off(e, bindSelector, fn)\n            unbindWithoutSelector && bindDomElem.off(e, fn)\n        })\n        this._storage.set(e, null)\n    }\n})\n\nconst buildForEachEventManagerProxyFn = (methodName) => {\n    return function() {\n        const args = arguments\n\n        this._eventManagers.forEach((eventManager) => {\n            eventManager[methodName].apply(eventManager, args)\n        })\n\n        return this\n    }\n}\n\n/**\n * @class CollectionEventManager\n */\nconst CollectionEventManager = inherit(/** @lends CollectionEventManager.prototype */{\n    /**\n     * @constructor\n     * @param {Array} eventManagers Array of event managers\n     */\n    __constructor(eventManagers) {\n        this._eventManagers = eventManagers\n    },\n\n    /**\n     * Adds an event handler\n     * @param {String|Object|events:Event} e Event type\n     * @param {Object} [data] Additional data that the handler gets as e.data\n     * @param {Function} fn Handler\n     * @returns {CollectionEventManager} this\n     */\n    on : buildForEachEventManagerProxyFn('on'),\n\n    /**\n     * Adds an event handler\n     * @param {String} e Event type\n     * @param {Object} [data] Additional data that the handler gets as e.data\n     * @param {Function} fn Handler\n     * @returns {CollectionEventManager} this\n     */\n    once : buildForEachEventManagerProxyFn('once'),\n\n    /**\n     * Removes event handler or handlers\n     * @param {String|Object|events:Event} [e] Event type\n     * @param {Function} [fn] Handler\n     * @returns {CollectionEventManager} this\n     */\n    un : buildForEachEventManagerProxyFn('un')\n})\n\n/**\n * @class EventManagerFactory\n * @exports i-bem-dom__events:EventManagerFactory\n */\nconst EventManagerFactory = inherit(/** @lends EventManagerFactory.prototype */{\n    __constructor(getEntityCls) {\n        this._storageSuffix = identify()\n        this._getEntityCls = getEntityCls\n        this._eventManagerCls = EventManager\n    },\n\n    /**\n     * Instantiates event manager\n     * @param {Function|i-bem-dom:BemDomEntity} ctx BemDomEntity class or instance\n     * @param {*} bindCtx context to bind\n     * @param {jQuery} bindScope bind scope\n     * @returns {EventManager}\n     */\n    getEventManager(ctx, bindCtx, bindScope) {\n        if(bindCtx instanceof BemDomCollection) {\n            return new CollectionEventManager(bindCtx.map((entity) => {\n                return this.getEventManager(ctx, entity, bindScope)\n            }, this))\n        }\n\n        const ctxId = identify(ctx)\n        let ctxStorage = eventStorage.get(ctxId)\n        const storageSuffix = this._storageSuffix\n        const isBindToInstance = typeof ctx !== 'function'\n        let ctxCls\n        let selector = ''\n\n        if(isBindToInstance) {\n            ctxCls = ctx.__self\n        } else {\n            ctxCls = ctx\n            selector = ctx._buildSelector()\n        }\n\n        const params = this._buildEventManagerParams(bindCtx, bindScope, selector, ctxCls)\n        const storageKey = params.key + storageSuffix\n\n        if(!ctxStorage) {\n            ctxStorage = {}\n            eventStorage.set(ctxId, ctxStorage)\n            if(isBindToInstance) {\n                ctx._events().on({ modName : 'js', modVal : '' }, () => {\n                    Object.keys(ctxStorage).forEach(key => {\n                        ctxStorage[key] && ctxStorage[key].un()\n                    })\n                    eventStorage.delete(ctxId)\n                })\n            }\n        }\n\n        return ctxStorage[storageKey] ||\n            (ctxStorage[storageKey] = this._createEventManager(ctx, params, isBindToInstance))\n    },\n\n    _buildEventManagerParams(bindCtx, bindScope, ctxSelector, ctxCls) {\n        const res = {\n            bindEntityCls : null,\n            bindDomElem : bindScope,\n            bindToArbitraryDomElem : false,\n            bindSelector : ctxSelector,\n            ctxSelector : ctxSelector,\n            key : ''\n        }\n\n        if(bindCtx) {\n            const typeOfCtx = typeof bindCtx\n\n            if(bindCtx.jquery) {\n                res.bindDomElem = bindCtx\n                res.key = identify.apply(null, bindCtx.get())\n                res.bindToArbitraryDomElem = true\n            } else if(bindCtx === winNode || bindCtx === docNode || (typeOfCtx === 'object' && bindCtx.nodeType === 1)) { // NOTE: duck-typing check for \"is-DOM-element\"\n                res.bindDomElem = $(bindCtx)\n                res.key = identify(bindCtx)\n                res.bindToArbitraryDomElem = true\n            } else if(typeOfCtx === 'object' && bindCtx.__self) { // bem entity instance\n                res.bindDomElem = bindCtx.domElem\n                res.key = bindCtx._uniqId\n                res.bindEntityCls = bindCtx.__self\n            } else if(typeOfCtx === 'string' || typeOfCtx === 'object' || typeOfCtx === 'function') {\n                let blockName, elemName, modName, modVal\n                if(typeOfCtx === 'string') { // elem name\n                    blockName = ctxCls._blockName\n                    elemName = bindCtx\n                } else if(typeOfCtx === 'object') { // bem entity with optional mod val\n                    blockName = bindCtx.block?\n                        bindCtx.block.getName() :\n                        ctxCls._blockName\n                    elemName = typeof bindCtx.elem === 'function'?\n                        bindCtx.elem.getName() :\n                        bindCtx.elem\n                    modName = bindCtx.modName\n                    /* treat modVal: false as modVal: '' (modifier removal) — #1457 */\n                    modVal = bindCtx.modVal === false? '' : bindCtx.modVal\n                } else if(bindCtx.getName() === bindCtx.getEntityName()) { // block class\n                    blockName = bindCtx.getName()\n                } else { // elem class\n                    blockName = ctxCls._blockName\n                    elemName = bindCtx.getName()\n                }\n\n                const entityName = bemInternal.buildClassName(blockName, elemName)\n                res.bindEntityCls = this._getEntityCls(entityName)\n                res.bindSelector = '.' + (res.key = entityName + bemInternal.buildModPostfix(modName, modVal))\n            }\n        } else {\n            res.bindEntityCls = ctxCls\n        }\n\n        return res\n    },\n\n    _createEventManager(ctx, params, isInstance) {\n        throw new Error('not implemented')\n    }\n})\n\nexport default { EventManagerFactory }\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__init/_auto/i-bem-dom__init_auto.deps.js",
    "content": "({\n    shouldDeps : ['jquery', 'next-tick']\n})\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__init/_auto/i-bem-dom__init_auto.js",
    "content": "/**\n * Auto initialization on DOM ready\n */\n\nimport init from 'bem:i-bem-dom__init'\nimport $ from 'bem:jquery'\nimport nextTick from 'bem:next-tick'\n\n$(function() {\n    nextTick(init)\n})\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__init/i-bem-dom__init.deps.js",
    "content": "({\n    shouldDeps : ['i-bem-dom']\n})\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__init/i-bem-dom__init.js",
    "content": "/**\n * @module i-bem-dom__init\n */\n\nimport bemDom from 'bem:i-bem-dom'\n\nexport default\n    /**\n     * Initializes blocks on a fragment of the DOM tree\n     * @param {jQuery} [ctx=scope] Root DOM node\n     * @returns {jQuery} ctx Initialization context\n     */\n    function(ctx) {\n        return bemDom.init(ctx)\n    }\n"
  },
  {
    "path": "common.blocks/i-bem-dom/__init/i-bem-dom__init.spec.js",
    "content": "modules.define('spec', ['i-bem'], function(provide, bem) {\n\ndescribe('i-bem-dom__init', function() {\n    it('block should exist on init', function(done) {\n        var name = 'b' + Math.random();\n\n        modules.define(name, ['i-bem-dom'], function(provide, bemDom) {\n            provide(bemDom.declBlock(this.name, {}));\n        });\n\n        modules.require(['i-bem-dom__init'], function() {\n            bem.entities.should.have.property(name);\n            done();\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.deps.js",
    "content": "({\n    shouldDeps : [\n        'inherit',\n        'jquery',\n        'objects',\n        'functions',\n        'dom',\n        { elem : 'init' },\n        { block : 'i-bem', elems : ['internal'] },\n        { elem : 'events', mods : { type : ['dom', 'bem'] } },\n        { elem : 'collection' }\n    ]\n})\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.en.md",
    "content": "# i-bem-dom\n\nA helper block for creating other blocks that have a DOM representation.\n\nThe block is implemented as a specialized JavaScript framework for web development using the BEM methodology.\n\nThere is a separate document with a detailed [user's guide](https://en.bem.info/technology/i-bem/v4/i-bem-js/).\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.js",
    "content": "/**\n * @module i-bem-dom\n */\n\nimport bem from 'bem:i-bem'\nimport bemInternal from 'bem:i-bem__internal'\nimport BemDomCollection from 'bem:i-bem-dom__collection'\nimport domEvents from 'bem:i-bem-dom__events_type_dom'\nimport bemEvents from 'bem:i-bem-dom__events_type_bem'\nimport inherit from 'bem:inherit'\nimport identify from 'bem:identify'\nimport objects from 'bem:objects'\nimport functions from 'bem:functions'\nimport $ from 'bem:jquery'\nimport dom from 'bem:dom'\n\n/**\n * Storage for DOM elements by unique key\n * @type Map\n */\nconst uniqIdToDomElems = new Map()\n\n/**\n * Storage for blocks by unique key\n * @type Map\n */\nconst uniqIdToEntity = new Map()\n\n/**\n * Storage for DOM element's parent nodes\n * @type Map\n */\nconst domNodesToParents = new Map()\n\n/**\n * Storage for block parameters\n * @type Map\n */\nconst domElemToParams = new Map()\n\n/**\n * Storage for DOM nodes that are being destructed\n * @type Map\n */\nconst destructingDomNodes = new Map()\n\nconst entities = bem.entities\n\nconst BEM_CLASS_NAME = 'i-bem'\nconst BEM_SELECTOR = '.' + BEM_CLASS_NAME\nconst BEM_PARAMS_ATTR = 'data-bem'\n\nconst NAME_PATTERN = bemInternal.NAME_PATTERN\n\nconst MOD_DELIM = bemInternal.MOD_DELIM\nconst ELEM_DELIM = bemInternal.ELEM_DELIM\n\nconst buildModPostfix = bemInternal.buildModPostfix\nconst buildClassName = bemInternal.buildClassName\n\nconst reverse = Array.prototype.reverse\n\nconst domEventManagerFactory = new domEvents.EventManagerFactory(getEntityCls)\nconst bemEventManagerFactory = new bemEvents.EventManagerFactory(getEntityCls)\n\n// eslint-disable-next-line prefer-const -- assigned later as object literal\nlet bemDom\n\n/**\n * Initializes entities on a DOM element\n * @param {jQuery} domElem DOM element\n * @param {String} uniqInitId ID of the \"initialization wave\"\n * @param {Object} [dropElemCacheQueue] queue of elems to be droped from cache\n */\nfunction initEntities(domElem, uniqInitId, dropElemCacheQueue) {\n    const domNode = domElem[0]\n    const params = getParams(domNode)\n\n    for(const entityName of Object.keys(params)) {\n        const splitted = entityName.split(ELEM_DELIM)\n        const blockName = splitted[0]\n        const elemName = splitted[1]\n        elemName &&\n            ((dropElemCacheQueue[blockName] ||\n                (dropElemCacheQueue[blockName] = {}))[elemName] = true)\n\n        initEntity(\n            entityName,\n            domElem,\n            processParams(params[entityName], entityName, uniqInitId))\n    }\n}\n\n/**\n * Initializes a specific entity on a DOM element, or returns the existing entity if it was already created\n * @param {String} entityName Entity name\n * @param {jQuery} domElem DOM element\n * @param {Object} [params] Initialization parameters\n * @param {Boolean} [ignoreLazyInit=false] Ignore lazy initialization\n * @param {Function} [callback] Handler to call after complete initialization\n */\nfunction initEntity(entityName, domElem, params, ignoreLazyInit, callback) {\n    const domNode = domElem[0]\n\n    if(destructingDomNodes.has(identify(domNode))) return\n\n    params || (params = processParams(getEntityParams(domNode, entityName), entityName))\n\n    const uniqId = params.uniqId\n    let entity = uniqIdToEntity.get(uniqId)\n\n    if(entity) {\n        if(entity.domElem.index(domNode) < 0) {\n            entity.domElem = entity.domElem.add(domElem)\n            objects.extend(entity.params, params)\n        }\n\n        return entity\n    }\n\n    uniqIdToDomElems.set(uniqId, uniqIdToDomElems.has(uniqId)?\n        uniqIdToDomElems.get(uniqId).add(domElem) :\n        domElem)\n\n    const parentDomNode = domNode.parentNode\n    if(!parentDomNode || parentDomNode.nodeType === 11) { // jquery doesn't unique disconnected node\n        $.uniqueSort(uniqIdToDomElems.get(uniqId))\n    }\n\n    const entityCls = getEntityCls(entityName)\n\n    entityCls._processInit()\n\n    if(ignoreLazyInit || params.lazyInit === false || !entityCls.lazyInit && !params.lazyInit) {\n        ignoreLazyInit && domElem.addClass(BEM_CLASS_NAME) // add css class for preventing memory leaks in further destructing\n\n        entity = new entityCls(uniqIdToDomElems.get(uniqId), params, !!ignoreLazyInit)\n        uniqIdToDomElems.delete(uniqId)\n        callback && callback.apply(entity, [...arguments].slice(4))\n        return entity\n    }\n}\n\nfunction getEntityCls(entityName) {\n    if(entities[entityName]) return entities[entityName]\n\n    const splitted = entityName.split(ELEM_DELIM)\n    return splitted[1]?\n        bemDom.declElem(splitted[0], splitted[1], {}, { lazyInit : true }) :\n        bemDom.declBlock(entityName, {}, { lazyInit : true })\n}\n\n/**\n * Processes and adds necessary entity parameters\n * @param {Object} params Initialization parameters\n * @param {String} entityName Entity name\n * @param {String} [uniqInitId] ID of the \"initialization wave\"\n */\nfunction processParams(params, entityName, uniqInitId) {\n    params.uniqId ||\n        (params.uniqId = (params.id?\n            entityName + '-id-' + params.id :\n            identify()) + (uniqInitId || identify()))\n\n    return params\n}\n\n/**\n * Helper for searching for a DOM element using a selector inside the context, including the context itself\n * @param {jQuery} ctx Context\n * @param {String} selector CSS selector\n * @param {Boolean} [excludeSelf=false] Exclude context from search\n * @returns {jQuery}\n */\nfunction findDomElem(ctx, selector, excludeSelf) {\n    const res = ctx.find(selector)\n    return excludeSelf?\n       res :\n       res.add(ctx.filter(selector))\n}\n\n/**\n * Returns parameters of an entity's DOM element\n * @param {HTMLElement} domNode DOM node\n * @returns {Object}\n */\nfunction getParams(domNode) {\n    const uniqId = identify(domNode)\n    if(domElemToParams.has(uniqId)) return domElemToParams.get(uniqId)\n    const params = extractParams(domNode)\n    domElemToParams.set(uniqId, params)\n    return params\n}\n\n/**\n * Returns parameters of an entity extracted from DOM node\n * @param {HTMLElement} domNode DOM node\n * @param {String} entityName\n * @returns {Object}\n */\n\nfunction getEntityParams(domNode, entityName) {\n    const params = getParams(domNode)\n    return params[entityName] || (params[entityName] = {})\n}\n\n/**\n * Retrieves entity parameters from a DOM element\n * @param {HTMLElement} domNode DOM node\n * @returns {Object}\n */\nfunction extractParams(domNode) {\n    const attrVal = domNode.getAttribute(BEM_PARAMS_ATTR)\n    return attrVal? JSON.parse(attrVal) : {}\n}\n\n/**\n * Uncouple DOM node from the entity. If this is the last node, then destroys the entity.\n * @param {BemDomEntity} entity entity\n * @param {HTMLElement} domNode DOM node\n */\nfunction removeDomNodeFromEntity(entity, domNode) {\n    if(entity.domElem.length === 1) {\n        entity.delMod('js')\n        uniqIdToEntity.delete(entity._uniqId)\n    } else {\n        entity.domElem = entity.domElem.not(domNode)\n    }\n}\n\n/**\n * Stores DOM node's parent nodes to the storage\n * @param {jQuery} domElem\n */\nfunction storeDomNodeParents(domElem) {\n    domElem.each(function() {\n        domNodesToParents.set(identify(this), this.parentNode)\n    })\n}\n\n/**\n * Clears the cache for elements in context\n * @param {jQuery} ctx\n */\nfunction dropElemCacheForCtx(ctx, dropElemCacheQueue) {\n    ctx.add(ctx.parents()).each((_, domNode) => {\n        const params = domElemToParams.get(identify(domNode))\n\n        params && objects.each(params, (entityParams) => {\n            const entity = uniqIdToEntity.get(entityParams.uniqId)\n            if(entity) {\n                const elemNames = dropElemCacheQueue[entity.__self._blockName]\n                elemNames && entity._dropElemCache(Object.keys(elemNames))\n            }\n        })\n    })\n}\n\n/**\n * Build key for elem\n * @param {Function|String|Object} elem Element class or name or description elem, modName, modVal\n * @returns {Object}\n */\nfunction buildElemKey(elem) {\n    if(typeof elem === 'string') {\n        elem = { elem : elem }\n    } else if(functions.isFunction(elem)) {\n        elem = { elem : elem.getName() }\n    } else if(functions.isFunction(elem.elem)) {\n        elem.elem = elem.elem.getName()\n    }\n\n    return {\n        elem : elem.elem,\n        mod : buildModPostfix(elem.modName, typeof elem.modVal === 'undefined' ? true : elem.modVal)\n    }\n}\n\n/**\n * Returns jQuery collection for provided HTML or BEM entity\n * @param {jQuery|String|BemDomEntity} html\n * @returns {jQuery}\n */\nfunction getJqueryCollection(html) {\n    if(typeof html === 'string') return $($.parseHTML(html, null, true))\n    if(html && html.domElem) return html.domElem\n    return $(html)\n}\n\n/**\n * Validates block to be class or specified description\n * @param {*} Block Any argument passed to find*Block as Block\n * @throws {Error} Will throw an error if the Block argument isn't correct\n */\nfunction validateBlockParam(Block) {\n    if(\n        typeof Block === 'string' ||\n        typeof Block === 'object' && typeof Block.block === 'string'\n    ) {\n        throw new Error('Block must be a class or description (block, modName, modVal) of the block to find')\n    }\n}\n\n/**\n * Returns base entities for declaration\n * @param {Function} baseCls block|elem class\n * @param {String} entityName entityName\n * @param {Function|Array.<Function>} [base] base block|elem + mixes\n * @returns {Array<Function>}\n */\nfunction getEntityBase(baseCls, entityName, base) {\n    base || (base = entities[entityName] || baseCls)\n\n    Array.isArray(base) || (base = [base])\n\n    if(!base[0].__bemEntity) {\n        base = base.slice()\n        base.unshift(entities[entityName] || baseCls)\n    }\n\n    return base\n}\n\n/**\n * @class BemDomEntity\n * @description Base mix for BEM entities that have DOM representation\n */\nconst BemDomEntity = inherit(/** @lends BemDomEntity.prototype */{\n    /**\n     * @constructor\n     * @private\n     * @param {jQuery} domElem DOM element that the entity is created on\n     * @param {Object} params parameters\n     * @param {Boolean} [initImmediately=true]\n     */\n    __constructor : function(domElem, params, initImmediately) {\n        /**\n         * DOM elements of entity\n         * @member {jQuery}\n         * @readonly\n         */\n        this.domElem = domElem\n\n        /**\n         * Cache for elements collections\n         * @member {Object}\n         * @private\n         */\n        this._elemsCache = {}\n\n        /**\n         * Cache for elements\n         * @member {Object}\n         * @private\n         */\n        this._elemCache = {}\n\n        /**\n         * References to parent entities which found current entity ever\n         * @type {Array}\n         * @private\n         */\n        this._findBackRefs = []\n\n        uniqIdToEntity.set(params.uniqId || identify(this), this)\n\n        this.__base(null, params, initImmediately)\n    },\n\n    /**\n     * @abstract\n     * @protected\n     * @returns {Block}\n     */\n    _block : function() {},\n\n    /**\n     * Lazy search for elements nested in a block (caches results)\n     * @protected\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @returns {BemDomCollection}\n     */\n    _elems : function(Elem) {\n        const key = buildElemKey(Elem)\n        const elemsCache = this._elemsCache[key.elem]\n\n        if(elemsCache && key.mod in elemsCache)\n            return elemsCache[key.mod]\n\n        const res = (elemsCache || (this._elemsCache[key.elem] = {}))[key.mod] =\n            this.findMixedElems(Elem).concat(this.findChildElems(Elem))\n\n        res.forEach(function(entity) {\n            entity._findBackRefs.push(this)\n        }, this)\n\n        return res\n    },\n\n    /**\n     * Lazy search for the first element nested in a block (caches results)\n     * @protected\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @returns {Elem}\n     */\n    _elem : function(Elem) {\n        const key = buildElemKey(Elem)\n        const elemCache = this._elemCache[key.elem]\n\n        // NOTE: can use this._elemsCache but it's too rare case\n        if(elemCache && key.mod in elemCache)\n            return elemCache[key.mod]\n\n        const res = (elemCache || (this._elemCache[key.elem] = {}))[key.mod] =\n            this.findMixedElem(Elem) || this.findChildElem(Elem)\n\n        res && res._findBackRefs.push(this)\n\n        return res\n    },\n\n    /**\n     * Clears the cache for elements\n     * @private\n     * @param {...(Function|String|Object)} elems Nested elements names or description elem, modName, modVal\n     * @returns {BemDomEntity} this\n     */\n    _dropElemCache : function(elems) {\n        if(!arguments.length) {\n            this._elemsCache = {}\n            this._elemCache = {}\n            return this\n        }\n\n        (Array.isArray(elems)? elems : [...arguments]).forEach(function(elem) {\n            const key = buildElemKey(elem)\n            if(key.mod) {\n                this._elemsCache[key.elem] && delete this._elemsCache[key.elem][key.mod]\n                this._elemCache[key.elem] && delete this._elemCache[key.elem][key.mod]\n            } else {\n                delete this._elemsCache[key.elem]\n                delete this._elemCache[key.elem]\n            }\n        }, this)\n\n        return this\n    },\n\n    /**\n     * Finds the first child block\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {Block}\n     */\n    findChildBlock : function(Block) {\n        validateBlockParam(Block)\n\n        return this._findEntities('find', Block, true)\n    },\n\n    /**\n     * Finds child blocks\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {BemDomCollection}\n     */\n    findChildBlocks : function(Block) {\n        validateBlockParam(Block)\n\n        return this._findEntities('find', Block)\n    },\n\n    /**\n     * Finds the first parent block\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {Block}\n     */\n    findParentBlock : function(Block) {\n        validateBlockParam(Block)\n\n        return this._findEntities('parents', Block, true)\n    },\n\n    /**\n     * Finds parent blocks\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {BemDomCollection}\n     */\n    findParentBlocks : function(Block) {\n        validateBlockParam(Block)\n\n        return this._findEntities('parents', Block)\n    },\n\n    /**\n     * Finds first mixed block\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {Block}\n     */\n    findMixedBlock : function(Block) {\n        validateBlockParam(Block)\n\n        return this._findEntities('filter', Block, true)\n    },\n\n    /**\n     * Finds mixed blocks\n     * @param {Function|Object} Block Block class or description (block, modName, modVal) of the block to find\n     * @returns {BemDomCollection}\n     */\n    findMixedBlocks : function(Block) {\n        validateBlockParam(Block)\n\n        return this._findEntities('filter', Block)\n    },\n\n    /**\n     * Finds the first child element\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @param {Boolean} [strictMode=false]\n     * @returns {Elem}\n     */\n    findChildElem : function(Elem, strictMode) {\n        return strictMode?\n            this._filterFindElemResults(this._findEntities('find', Elem)).get(0) :\n            this._findEntities('find', Elem, true)\n    },\n\n    /**\n     * Finds child elements\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @param {Boolean} [strictMode=false]\n     * @returns {BemDomCollection}\n     */\n    findChildElems : function(Elem, strictMode) {\n        const res = this._findEntities('find', Elem)\n\n        return strictMode?\n            this._filterFindElemResults(res) :\n            res\n    },\n\n    /**\n     * Finds the first parent element\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @param {Boolean} [strictMode=false]\n     * @returns {Elem}\n     */\n    findParentElem : function(Elem, strictMode) {\n        return strictMode?\n            this._filterFindElemResults(this._findEntities('parents', Elem))[0] :\n            this._findEntities('parents', Elem, true)\n    },\n\n    /**\n     * Finds parent elements\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @param {Boolean} [strictMode=false]\n     * @returns {BemDomCollection}\n     */\n    findParentElems : function(Elem, strictMode) {\n        const res = this._findEntities('parents', Elem)\n        return strictMode? this._filterFindElemResults(res) : res\n    },\n\n    /**\n     * Finds the first mixed element\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @returns {Elem}\n     */\n    findMixedElem : function(Elem) {\n        return this._findEntities('filter', Elem, true)\n    },\n\n    /**\n     * Finds mixed elements.\n     * @param {Function|String|Object} Elem Element class or name or description elem, modName, modVal\n     * @returns {BemDomCollection}\n     */\n    findMixedElems : function(Elem) {\n        return this._findEntities('filter', Elem)\n    },\n\n    /**\n     * Filters results of findElem helper execution in strict mode\n     * @private\n     * @param {BemDomCollection} res Elements\n     * @returns {BemDomCollection}\n     */\n    _filterFindElemResults : function(res) {\n        const block = this._block()\n        return res.filter((elem) => elem._block() === block)\n    },\n\n    /**\n     * Finds entities\n     * @private\n     * @param {String} select\n     * @param {Function|String|Object} entity\n     * @param {Boolean} [onlyFirst=false]\n     * @returns {*}\n     */\n    _findEntities : function(select, entity, onlyFirst) {\n        const entityName = functions.isFunction(entity)?\n                entity.getEntityName() :\n                typeof entity === 'object'?\n                    entity.block?\n                        entity.block.getEntityName() :\n                        typeof entity.elem === 'string'?\n                            this.__self._blockName + ELEM_DELIM + entity.elem :\n                            entity.elem.getEntityName() :\n                    this.__self._blockName + ELEM_DELIM + entity\n        const selector = '.' +\n                (typeof entity === 'object'?\n                    buildClassName(\n                        entityName,\n                        entity.modName,\n                        typeof entity.modVal === 'undefined'?\n                            true :\n                            entity.modVal) :\n                    entityName) +\n                (onlyFirst? ':first' : '')\n        const domElems = this.domElem[select](selector)\n\n        if(onlyFirst) return domElems[0]?\n            initEntity(entityName, domElems.eq(0), undefined, true)._setInitedMod() :\n            null\n\n        const res = []\n        const uniqIds = {}\n\n        domElems.each((i, domElem) => {\n            const block = initEntity(entityName, $(domElem), undefined, true)._setInitedMod()\n            if(!uniqIds[block._uniqId]) {\n                uniqIds[block._uniqId] = true\n                res.push(block)\n            }\n        })\n\n        return new BemDomCollection(res)\n    },\n\n    /**\n     * Returns an manager to bind and unbind DOM events for particular context\n     * @protected\n     * @param {Function|String|Object|Elem|BemDomCollection|document|window} [ctx=this.domElem] context to bind,\n     *     can be BEM-entity class, instance, collection of BEM-entities,\n     *     element name or description (elem, modName, modVal), document or window\n     * @returns {EventManager}\n     */\n    _domEvents : function(ctx) {\n        const bindScope = ctx === document || ctx === window ? $(ctx) : this.domElem\n        return domEventManagerFactory.getEventManager(this, ctx, bindScope)\n    },\n\n    /**\n     * Returns an manager to bind and unbind BEM events for particular context\n     * @protected\n     * @param {Function|String|BemDomEntity|BemDomCollection|Object} [ctx=this.domElem] context to bind,\n     *     can be BEM-entity class, instance, collection of BEM-entities,\n     *     element name or description (elem, modName, modVal)\n     * @returns {EventManager}\n     */\n    _events : function(ctx) {\n        return bemEventManagerFactory.getEventManager(this, ctx, this.domElem)\n    },\n\n    /**\n     * Executes the BEM entity's event handlers and delegated handlers\n     * @protected\n     * @param {String|Object|events:Event} e Event name\n     * @param {Object} [data] Additional information\n     * @returns {BemEntity} this\n     */\n    _emit : function(e, data) {\n        if((typeof e === 'object' && e.modName === 'js') || this.hasMod('js', 'inited')) {\n            bemEvents.emit(this, e, data)\n        }\n\n        return this\n    },\n\n    /** @override */\n    _extractModVal : function(modName) {\n        const domNode = this.domElem[0]\n        let matches\n\n        domNode &&\n            (matches = String(domNode.className)\n                .match(this.__self._buildModValRE(modName)))\n\n        return matches? matches[2] || true : ''\n    },\n\n    /** @override */\n    _onSetMod : function(modName, modVal, oldModVal) {\n        const _self = this.__self\n        const name = _self.getName()\n\n        this._findBackRefs.forEach(function(ref) {\n            oldModVal === '' || ref._dropElemCache({ elem : name, modName : modName, modVal : oldModVal })\n            ref._dropElemCache(modVal === ''? name : { elem : name, modName : modName, modVal : modVal })\n        })\n\n        this.__base.apply(this, arguments)\n\n        if(modName !== 'js' || modVal !== '') {\n            const classNamePrefix = _self._buildModClassNamePrefix(modName)\n            const classNameRE = _self._buildModValRE(modName)\n            const needDel = modVal === ''\n\n            this.domElem.each(function() {\n                const className = String(this.className)\n                let modClassName = classNamePrefix\n\n                modVal !== true && (modClassName += MOD_DELIM + modVal)\n\n                ;(oldModVal === true?\n                    classNameRE.test(className) :\n                    (' ' + className).indexOf(' ' + classNamePrefix + MOD_DELIM) > -1)?\n                        this.className = className.replace(\n                            classNameRE,\n                            (needDel? '' : '$1' + modClassName)) :\n                        needDel || $(this).addClass(modClassName)\n            })\n        }\n    },\n\n    /** @override */\n    _afterSetMod : function(modName, modVal, oldModVal) {\n        const eventData = { modName, modVal, oldModVal }\n        this\n            ._emit({ modName, modVal : '*' }, eventData)\n            ._emit({ modName, modVal }, eventData)\n    },\n\n    /**\n     * Checks whether an entity is in the entity\n     * @param {BemDomEntity} entity entity\n     * @returns {Boolean}\n     */\n    containsEntity : function(entity) {\n        return dom.contains(this.domElem, entity.domElem)\n    },\n\n    /**\n     * Replaces the content of the entity's DOM element\n     * @param {jQuery|String|BemDomEntity} content New content\n     * @returns {jQuery}\n     */\n    update : function(content) {\n        return bemDom.update(this.domElem, content)\n    },\n\n    /**\n     * Appends content to the entity's DOM element\n     * @param {jQuery|String|BemDomEntity} content Content to be added\n     * @returns {jQuery}\n     */\n    append : function(content) {\n        return bemDom.append(this.domElem, content)\n    },\n\n    /**\n     * Prepends content to the entity's DOM element\n     * @param {jQuery|String|BemDomEntity} content Content to be added\n     * @returns {jQuery}\n     */\n    prepend : function(content) {\n        return bemDom.prepend(this.domElem, content)\n    },\n\n    /**\n     * Adds content before the entity's DOM element\n     * @param {jQuery|String|BemDomEntity} content Content to be added\n     * @returns {jQuery}\n     */\n    before : function(content) {\n        return bemDom.before(this.domElem, content)\n    },\n\n    /**\n     * Adds content after the entity's DOM element\n     * @param {jQuery|String|BemDomEntity} content Content to be added\n     * @returns {jQuery}\n     */\n    after : function(content) {\n        return bemDom.after(this.domElem, content)\n    }\n\n}, /** @lends BemDomEntity */{\n    /** @override */\n    create : function() {\n        throw Error('bemDom entities can not be created otherwise than from DOM')\n    },\n\n    /** @override */\n    _processInit : function(heedInit) {\n        if(this.onInit && this._inited == heedInit) {\n            this.__base(heedInit)\n\n            this.onInit()\n\n            const name = this.getName()\n            const origOnInit = this.onInit\n\n            // allow future calls of init only in case of inheritance in other block\n            this.init = function() {\n                this.getName() === name && origOnInit.apply(this, arguments)\n            }\n        }\n    },\n\n    /**\n     * Returns an manager to bind and unbind events for particular context\n     * @protected\n     * @param {Function|String|Object} [ctx] context to bind,\n     *     can be BEM-entity class, instance, element name or description (elem, modName, modVal)\n     * @returns {EventManager}\n     */\n    _domEvents : function(ctx) {\n        return domEventManagerFactory.getEventManager(this, ctx, bemDom.scope)\n    },\n\n    /**\n     * Returns an manager to bind and unbind BEM events for particular context\n     * @protected\n     * @param {Function|String|Object} [ctx] context to bind,\n     *     can be BEM-entity class, instance, element name or description (block or elem, modName, modVal)\n     * @returns {EventManager}\n     */\n    _events : function(ctx) {\n        return bemEventManagerFactory.getEventManager(this, ctx, bemDom.scope)\n    },\n\n    /**\n     * Builds a prefix for the CSS class of a DOM element of the entity, based on modifier name\n     * @private\n     * @param {String} modName Modifier name\n     * @returns {String}\n     */\n    _buildModClassNamePrefix : function(modName) {\n        return this.getEntityName() + MOD_DELIM + modName\n    },\n\n    /**\n     * Builds a regular expression for extracting modifier values from a DOM element of an entity\n     * @private\n     * @param {String} modName Modifier name\n     * @returns {RegExp}\n     */\n    _buildModValRE : function(modName) {\n        return new RegExp(\n            '(\\\\s|^)' +\n            this._buildModClassNamePrefix(modName) +\n            '(?:' + MOD_DELIM + '(' + NAME_PATTERN + '))?(?=\\\\s|$)')\n    },\n\n    /**\n     * Builds a CSS class name corresponding to the entity and modifier\n     * @protected\n     * @param {String} [modName] Modifier name\n     * @param {String} [modVal] Modifier value\n     * @returns {String}\n     */\n    _buildClassName : function(modName, modVal) {\n        return buildClassName(this.getEntityName(), modName, modVal)\n    },\n\n    /**\n     * Builds a CSS selector corresponding to an entity and modifier\n     * @protected\n     * @param {String} [modName] Modifier name\n     * @param {String} [modVal] Modifier value\n     * @returns {String}\n     */\n    _buildSelector : function(modName, modVal) {\n        return '.' + this._buildClassName(modName, modVal)\n    }\n})\n\n/**\n * @class Block\n * @description Base class for creating BEM blocks that have DOM representation\n * @augments i-bem:Block\n * @exports i-bem-dom:Block\n */\nconst Block = inherit([bem.Block, BemDomEntity], /** @lends Block.prototype */{\n    /** @override */\n    _block : function() {\n        return this\n    }\n})\n\n/**\n * @class Elem\n * @description Base class for creating BEM elements that have DOM representation\n * @augments i-bem:Elem\n * @exports i-bem-dom:Elem\n */\nconst Elem = inherit([bem.Elem, BemDomEntity], /** @lends Elem.prototype */{\n    /** @override */\n    _block : function() {\n        return this._blockInstance || (this._blockInstance = this.findParentBlock(getEntityCls(this.__self._blockName)))\n    }\n})\n\n/**\n * Returns a block on a DOM element and initializes it if necessary\n * @param {Function} BemDomEntity entity\n * @param {Object} [params] entity parameters\n * @returns {BemDomEntity|null}\n */\n$.fn.bem = function(BemDomEntity, params) {\n    const entity = initEntity(BemDomEntity.getEntityName(), this, params, true)\n    return entity? entity._setInitedMod() : null\n}\n\n/**\n * Returns an existing BEM entity instance from a DOM node without initialization\n * @param {HTMLElement} domNode DOM node\n * @param {Function} BemDomEntity entity class\n * @returns {BemDomEntity|null}\n */\nfunction getEntityFromDom(domNode, BemDomEntity) {\n    const entityName = BemDomEntity.getEntityName()\n    const params = getParams(domNode)\n    const entityParams = params[entityName]\n\n    return entityParams && entityParams.uniqId?\n        uniqIdToEntity.get(entityParams.uniqId) || null :\n        null\n}\n\nbemDom = {\n    /**\n     * Scope (set on DOM ready)\n     * @type jQuery\n     */\n    scope : null,\n\n    /**\n     * Document shortcut\n     * @type jQuery\n     */\n    doc : null,\n\n    /**\n     * Window shortcut\n     * @type jQuery\n     */\n    win : null,\n\n    /**\n     * Base bemDom block\n     * @type Function\n     */\n    Block,\n\n    /**\n     * Base bemDom element\n     * @type Function\n     */\n    Elem,\n\n    /**\n     * @param {*} entity\n     * @returns {Boolean}\n     */\n    isEntity : function(entity) {\n        return entity instanceof Block || entity instanceof Elem\n    },\n\n    /**\n     * Declares DOM-based block and creates block class\n     * @param {String|Function} blockName Block name or block class\n     * @param {Function|Array.<Function>} [base] base block + mixes\n     * @param {Object} [props] Methods\n     * @param {Object} [staticProps] Static methods\n     * @returns {Function} Block class\n     */\n    declBlock : function(blockName, base, props, staticProps) {\n        if(!base || (typeof base === 'object' && !Array.isArray(base))) {\n            staticProps = props\n            props = base\n            base = typeof blockName === 'string'?\n                entities[blockName] || Block :\n                blockName\n        }\n\n        base = getEntityBase(Block, blockName, base)\n\n        return bem.declBlock(blockName, base, props, staticProps)\n    },\n\n    /**\n     * Declares elem and creates elem class\n     * @param {String} blockName Block name\n     * @param {String} elemName Elem name\n     * @param {Function|Array.<Function>} [base] base elem + mixes\n     * @param {Object} [props] Methods\n     * @param {Object} [staticProps] Static methods\n     * @returns {Function} Elem class\n     */\n    declElem : function(blockName, elemName, base, props, staticProps) {\n        if(typeof blockName === 'function') {\n            if(typeof elemName !== 'string') {\n                // declElem(ElemClass, base?, props?, staticProps?) — redeclaration of elem class\n                staticProps = props\n                props = base\n                base = elemName\n                elemName = blockName._name\n                blockName = blockName._blockName\n            } else {\n                // declElem(BlockClass, 'elemName', ...) — block class as first arg\n                blockName = blockName.getName()\n            }\n        }\n\n        const entityName = blockName + ELEM_DELIM + elemName\n\n        if(!base || (typeof base === 'object' && !Array.isArray(base))) {\n            staticProps = props\n            props = base\n            base = entities[entityName] || Elem\n        }\n\n        base = getEntityBase(Elem, entityName, base)\n\n        return bem.declElem(blockName, elemName, base, props, staticProps)\n    },\n\n    declMixin : bem.declMixin,\n\n    /**\n     * Initializes blocks on a fragment of the DOM tree\n     * @param {jQuery|String} [ctx=scope] Root DOM node\n     * @returns {jQuery} ctx Initialization context\n     */\n    init : function(ctx) {\n        ctx = typeof ctx === 'string'?\n            $(ctx) :\n            ctx || bemDom.scope\n\n        const dropElemCacheQueue = {}\n        const uniqInitId = identify()\n\n        // NOTE: we find only js-entities, so cahced elems without js can't be dropped from cache\n        findDomElem(ctx, BEM_SELECTOR).each(function() {\n            initEntities($(this), uniqInitId, dropElemCacheQueue)\n        })\n\n        bem._runInitFns()\n\n        dropElemCacheForCtx(ctx, dropElemCacheQueue)\n\n        return ctx\n    },\n\n    /**\n     * @param {jQuery} ctx Root DOM node\n     * @param {Boolean} [excludeSelf=false] Exclude the main domElem\n     * @param {Boolean} [destructDom=false] Remove DOM node during destruction\n     * @private\n     */\n    _destruct : function(ctx, excludeSelf, destructDom) {\n        let _ctx\n        const currentDestructingDomNodes = []\n\n        storeDomNodeParents(_ctx = excludeSelf? ctx.children() : ctx)\n\n        reverse.call(findDomElem(_ctx, BEM_SELECTOR)).each((_, domNode) => {\n            const params = getParams(domNode)\n            const domNodeId = identify(domNode)\n\n            destructingDomNodes.set(domNodeId, true)\n            currentDestructingDomNodes.push(domNodeId)\n\n            objects.each(params, (entityParams) => {\n                if(entityParams.uniqId) {\n                    const entity = uniqIdToEntity.get(entityParams.uniqId)\n                    entity?\n                        removeDomNodeFromEntity(entity, domNode) :\n                        uniqIdToDomElems.delete(entityParams.uniqId)\n                }\n            })\n            domElemToParams.delete(identify(domNode))\n        })\n\n        // NOTE: it was moved here as jquery events aren't triggered on detached DOM elements\n        destructDom &&\n            (excludeSelf? ctx.empty() : ctx.remove())\n\n        // flush parent nodes storage that has been filled above\n        domNodesToParents.clear()\n\n        currentDestructingDomNodes.forEach((domNodeId) => {\n            destructingDomNodes.delete(domNodeId)\n        })\n    },\n\n    /**\n     * Destroys blocks on a fragment of the DOM tree\n     * @param {jQuery} ctx Root DOM node\n     * @param {Boolean} [excludeSelf=false] Exclude the main domElem\n     */\n    destruct : function(ctx, excludeSelf) {\n        this._destruct(ctx, excludeSelf, true)\n    },\n\n    /**\n     * Detaches blocks on a fragment of the DOM tree without DOM tree destruction\n     * @param {jQuery} ctx Root DOM node\n     * @param {Boolean} [excludeSelf=false] Exclude the main domElem\n     */\n    detach : function(ctx, excludeSelf) {\n        this._destruct(ctx, excludeSelf)\n    },\n\n    /**\n     * Replaces a fragment of the DOM tree inside the context, destroying old blocks and intializing new ones\n     * @param {jQuery} ctx Root DOM node\n     * @param {jQuery|String} content New content\n     * @returns {jQuery} Updated root DOM node\n     */\n    update : function(ctx, content) {\n        this.destruct(ctx, true)\n        return this.init(ctx.html(content))\n    },\n\n    /**\n     * Changes a fragment of the DOM tree including the context and initializes blocks.\n     * @param {jQuery} ctx Root DOM node\n     * @param {jQuery|String} content Content to be added\n     * @returns {jQuery} New content\n     */\n    replace : function(ctx, content) {\n        const prev = ctx.prev()\n        const parent = ctx.parent()\n\n        content = getJqueryCollection(content)\n\n        this.destruct(ctx)\n\n        return this.init(prev.length?\n            content.insertAfter(prev) :\n            content.prependTo(parent))\n    },\n\n    /**\n     * Adds a fragment of the DOM tree at the end of the context and initializes blocks\n     * @param {jQuery} ctx Root DOM node\n     * @param {jQuery|String} content Content to be added\n     * @returns {jQuery} New content\n     */\n    append : function(ctx, content) {\n        return this.init(getJqueryCollection(content).appendTo(ctx))\n    },\n\n    /**\n     * Adds a fragment of the DOM tree at the beginning of the context and initializes blocks\n     * @param {jQuery} ctx Root DOM node\n     * @param {jQuery|String} content Content to be added\n     * @returns {jQuery} New content\n     */\n    prepend : function(ctx, content) {\n        return this.init(getJqueryCollection(content).prependTo(ctx))\n    },\n\n    /**\n     * Adds a fragment of the DOM tree before the context and initializes blocks\n     * @param {jQuery} ctx Contextual DOM node\n     * @param {jQuery|String} content Content to be added\n     * @returns {jQuery} New content\n     */\n    before : function(ctx, content) {\n        return this.init(getJqueryCollection(content).insertBefore(ctx))\n    },\n\n    /**\n     * Adds a fragment of the DOM tree after the context and initializes blocks\n     * @param {jQuery} ctx Contextual DOM node\n     * @param {jQuery|String} content Content to be added\n     * @returns {jQuery} New content\n     */\n    after : function(ctx, content) {\n        return this.init(getJqueryCollection(content).insertAfter(ctx))\n    },\n\n    /**\n     * Returns an existing BEM entity instance from a DOM node without initialization\n     * @param {HTMLElement} domNode DOM node\n     * @param {Function} BemDomEntity entity class\n     * @returns {BemDomEntity|null}\n     */\n    getFromDom : function(domNode, BemDomEntity) {\n        return getEntityFromDom(domNode, BemDomEntity)\n    },\n\n    /**\n     * Initializes a BEM entity on a DOM node and returns the instance\n     * @param {HTMLElement|jQuery} domNode DOM node or jQuery element\n     * @param {Function|String} Entity Entity class or entity name\n     * @param {Object} [params] Initialization parameters\n     * @returns {BemDomEntity|null}\n     */\n    initOnDom : function(domNode, Entity, params) {\n        const domElem = domNode.jquery ? domNode : $(domNode)\n        const entityName = typeof Entity === 'string' ? Entity : Entity.getEntityName()\n        const entity = initEntity(entityName, domElem, params, true)\n        return entity ? entity._setInitedMod() : null\n    }\n}\n\n// Initialize DOM-dependent properties on DOM ready\n$(function() {\n    bemDom.scope = $('body')\n    bemDom.doc = $(document)\n    bemDom.win = $(window)\n})\n\nexport default bemDom\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.ru.md",
    "content": "# i-bem-dom\n\nБлок-хелпер, позволяющий создавать другие блоки, имеющие DOM-представление.\n\nРеализация блока представляет собой специализированный JavaScript-фреймворк для веб-разработки в рамках методологии БЭМ.\n\nВ виде отдельного документа доступно [подробное руководство пользователя](https://ru.bem.info/technology/i-bem/v4/i-bem-js/).\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.spec.js",
    "content": "modules.define('spec', [\n    'i-bem',\n    'i-bem-dom',\n    'i-bem-dom__collection',\n    'objects',\n    'functions',\n    'jquery',\n    'chai',\n    'sinon',\n    'BEMHTML'\n], function(provide,\n    bem,\n    bemDom,\n    BemDomCollection,\n    objects,\n    functions,\n    $,\n    chai,\n    sinon,\n    BEMHTML\n) {\n\nvar undef,\n    expect = chai.expect;\n\ndescribe('i-bem-dom', function() {\n    var rootNode;\n\n    afterEach(function() {\n        if(rootNode) {\n            bemDom.destruct(rootNode);\n            rootNode = null;\n        }\n\n        objects.each(bem.entities, function(_, entityName) {\n            delete bem.entities[entityName];\n        });\n    });\n\n    describe('decl', function() {\n        it('should enable to inherit block to itself', function() {\n            var spy1 = sinon.spy(),\n                spy2 = sinon.spy(),\n                Block = bemDom.declBlock('block', {\n                    onSetMod : {\n                        js : {\n                            inited : spy1\n                        }\n                    }\n                }),\n                Block2 = bemDom.declBlock('block', {\n                    onSetMod : {\n                        js : {\n                            inited : spy2\n                        }\n                    }\n                }),\n                block = (rootNode = createDomNode({\n                    block : 'block'\n                })).bem(Block);\n\n            Block2.should.be.equal(Block);\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n        });\n\n        it('should enable to inherit block to itself using entity class', function() {\n            var spy1 = sinon.spy(),\n                spy2 = sinon.spy(),\n                Block = bemDom.declBlock('block', {\n                    onSetMod : {\n                        js : {\n                            inited : spy1\n                        }\n                    }\n                }),\n                Block2 = bemDom.declBlock(Block, {\n                    onSetMod : {\n                        js : {\n                            inited : spy2\n                        }\n                    }\n                }),\n                block = (rootNode = createDomNode({\n                    block : 'block'\n                })).bem(Block);\n\n            Block2.should.be.equal(Block);\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n        });\n\n        it('should enable to inherit elem to itself', function() {\n            var spy1 = sinon.spy(),\n                spy2 = sinon.spy(),\n                Elem = bemDom.declElem('block', 'elem', {\n                    onSetMod : {\n                        js : {\n                            inited : spy1\n                        }\n                    }\n                }),\n                Elem2 = bemDom.declElem('block', 'elem', {\n                    onSetMod : {\n                        js : {\n                            inited : spy2\n                        }\n                    }\n                }),\n                elem = (rootNode = createDomNode({\n                    block : 'block',\n                    elem : 'elem'\n                })).bem(Elem);\n\n            Elem2.should.be.equal(Elem);\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n        });\n\n        it('should enable to inherit elem to itself using entity class', function() {\n            var spy1 = sinon.spy(),\n                spy2 = sinon.spy(),\n                Elem = bemDom.declElem('block', 'elem', {\n                    onSetMod : {\n                        js : {\n                            inited : spy1\n                        }\n                    }\n                }),\n                Elem2 = bemDom.declElem(Elem, {\n                    onSetMod : {\n                        js : {\n                            inited : spy2\n                        }\n                    }\n                }),\n                elem = (rootNode = createDomNode({\n                    block : 'block',\n                    elem : 'elem'\n                })).bem(Elem);\n\n            Elem2.should.be.equal(Elem);\n            spy1.should.not.have.been.called;\n            spy2.should.have.been.called;\n        });\n\n        it('should enable to mix block', function() {\n            var MixBlock = bemDom.declMixin({}),\n\n                Block = bemDom.declBlock('block', MixBlock),\n                block = (rootNode = createDomNode({\n                    block : 'block'\n                })).bem(Block),\n\n                Elem = bemDom.declElem('block', 'elem', MixBlock),\n                elem = (rootNode = createDomNode({\n                    block : 'block',\n                    elem : 'elem'\n                })).bem(Elem);\n\n            block.should.be.instanceOf(bemDom.Block);\n            elem.should.be.instanceOf(bemDom.Elem);\n        });\n\n        it('should enable to inherit and mix blocks', function() {\n            var MixBlock = bemDom.declMixin({}),\n\n                Block1 = bemDom.declBlock('block1'),\n                Block2 = bemDom.declBlock('block2', [Block1, MixBlock]),\n                block2 = (rootNode = createDomNode({\n                    block : 'block2'\n                })).bem(Block2),\n\n                Elem1 = bemDom.declElem('block', 'elem1'),\n                Elem2 = bemDom.declElem('block', 'elem2', [Elem1, MixBlock]),\n                elem2 = (rootNode = createDomNode({\n                    block : 'block',\n                    elem : 'elem2'\n                })).bem(Elem2);\n\n            block2.should.be.instanceOf(Block1);\n            elem2.should.be.instanceOf(Elem1);\n        });\n    });\n\n    describe('getMod', function() {\n        it('should return properly extracted mod from html', function() {\n            var Block = bemDom.declBlock('block');\n\n            [\n                {\n                    mods : undef,\n                    val : ''\n                },\n                {\n                    mods : { m1 : 'v1' },\n                    val : 'v1'\n                },\n                {\n                    mods : { m1 : 'v1' },\n                    mix : { block : 'block2', mods : { 'm1' : 'v2' } },\n                    val : 'v1'\n                },\n                {\n                    mods : { m1 : true },\n                    val : true\n                }\n            ].forEach(function(data) {\n                (rootNode = createDomNode({\n                    block : 'block',\n                    mods : data.mods,\n                    mix : data.mix\n                })).bem(Block).getMod('m1')\n                    .should.be.eql(data.val);\n\n                bemDom.destruct(rootNode);\n            });\n        });\n\n        it('should return properly extracted elem mod from html', function() {\n            var Block = bemDom.declBlock('block');\n\n            [\n                {\n                    elemMods : undef,\n                    val : ''\n                },\n                {\n                    elemMods : { m1 : 'v1' },\n                    val : 'v1'\n                },\n                {\n                    elemMods : { m1 : 'v1', m2 : 'v2' },\n                    val : 'v1'\n                },\n                {\n                    elemMods : { m1 : 'v1', m2 : 'v11' },\n                    mix : { elem : 'elem1', elemMods : { m1 : 'v2' } },\n                    val : 'v1'\n                },\n                {\n                    elemMods : { m1 : true },\n                    val : true\n                }\n            ].forEach(function(data) {\n                (rootNode = createDomNode({\n                    block : 'block',\n                    content : {\n                        elem : 'elem',\n                        elemMods : data.elemMods,\n                        mix : data.mix\n                    }\n                })).bem(Block)._elem('elem').getMod('m1')\n                    .should.be.equal(data.val);\n\n                bemDom.destruct(rootNode);\n            });\n        });\n    });\n\n    describe('setMod', function() {\n        it('should properly set CSS class names', function() {\n            var Block = bemDom.declBlock('block');\n\n            [\n                {\n                    beforeMods : undef,\n                    afterCls : 'block i-bem block_js_inited block_m1_v1',\n                    mods : { m1 : 'v1' }\n                },\n                {\n                    beforeMods : { m6 : true, m7 : 'v7' },\n                    afterCls : 'block i-bem block_js_inited block_m1_v1 block_m2_v2 block_m3 block_m4_v4 block_m5',\n                    mods : { m1 : 'v1', m2 : 'v2', m3 : true, m4 : 'v4', m5 : true, m6 : false, m7 : '' }\n                },\n                {\n                    beforeMods : { m6 : true, m7 : 'v7' },\n                    afterCls : 'block bla-block bla-block_m3 bla-block_m1_v1 i-bem block_js_inited block_m1_v1 block_m2_v2 block_m3 block_m4_v4 block_m5',\n                    mods : { m1 : 'v1', m2 : 'v2', m3 : true, m4 : 'v4', m5 : true, m6 : false, m7 : '' },\n                    mix : { block : 'bla-block', mods : { m3 : true, m1 : 'v1' } }\n                }\n            ].forEach(function(data) {\n                var block = (rootNode = createDomNode({\n                        block : 'block',\n                        mods : data.beforeMods,\n                        mix : data.mix\n                    })).bem(Block);\n\n                objects.each(data.mods, function(modVal, modName) {\n                    modName === 'm3'?\n                        block.setMod(modName) :\n                        block.setMod(modName, modVal);\n                });\n\n                block.domElem[0].className.should.be.equal(data.afterCls);\n\n                bemDom.destruct(rootNode);\n            });\n        });\n\n        it('should properly set elem CSS class names', function() {\n            var Block = bemDom.declBlock('block'),\n                rootNode;\n\n            [\n                {\n                    elemMods : undef,\n                    afterCls : 'block__elem i-bem block__elem_js_inited block__elem_m1_v1',\n                    mods : { m1 : 'v1' }\n                },\n                {\n                    elemMods : { m6 : true, m7 : 'v7' },\n                    afterCls : 'block__elem i-bem block__elem_js_inited block__elem_m1_v1 block__elem_m2_v2 block__elem_m3 block__elem_m4_v4 block__elem_m5',\n                    mods : { m1 : 'v1', m2 : 'v2', m3 : true, m4 : 'v4', m5 : true, m6 : false, m7 : '' }\n                }\n            ].forEach(function(data) {\n                var elem = (rootNode = createDomNode({\n                        block : 'block',\n                        content : {\n                            elem : 'elem',\n                            mods : data.elemMods\n                        }\n                    })).bem(Block)._elem('elem');\n\n                objects.each(data.mods, function(modVal, modName) {\n                    modName === 'm3'?\n                        elem.setMod(modName) :\n                        elem.setMod(modName, modVal);\n                });\n\n                elem.domElem[0].className.should.be.equal(data.afterCls);\n\n                bemDom.destruct(rootNode);\n            });\n        });\n    });\n\n    describe('find*Block(s)', function() {\n        var rootBlock,\n            B1Block, B3Block, B4Block, B5Block;\n\n        beforeEach(function() {\n            var RootBlock = bemDom.declBlock('root');\n            B1Block = bemDom.declBlock('b1');\n            B3Block = bemDom.declBlock('b3');\n            B4Block = bemDom.declBlock('b4');\n            B5Block = bemDom.declBlock('b5');\n\n            rootNode = createDomNode({\n                block : 'root',\n                content : {\n                    block : 'b1',\n                    js : { id : '1' },\n                    mods : { m2 : 'v1', m3 : true },\n                    content : [\n                        { block : 'b2' },\n                        {\n                            block : 'b1',\n                            mods : { m1 : 'v1' },\n                            js : { id : '2' }\n                        },\n                        {\n                            block : 'b3',\n                            mods : { m1 : true },\n                            js : { id : '5' },\n                            mix : [\n                                { block : 'b4', js : { id : '6' } },\n                                {\n                                    block : 'b5',\n                                    mods : { m1 : 'v1' },\n                                    js : { id : '7' }\n                                }\n                            ],\n                            content : {\n                                block : 'b1',\n                                mods : { m1 : 'v2' },\n                                js : { id : '3' },\n                                content : {\n                                    block : 'b1',\n                                    mods : { m1 : true },\n                                    js : { id : '4' }\n                                }\n                            }\n                        },\n                        {\n                            block : 'b3', js : { id : '5' },\n                            mix : { block : 'b4', js : { id : '8' } }\n                        }\n                    ]\n                }\n            });\n\n            rootBlock = rootNode.bem(RootBlock);\n        });\n\n        describe('findChildBlocks', function() {\n            it('should throw error if Block given as string', function() {\n                function find() {\n                    rootBlock.findChildBlocks('string');\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should throw error if Block given as description object with block as string', function() {\n                function find() {\n                    rootBlock.findChildBlocks({ block : 'string' });\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should return BEM-collection', function() {\n                rootBlock.findChildBlocks(B1Block).should.be.instanceOf(BemDomCollection);\n            });\n\n            it('should return instances of Block founded by class', function() {\n                rootBlock.findChildBlocks(B1Block).forEach(function(block) {\n                    block.should.be.instanceOf(B1Block);\n                });\n            });\n\n            it('should find all blocks by block class', function() {\n                getEntityIds(rootBlock.findChildBlocks(B1Block)).should.be.eql(['1', '2', '3', '4']);\n            });\n\n            it('should find all blocks by block class, modName and modVal', function() {\n                getEntityIds(rootBlock.findChildBlocks({ block : B1Block, modName : 'm1', modVal : 'v1' }))\n                    .should.be.eql(['2']);\n            });\n\n            it('should find all blocks by block class and boolean mod', function() {\n                getEntityIds(rootBlock.findChildBlocks({ block : B1Block, modName : 'm1', modVal : true }))\n                    .should.be.eql(['4']);\n            });\n\n            it('should find all blocks by block class and boolean mod without modVal', function() {\n                getEntityIds(rootBlock.findChildBlocks({ block : B1Block, modName : 'm1' }))\n                    .should.be.eql(['4']);\n            });\n        });\n\n        describe('findChildBlock', function() {\n            it('should throw error if Block given as string', function() {\n                function find() {\n                    rootBlock.findChildBlock('string');\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should throw error if Block given as description object with block as string', function() {\n                function find() {\n                    rootBlock.findChildBlock({ block : 'string' });\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should return instance of Block found by class', function() {\n                rootBlock.findChildBlock(B1Block).should.be.instanceOf(B1Block);\n            });\n\n            it('should return null if nothing found', function() {\n                var B99Block = bemDom.declBlock('b99');\n                expect(rootBlock.findChildBlock(B99Block)).to.be.null;\n            });\n\n            it('should find first block by block class', function() {\n                rootBlock.findChildBlock(B1Block).params.id\n                    .should.be.equal('1');\n            });\n\n            it('should find first block by block class, modName and modVal', function() {\n                rootBlock.findChildBlock({ block : B1Block, modName : 'm1', modVal : 'v1' })\n                    .params.id\n                        .should.be.equal('2');\n            });\n\n            it('should find first block by block class and boolean mod', function() {\n                rootBlock.findChildBlock({ block : B1Block, modName : 'm1', modVal : true })\n                    .params.id\n                        .should.be.equal('4');\n            });\n\n            it('should find first block by block class and boolean mod without modVal', function() {\n                rootBlock.findChildBlock({ block : B1Block, modName : 'm1' })\n                    .params.id\n                        .should.be.equal('4');\n            });\n        });\n\n        describe('findParentBlocks', function() {\n            var leafBlock;\n\n            beforeEach(function() {\n                leafBlock = rootBlock.findChildBlock({ block : B1Block, modName : 'm1', modVal : true });\n            });\n\n            it('should throw error if Block given as string', function() {\n                function find() {\n                    rootBlock.findParentBlocks('string');\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should throw error if Block given as description object with block as string', function() {\n                function find() {\n                    rootBlock.findParentBlocks({ block : 'string' });\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should return BEM-collection', function() {\n                leafBlock.findParentBlocks(B1Block).should.be.instanceOf(BemDomCollection);\n            });\n\n            it('should find all ancestor blocks by block class', function() {\n                getEntityIds(leafBlock.findParentBlocks(B1Block)).should.be.eql(['3', '1']);\n            });\n\n            it('should find all ancestor blocks by block class, modName and modVal', function() {\n                getEntityIds(leafBlock.findParentBlocks({ block : B1Block, modName : 'm1', modVal : 'v2' }))\n                    .should.be.eql(['3']);\n            });\n\n            it('should find all ancestor blocks by block class and boolean mod', function() {\n                getEntityIds(leafBlock.findParentBlocks({ block : B3Block, modName : 'm1', modVal : true }))\n                    .should.be.eql(['5']);\n            });\n\n            it('should find all ancestor blocks by block class and boolean mod without modVal', function() {\n                getEntityIds(leafBlock.findParentBlocks({ block : B3Block, modName : 'm1' }))\n                    .should.be.eql(['5']);\n            });\n        });\n\n        describe('findParentBlock', function() {\n            var leafBlock;\n\n            beforeEach(function() {\n                leafBlock = rootBlock.findChildBlock({ block : B1Block, modName : 'm1', modVal : true });\n            });\n\n            it('should throw error if Block given as string', function() {\n                function find() {\n                    rootBlock.findParentBlock('string');\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should throw error if Block given as description object with block as string', function() {\n                function find() {\n                    rootBlock.findParentBlock({ block : 'string' });\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should find first ancestor block by block class', function() {\n                leafBlock.findParentBlock(B1Block).params.id.should.be.equal('3');\n            });\n\n            it('should find first ancestor block by block class, modName and modVal', function() {\n                leafBlock.findParentBlock({ block : B1Block, modName : 'm2', modVal : 'v1' })\n                    .params.id\n                        .should.be.equal('1');\n            });\n\n            it('should find first ancestor block by block class and boolean mod', function() {\n                leafBlock.findParentBlock({ block : B1Block, modName : 'm3', modVal : true })\n                    .params.id\n                        .should.be.equal('1');\n            });\n\n            it('should find first ancestor block by block class and boolean mod without modVal', function() {\n                leafBlock.findParentBlock({ block : B1Block, modName : 'm3' })\n                    .params.id\n                        .should.be.equal('1');\n            });\n        });\n\n        describe('findMixedBlocks', function() {\n            it('should throw error if Block given as string', function() {\n                function find() {\n                    rootBlock.findMixedBlocks('string');\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should throw error if Block given as description object with block as string', function() {\n                function find() {\n                    rootBlock.findMixedBlocks({ block : 'string' });\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should return BEM-collection', function() {\n                rootBlock.findChildBlock({ block : B3Block }).findMixedBlocks(B4Block)\n                    .should.be.instanceOf(BemDomCollection);\n            });\n\n            it('should find all mixed blocks by block class', function() {\n                getEntityIds(\n                    rootBlock.findChildBlock({ block : B3Block }).findMixedBlocks(B4Block))\n                        .should.be.eql(['6', '8']);\n            });\n        });\n\n        describe('findMixedBlock', function() {\n            it('should throw error if Block given as string', function() {\n                function find() {\n                    rootBlock.findMixedBlock('string');\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should throw error if Block given as description object with block as string', function() {\n                function find() {\n                    rootBlock.findMixedBlock({ block : 'string' });\n                }\n\n                find.should.throw(Error, 'Block must be a class or description (block, modName, modVal) of the block to find');\n            });\n\n            it('should find first mixed block by block class', function() {\n                rootBlock.findChildBlock({ block : B3Block })\n                    .findMixedBlock(B4Block)\n                        .params.id\n                            .should.be.equal('6');\n            });\n\n            it('should find mixed block by block class, modName and modVal', function() {\n                rootBlock.findChildBlock({ block : B4Block })\n                    .findMixedBlock({ block : B5Block, modName : 'm1', modVal : 'v1' })\n                        .params.id\n                            .should.be.equal('7');\n            });\n\n            it('should find mixed block by block class and boolean mod', function() {\n                rootBlock.findChildBlock({ block : B4Block })\n                    .findMixedBlock({ block : B3Block, modName : 'm1', modVal : true })\n                        .params.id\n                            .should.be.equal('5');\n            });\n\n            it('should find mixed block by block class and boolean mod without modVal', function() {\n                rootBlock.findChildBlock({ block : B4Block })\n                    .findMixedBlock({ block : B3Block, modName : 'm1' })\n                        .params.id\n                            .should.be.equal('5');\n            });\n        });\n    });\n\n    describe('find*Elem(s)', function() {\n        var b1Block,\n            B1E1Elem, B1E2Elem, B1E3Elem, B1E4Elem, B1E5Elem, B1E6Elem;\n\n        beforeEach(function() {\n            var B1Block = bemDom.declBlock('b1');\n\n            B1E1Elem = bemDom.declElem('b1', 'e1');\n            B1E2Elem = bemDom.declElem('b1', 'e2');\n            B1E3Elem = bemDom.declElem('b1', 'e3');\n            B1E4Elem = bemDom.declElem('b1', 'e4');\n            B1E5Elem = bemDom.declElem('b1', 'e5');\n            B1E6Elem = bemDom.declElem('b1', 'e6');\n\n            rootNode = createDomNode(\n                {\n                    block : 'b1',\n                    content : [\n                        {\n                            block : 'b2',\n                            content : { elem : 'e1' }\n                        },\n                        {\n                            elem : 'e2',\n                            content : { elem : 'e5', js : { id : '9' } }\n                        },\n                        {\n                            elem : 'e1',\n                            elemMods : { m2 : 'v1' },\n                            js : { id : '1' },\n                            content : [\n                                {\n                                    elem : 'e1',\n                                    elemMods : { m1 : 'v1' },\n                                    js : { id : '2' }\n                                },\n                                {\n                                    block : 'b3',\n                                    content : {\n                                        block : 'b1',\n                                        elem : 'e1',\n                                        elemMods : { m1 : 'v2', m2 : true },\n                                        mix : [\n                                            { elem : 'e3', js : { id : '5' } },\n                                            { elem : 'e4', js : { id : '6' } }\n                                        ],\n                                        js : { id : '3' },\n                                        content : [\n                                            {\n                                                elem : 'e1',\n                                                elemMods : { m1 : true },\n                                                js : { id : '4' }\n                                            },\n                                            {\n                                                block : 'b1',\n                                                content : { elem : 'e6', js : { id : '10' } }\n                                            }\n                                        ]\n                                    }\n                                },\n                                { elem : 'e6', js : { id : '11' } }\n                            ]\n                        },\n                        {\n                            block : 'b1', elem : 'e3', js : { id : '5' },\n                            mix : {\n                                elem : 'e4',\n                                elemMods : { m2 : 'v1' },\n                                js : { id : '8' }\n                            }\n                        }\n                    ]\n                });\n\n            b1Block = rootNode.bem(B1Block);\n        });\n\n        describe('findChildElems', function() {\n            it('should return BEM-collection', function() {\n                b1Block.findChildElems(B1E1Elem).should.be.instanceOf(BemDomCollection);\n            });\n\n            it('should return instances of Elem founded by class', function() {\n                b1Block.findChildElems(B1E1Elem).forEach(function(elem) {\n                    elem.should.be.instanceOf(B1E1Elem);\n                });\n            });\n\n            it('should find all elems by elem class', function() {\n                getEntityIds(b1Block.findChildElems(B1E1Elem)).should.be.eql(['1', '2', '3', '4']);\n            });\n\n            it('should find all elems by elem name', function() {\n                getEntityIds(b1Block.findChildElems('e1')).should.be.eql(['1', '2', '3', '4']);\n            });\n\n            it('should find all elems by elem class, modName and modVal', function() {\n                getEntityIds(\n                        b1Block.findChildElems({ elem : B1E1Elem, modName : 'm1', modVal : 'v1' }))\n                    .should.be.eql(['2']);\n            });\n\n            it('should find all elems by elem class and boolean mod', function() {\n                getEntityIds(\n                        b1Block.findChildElems({ elem : B1E1Elem, modName : 'm1', modVal : true }))\n                    .should.be.eql(['4']);\n            });\n\n            it('should find all elems by elem class and boolean mod without modVal', function() {\n                getEntityIds(\n                        b1Block.findChildElems({ elem : B1E1Elem, modName : 'm1' }))\n                    .should.be.eql(['4']);\n            });\n\n            it('should find elems in strict mode', function() {\n                getEntityIds(b1Block.findChildElems(B1E6Elem, true)).should.be.eql(['11']);\n            });\n        });\n\n        describe('findChildElem', function() {\n            it('should return instance of Elem founded by class', function() {\n                b1Block.findChildElem(B1E1Elem).should.be.instanceOf(B1E1Elem);\n            });\n\n            it('should return null if nothing found', function() {\n                var B99Elem = bemDom.declElem('b1', 'e99');\n                expect(b1Block.findChildElem(B99Elem)).to.be.null;\n            });\n\n            it('should find first elem by elem class', function() {\n                b1Block.findChildElem(B1E1Elem).params.id.should.be.equal('1');\n            });\n\n            it('should find first elem by elem name', function() {\n                b1Block.findChildElem('e1').params.id.should.be.equal('1');\n            });\n\n            it('should find first elem by elem class, modName and modVal', function() {\n                b1Block.findChildElem({ elem : B1E1Elem, modName : 'm1', modVal : 'v1' })\n                    .params.id\n                        .should.be.equal('2');\n            });\n\n            it('should find first elem by elem name, modName and modVal', function() {\n                b1Block.findChildElem({ elem : 'e1', modName : 'm1', modVal : 'v1' })\n                    .params.id\n                        .should.be.equal('2');\n            });\n\n            it('should find first elem by elem class and boolean mod', function() {\n                b1Block.findChildElem({ elem : B1E1Elem, modName : 'm1', modVal : true })\n                    .params.id\n                        .should.be.equal('4');\n            });\n\n            it('should find first elem by elem class and boolean mod without modVal', function() {\n                b1Block.findChildElem({ elem : B1E1Elem, modName : 'm1' })\n                    .params.id\n                        .should.be.equal('4');\n            });\n\n            it('should find first elem inside elem', function() {\n                b1Block\n                    .findChildElem({ elem : B1E2Elem })\n                    .findChildElem({ elem : B1E5Elem })\n                    .params.id\n                        .should.be.equal('9');\n            });\n\n            it('should find elem in strict mode', function() {\n                b1Block.findChildElem(B1E6Elem, true)\n                    .params.id\n                        .should.be.equal('11');\n            });\n        });\n\n        describe('findParentElems', function() {\n            var leafEntity;\n\n            beforeEach(function() {\n                leafEntity = b1Block.findChildElem({ elem : B1E1Elem, modName : 'm1', modVal : true });\n            });\n\n            it('should return BEM-collection', function() {\n                leafEntity.findParentElems(B1E1Elem).should.be.instanceOf(BemDomCollection);\n            });\n\n            it('should find all ancestor elems by elem class', function() {\n                getEntityIds(leafEntity.findParentElems(B1E1Elem)).should.be.eql(['3', '1']);\n            });\n\n            it('should find all ancestor elems by elem class, modName and modVal', function() {\n                getEntityIds(leafEntity.findParentElems({ elem : B1E1Elem, modName : 'm2', modVal : 'v1' }))\n                    .should.be.eql(['1']);\n            });\n\n            it('should find all ancestor elems by elem name, modName and modVal', function() {\n                getEntityIds(leafEntity.findParentElems({ elem : 'e1', modName : 'm2', modVal : 'v1' }))\n                    .should.be.eql(['1']);\n            });\n\n            it('should find all ancestor elems by elem class and boolean mod', function() {\n                getEntityIds(leafEntity.findParentElems({ elem : B1E1Elem, modName : 'm2', modVal : true }))\n                    .should.be.eql(['3']);\n            });\n\n            it('should find all ancestor elems by elem class and boolean mod without modVal', function() {\n                getEntityIds(leafEntity.findParentElems({ elem : B1E1Elem, modName : 'm2' }))\n                    .should.be.eql(['3']);\n            });\n        });\n\n        describe('findParentElem', function() {\n            var leafEntity;\n\n            beforeEach(function() {\n                leafEntity = b1Block.findChildElem({ elem : B1E1Elem, modName : 'm1', modVal : true });\n            });\n\n            it('should find first ancestor elem by elem class', function() {\n                leafEntity.findParentElem(B1E1Elem)\n                    .params.id\n                        .should.be.equal('3');\n            });\n\n            it('should find first ancestor elem by elem class, modName and modVal', function() {\n                leafEntity.findParentElem({ elem : B1E1Elem, modName : 'm2', modVal : 'v1' })\n                    .params.id\n                        .should.be.equal('1');\n            });\n\n            it('should find first ancestor elem by elem name, modName and modVal', function() {\n                leafEntity.findParentElem({ elem : 'e1', modName : 'm2', modVal : 'v1' })\n                    .params.id\n                        .should.be.equal('1');\n            });\n\n            it('should find first ancestor elem by elem class and boolean mod', function() {\n                leafEntity.findParentElem({ elem : B1E1Elem, modName : 'm2', modVal : true })\n                    .params.id\n                        .should.be.equal('3');\n            });\n\n            it('should find first ancestor elem by elem class and boolean mod without modVal', function() {\n                leafEntity.findParentElem({ elem : B1E1Elem, modName : 'm2' })\n                    .params.id\n                        .should.be.equal('3');\n            });\n        });\n\n        describe('findMixedElems', function() {\n            it('should return BEM-collection', function() {\n                b1Block.findChildElem(B1E3Elem).findMixedElems(B1E4Elem)\n                    .should.be.instanceOf(BemDomCollection);\n            });\n\n            it('should find all mixed elems by elem class', function() {\n                getEntityIds(\n                    b1Block.findChildElem(B1E3Elem).findMixedElems(B1E4Elem))\n                        .should.be.eql(['6', '8']);\n            });\n        });\n\n        describe('findMixedElem', function() {\n            it('should find mixed elem by elem class', function() {\n                b1Block.findChildElem(B1E3Elem)\n                    .findMixedElem(B1E4Elem)\n                        .params.id\n                            .should.be.equal('6');\n            });\n\n            it('should find first mixed elem by elem name', function() {\n                b1Block.findChildElem(B1E3Elem)\n                    .findMixedElem('e4')\n                        .params.id\n                            .should.be.equal('6');\n            });\n\n            it('should find first mixed elem by elem class, modName and modVal', function() {\n                b1Block.findChildElem(B1E3Elem)\n                    .findMixedElem({ elem : B1E4Elem, modName : 'm2', modVal : 'v1' })\n                        .params.id\n                            .should.be.equal('8');\n            });\n\n            it('should find first mixed elem by elem class and boolean mod', function() {\n                b1Block.findChildElem(B1E3Elem)\n                    .findMixedElem({ elem : B1E1Elem, modName : 'm2', modVal : true })\n                        .params.id\n                            .should.be.equal('3');\n            });\n\n            it('should find first mixed elem by elem class and boolean mod without modVal', function() {\n                b1Block.findChildElem(B1E3Elem)\n                    .findMixedElem({ elem : B1E1Elem, modName : 'm2' })\n                        .params.id\n                            .should.be.equal('3');\n            });\n        });\n    });\n\n    describe('elem(s)', function() {\n        var b1Block,\n            B1E1Elem,\n            B1E2Elem,\n            B1E3Elem,\n            B1E4Elem,\n            spy;\n\n        beforeEach(function() {\n            var B1Block = bemDom.declBlock('b1');\n\n            B1E1Elem = bemDom.declElem('b1', 'e1');\n            B1E2Elem = bemDom.declElem('b1', 'e2');\n            B1E3Elem = bemDom.declElem('b1', 'e3');\n            B1E4Elem = bemDom.declElem('b1', 'e4');\n\n            rootNode = createDomNode({\n                block : 'b1',\n                mix : { elem : 'e1', js : { id : 1 } },\n                content : [\n                    { elem : 'e1', elemMods : { m1 : 'v1' }, js : { id : 2 } },\n                    {\n                        elem : 'e2', js : { id : 3 },\n                        content : [\n                            {\n                                elem : 'e1', js : { id : 4 },\n                                elemMods : { inner : 'no' }\n                            },\n                            {\n                                elem : 'e3', js : { id : 5 },\n                                elemMods : { inner : 'no', bool : true }\n                            }\n                        ]\n                    },\n                    {\n                        elem : 'e3', js : { id : 6 },\n                        content : {\n                            elem : 'e2', js : { id : 7 },\n                            elemMods : { inner : 'yes', bool : true },\n                            content : {\n                                elem : 'e1', js : { id : 8 },\n                                elemMods : { inner : 'yes', bool : true }\n                            }\n                        }\n                    },\n                    { elem : 'e2', js : { id : 9 }, elemMods : { bool : true } },\n                    { elem : 'e4' }\n                ]\n            });\n\n            b1Block = rootNode.bem(B1Block);\n        });\n\n        afterEach(function() {\n            spy.restore();\n        });\n\n        describe('elems', function() {\n            beforeEach(function() {\n                spy = sinon.spy(b1Block, 'findChildElems');\n            });\n\n            it('should find all elems by elem class', function() {\n                getEntityIds(b1Block._elems(B1E1Elem))\n                    .should.be.eql([1, 2, 4, 8]);\n            });\n\n            it('should find all elems by elem class modName and modVal', function() {\n                getEntityIds(b1Block._elems({ elem : B1E1Elem, modName : 'm1', modVal : 'v1' }))\n                    .should.be.eql([2]);\n            });\n\n            it('should cache found elems', function() {\n                b1Block._elems(B1E1Elem).should.be.equal(b1Block._elems(B1E1Elem));\n                spy.should.be.calledOnce;\n            });\n\n            it('should cache found elems with respect to mods', function() {\n                b1Block._elems({ elem : B1E1Elem, modName : 'm1', modVal : 'v1' });\n                spy.should.be.calledOnce;\n\n                b1Block._elems(B1E1Elem);\n                spy.should.be.calledTwice;\n            });\n\n            it('should find elems by boolean mod without modVal', function() {\n                getEntityIds(b1Block._elems({ elem : B1E1Elem, modName : 'bool' }))\n                    .should.be.eql([8]);\n            });\n\n            it('should cache found elems with respect to boolean mod without modVal', function() {\n                b1Block._elems({ elem : B1E1Elem, modName : 'bool' });\n                spy.should.be.calledOnce;\n\n                b1Block._elems({ elem : B1E1Elem, modName : 'bool' });\n                spy.should.be.calledOnce;\n\n                b1Block._elems(B1E1Elem);\n                spy.should.be.calledTwice;\n            });\n\n            it('should not drop elems cache in case elem mods change', function() {\n                var elem = b1Block._elems(B1E1Elem).get(0);\n                spy.should.be.calledOnce;\n\n                elem.setMod('m2', 'v1');\n\n                b1Block._elems(B1E1Elem);\n                spy.should.be.calledOnce;\n            });\n\n            it('should drop elems cache in case mods change', function() {\n                var elem = b1Block._elem(B1E1Elem);\n\n                b1Block._elems({ elem : B1E1Elem, modName : 'm2', modVal : 'v1' });\n                spy.should.be.calledOnce;\n\n                elem.setMod('m2', 'v1');\n                b1Block._elems({ elem : B1E1Elem, modName : 'm2', modVal : 'v1' });\n                spy.should.be.calledTwice;\n\n                elem.delMod('m2');\n                b1Block._elems({ elem : B1E1Elem, modName : 'm2', modVal : 'v1' });\n                spy.should.be.calledThrice;\n            });\n        });\n\n        describe('elem', function() {\n            beforeEach(function() {\n                spy = sinon.spy(b1Block, 'findMixedElem');\n            });\n\n            it('should find first elem by elem class', function() {\n                b1Block._elem(B1E1Elem)\n                    .params.id\n                        .should.be.equal(1);\n            });\n\n            it('should find first elem by elem class modName and modVal', function() {\n                b1Block._elem({ elem : B1E1Elem, modName : 'm1', modVal : 'v1' })\n                    .params.id\n                        .should.be.equal(2);\n            });\n\n            it('should cache found elem', function() {\n                b1Block._elem(B1E1Elem);\n                b1Block._elem(B1E1Elem);\n                spy.should.be.calledOnce;\n            });\n\n            it('should cache found elem with respect to mods', function() {\n                b1Block._elem({ elem : B1E1Elem, modName : 'm1', modVal : 'v1' });\n                spy.should.be.calledOnce;\n\n                b1Block._elem(B1E1Elem);\n                spy.should.be.calledTwice;\n            });\n\n            it('should not drop elem cache in case elem mods change', function() {\n                var elem = b1Block._elem(B1E1Elem);\n                spy.should.be.calledOnce;\n\n                elem.setMod('m2', 'v1');\n\n                b1Block._elem(B1E1Elem);\n                spy.should.be.calledOnce;\n            });\n\n            it('should drop elem cache in case mods change', function() {\n                var elem = b1Block._elems(B1E1Elem).get(0);\n\n                b1Block._elem({ elem : B1E1Elem, modName : 'm2', modVal : 'v1' });\n                spy.should.be.calledOnce;\n\n                elem.setMod('m2', 'v1');\n                b1Block._elem({ elem : B1E1Elem, modName : 'm2', modVal : 'v1' });\n                spy.should.be.calledTwice;\n\n                elem.delMod('m2');\n                b1Block._elem({ elem : B1E1Elem, modName : 'm2', modVal : 'v1' });\n                spy.should.be.calledThrice;\n            });\n        });\n\n        describe('drop cache', function() {\n            var b1e2DomElem;\n\n            beforeEach(function() {\n                b1e2DomElem = b1Block.findChildElem(B1E2Elem).domElem;\n                spy = sinon.spy(b1Block, 'findChildElems');\n            });\n\n            describe('for affected elems', function() {\n                it('should drop elems cache on DOM destruct', function() {\n                    b1Block._elems(B1E1Elem);\n\n                    bemDom.destruct(b1e2DomElem);\n\n                    b1Block._elems(B1E1Elem);\n                    spy.should.be.calledTwice;\n                });\n\n                it('should drop elems cache on DOM update', function() {\n                    b1Block._elems(B1E1Elem);\n\n                    bemDom.update(b1e2DomElem, BEMHTML.apply({\n                        block : 'b1',\n                        elem : 'e1'\n                    }));\n\n                    b1Block._elems(B1E1Elem);\n                    spy.should.be.calledTwice;\n                });\n\n                it('should drop elems cache on DOM replace', function() {\n                    b1Block._elems(B1E1Elem);\n\n                    bemDom.replace(b1e2DomElem, BEMHTML.apply({\n                        block : 'b1',\n                        elem : 'e1'\n                    }));\n\n                    b1Block._elems(B1E1Elem);\n                    spy.should.be.calledTwice;\n                });\n\n                it('should drop elems cache on DOM append', function() {\n                    b1Block._elems(B1E1Elem);\n\n                    bemDom.append(b1Block.domElem, BEMHTML.apply({\n                        block : 'b1',\n                        elem : 'e1',\n                        js : true\n                    }));\n\n                    b1Block._elems(B1E1Elem);\n                    spy.should.be.calledTwice;\n                });\n\n                // NOTE: does't work because of too complex elems cache maintaince in case of elems without js\n                it.skip('should drop elems cache on DOM append of elems without js', function() {\n                    b1Block._elems(B1E1Elem);\n\n                    bemDom.append(b1Block.domElem, BEMHTML.apply({\n                        block : 'b1',\n                        elem : 'e1'\n                    }));\n\n                    b1Block._elems(B1E1Elem);\n                    spy.should.be.calledTwice;\n                });\n\n                it('should drop elems cache on DOM update in case of elem without data-bem', function() {\n                    b1Block._elems(B1E1Elem);\n\n                    bemDom.append(b1Block.domElem, BEMHTML.apply({\n                        block : 'b1',\n                        elem : 'e2',\n                        js : true,\n                        mix : { block : 'b1', elem : 'e1', js : true }\n                    }));\n\n                    b1Block._elems(B1E1Elem);\n                    spy.should.be.calledTwice;\n                });\n            });\n\n            describe('for not affected elems', function() {\n                it('should not drop elems cache on DOM destruct', function() {\n                    b1Block._elems(B1E4Elem);\n\n                    bemDom.destruct(b1e2DomElem);\n\n                    b1Block._elems(B1E4Elem);\n                    spy.should.have.been.calledOnce;\n                });\n\n                it('should not drop elems cache on DOM update', function() {\n                    b1Block._elems(B1E4Elem);\n\n                    bemDom.update(b1e2DomElem, BEMHTML.apply({\n                        block : 'b1',\n                        elem : 'e1'\n                    }));\n\n                    b1Block._elems(B1E4Elem);\n                    spy.should.have.been.calledOnce;\n                });\n\n                it('should not drop elems cache on DOM replace', function() {\n                    b1Block._elems(B1E4Elem);\n\n                    bemDom.replace(b1e2DomElem, BEMHTML.apply({\n                        block : 'b1',\n                        elem : 'e1'\n                    }));\n\n                    b1Block._elems(B1E4Elem);\n                    spy.should.have.been.calledOnce;\n                });\n            });\n        });\n    });\n\n    describe('bemDom.init', function() {\n        var spy;\n        beforeEach(function() {\n            spy = sinon.spy();\n        });\n\n        it('should init block', function() {\n            bemDom.declBlock('block', {\n                onSetMod : {\n                    js : {\n                        inited : spy\n                    }\n                }\n            });\n\n            rootNode = createDomNode({\n                tag : 'div',\n                content : { block : 'block', js : true }\n            });\n\n            spy.should.have.been.called;\n        });\n\n        it('should properly init block with multiple DOM nodes', function(done) {\n            bemDom.declBlock('block', {\n                onSetMod : {\n                    js : {\n                        inited : function() {\n                            this.domElem.length.should.be.equal(2);\n                            done();\n                        }\n                    }\n                }\n            });\n\n            rootNode = createDomNode({\n                tag : 'div',\n                content : [\n                    { block : 'block', js : { id : 'id' } },\n                    { block : 'block', js : { id : 'id' } }\n                ]\n            });\n        });\n\n        it('should properly init elem with multiple DOM nodes', function(done) {\n            bemDom.declBlock('block');\n\n            bemDom.declElem('block', 'e1', {\n                onSetMod : {\n                    js : {\n                        inited : function() {\n                            this.domElem.length.should.be.equal(2);\n                            done();\n                        }\n                    }\n                }\n            });\n\n            rootNode = createDomNode({\n                block : 'block',\n                content : [\n                    { elem : 'e1', js : { id : 'id' } },\n                    { elem : 'e1', js : { id : 'id' } }\n                ]\n            });\n        });\n\n        it('shouldn\\'t init lazy block', function() {\n            bemDom.declBlock('block', {\n                onSetMod : {\n                    js : {\n                        inited : spy\n                    }\n                }\n            }, {\n                lazyInit : true\n            });\n\n            rootNode = initDom({\n                tag : 'div',\n                content : { block : 'block', js : true }\n            });\n\n            spy.should.not.have.been.called;\n        });\n\n        it('should allow to pass string', function() {\n            bemDom.declBlock('block', {\n                onSetMod : {\n                    js : {\n                        inited : spy\n                    }\n                }\n            });\n\n            rootNode = initDom({\n                tag : 'div',\n                content : { block : 'block', js : true }\n            });\n\n            spy.should.have.been.called;\n        });\n    });\n\n    describe('bemDom.detach', function() {\n        it('should detach block but leave DOM node', function() {\n            var spy = sinon.spy();\n            bemDom.declBlock('block1', {\n                onSetMod : {\n                    js : {\n                        '' : spy\n                    }\n                }\n            });\n\n            rootNode = createDomNode({\n                tag : 'div',\n                content : { block : 'block1', js : true }\n            });\n\n            bemDom.detach(rootNode.find('.block1'));\n\n            spy.should.have.been.calledOnce;\n            rootNode.find('.block1').length.should.be.equal(1);\n        });\n    });\n\n    describe('bemDom.destruct', function() {\n        var spy;\n        beforeEach(function() {\n            spy = sinon.spy();\n        });\n\n        it('should destruct block only if it has no dom nodes', function() {\n            bemDom.declBlock('block', {\n                onSetMod : {\n                    js : {\n                        '' : spy\n                    }\n                }\n            });\n\n            rootNode = createDomNode({\n                tag : 'div',\n                content : [\n                    { block : 'block', js : { id : 'block' } },\n                    { block : 'block', js : { id : 'block' } }\n                ]\n            });\n\n            bemDom.destruct(rootNode.find('.block :eq(0)'));\n            spy.should.not.have.been.called;\n\n            bemDom.destruct(rootNode.find('.block'));\n            spy.should.have.been.called;\n        });\n\n        it('should destruct implicitly inited block', function() {\n            var Block = bemDom.declBlock('block', {\n                    onSetMod : {\n                        js : {\n                            '' : spy\n                        }\n                    }\n                });\n\n            rootNode = createDomNode({\n                tag : 'div',\n                content : { block : 'block' }\n            });\n\n            var blockNode = rootNode.find('.block');\n            blockNode.bem(Block);\n            bemDom.destruct(blockNode);\n            spy.should.have.been.called;\n        });\n\n        // see https://github.com/bem/bem-core/issues/1383\n        it('should not re-initialize destructing entities during destruct', function() {\n            var Block = bemDom.declBlock('block', {\n                    __constructor : function() {\n                        this.__base.apply(this, arguments);\n                        spy();\n                    }\n                }, {\n                    onInit : function() {\n                        this._domEvents().on('blur', functions.noop);\n                    }\n                });\n\n            rootNode = initDom({\n                block : 'block',\n                js : true,\n                tag : 'input'\n            });\n\n            rootNode.focus();\n            bemDom.destruct(rootNode);\n            spy.should.have.been.calledOnce;\n        });\n    });\n\n    describe('bemDom.update', function() {\n        it('should properly update tree', function() {\n            var spyBlock1Destructed = sinon.spy(),\n                spyBlock2Inited = sinon.spy();\n\n            bemDom.declBlock('block1', {\n                onSetMod : {\n                    js : {\n                        '' : spyBlock1Destructed\n                    }\n                }\n            });\n            bemDom.declBlock('block2', {\n                onSetMod : {\n                    js : {\n                        inited : spyBlock2Inited\n                    }\n                }\n            });\n\n            rootNode = createDomNode({\n                tag : 'div',\n                content : { block : 'block1', js : true }\n            });\n\n            bemDom.update(rootNode, BEMHTML.apply({ block : 'block2', js : true }))\n                .should.be.equal(rootNode);\n\n            spyBlock1Destructed.called.should.be.true;\n            spyBlock2Inited.called.should.be.true;\n        });\n\n        it('should allow to pass simple string', function() {\n            var domElem = $('<div/>');\n            bemDom.update(domElem, 'simple string');\n            domElem.html().should.be.equal('simple string');\n        });\n    });\n\n    describe('bemDom.before', function() {\n        it('should properly update tree', function() {\n            var spyBlock2Inited = sinon.spy(),\n                block2DomElem;\n\n            bemDom.declBlock('block2', {\n                onSetMod : {\n                    js : {\n                        inited : function() {\n                            spyBlock2Inited();\n                            block2DomElem = this.domElem;\n                        }\n                    }\n                }\n            });\n\n            rootNode = createDomNode({\n                tag : 'div',\n                content : { block : 'block1', js : true }\n            });\n\n            var newCtx = bemDom.before(rootNode.find('.block1'), BEMHTML.apply({ block : 'block2', js : true }));\n\n            newCtx.is(block2DomElem).should.be.true;\n            rootNode.children().eq(0).is(block2DomElem).should.be.true;\n            spyBlock2Inited.called.should.be.true;\n        });\n    });\n\n    describe('bemDom.replace', function() {\n        it('should properly replace tree', function() {\n            var spyBlock1Destructed = sinon.spy(),\n                spyBlock2Inited = sinon.spy();\n\n            bemDom.declBlock('block1', {\n                onSetMod : {\n                    js : {\n                        '' : spyBlock1Destructed\n                    }\n                }\n            });\n            bemDom.declBlock('block2', {\n                onSetMod : {\n                    js : {\n                        inited : spyBlock2Inited\n                    }\n                }\n            });\n\n            rootNode = initDom({\n                tag : 'div',\n                content : { block : 'block1', js : true }\n            });\n\n            bemDom.replace(rootNode.find('.block1'), BEMHTML.apply({ block : 'block2', js : true }));\n\n            spyBlock1Destructed.should.have.been.calledOnce;\n            spyBlock2Inited.should.have.been.calledOnce;\n\n            rootNode.html().should.be.equal('<div class=\"block2 i-bem block2_js_inited\" data-bem=\"{&quot;block2&quot;:{}}\"></div>');\n\n            bemDom.destruct(rootNode);\n\n            rootNode = createDomNode({\n                    tag : 'div',\n                    content : [{ tag : 'p' }, { block : 'block1', js : true }, { tag : 'p' }]\n                });\n\n            bemDom.replace(rootNode.find('.block1'), BEMHTML.apply({ block : 'block2', js : true }));\n\n            spyBlock1Destructed.should.have.been.calledTwice;\n            spyBlock2Inited.should.have.been.calledTwice;\n\n            rootNode.html().should.be.equal('<p></p><div class=\"block2 i-bem block2_js_inited\" data-bem=\"{&quot;block2&quot;:{}}\"></div><p></p>');\n        });\n    });\n\n    // don't add specs for other DOM changing methods as they are implemented the same way\n\n    describe('params', function() {\n        it('should properly join params', function(done) {\n            var Block = bemDom.declBlock('block', {\n                    _getDefaultParams : function() {\n                        return { p1 : 1 };\n                    }\n                });\n\n            bemDom.declBlock('block2', {\n                onSetMod : {\n                    'js' : {\n                        'inited' : function() {\n                            var params = this.findMixedBlock(Block).params;\n                            params.p1.should.be.equal(1);\n                            params.p2.should.be.equal(2);\n                            params.p3.should.be.equal(3);\n\n                            done();\n                        }\n                    }\n                }\n            });\n\n            rootNode = createDomNode({\n                tag : 'div',\n                content : [\n                    { block : 'block', js : { id : 'bla', p2 : 2 }, mix : { block : 'block2', js : true } },\n                    { block : 'block', js : { id : 'bla', p3 : 3 } }\n                ]\n            });\n        });\n    });\n\n    describe('containsEntity', function() {\n        var domElem, block, block2;\n        beforeEach(function() {\n            var Block = bemDom.declBlock('block'),\n                Block2 = bemDom.declBlock('block2');\n\n            domElem = initDom([\n                {\n                    block : 'block',\n                    js : { id : '1' },\n                    content : [\n                        { elem : 'e1' },\n                        { elem : 'e2' }\n                    ]\n                },\n                {\n                    block : 'block',\n                    js : { id : '1' },\n                    content : [\n                        { elem : 'e1' },\n                        { elem : 'e2', content : { elem : 'e2-1' } }\n                    ]\n                },\n                {\n                    block : 'block2'\n                }\n            ]);\n\n            block = domElem.filter('.block').bem(Block);\n            block2 = domElem.filter('.block2').bem(Block2);\n        });\n\n        it('should properly checks for nested entities', function() {\n            block.containsEntity(block._elem('e2-1')).should.be.true;\n            block.containsEntity(block2).should.be.false;\n        });\n    });\n\n    describe('onInit', function() {\n        var spy, Block;\n\n        beforeEach(function() {\n            spy = sinon.spy();\n\n            Block = bemDom.declBlock('block', {}, {\n                onInit : spy\n            });\n        });\n\n        it('should have been called once', function() {\n            rootNode = initDom([{\n                block : 'block',\n                js : true\n            }, {\n                block : 'block',\n                js : true\n            }]);\n\n            spy.should.have.been.calledOnce;\n        });\n\n        it('should have been properly called in case of additional declaration after first initialization', function() {\n            rootNode = initDom({\n                block : 'block',\n                js : true\n            });\n\n            var spy2 = sinon.spy();\n\n            bemDom.declBlock('block', {}, {\n                onInit : spy2\n            });\n\n            spy.should.have.been.calledOnce;\n            spy2.should.have.been.calledOnce;\n        });\n    });\n\n    describe('lazy init', function() {\n        var spy;\n\n        it('should be possible to force initialization', function() {\n            spy = sinon.spy();\n\n            bemDom.declBlock('block', {\n                onSetMod : {\n                    'js' : {\n                        'inited' : spy\n                    }\n                }\n            }, {\n                lazyInit : true\n            });\n\n            rootNode = initDom({\n                block : 'block',\n                js : { lazyInit : false }\n            });\n\n            spy.should.have.been.called;\n        });\n\n        it('should be possible to force lazy initialization', function() {\n            spy = sinon.spy();\n\n            bemDom.declBlock('block', {\n                onSetMod : {\n                    'js' : {\n                        'inited' : spy\n                    }\n                }\n            }, {\n                lazyInit : false\n            });\n\n            rootNode = initDom({\n                block : 'block',\n                js : { lazyInit : true }\n            });\n\n            spy.should.have.not.been.called;\n        });\n\n        describe('on DOM events', function() {\n            beforeEach(function() {\n                spy = sinon.spy();\n\n                bemDom.declBlock('block', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : spy\n                        }\n                    }\n                }, {\n                    lazyInit : true,\n                    onInit : function() {\n                        this._domEvents().on('click', functions.noop);\n                    }\n                });\n\n                rootNode = initDom({\n                    block : 'block',\n                    js : true\n                });\n            });\n\n            it('should init block on DOM event', function() {\n                spy.should.not.have.been.called;\n                rootNode.trigger('click');\n                spy.should.have.been.called;\n            });\n        });\n\n        describe('on BEM events', function() {\n            var block2;\n            beforeEach(function() {\n                spy = sinon.spy();\n\n                bemDom.declBlock('block', {\n                    onSetMod : {\n                        'js' : {\n                            'inited' : spy\n                        }\n                    }\n                }, {\n                    lazyInit : true,\n                    onInit : function() {\n                        this._events(Block2).on('click', functions.noop);\n                    }\n                });\n\n                var Block2 = bemDom.declBlock('block2');\n\n                block2 = initDom({\n                        block : 'block',\n                        js : true,\n                        content : {\n                            block : 'block2',\n                            js : true\n                        }\n                    })\n                    .find(Block2._buildSelector())\n                    .bem(Block2);\n            });\n\n            it('should init block on BEM event', function() {\n                spy.should.not.have.been.called;\n                block2._emit('click');\n                spy.should.have.been.called;\n            });\n        });\n    });\n\n    describe('modules.define patching', function() {\n        it('should provide bemDom block', function(done) {\n            var name = 'b' + Math.random(),\n                spy = sinon.spy();\n\n            modules.define(name, ['i-bem-dom'], function(provide, bemDom) {\n                spy();\n                provide(bemDom.declBlock(this.name, {}));\n            });\n\n            modules.define(name, function(provide, Prev) {\n                spy();\n                Prev.should.be.eql(bem.entities[this.name]);\n                provide(bemDom.declBlock(this.name, {}));\n            });\n\n            modules.require([name], function(Block) {\n                spy.should.have.been.calledTwice;\n                Block.should.be.eql(bem.entities[name]);\n                done();\n            });\n        });\n    });\n});\n\nprovide();\n\nfunction createDomNode(bemjson) {\n    return bemDom.init(BEMHTML.apply(bemjson));\n}\n\nfunction initDom(bemjson) {\n    return createDomNode(bemjson).appendTo(bemDom.scope);\n}\n\nfunction getEntityIds(entities) {\n    return entities.map(function(entity) {\n        return entity.params.id;\n    });\n}\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.tests/benchmarks.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'v3 benchmarks',\n    head : {\n        elem : 'js', url : 'https://yastatic.net/jquery/2.1.3/jquery.js'\n    },\n    styles : { elem : 'css', url : '_benchmarks.css' },\n    scripts : { elem : 'js', url : '_benchmarks.js' },\n    content : Array.apply(null, { length : 1000 }).map(function() {\n        return {\n            block : 'b1',\n            js : true,\n            content : {\n                block : 'b2',\n                js : true\n            }\n        };\n    })\n});\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.tests/benchmarks.blocks/b1/b1.deps.js",
    "content": "({\n    mustDeps : {\n        block : 'i-bem-dom'\n    },\n    shouldDeps : 'b2'\n})\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.tests/benchmarks.blocks/b1/b1.js",
    "content": "modules.define('b1', ['i-bem-dom', 'b2'], function(provide, bemDom, B2) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._events(B2).on('click', this._onEvent);\n            }\n        }\n    },\n\n    _onEvent : function() { }\n}));\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.tests/benchmarks.blocks/b2/b2.deps.js",
    "content": "({\n    mustDeps : {\n        block : 'i-bem-dom'\n    }\n})\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.tests/benchmarks.blocks/b2/b2.js",
    "content": "modules.define('b2', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name));\n\n});\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.tests/benchmarks.blocks/page/page.deps.js",
    "content": "({\n    noDeps : {\n        block : 'i-bem-dom',\n        elem : 'init',\n        mods : { auto : true }\n    },\n    shouldDeps : [\n        {\n            block : 'i-bem-dom',\n            elem : 'init'\n        },\n        'jquery'\n    ]\n})\n"
  },
  {
    "path": "common.blocks/i-bem-dom/i-bem-dom.tests/benchmarks.blocks/page/page.js",
    "content": "modules.require(['i-bem-dom__init', 'jquery'], function(init, $) {\n\n$(function() {\n    var timeStart = Date.now();\n    init();\n    var time = Date.now() - timeStart;\n    $('body').append($('<p>' + time + '</p>'));\n});\n\n});\n"
  },
  {
    "path": "common.blocks/i18n/i18n.deps.js",
    "content": "({\n    tech : 'tmpl-spec.js',\n    shouldDeps : 'greeting-card'\n})\n"
  },
  {
    "path": "common.blocks/i18n/i18n.en.md",
    "content": "# i18n\n\nThis block provides a function for project internationalization.\nIt can be used in a browser and in a node.js environment.\n\n## Signature\n\n```js\n/**\n * @exports\n * @param {String} keyset\n * @param {String} key\n * @param {Object} [params]\n * @returns {String}\n */\ni18n(keyset, key, params);\n```\n\nFor example:\n\n```js\ni18n('keyset1', 'key2', { a : '1' });\n```\n\nUse the `decl` method to add translations:\n\n```js\ni18n.decl({\n    keyset1 : {\n        key1 : 'keyset1 key1 string',\n        key2 : function(params) {\n            return 'keyset1 key2 function ' + JSON.stringify(params);\n        },\n        key3 : function(params) {\n            return 'keyset1 key3 ' + this('keyset1', 'key2', params);\n        }\n    }\n});\n```\n\nFor information about building an internationalized project, see  [enb-bem-i18n](https://ru.bem.info/tools/bem/enb-bem-i18n/readme/).\n"
  },
  {
    "path": "common.blocks/i18n/i18n.i18n.js",
    "content": "export default {\n    i18n : {\n        i18n : function() {\n            let data;\n\n            /**\n             * @param {String} keyset\n             * @param {String} key\n             * @param {Object} [params]\n             * @returns {String}\n             */\n            function i18n(keyset, key, params) {\n                if(!data) throw Error('i18n need to be filled with data');\n                const val = data[keyset] && data[keyset][key];\n                return typeof val === 'undefined'?\n                keyset + ':' + key :\n                    typeof val === 'string'?\n                        val :\n                        val.call(i18n, params, i18n);\n            }\n\n            i18n.decl = function(i18nData) {\n                if(!data) {\n                    data = i18nData;\n                    return this;\n                }\n\n                for(const ks in i18nData) {\n                    const dataKs = data[ks] || (data[ks] = {}),\n                        i18nDataKs = i18nData[ks];\n\n                    for(const k in i18nDataKs)\n                        dataKs[k] = i18nDataKs[k];\n                }\n\n                return this;\n            };\n\n            return i18n;\n        }\n    }\n};\n"
  },
  {
    "path": "common.blocks/i18n/i18n.ru.md",
    "content": "# i18n\n\nБлок предоставляет функцию для интернационализации проекта.\nМожет быть использован в браузере и в node.js-окружении.\n\n## Сигнатура\n\n```js\n/**\n * @exports\n * @param {String} keyset\n * @param {String} key\n * @param {Object} [params]\n * @returns {String}\n */\ni18n(keyset, key, params);\n```\n\nНапример:\n\n```js\ni18n('keyset1', 'key2', { a : '1' });\n```\n\nДля добавления переводов используется метод `decl`:\n\n```js\ni18n.decl({\n    keyset1 : {\n        key1 : 'keyset1 key1 string',\n        key2 : function(params) {\n            return 'keyset1 key2 function ' + JSON.stringify(params);\n        },\n        key3 : function(params) {\n            return 'keyset1 key3 ' + this('keyset1', 'key2', params);\n        }\n    }\n});\n```\n\nДокументацию на сборку проекта с интернационализацией см. в пакете для сборки [enb-bem-i18n](https://ru.bem.info/tools/bem/enb-bem-i18n/readme/).\n"
  },
  {
    "path": "common.blocks/i18n/i18n.test.js",
    "content": "import { describe, it, beforeEach } from 'node:test';\nimport assert from 'node:assert/strict';\n\nimport i18nModule from './i18n.i18n.js';\n\nconst init = i18nModule.i18n.i18n;\n\ndescribe('i18n', function() {\n    let i18n;\n\n    beforeEach(function () {\n        i18n = init();\n\n        i18n.decl({\n            'keyset1' : {\n                'key1' : 'keyset1 key1 string',\n                'key2' : function(params) {\n                    return 'keyset1 key2 function ' + JSON.stringify(params);\n                },\n                'key3' : function(params) {\n                    return 'keyset1 key3 ' + this('keyset1', 'key2', params);\n                }\n            }\n        });\n    });\n\n    it('should throw exception without data', function() {\n        const empty = init();\n        assert.throws(function() { empty('keyset1', 'key1'); }, Error);\n    });\n\n    it('should return \"keyset:key\" if they do not exist in data', function() {\n        assert.equal(i18n('undefkeyset', 'undefkey'), 'undefkeyset:undefkey');\n        assert.equal(i18n('keyset1', 'undefkey'), 'keyset1:undefkey');\n    });\n\n    it('should return string value', function() {\n        assert.equal(i18n('keyset1', 'key1'), 'keyset1 key1 string');\n    });\n\n    it('should return value as function result', function() {\n        assert.equal(i18n('keyset1', 'key2', { a : '1' }), 'keyset1 key2 function {\"a\":\"1\"}');\n    });\n\n    it('should properly call another i18n items', function() {\n        assert.equal(i18n('keyset1', 'key3', { b : '2' }), 'keyset1 key3 keyset1 key2 function {\"b\":\"2\"}');\n    });\n\n    it('should properly extend existed data', function() {\n        i18n.decl({\n            'keyset1' : {\n                'key0' : 'keyset1 key0 string',\n                'key1' : 'keyset1 key1 new string'\n            },\n            'keyset2' : {\n                'key1' : 'keyset2 key1 string'\n            }\n        });\n\n        assert.equal(i18n('keyset1', 'key0'), 'keyset1 key0 string');\n        assert.equal(i18n('keyset1', 'key1'), 'keyset1 key1 new string');\n        assert.equal(i18n('keyset1', 'key2', { a : '1' }), 'keyset1 key2 function {\"a\":\"1\"}');\n        assert.equal(i18n('keyset2', 'key1'), 'keyset2 key1 string');\n    });\n});\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/logo/logo.bemhtml.js",
    "content": "block('logo').content()(function() {\n    return this.i18n('logo', 'yandex');\n});\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/logo/logo.bh.js",
    "content": "module.exports = function (bh) {\n    bh.match('logo', function (ctx) {\n        var i18n = bh.lib.i18n;\n\n        ctx.content(i18n('logo', 'yandex'));\n    });\n};\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/logo/logo.deps.js",
    "content": "({\n    shouldDeps : [\n        { block : 'i-bem', elems : 'dom' },\n        { block : 'i18n' }\n    ]\n})\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/logo/logo.i18n/en.js",
    "content": "module.exports = {\n    'yandex-service' : {\n        'Lego' : 'Lego'\n    },\n    'logo' : {\n        'yandex' : 'Yandex'\n    }\n};\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/logo/logo.i18n/ru.js",
    "content": "module.exports = {\n    'yandex-service' : {\n        'Lego' : 'Лего'\n    },\n    'logo' : {\n        'yandex' : 'Яндекс'\n    }\n};\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/logo/logo.i18n.js",
    "content": "module.exports = {\n    'logo' : {\n        'yandex-service' : function(serviceName, i18n) {\n            return i18n('logo', 'yandex') + '.' + i18n('yandex-service', serviceName);\n        }\n    }\n};\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/logo/logo.js",
    "content": "modules.define('logo', ['i-bem__dom', 'i18n'], function(provide, BEMDOM, i18n) {\n\nprovide(BEMDOM.decl(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                var domElem = this.domElem,\n                    params = this.params;\n\n                this.bindTo('click', function () {\n                    domElem.text(i18n('logo', 'yandex-service', params.service));\n                });\n            }\n        }\n    }\n}));\n\n});\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/page/__js/page__js.bemhtml.js",
    "content": "block('page').elem('js').def()(function() {\n    this.ctx.url = this.i18n('page', 'js');\n    return applyNext();\n});\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/page/__js/page__js.bh.js",
    "content": "module.exports = function (bh) {\n    bh.match('page__js', function (ctx, json) {\n        var i18n = bh.lib.i18n;\n\n        json.url = i18n('page', 'js');\n    });\n};\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/page/page.i18n/en.js",
    "content": "module.exports = {\n    'page' : {\n        'js' : 'simple.en.js'\n    }\n};\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/blocks/page/page.i18n/ru.js",
    "content": "module.exports = {\n    'page' : {\n        'js' : 'simple.ru.js'\n    }\n};\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tests/simple.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'i18n: test',\n    scripts : { elem : 'js' },\n    content : {\n        block : 'logo',\n        js : { service : 'Lego' }\n    }\n});\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tmpl-specs/10-simple.bemjson.js",
    "content": "({\n    block : 'greeting-card'\n})\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tmpl-specs/10-simple.html",
    "content": "<div class=\"greeting-card\">greeting-card:message</div>\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tmpl-specs/blocks/greeting-card/greeting-card.bemhtml.js",
    "content": "block('greeting-card').content()(function () {\n    var i18n = this.require('i18n');\n\n    return i18n('greeting-card', 'message');\n});\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tmpl-specs/blocks/greeting-card/greeting-card.bh.js",
    "content": "module.exports = function (bh) {\n    bh.match('greeting-card', function (ctx) {\n        var i18n = bh.lib.i18n,\n            message = i18n('greeting-card', 'message');\n\n        ctx.content(message);\n    });\n};\n"
  },
  {
    "path": "common.blocks/i18n/i18n.tmpl-specs/blocks/greeting-card/greeting-card.deps.js",
    "content": "({\n    mustDeps : ['i-bem', 'i18n']\n})\n"
  },
  {
    "path": "common.blocks/identify/identify.en.md",
    "content": "# identify\n\nThis block provides a function for working with unique identifiers. It allows you to:\n\n* Create object identifiers.\n* Check whether objects have an identifier.\n* Create a unique identifier string.\n\n**Accepted arguments:**\n\n* [`obj {Object}`] – The object to identify.\n* [`onlyGet {Boolean}`] – Flag for checking whether the object has an identifier. If `true`, the function returns a string with the identifier if the object was previously assigned an identifier. By default, `false`.\n\n**Return value:** `String`. A string with the identifier assigned to the object. Subsequent calls will always return the same identifier.\n\nExample:\n\n```js\nmodules.require(['identify'], function(identify) {\n    var a = {},\n        b = {},\n        identA = identify(a);\n\n    console.log(identA === identify(a)); //true\n    console.log(identA === identify(b)); //false\n});\n```\n\nWhen called without arguments, the function returns a string with a unique identifier every time.\n\nExample:\n\n```js\nmodules.require(['identify'], function(identify) {\n    var a = identify(),\n        b = identify();\n\n    console.log(a === b); //false\n});\n```\n\n## Public block technologies\n\nThe block is implemented in:\n\n* `vanilla.js`\n"
  },
  {
    "path": "common.blocks/identify/identify.ru.md",
    "content": "# identify\n\nБлок предоставляет функцию для работы с уникальными идентификаторами, которая позволяет:\n\n* создавать идентификаторы объектов;\n* проверять у объектов наличие идентификатора;\n* создавать уникальную строку-идентификатор.\n\n**Принимаемые аргументы:**\n\n* [`obj {Object}`] – идентифицируемый объект.\n* [`onlyGet {Boolean}`] – флаг для проверки наличия у объекта идентификатора. Если `true`, функция будет возвращать строку с идентификатором только если объект был заранее идентифицирован. По умолчанию `false`.\n\n**Возвращаемое значение:** `String`. Строка с идентификатором, присвоенным объекту. При последующих вызовах всегда будет возвращаться один и тот же идентификатор.\n\nПример:\n\n```js\nmodules.require(['identify'], function(identify) {\n    var a = {},\n        b = {},\n        identA = identify(a);\n\n    console.log(identA === identify(a)); // true\n    console.log(identA === identify(b)); // false\n});\n```\n\nПри вызове без аргументов, функция будет каждый раз возвращать строку с уникальным идентификатором.\n\nПример:\n\n```js\nmodules.require(['identify'], function(identify) {\n    var a = identify(),\n        b = identify();\n\n    console.log(a === b); // false\n});\n```\n\n## Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `vanilla.js`\n"
  },
  {
    "path": "common.blocks/identify/identify.spec.js",
    "content": "modules.define('spec', [\n    'identify',\n    'chai'\n], function(provide,\n    identify,\n    chai\n) {\n\nvar should = chai.should();\n\ndescribe('identify', function() {\n    it('should return different values for different objects', function() {\n        var obj1 = {},\n            obj2 = {};\n        identify(obj1).should.not.be.equal(identify(obj2));\n    });\n\n    it('should return same values for same objects', function() {\n        var obj1 = {},\n            obj2 = {};\n        identify(obj1).should.be.equal(identify(obj1));\n        identify(obj2).should.be.equal(identify(obj2));\n    });\n\n    it('should use \"uniqueID\" property if exists', function() {\n        var obj = { uniqueID : 'id007' };\n        identify(obj).should.be.equal('id007');\n    });\n\n    it('should generate unique values for each calls if no params passed', function() {\n        var id1 = identify(),\n            id2 = identify(),\n            id3 = identify();\n\n        should.exist(id1);\n        should.exist(id2);\n        should.exist(id3);\n\n        id1.should.not.be.equal(id2);\n        id1.should.not.be.equal(id3);\n        id2.should.not.be.equal(id3);\n    });\n\n    it('should accept several arguments', function() {\n        var obj1 = {},\n            obj2 = {};\n        identify(obj1, obj2).should.be.equal(identify(obj1) + identify(obj2));\n    });\n\n    it('should not depend on order of several arguments', function() {\n        var obj1 = {},\n            obj2 = {};\n        identify(obj1, obj2).should.be.equal(identify(obj2, obj1));\n    });\n\n    it('should properly process arguments', function() {\n        var obj1 = {},\n            obj2,\n            obj3 = '123',\n            obj4 = null,\n            obj5 = function() {};\n\n        [obj2, obj3, obj4].forEach(function(obj) {\n            identify(obj).should.be.equal('');\n        });\n\n        identify(obj1, obj2, obj3, obj4, obj5).should.be.equal(identify(obj1, obj5));\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/identify/identify.vanilla.js",
    "content": "/**\n * @module identify\n */\n\nlet counter = 0\nconst expando = '__' + (+new Date),\n  get = () => 'uniq' + (++counter),\n  identify = obj => {\n    if((typeof obj === 'object' && obj !== null) || typeof obj === 'function') {\n      let key\n      if('uniqueID' in obj) {\n        obj === globalThis.document && (obj = obj.documentElement)\n        key = 'uniqueID'\n      } else {\n        key = expando\n      }\n      return key in obj\n        ? obj[key]\n        : obj[key] = get()\n    }\n\n    return ''\n  }\n\nexport default\n  /**\n   * Makes unique ID\n   * @param {...Object} obj Object that needs to be identified\n   * @returns {String} ID\n   */\n  function(obj) {\n    if(arguments.length) {\n      if(arguments.length === 1) {\n        return identify(obj)\n      }\n\n      const res = []\n      for(const arg of arguments)\n        res.push(identify(arg))\n      return res.sort().join('')\n    }\n\n    return get()\n  }\n"
  },
  {
    "path": "common.blocks/idle/_start/idle_start_auto.js",
    "content": "/**\n * Automatically starts idle module\n */\n\nimport idle from 'bem:idle';\n\nidle.start();\n"
  },
  {
    "path": "common.blocks/idle/idle.deps.js",
    "content": "({\n    shouldDeps : ['events', 'inherit', 'jquery']\n})\n"
  },
  {
    "path": "common.blocks/idle/idle.en.md",
    "content": "# idle\n\nThis block provides an object with a set of methods for generating an event when user activity ends (i.e. the user switches to another window or doesn't finish actions).\n\n## Overview\n\n### Object events\n\nThe following events are available:\n\n| Name | Description |\n| -------- | -------- |\n| <a href=\"#events-idle\">idle</a> | The browser is idle. |\n| <a href=\"#events-wakeup\">wakeup</a> | The user has resumed activity. |\n\n### Properties and methods of the object\n\n| Name | Returned value | Description |\n| -------- | --- | -------- |\n| <a href=\"#fields-start\">start</a>() | - | Starts tracking user activity. |\n| <a href=\"#fields-stop\">stop</a>() | - | Stops tracking user activity. |\n| <a href=\"#fields-isIdle\">isIdle</a>() | `Boolean` | Checks the current state. |\n\n### Block modifiers\n\n| Modifier | Acceptable values | Usage | Description |\n| ----------- | ------------------- | --------------------- | -------- |\n| <a href=\"#modifiers-start\">start</a> | `auto` | `JS` | Automatically starts tracking user activity. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `js`\n\n## Description\n\nSubscribing to the block's event allows you to suspend operations, such as displaying animation, when there isn't any user activity.\n\nThe block is a descendant of the `Emitter` class in the `events` block, which allows it to call these methods.\n\n```js\nmodules.require(['idle'], function(idle) {\n\nidle\n    .on({\n        idle : function() {\n            // idle event handler\n        },\n        wakeup : function() {\n            // wakeup event handler\n        }\n    })\n    .start(); // start event generation\n\n});\n```\n\n<a name=\"events\"></a>\n\n### Object events\n\n<a name=\"events-idle\"></a>\n\n#### `idle` event\n\nGenerated when user activity ends.\n\n<a name=\"events-wakeup\"></a>\n\n#### `wakeup` event\n\nGenerated when user activity resumes.\n\n<a name=\"fields\"></a>\n\n### Properties and methods of the object\n\n<a name=\"fields-start\"></a>\n\n#### `start` method\n\nStarts tracking user activity.\n\nDoesn't accept arguments.\n\nNo return value.\n\n```js\nmodules.require(['idle'], function(idle) {\n\nidle.start()\n\n});\n```\n\n<a name=\"fields-stop\"></a>\n\n#### `stop` method\n\nStops user activity tracking.\n\nDoesn't accept arguments.\n\nNo return value.\n\n```js\nmodules.require(['idle'], function(idle) {\n\nidle.start() // start tracking activity\nidle.stop() // stop tracking activity\n\n});\n```\n\n<a name=\"fields-isIdle\"></a>\n\n#### `isIdle` method\n\nChecks whether there is any user activity.\n\nDoesn't accept arguments.\n\n**Return value:** `Boolean`. If there isn't any activity, `true`.\n\n```js\nmodules.require(['idle'], function(idle) {\n\nidle.isIdle() // true or false, depending on the current state\n\n});\n```\n\n<a name=\"modifiers\"></a>\n\n### Block modifiers\n\n<a name=\"modifiers-start\"></a>\n\n#### `start` modifier\n\nAcceptable values: `'auto'`.\n\nUsage: enabled in the `deps.js` dependencies file.\n\nAutomatically starts tracking user activity.\n"
  },
  {
    "path": "common.blocks/idle/idle.js",
    "content": "/**\n * @module idle\n */\n\nimport inherit from 'bem:inherit'\nimport events from 'bem:events'\nimport $ from 'bem:jquery'\n\nconst IDLE_TIMEOUT = 3000,\n    USER_EVENTS = 'mousemove keydown click',\n    /**\n     * @class Idle\n     * @augments events:Emitter\n     */\n    Idle = inherit(events.Emitter, /** @lends Idle.prototype */{\n        /**\n         * @constructor\n         */\n        __constructor : function() {\n            this._timer = null\n            this._isStarted = false\n            this._isIdle = false\n        },\n\n        /**\n         * Starts monitoring of idle state\n         */\n        start : function() {\n            if(!this._isStarted) {\n                this._isStarted = true\n                this._startTimer()\n                this._onUserActionBound = this._onUserAction.bind(this)\n                $(document).on(USER_EVENTS, this._onUserActionBound)\n            }\n        },\n\n        /**\n         * Stops monitoring of idle state\n         */\n        stop : function() {\n            if(this._isStarted) {\n                this._isStarted = false\n                this._stopTimer()\n                $(document).off(USER_EVENTS, this._onUserActionBound)\n            }\n        },\n\n        /**\n         * Returns whether state is idle\n         * @returns {Boolean}\n         */\n        isIdle : function() {\n            return this._isIdle\n        },\n\n        _onUserAction : function() {\n            if(this._isIdle) {\n                this._isIdle = false\n                this.emit('wakeup')\n            }\n\n            this._stopTimer()\n            this._startTimer()\n        },\n\n        _startTimer : function() {\n            this._timer = setTimeout(\n                () => this._onTimeout(),\n                IDLE_TIMEOUT)\n        },\n\n        _stopTimer : function() {\n            this._timer && clearTimeout(this._timer)\n        },\n\n        _onTimeout : function() {\n            this._isIdle = true\n            this.emit('idle')\n        }\n    })\n\n/**\n * @type Idle\n */\nexport default new Idle()\n"
  },
  {
    "path": "common.blocks/idle/idle.ru.md",
    "content": "# idle\n\nБлок предоставляет объект, содержащий набор методов для генерации события в момент прекращения пользовательской активности (т.е. пользователь работает с другим окном или не совершает действий).\n\n## Обзор\n\n### События объекта\n\nДоступен следующий набор событий:\n\n| Имя | Описание |\n| -------- | -------- |\n| <a href=\"#events-idle\">idle</a> | Браузер простаивает. |\n| <a href=\"#events-wakeup\">wakeup</a> | Пользователь возобновил активность. |\n\n### Свойства и методы объекта\n\n| Имя | Возвращаемое значение | Описание |\n| -------- | --- | -------- |\n| <a href=\"#fields-start\">start</a>() | - | Запуск отслеживания пользовательской активности. |\n| <a href=\"#fields-stop\">stop</a>() | - | Остановка отслеживания пользовательской активности. |\n| <a href=\"#fields-isIdle\">isIdle</a>() | `Boolean` | Проверка текущего состояния. |\n\n### Модификаторы блока\n\n| Модификатор | Допустимые значения | Способы использования | Описание |\n| ----------- | ------------------- | --------------------- | -------- |\n| <a href=\"#modifiers-start\">start</a> | `auto` | `JS` | Автоматический запуск отслеживания пользовательской активности. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `js`\n\n## Описание\n\nПодписавшись на события блока можно приостанавливать выполнение операций, например, отображение анимации, при отсутствии пользовательской активности.\n\nБлок наследуется от класса `Emitter` блока `events`, что позволяет вызывать его методы.\n\n```js\nmodules.require(['idle'], function(idle) {\n\nidle\n    .on({\n        idle : function() {\n            // обработчик события idle\n        },\n        wakeup : function() {\n            // обработчик события wakeup\n        }\n    })\n    .start(); // запуск генерации событий\n\n});\n```\n\n<a name=\"events\"></a>\n\n### События объекта\n\n<a name=\"events-idle\"></a>\n\n#### Событие `idle`\n\nГенерируется при прекращении пользовательской активности.\n\n<a name=\"events-wakeup\"></a>\n\n#### Событие `wakeup`\n\nГенерируется в момент возобновления пользовательской активности.\n\n<a name=\"fields\"></a>\n\n### Свойства и методы объекта\n\n<a name=\"fields-start\"></a>\n\n#### Метод `start`\n\nЗапуск отслеживания пользовательской активности.\n\nНе принимает аргументов.\n\nНе имеет возвращаемого значения.\n\n```js\nmodules.require(['idle'], function(idle) {\n\nidle.start()\n\n});\n```\n\n<a name=\"fields-stop\"></a>\n\n#### Метод `stop`\n\nСлужит для прекращения отслеживания пользовательской активности.\n\nНе принимает аргументов.\n\nНе имеет возвращаемого значения.\n\n```js\nmodules.require(['idle'], function(idle) {\n\nidle.start() // начинаем отслеживать активность\nidle.stop() // прекращаем отслеживать активность\n\n});\n```\n\n<a name=\"fields-isIdle\"></a>\n\n#### Метод `isIdle`\n\nСлужит для проверки наличия пользовательской активности.\n\nНе принимает аргументов.\n\n**Возвращаемое значение:** `Boolean`. В случае если активность отсутствует – `true`.\n\n```js\nmodules.require(['idle'], function(idle) {\n\nidle.isIdle() // true или false, в зависимости от текущего состояния\n\n});\n```\n\n<a name=\"modifiers\"></a>\n\n### Модификаторы блока\n\n<a name=\"modifiers-start\"></a>\n\n#### Модификатор `start`\n\nДопустимые значения: `'auto'`.\n\nСпособ использования: подключается в файле зависимостей `deps.js`.\n\nАвтоматический запуск отслеживания пользовательской активности.\n"
  },
  {
    "path": "common.blocks/inherit/inherit.en.md",
    "content": "# inherit\n\nThis block provides a function for declaring and inheriting classes.\n\n## Overview\n\n### Usage\n\n| Use | Signature | Return type | Description |\n| ----- | --------- | --------------------- | -------- |\n| <a href=\"#runmode-declare\">Declaring a base class</a> | inherit(<br>`props {Object}`, <br>`[staticProps {Object}]`) | `Function` | Use for creating (declaring) a base class from the object properties. |\n| <a href=\"#runmode-extend\">Creating a derived class</a> | inherit(<br>`BaseClass {Function} `&#124;` {Array}`, <br>`props {Object}`, <br>`[staticProps {Object}]`) | `Function` | Use for inheriting and redefining the properties and methods of a base class. |\n\n### Special fields of the declared class\n\n| Name | Data type | Description |\n| --- | ---------- | -------- |\n| <a href=\"#constructor\">__constructor</a> | `Function` | The function that will be called when creating a class instance. |\n\n### Special fields of the declared class instance\n\n| Field | Data type | Description |\n| ---- | ---------- | -------- |\n| <a href=\"#self\">__self</a> | `*` | Allows you to access the class and its instance. |\n| <a href=\"#base\">__base</a> | `Function` | Allows you to use the methods of the base class inside the derived class (super call). |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `vanilla.js`\n\n## Description\n\nUse the `inherit` function to:\n\n* Create a class using a declaration.\n* Set a constructor method.\n* Use mix-ins.\n* Call the methods of the base implementation (super call).\n* Get access to static properties of a class from its instance.\n\nThis is the main block inheritance mechanism in `bem-core`.\n\nThe function is polymorphic and, depending on the first argument type, it can be used for:\n\n* `Object` type – declaring the base class.\n* `Function` type – deriving a class from the base class.\n\nThe signature of the function's other arguments depends on how it is run.\n\n### Usage\n\n<a name=\"runmode-declare\"></a>\n\n#### Declaring a base class\n\nThis approach allows you to define the base class by passing the function an object with the class properties.\n\n**Accepted arguments:**\n\n* `props {Object}` – An object with its own properties for the base class. Required argument.\n* [`staticProps {Object}`] – An object with static properties of the base class.\n\n**Return value:** `Function`. The fully-formed class.\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar props = {}, // object for the base class properties\n    baseClass = inherit(props); // base class\n\n});\n```\n\n##### Base class with static properties\n\nProperties of the `staticProps` object are added as static properties for the class being created.\n\nExample:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit(props, {\n    callMe : function() {\n        console.log('mr.Static');\n    }\n});\n\nA.callMe(); // mr.Static\n\n});\n```\n\n##### Special fields of the declared class\n\n<a name=\"constructor\"></a>\n\n###### `__constructor` field\n\nType: `Function`.\n\nThe object with the base class properties can contain the reserved `__constructor` property, a function that is called automatically when a class instance is created.\n\nExample:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n        __constructor : function(property) { // constructor\n            this.property = property;\n        },\n\n        getProperty : function() {\n            return this.property + ' of instanceA';\n        }\n    }),\n    aInst = new A('Property');\n\naInst.getProperty(); // Property of instanceA\n\n});\n```\n\n<a name=\"runmode-extend\"></a>\n\n#### Creating a derived class\n\nThis approach allows you to create a derived class from the base class and the objects with the static properties and the custom properties.\n\n**Accepted arguments:**\n\n* `BaseClass {Function} | {Array}` – The base class. Can be an array of mix-in functions. Required argument.\n* `props {Object}` – Custom properties (added to the prototype). Required argument.\n* [`staticProps {Object}`] – Static properties.\n\nIf one of the objects contains properties that already exist in the base class, the base class properties are redefined.\n\n**Return value:** `Function`. Derived class.\n\nExample:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n    getType : function() {\n        return 'A';\n    }\n});\n\n// class derived from A\nvar B = inherit(A, {\n    getType : function() { // redefinition + super call\n        return this.__base() + 'B';\n    }\n});\n\nvar instanceOfB = new B();\n\ninstanceOfB.getType(); // returns 'AB'\n\n});\n```\n\n##### Creating a derived class with mix-ins\n\nWhen declaring a derived class, you can specify an additional set of functions. Their properties will be mixed in to the created class. To do this, the first argument for `inherit` should specify an array that has the base class as its first element, followed by the functions to mix in.\n\nExample:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n    getA : function() {\n        return 'A';\n    }\n});\n\nvar B = inherit({\n    getB : function() {\n        return 'B';\n    }\n});\n\n// class derived from A and B\nvar C = inherit([A, B], {\n    getAll : function() {\n        return this.getA() + this.getB();\n    }\n});\n\nvar instanceOfC = new C();\n\ninstanceOfC.getAll(); // returns 'AB'\n\n});\n```\n\n##### Special fields of the declared class instance\n\n<a name=\"self\"></a>\n\n###### `__self` field\n\nType: `*`.\n\nAllows you to access the class and its instance.\n\nExample:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n        getStaticProperty : function() {\n            return this.__self.staticMethod; // access to static methods\n        }\n    }, {\n        staticProperty : 'staticA',\n\n        staticMethod : function() {\n            return this.staticProperty;\n        }\n    }),\n    aInst = new A();\n\naInst.getStaticProperty(); //staticA\n\n});\n```\n\n<a name=\"base\"></a>\n\n###### `__base`\n\nType: `Function`.\n\nAllows you to call base class methods inside the derived class (super call). When used in a static method, it will call the static method of the same name in the base class.\n\nExample:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n    getType : function() {\n        return 'A';\n    }\n}, {\n    staticProperty : 'staticA',\n\n    staticMethod : function() {\n        return this.staticProperty;\n    }\n});\n\n// class derived from A\nvar B = inherit(A, {\n    getType : function() { // redefinition + super call\n        return this.__base() + 'B';\n    }\n}, {\n    staticMethod : function() { // static redefinition + super call\n        return this.__base() + ' of staticB';\n    }\n});\n\nvar instanceOfB = new B();\n\ninstanceOfB.getType(); // returns 'AB'\nB.staticMethod(); // returns 'staticA of staticB'\n\n});\n```\n\n<a name=\"extra-examples\"></a>\n\n### More examples\n\nFor more examples, see the repository of the [inherit](https://github.com/dfilatov/inherit) library.\n"
  },
  {
    "path": "common.blocks/inherit/inherit.ru.md",
    "content": "# inherit\n\nБлок предоставляет функцию, реализующую механизмы для объявления и наследования классов.\n\n## Обзор\n\n### Способы использования функции\n\n| Способ | Сигнатура | Тип возвращаемого значения | Описание |\n| ----- | --------- | --------------------- | -------- |\n| <a href=\"#runmode-declare\">Объявление базового класса</a> | inherit(<br>`props {Object}`, <br>`[staticProps {Object}]`) | `Function` | Служит для создания (декларации), базового класса на основе свойств объекта. |\n| <a href=\"#runmode-extend\">Создание производного класса</a> | inherit(<br>`BaseClass {Function} `&#124;` {Array}`, <br>`props {Object}`, <br>`[staticProps {Object}]`) | `Function` | Позволяет наследовать и доопределять свойства и методы базового класса. |\n\n### Специальные поля объявляемого класса\n\n| Имя | Тип данных | Описание |\n| --- | ---------- | -------- |\n| <a href=\"#constructor\">__constructor</a> | `Function` | Функция, которая будет вызвана в ходе создании экземпляра класса. |\n\n### Специальные поля экземпляра объявляемого класса\n\n| Поле | Тип данных | Описание |\n| ---- | ---------- | -------- |\n| <a href=\"#self\">__self</a> | `*` | Позволяет получить доступ к классу из его экземпляра. |\n| <a href=\"#base\">__base</a> | `Function` | Позволяет внутри производного класса использовать методы базового (supercall). |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `vanilla.js`\n\n## Описание\n\nФункция `inherit` позволяет:\n\n* создавать класс по декларации;\n* задавать метод-конструктор;\n* использовать миксины;\n* вызывать методы базовой реализации (super call);\n* получать доступ к статическим свойствам класса из его экземпляра.\n\nБлок является основой механизма наследования блоков в `bem-core`.\n\nФункция полиморфна и, в зависимости от типа первого аргумента, может быть использована для:\n\n* тип `Object` – объявления базового класса.\n* тип `Function` – создания производного класса на основе базового.\n\nСигнатуры других аргументов функции зависят от способа выполнения.\n\n### Способы использования функции\n\n<a name=\"runmode-declare\"></a>\n\n#### Объявление базового класса\n\nСпособ позволяет объявить базовый класс, передав функции объект со свойствами класса.\n\n**Принимаемые аргументы:**\n\n* `props {Object}` – объект с собственными свойствами базового класса. Обязательный аргумент.\n* [`staticProps {Object}`] – объект со статическими свойствами базового класса.\n\n**Возвращаемое значение:** `Function`. Полностью сформированный класс.\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar props = {}, // объект свойств базового класса\n    baseClass = inherit(props); // базовый класс\n\n});\n```\n\n##### Базовый класс со статическими свойствами\n\nСвойства объекта `staticProps` добавляются как статические к создаваемому классу.\n\nПример:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit(props, {\n    callMe : function() {\n        console.log('mr.Static');\n    }\n});\n\nA.callMe(); // mr.Static\n\n});\n```\n\n##### Специальные поля объявляемого класса\n\n<a name=\"constructor\"></a>\n\n###### Поле `__constructor`\n\nТип: `Function`.\n\nОбъект собственных свойств базового класса может содержать зарезервированное свойство `__constructor` – функцию, которая будет автоматически вызвана при создании экземпляра класса.\n\nПример:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n        __constructor : function(property) { // конструктор\n            this.property = property;\n        },\n\n        getProperty : function() {\n            return this.property + ' of instanceA';\n        }\n    }),\n    aInst = new A('Property');\n\naInst.getProperty(); // Property of instanceA\n\n});\n```\n\n<a name=\"runmode-extend\"></a>\n\n#### Создание производного класса\n\nСпособ позволяет создать производный класс на основе базового класса и объектов статических и собственных свойств.\n\n**Принимаемые аргументы:**\n\n* `BaseClass {Function} | {Array}` – базовый класс. Может быть массивом функций-миксинов. Обязательный аргумент.\n* `props {Object}` – собственные свойства (добавляются к прототипу). Обязательный аргумент.\n* [`staticProps {Object}`] – статические свойства.\n\nЕсли один из объектов содержит свойства, которые уже есть в базовом классе – свойства базового класса будут переопределены.\n\n**Возвращаемое значение:** `Function`. Производный класс.\n\nПример:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n    getType : function() {\n        return 'A';\n    }\n});\n\n// класс, производный от A\nvar B = inherit(A, {\n    getType : function() { // переопределение + 'super' call\n        return this.__base() + 'B';\n    }\n});\n\nvar instanceOfB = new B();\n\ninstanceOfB.getType(); // возвращает 'AB'\n\n});\n```\n\n##### Создание производного класса с миксинами\n\nПри объявлении производного класса можно указать дополнительный набор функций. Их свойства будут примешаны к создаваемому классу. Для этого первым аргументом `inherit` нужно указать массив, первым элементом которого должен быть базовый класс, а последующими – примешиваемые функции.\n\nПример:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n    getA : function() {\n        return 'A';\n    }\n});\n\nvar B = inherit({\n    getB : function() {\n        return 'B';\n    }\n});\n\n// класс, производный от A и B\nvar C = inherit([A, B], {\n    getAll : function() {\n        return this.getA() + this.getB();\n    }\n});\n\nvar instanceOfC = new C();\n\ninstanceOfC.getAll(); // возвращает 'AB'\n\n});\n```\n\n##### Специальные поля экземпляра объявляемого класса\n\n<a name=\"self\"></a>\n\n###### Поле `__self`\n\nТип: `*`.\n\nПозволяет получить доступ к классу из его экземпляра.\n\nПример:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n        getStaticProperty : function() {\n            return this.__self.staticMethod; // доступ к статическим методам\n        }\n    }, {\n        staticProperty : 'staticA',\n\n        staticMethod : function() {\n            return this.staticProperty;\n        }\n    }),\n    aInst = new A();\n\naInst.getStaticProperty(); //staticA\n\n});\n```\n\n<a name=\"base\"></a>\n\n###### `__base`\n\nТип: `Function`.\n\nПозволяет внутри производного класса вызывать одноименные методы базового (supercall). При использовании в статическом методе, будет вызван одноименный статический метод базового класса.\n\nПример:\n\n```js\nmodules.require(['inherit'], function(inherit) {\n\nvar A = inherit({\n    getType : function() {\n        return 'A';\n    }\n}, {\n    staticProperty : 'staticA',\n\n    staticMethod : function() {\n        return this.staticProperty;\n    }\n});\n\n// класс, производный от A\nvar B = inherit(A, {\n    getType : function() { // переопределение + 'super' call\n        return this.__base() + 'B';\n    }\n}, {\n    staticMethod : function() { // статическое переопределение + 'super' call\n        return this.__base() + ' of staticB';\n    }\n});\n\nvar instanceOfB = new B();\n\ninstanceOfB.getType(); // возвращает 'AB'\nB.staticMethod(); // возвращает 'staticA of staticB'\n\n});\n```\n\n<a name=\"extra-examples\"></a>\n\n### Дополнительные примеры\n\nДополнительные примеры смотрите в репозитории библиотеки [inherit](https://github.com/dfilatov/inherit).\n"
  },
  {
    "path": "common.blocks/inherit/inherit.spec.js",
    "content": "modules.define('spec', ['inherit'], function(provide, inherit) {\n\ndescribe('inherit', function() {\n    describe('instance', function() {\n        it('should be instance of class', function() {\n            var Cls = inherit({}),\n                instance = new Cls();\n\n            instance.should.be.instanceOf(Cls);\n        });\n\n        it('should be instance of all classes in hierarchy', function() {\n            var ClsA = inherit({}),\n                ClsB = inherit(ClsA, {}),\n                ClsC = inherit(ClsB, {}),\n                instance = new ClsC();\n\n            instance.should.be.instanceOf(ClsA);\n            instance.should.be.instanceOf(ClsB);\n            instance.should.be.instanceOf(ClsC);\n        });\n\n        it('should be instance of constructor return value', function() {\n            var ClsA = inherit({}),\n                ClsB = inherit({\n                    __constructor : function() {\n                        return new ClsA();\n                    }\n                }),\n                instance = new ClsB();\n\n            instance.should.be.instanceOf(ClsA);\n            instance.should.not.be.instanceOf(ClsB);\n        });\n\n        it('instance should have properties from constructor', function() {\n            var Cls = inherit({\n                    __constructor : function() {\n                        this._p1 = 'v1';\n                        this._p2 = 'v2';\n                    }\n                }),\n                instance = new Cls();\n\n            instance._p1.should.be.equal('v1');\n            instance._p2.should.be.equal('v2');\n        });\n\n        it('\"__self\" property should be pointed to class', function() {\n            var Cls = inherit({}),\n                instance = new Cls();\n\n            instance.__self.should.be.equal(Cls);\n        });\n\n        it('should override methods of base class', function() {\n            var ClsA = inherit({\n                    method1 : function() {\n                        return 'A1';\n                    },\n                    method2 : function() {\n                        return 'A2';\n                    }\n                }),\n                ClsB = inherit(ClsA, {\n                    method1 : function() {\n                        return 'B1';\n                    }\n                }),\n                ClsC = inherit(ClsB, {\n                    method2 : function() {\n                        return 'C2';\n                    }\n                }),\n                instance = new ClsC();\n\n            instance.method1().should.be.equal('B1');\n            instance.method2().should.be.equal('C2');\n        });\n\n        it('__base should call methods of base class', function() {\n            var ClsA = inherit({\n                    method1 : function() {\n                        return 'A1';\n                    },\n                    method2 : function() {\n                        return 'A2';\n                    }\n                }),\n                ClsB = inherit(ClsA, {\n                    method1 : function() {\n                        return this.__base() + 'B1';\n                    }\n                }),\n                ClsC = inherit(ClsB, {\n                    method1 : function() {\n                        return this.__base() + 'C1';\n                    },\n\n                    method2 : function() {\n                        return this.__base() + 'C2';\n                    }\n                }),\n                instance = new ClsC();\n\n            instance.method1().should.be.equal('A1B1C1');\n            instance.method2().should.be.equal('A2C2');\n        });\n    });\n\n    describe('static', function() {\n        it('properties should be assigned', function() {\n            var Cls = inherit({}, {\n                method : function() {\n                    return 'method';\n                },\n\n                prop : 'val'\n            });\n\n            Cls.method().should.be.equal('method');\n            Cls.prop.should.be.equal('val');\n        });\n\n        it('properties should override properties of base class', function() {\n            var ClsA = inherit({}, {\n                    method1 : function() {\n                        return 'A1';\n                    },\n                    method2 : function() {\n                        return 'A2';\n                    }\n                }),\n                ClsB = inherit(ClsA, {}, {\n                    method1 : function() {\n                        return 'B1';\n                    }\n                }),\n                ClsC = inherit(ClsB, {}, {\n                    method2 : function() {\n                        return 'C2';\n                    }\n                });\n\n            ClsC.method1().should.be.equal('B1');\n            ClsC.method2().should.be.equal('C2');\n        });\n\n        it('__base should call methods of base class', function() {\n            var ClsA = inherit({}, {\n                    method1 : function() {\n                        return 'A1';\n                    },\n                    method2 : function() {\n                        return 'A2';\n                    }\n                }),\n                ClsB = inherit(ClsA, {}, {\n                    method1 : function() {\n                        return this.__base() + 'B1';\n                    }\n                }),\n                ClsC = inherit(ClsB, {}, {\n                    method1 : function() {\n                        return this.__base() + 'C1';\n                    },\n\n                    method2 : function() {\n                        return this.__base() + 'C2';\n                    }\n                });\n\n            ClsC.method1().should.be.equal('A1B1C1');\n            ClsC.method2().should.be.equal('A2C2');\n        });\n    });\n\n    describe('mixin', function() {\n        it('properties should be assigned', function() {\n            var ClsA = inherit({\n                    method : function() {\n                        return 'method';\n                    }\n                }),\n                Mix1 = inherit({\n                    method1 : function() {\n                        return 'mix1method';\n                    }\n                }),\n                Mix2 = inherit({\n                    method2 : function() {\n                        return 'mix2method';\n                    }\n                }),\n                ClsB = inherit([ClsA, Mix1, Mix2]),\n                instance = new ClsB();\n\n            instance.method().should.be.equal('method');\n            instance.method1().should.be.equal('mix1method');\n            instance.method2().should.be.equal('mix2method');\n        });\n\n        it('static properties should be assigned', function() {\n            var ClsA = inherit({}, {\n                    method : function() {\n                        return 'method';\n                    }\n                }),\n                Mix1 = inherit({}, {\n                    method1 : function() {\n                        return 'mix1method';\n                    }\n                }),\n                Mix2 = inherit({}, {\n                    method2 : function() {\n                        return 'mix2method';\n                    }\n                }),\n                ClsB = inherit([ClsA, Mix1, Mix2]);\n\n            ClsB.method().should.be.equal('method');\n            ClsB.method1().should.be.equal('mix1method');\n            ClsB.method2().should.be.equal('mix2method');\n        });\n\n        it('__base should call methods of previous object', function() {\n            var ClsA = inherit({\n                    method : function() {\n                        return 'methodA';\n                    }\n                }),\n                Mix1 = inherit({\n                    method : function() {\n                        return this.__base() + '_mix1method';\n                    }\n                }),\n                Mix2 = inherit({\n                    method : function() {\n                        return this.__base() + '_mix2method';\n                    }\n                }),\n                ClsB = inherit([ClsA, Mix1, Mix2], {\n                    method : function() {\n                        return this.__base() + '_methodB';\n                    }\n                }),\n                instance = new ClsB();\n\n            instance.method().should.be.equal('methodA_mix1method_mix2method_methodB');\n        });\n\n        it('__base in static methods should call methods of previous object', function() {\n            var ClsA = inherit(null, {\n                    method : function() {\n                        return 'methodA';\n                    }\n                }),\n                Mix1 = inherit(null, {\n                    method : function() {\n                        return this.__base() + '_mix1method';\n                    }\n                }),\n                Mix2 = inherit(null, {\n                    method : function() {\n                        return this.__base() + '_mix2method';\n                    }\n                }),\n                ClsB = inherit([ClsA, Mix1, Mix2], null, {\n                    method : function() {\n                        return this.__base() + '_methodB';\n                    }\n                });\n\n            ClsB.method().should.be.equal('methodA_mix1method_mix2method_methodB');\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/inherit/inherit.vanilla.js",
    "content": "/**\n * @module inherit\n * @version 2.2.6\n * @author Filatov Dmitry <dfilatov@yandex-team.ru>\n */\n\nconst noop = () => {},\n  extend = (o1, o2) => {\n    if(o2) {\n      for(const [key, val] of Object.entries(o2))\n        o1[key] = val\n    }\n    return o1\n  },\n  toStr = Object.prototype.toString,\n  isFunction = obj => toStr.call(obj) === '[object Function]'\n\nfunction override(base, res, add) {\n  for(const name of Object.keys(add)) {\n    if(name === '__self') continue\n\n    const prop = add[name]\n    if(isFunction(prop) &&\n        (!prop.prototype || !prop.prototype.__self) &&\n        (prop.toString().indexOf('.__base') > -1)) {\n      res[name] = (name => {\n        const baseMethod = base[name]\n            ? base[name]\n            : name === '__constructor'\n              ? res.__self.__parent\n              : noop,\n          result = function() {\n            const baseSaved = this.__base\n            this.__base = result.__base\n            const res = prop.apply(this, arguments)\n            this.__base = baseSaved\n            return res\n          }\n        result.__base = baseMethod\n        return result\n      })(name)\n    } else {\n      res[name] = prop\n    }\n  }\n}\n\nfunction applyMixins(mixins, res) {\n  for(let i = 1; i < mixins.length; i++) {\n    const mixin = mixins[i]\n    res\n      ? isFunction(mixin)\n        ? inherit.self(res, mixin.prototype, mixin)\n        : inherit.self(res, mixin)\n      : res = isFunction(mixin)\n        ? inherit(mixins[0], mixin.prototype, mixin)\n        : inherit(mixins[0], mixin)\n  }\n  return res || mixins[0]\n}\n\n/**\n* Creates class\n* @param {Function|Array} [baseClass|baseClassAndMixins] class (or class and mixins) to inherit from\n* @param {Object} prototypeFields\n* @param {Object} [staticFields]\n* @returns {Function} class\n*/\nfunction inherit() {\n  const args = arguments,\n    withMixins = Array.isArray(args[0]),\n    hasBase = withMixins || isFunction(args[0]),\n    base = hasBase ? withMixins ? applyMixins(args[0]) : args[0] : noop,\n    props = args[hasBase ? 1 : 0] || {},\n    staticProps = args[hasBase ? 2 : 1],\n    res = props.__constructor || (hasBase && base.prototype && base.prototype.__constructor)\n      ? function() {\n          return this.__constructor.apply(this, arguments)\n        }\n      : hasBase\n        ? function() {\n            return base.apply(this, arguments)\n          }\n        : function() {}\n\n  if(!hasBase) {\n    res.prototype = props\n    res.prototype.__self = res.prototype.constructor = res\n    return extend(res, staticProps)\n  }\n\n  extend(res, base)\n\n  res.__parent = base\n\n  const basePtp = base.prototype,\n    resPtp = res.prototype = Object.create(basePtp)\n\n  resPtp.__self = resPtp.constructor = res\n\n  props && override(basePtp, resPtp, props)\n  staticProps && override(base, res, staticProps)\n\n  return res\n}\n\ninherit.self = function() {\n  const args = arguments,\n    withMixins = Array.isArray(args[0]),\n    base = withMixins ? applyMixins(args[0], args[0][0]) : args[0],\n    props = args[1],\n    staticProps = args[2],\n    basePtp = base.prototype\n\n  props && override(basePtp, basePtp, props)\n  staticProps && override(base, base, staticProps)\n\n  return base\n}\n\nexport default inherit\n"
  },
  {
    "path": "common.blocks/jquery/__config/jquery__config.js",
    "content": "/**\n * @module jquery__config\n * @description Configuration for jQuery.\n * jQuery is now provided via npm package (peer dependency).\n */\n\nexport default {\n    /**\n     * Required jQuery version range\n     * @type {String}\n     */\n    version : '>=4.0.0'\n};\n"
  },
  {
    "path": "common.blocks/jquery/__config/jquery__config.ru.md",
    "content": "<a name=\"elems-config\"></a>\n\n# Элемент `config` блока `jquery`\n\nЭлемент предоставляет объект с настройками подключаемой библиотеки jQuery. Настройки хранятся как свойства объекта.\n\n<a name=\"fields\"></a>\n\n## Свойства и методы объекта\n\n<a name=\"fields-url\"></a>\n\n### Свойство `url`\n\nТип: `String`.\n\nСодержит строку с URL для загрузки jQuery.\nВ проекте значение свойства может быть переопределено. Тогда при подключении блока будет использовано новое значение, если библиотека jQuery не была подключена предварительно.\n\n```js\nmodules.define('jquery__config', function(provide) {\n\nprovide({ url: '//foo.bar/my-custom-jquery.js' });\n\n});\n```\n"
  },
  {
    "path": "common.blocks/jquery/jquery.deps.js",
    "content": "({\n    shouldDeps : [\n        { elem : 'config' }\n    ]\n})\n"
  },
  {
    "path": "common.blocks/jquery/jquery.en.md",
    "content": "# jquery\n\nThis block is for downloading the [jQuery](https://jquery.com) library and its extensions and enabling them on a page.\nExtensions are enabled via dependencies on the block elements.\n\n## Usage\n\n```js\nmodules.require(['jquery'], function($) {\n    console.log($);\n});\n```\n\n## Overview\n\n### Elements of the block\n\n| Element | Usage | Description |\n| --------| --------------------- | -------- |\n| <a href=\"#elems-config\">config</a> | `JS` | jQuery configuration. |\n\n### Properties and methods of the block elements\n\n| Element| Name | Return type | Description |\n| -------| --- | ----------------------------- | -------- |\n| <a href=\"#elems-config\">config</a> | <a href=\"#fields-url\">url</a> | `String` | String with the URL for connecting the jQuery library. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `js`\n"
  },
  {
    "path": "common.blocks/jquery/jquery.js",
    "content": "/**\n * @module jquery\n * @description Provide jQuery from npm package.\n */\n\nimport jQuery from 'jquery'\n\n/**\n * @type Function\n */\nexport default jQuery\n"
  },
  {
    "path": "common.blocks/jquery/jquery.ru.md",
    "content": "# jquery\n\nБлок служит для загрузки и подключения на страницу библиотеки [jQuery](https://jquery.com) и ее расширений.\nРасширения подключаются через зависимости от элементов блока.\n\n## Способы использования\n\n```js\nmodules.require(['jquery'], function($) {\n    console.log($);\n});\n```\n\n## Обзор\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| --------| --------------------- | -------- |\n| <a href=\"#elems-config\">config</a> | `JS` | Настройки jQuery. |\n\n### Свойства и методы элементов блока\n\n| Элемент| Имя | Тип возвращаемого значения | Описание |\n| -------| --- | ----------------------------- | -------- |\n| <a href=\"#elems-config\">config</a> | <a href=\"#fields-url\">url</a> | `String` | Строка с URL, подключаемой библиотеки jQuery. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `js`\n"
  },
  {
    "path": "common.blocks/jquery/jquery.ru.title.txt",
    "content": "Блок с плагинами для jQuery\n"
  },
  {
    "path": "common.blocks/keyboard/__codes/keyboard__codes.js",
    "content": "/**\n * @module keyboard__codes\n */\nexport default {\n    /** @type {Number} */\n    BACKSPACE : 8,\n    /** @type {Number} */\n    TAB : 9,\n    /** @type {Number} */\n    ENTER : 13,\n    /** @type {Number} */\n    CAPS_LOCK : 20,\n    /** @type {Number} */\n    ESC : 27,\n    /** @type {Number} */\n    SPACE : 32,\n    /** @type {Number} */\n    PAGE_UP : 33,\n    /** @type {Number} */\n    PAGE_DOWN : 34,\n    /** @type {Number} */\n    END : 35,\n    /** @type {Number} */\n    HOME : 36,\n    /** @type {Number} */\n    LEFT : 37,\n    /** @type {Number} */\n    UP : 38,\n    /** @type {Number} */\n    RIGHT : 39,\n    /** @type {Number} */\n    DOWN : 40,\n    /** @type {Number} */\n    INSERT : 45,\n    /** @type {Number} */\n    DELETE : 46\n}\n"
  },
  {
    "path": "common.blocks/keyboard/keyboard.en.md",
    "content": "# keyboard\n\nThis block is used for working with keyboard input.\n\n## Overview\n\n### Elements of the block\n\n| Element | Usage | Description |\n| --------| --------------------- | -------- |\n| <a href=\"#elems-codes\">codes</a> | `JS` | Provides an object with a set of constant names for frequently used keyboard codes. |\n\n### Properties and methods of the block elements\n\n| Element | Name | Type |\n| ------- | --- | --- |\n| <a href=\"#elems-codes\">codes</a> | BACKSPACE | `String` |\n| | TAB | `String` |\n| | ENTER | `String` |\n| | CAPS_LOCK | `String` |\n| | ESC | `String` |\n| | SPACE | `String` |\n| | PAGE_UP | `String` |\n| | PAGE_DOWN | `String` |\n| | END | `String` |\n| | HOME | `String` |\n| | LEFT | `String` |\n| | UP | `String` |\n| | RIGHT | `String` |\n| | DOWN | `String` |\n| | INSERT | `String` |\n| | DELETE | `String` |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `js`\n\n## Description\n\n<a name=\"elems\"></a>\n\n### Elements of the block\n\n<a name=\"elems-codes\"></a>\n\n#### `codes` element\n\nProvides an object with a set of constant names for frequently used keyboard codes.\n\n<a name=\"elems-codes-fields\"></a>\n\n##### Properties and methods of the object\n\nType: `String`.\n\nThe name values (object properties) are the key codes. Using meaningful names instead of the key codes makes the code easier to understand.\n\nFor example, the `_onKeyDown` method uses the names of the `UP` and `DOWN` keys when checking the `keyCode` field for an event object:\n\n```js\nmodules.define('input', ['i-bem-dom', 'keyboard__codes'], function(provide, bemDom, keyCodes) {\n\nprovide(bemDom.declBlock(this.name, /** @lends input.prototype */{\n    onSetMod : {\n        js : {\n            inited : function() {\n                this._domEvents().on('keydown', this._onKeyDown);\n            }\n        }\n    },\n\n    _onKeyDown : function(e) {\n        if((e.keyCode === keyCodes.UP || e.keyCode === keyCodes.DOWN) && !e.shiftKey) {\n            // ...\n        }\n    }\n}));\n});\n```\n\nThe following properties are available:\n\n* `BACKSPACE`\n* `TAB`\n* `ENTER`\n* `CAPS_LOCK`\n* `ESC`\n* `SPACE`\n* `PAGE_UP`\n* `PAGE_DOWN`\n* `END`\n* `HOME`\n* `LEFT`\n* `UP`\n* `RIGHT`\n* `DOWN`\n* `INSERT`\n* `DELETE`\n"
  },
  {
    "path": "common.blocks/keyboard/keyboard.ru.md",
    "content": "# keyboard\n\nБлок предназначен для работы с клавиатурным вводом.\n\n## Обзор\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| --------| --------------------- | -------- |\n| <a href=\"#elems-codes\">codes</a> | `JS` | Предоставляет объект, содержащий набор констант – имен часто используемых клавиатурных кодов. |\n\n### Свойства и методы элементов блока\n\n| Элемент | Имя | Тип |\n| ------- | --- | --- |\n| <a href=\"#elems-codes\">codes</a> | BACKSPACE | `String` |\n| | TAB | `String` |\n| | ENTER | `String` |\n| | CAPS_LOCK | `String` |\n| | ESC | `String` |\n| | SPACE | `String` |\n| | PAGE_UP | `String` |\n| | PAGE_DOWN | `String` |\n| | END | `String` |\n| | HOME | `String` |\n| | LEFT | `String` |\n| | UP | `String` |\n| | RIGHT | `String` |\n| | DOWN | `String` |\n| | INSERT | `String` |\n| | DELETE | `String` |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `js`\n\n## Описание\n\n<a name=\"elems\"></a>\n\n### Элементы блока\n\n<a name=\"elems-codes\"></a>\n\n#### Элемент `codes`\n\nПредоставляет объект, содержащий набор констант – имен часто используемых клавиатурных кодов.\n\n<a name=\"elems-codes-fields\"></a>\n\n##### Свойства и методы объекта\n\nТип: `String`.\n\nЗначениями имен (свойств объекта) являются коды клавиш. Использование осмысленных имен вместо кодов клавиш делает код понятнее.\n\nНапример, метод `_onKeyDown` использует имена клавиш `UP` и `DOWN` при проверке поля `keyCode` объекта события:\n\n```js\nmodules.define('input', ['i-bem-dom', 'keyboard__codes'], function(provide, bemDom, keyCodes) {\n\nprovide(bemDom.declBlock(this.name, /** @lends input.prototype */{\n    onSetMod : {\n        js : {\n            inited : function() {\n                this._domEvents().on('keydown', this._onKeyDown);\n            }\n        }\n    },\n\n    _onKeyDown : function(e) {\n        if((e.keyCode === keyCodes.UP || e.keyCode === keyCodes.DOWN) && !e.shiftKey) {\n            // ...\n        }\n    }\n}));\n});\n```\n\nДоступен следующий набор свойств:\n\n* `BACKSPACE`\n* `TAB`\n* `ENTER`\n* `CAPS_LOCK`\n* `ESC`\n* `SPACE`\n* `PAGE_UP`\n* `PAGE_DOWN`\n* `END`\n* `HOME`\n* `LEFT`\n* `UP`\n* `RIGHT`\n* `DOWN`\n* `INSERT`\n* `DELETE`\n"
  },
  {
    "path": "common.blocks/loader/_type/loader_type_bundle.js",
    "content": "/**\n * @module loader_type_bundle\n * @description Load BEM bundle (JS+CSS) from external URL.\n */\n\nconst LOADING_TIMEOUT = 30000\nconst doc = document\nlet head\nconst bundles = new Map()\n\nconst handleError = (bundleId) => {\n    const bundleDesc = bundles.get(bundleId)\n\n    if(!bundleDesc) return\n\n    const fns = bundleDesc.errorFns\n\n    clearTimeout(bundleDesc.timer)\n\n    for(const fn of fns) fn()\n    bundles.delete(bundleId)\n}\n\nconst appendCss = (css) => {\n    const style = doc.createElement('style')\n    style.type = 'text/css'\n    head.appendChild(style)\n    style.appendChild(doc.createTextNode(css))\n}\n\n/**\n * Loads bundle\n * @param {String} id\n * @param {String} url\n * @param {Function} onSuccess\n * @param {Function} [onError]\n */\nconst load = (id, url, onSuccess, onError) => {\n    const bundle = bundles.get(id)\n    if(bundle) {\n        if(bundle.successFns) { // bundle is being loaded\n            bundle.successFns.push(onSuccess)\n            onError && bundle.errorFns.push(onError)\n        } else { // bundle was loaded before\n            setTimeout(onSuccess, 0)\n        }\n        return\n    }\n\n    const script = doc.createElement('script')\n    const errorFn = () => {\n        handleError(id)\n    }\n\n    script.type = 'text/javascript'\n    script.charset = 'utf-8'\n    script.src = url\n    script.onerror = errorFn // for browsers that support\n    setTimeout(() => {\n        (head || (head = doc.getElementsByTagName('head')[0])).insertBefore(script, head.firstChild)\n    }, 0)\n\n    bundles.set(id, {\n        successFns : [onSuccess],\n        errorFns : onError? [onError] : [],\n        timer : setTimeout(errorFn, LOADING_TIMEOUT)\n    })\n}\n\nload._loaded = (bundle) => {\n    const bundleDesc = bundles.get(bundle.id)\n\n    if(!bundleDesc) return\n\n    clearTimeout(bundleDesc.timer)\n\n    bundle.js && bundle.js.call(globalThis)\n\n    bundle.css && appendCss(bundle.css)\n\n    if(bundle.hcss) {\n        const styles = []\n        const _ycssjs = window._ycssjs\n\n        bundle.hcss.forEach((hsh) => {\n            if(_ycssjs) {\n                if(hsh[0] in _ycssjs) return\n                _ycssjs(hsh[0])\n            }\n\n            styles.push(hsh[1])\n        })\n\n        styles.length && appendCss(styles.join(''))\n    }\n\n    const onSuccess = () => {\n        const fns = bundleDesc.successFns\n        for(const fn of fns) fn()\n        delete bundleDesc.successFns\n    }\n\n    onSuccess()\n}\n\nexport default load\n"
  },
  {
    "path": "common.blocks/loader/_type/loader_type_js.js",
    "content": "/**\n * @module loader_type_js\n * @description Load JS from external URL.\n */\n\nconst loading = new Map()\nconst loaded = new Map()\nconst head = document.getElementsByTagName('head')[0]\n\nconst runCallbacks = (path, type) => {\n    const cbs = loading.get(path)\n    loading.delete(path)\n    for(const cb of cbs) {\n        cb[type] && cb[type]()\n    }\n}\n\nconst onSuccess = (path) => {\n    loaded.set(path, true)\n    runCallbacks(path, 'success')\n}\n\nconst onError = (path) => {\n    runCallbacks(path, 'error')\n}\n\nexport default\n    /**\n     * @param {String} path resource link\n     * @param {Function} [success] to be called if the script succeeds\n     * @param {Function} [error] to be called if the script fails\n     */\n    (path, success, error) => {\n        if(loaded.has(path)) {\n            success && success()\n            return\n        }\n\n        if(loading.get(path)) {\n            loading.get(path).push({ success, error })\n            return\n        }\n\n        loading.set(path, [{ success, error }])\n\n        const script = document.createElement('script')\n        script.type = 'text/javascript'\n        script.charset = 'utf-8'\n        script.src = (location.protocol === 'file:' && !path.indexOf('//')? 'http:' : '') + path\n\n        script.onload = () => {\n            script.onload = script.onerror = null\n            onSuccess(path)\n        }\n\n        script.onerror = () => {\n            script.onload = script.onerror = null\n            onError(path)\n        }\n\n        head.insertBefore(script, head.lastChild)\n    }\n"
  },
  {
    "path": "common.blocks/loader/_type/loader_type_js.spec.js",
    "content": "modules.define('spec', [\n    'loader_type_js',\n    'sinon'\n], function(provide,\n    loader,\n    sinon\n) {\n\ndescribe('loader_type_js', function() {\n    it('should call success callback', function(done) {\n        var spyError = sinon.spy();\n\n        loader('data:text/javascript;charset=utf-8,;', function() {\n            spyError.should.not.have.been.called;\n\n            done();\n        }, spyError);\n    });\n\n    it('should call error callback', function(done) {\n        var spySuccess = sinon.spy();\n\n        loader('about:error', spySuccess, function() {\n            spySuccess.should.not.have.been.called;\n\n            done();\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/loader/loader.en.md",
    "content": "# loader\n\nUse the `loader` block for downloading and connecting scripts by URLs.\n\n## Overview\n\n### Block modifiers\n\n| Modifier | Acceptable values | Usage | Description |\n| ----------- | ------------------- | --------------------- | -------- |\n| <a href=\"#modifiers-type\">type</a> | `'js'`, `'bundle'` | `JS` | Uses a URL to get and connect JS code or a bundle. |\n\n### Functions enabled by block elements\n\n| Modifier | Function | Returned value | Description |\n| ----------- | --- | ----------------------------- | -------- |\n| <a href=\"#modifiers-type-js\">js</a> | loader(</br>`id {String}`,</br> `url {String}`,</br> `[success {Function}]`,</br> `[error {Function}]`) | - | Downloads and connects a fragment of JavaScript code. |\n| <a href=\"#modifiers-type-bundle\">bundle</a> | loader(</br>`url {String}`,</br> `success {Function}`,</br> `[error {Function}]`) | - | Downloads and connects a bundle of CSS and JS files. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `js`\n\n## Description\n\n<a name=\"modifiers\"></a>\n\n### Block modifiers\n\n<a name=\"modifiers-type\"></a>\n\n#### `type` modifier\n\nProvides a set of functions to download and connect different data types.\n\nAcceptable values: `'js'`, `'bundle'`.\n\nUsage: `JS`.\n\nDepending on the value of the `type` modifier, the `loader` block lets you download from a URL and connect:\n\n* `js` – A JavaScript fragment.\n* `bundle` – A bundle of CSS and JS files.\n\n<a name=\"modifiers-type-js\"></a>\n\n##### `type` modifier with the `js` value\n\nProvides a function to download and connect a JavaScript fragment.\n\n**Accepted arguments:**\n\n* `url {String}` – URL of the JavaScript fragment to download. Required argument.\n* [`success {Function}`] – The callback function to run when the code is loaded successfully.\n* [`error {Function}`] – The callback function to run when the code couldn't load because of an error.\n\nNo return value.\n\nFor example, `loader_type_js` can be used for downloading and enabling jQuery:\n\n```js\nmodules.define('jquery', ['loader_type_js'], function(provide, loader) {\n\n    loader(\n        'https://yastatic.net/jquery/2.2.0/jquery.min.js',\n        function() { provide(jQuery) });\n\n});\n```\n\nFor a more advanced example, see the [common.blocks/jquery](https://github.com/bem/bem-core/blob/v2/common.blocks/jquery/jquery.js) block in the `bem-core` library.\n\n<a name=\"modifiers-type-bundle\"></a>\n\n##### `type` modifier with the `bundle` value\n\nProvides a function to download and connect a bundle of CSS and JS files.\n\n**Accepted arguments:**\n\n* `id {String}` – Bundle ID. Required argument.\n* `url {String}` – The path to the bundle file in URL format. Required argument.\n* `onSuccess {Function}` – The callback to run when the bundle is loaded successfully. Required argument.\n* [`onError {Function}`] – The callback to run when the bundle didn't load.\n\nNo return value.\n\nThe specification for the `bundle` technology is currently under development. For more details, write your questions in the [forum](https://ru.bem.info/forum/).\n\n###### `_loaded` static method\n\nThe function connected with the `type_bundle` modifier has the `_loaded` static method. It is used as a helper method after successfully loading the bundle.\n\n**Accepted arguments:**\n\n* `id {String}` – Bundle ID. Required argument.\n\nNo return value.\n"
  },
  {
    "path": "common.blocks/loader/loader.ru.md",
    "content": "# loader\n\nБлок `loader` служит для загрузки и подключения скриптов по URL.\n\n## Обзор\n\n### Модификаторы блока\n\n| Модификатор | Допустимые значения | Способы использования | Описание |\n| ----------- | ------------------- | --------------------- | -------- |\n| <a href=\"#modifiers-type\">type</a> | `'js'`, `'bundle'` | `JS` | Позволяет по URL получить и подключить JS-код или бандл. |\n\n### Функции, подключаемые модификаторами блока\n\n| Модификатор | Функция | Возвращаемое значение | Описание |\n| ----------- | --- | ----------------------------- | -------- |\n| <a href=\"#modifiers-type-js\">js</a> | loader(</br>`id {String}`,</br> `url {String}`,</br> `[success {Function}]`,</br> `[error {Function}]`) | - | Загружает и подключает фрагмент JavaScript-кода. |\n| <a href=\"#modifiers-type-bundle\">bundle</a> | loader(</br>`url {String}`,</br> `success {Function}`,</br> `[error {Function}]`) | - | Загружает и подключает пакет, собранный из CSS и JS-файлов – «бандл». |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `js`\n\n## Описание\n\n<a name=\"modifiers\"></a>\n\n### Модификаторы блока\n\n<a name=\"modifiers-type\"></a>\n\n#### Модификатор `type`\n\nПредоставляет набор функций для загрузки и подключение различных типов данных.\n\nДопустимые значения: `'js'`, `'bundle'`.\n\nСпособ использования: `JS`.\n\nВ зависимости от значения модификатора `type` блок `loader` позволяет получить по URL и подключить:\n\n* `js` – фрагмент JS-кода.\n* `bundle` – пакет, собранный из CSS и JS-файлов – «бандл».\n\n<a name=\"modifiers-type-js\"></a>\n\n##### Модификатор `type` в значении `js`\n\nПредоставляет функцию, позволяющую загрузить и подключить фрагмент JS-кода.\n\n**Принимаемые аргументы:**\n\n* `url {String}` – URL загружаемого фрагмента JS-кода. Обязательный аргумент.\n* [`success {Function}`] – callback-функция, выполняемая по завершению загрузки кода.\n* [`error {Function}`] – callback-функция, выполняемая при ошибке в ходе загрузки кода.\n\nНе имеет возвращаемого значения.\n\nНапример, `loader_type_js` может использоваться для загрузки и подключения jQuery:\n\n```js\nmodules.define('jquery', ['loader_type_js'], function(provide, loader) {\n\n    loader(\n        'https://yastatic.net/jquery/2.2.0/jquery.min.js',\n        function() { provide(jQuery) });\n\n});\n```\n\nРасширенный пример смотрите в блоке [common.blocks/jquery](https://github.com/bem/bem-core/blob/v2/common.blocks/jquery/jquery.js) библиотеки `bem-core`.\n\n<a name=\"modifiers-type-bundle\"></a>\n\n##### Модификатор `type` в значении `bundle`\n\nПредоставляет функцию, позволяющую загрузить и подключить пакет, собранный из CSS и JS-файлов – «бандл».\n\n**Принимаемые аргументы:**\n\n* `id {String}` – идентификатор бандла. Обязательный аргумент.\n* `url {String}` – путь до файла бандла в формате URL. Обязательный аргумент.\n* `onSuccess {Function}` – callback, вызываемая по завершению загрузки бандла. Обязательный аргумент.\n* [`onError {Function}`] – callback, вызываемая при неудачной загрузке бандла.\n\nНе имеет возвращаемого значения.\n\nСпецификации технологии `bundle` находятся в процессе разработки. Для получения детальной информации пишите на [форум](https://ru.bem.info/forum/).\n\n###### Статический метод `_loaded`\n\nФункция, подключаемая с модификатором `type_bundle`, обладает статическим методом – `_loaded`. Он используется как вспомогательный после успешной загрузки бандла.\n\n**Принимаемые аргументы:**\n\n* `id {String}` – идентификатор бандла. Обязательный аргумент.\n\nНе имеет возвращаемого значения.\n"
  },
  {
    "path": "common.blocks/next-tick/next-tick.en.md",
    "content": "# next-tick\n\nThis block provides a function that performs an asynchronous call of the callback function passed as an argument in the next tick of the event loop.\n\n`next-tick` – A polyfill that implements:\n\n* A simulated event loop for outdated browser versions.\n* A unified interface for working with various browsers and NodeJS.\n\nThis function works in cases when you need the callback to be invoked after the other functions in the event loop have finished. For example, you need to be sure that data will be available that is dynamically calculated in the current loop.\n\n**Accepted arguments:**\n\n* `fn {Function}` – The function to invoke in the next event loop. Required argument.\n\nNo return value.\n\nExample:\n\n```js\nmodules.require(['next-tick', 'events'], function(nextTick, events) {\n\nvar event = new events.Event();\n\nnextTick(function() { event.emit('click') }); \n\n// ··· \n\nevent.on('click', function(e) { console.log(e.type) })\n});\n```\n\n## Order of callbacks\n\nThe block forms a queue within the event cycle, adding each subsequent callback function to the end of the queue. The callbacks are invoked in order.\n\nExample:\n\n```js\nmodules.require(['next-tick'], function(nextTick) {\n  \nvar order = [];\n\nnextTick(function() { order.push(1); });\nnextTick(function() { order.push(2); });\nnextTick(function() { order.push(3); });\nnextTick(function() { console.log(order); }); // should be [1, 2, 3]\n});\n```\n\n## Public block technologies\n\nThe block is implemented in:\n\n* `vanilla.js`\n"
  },
  {
    "path": "common.blocks/next-tick/next-tick.ru.md",
    "content": "# next-tick\n\nБлок предоставляет функцию, производящую асинхронный вызов callback-функции, переданной аргументом, в следующем витке событийного цикла.\n\n`next-tick` – полифил, реализующий:\n\n* симуляцию событийного цикла для старых версий браузеров;\n* унифицированный интерфейс для работы с различными браузерами и NodeJS.\n\nФункция подходит для случаев, когда нужно, чтобы callback был выполнен после того, как другие функции в рамках событийного цикла отработали. Например, чтобы убедиться что доступны данные, динамически вычислявшиеся в текущем цикле.\n\n**Принимаемые аргументы:**\n\n* `fn {Function}` – функция, которую нужно вызвать в следующем событийном цикле. Обязательный аргумент.\n\nНе имеет возвращаемого значения.\n\nПример:\n\n```js\nmodules.require(['next-tick', 'events'], function(nextTick, events) {\n\nvar event = new events.Event();\n\nnextTick(function() { event.emit('click') }); \n\n// ··· \n\nevent.on('click', function(e) { console.log(e.type) })\n});\n```\n\n## Порядок вызова callback\n\nВ рамках событийного цикла работы блок формирует очередь, добавляя каждую следующую callback функцию в ее конец. Порядок вызова callback сохраняется.\n\nПример:\n\n```js\nmodules.require(['next-tick'], function(nextTick) {\n  \nvar order = [];\n\nnextTick(function() { order.push(1); });\nnextTick(function() { order.push(2); });\nnextTick(function() { order.push(3); });\nnextTick(function() { console.log(order); }); // should be [1, 2, 3]\n});\n```\n\n## Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `vanilla.js`\n"
  },
  {
    "path": "common.blocks/next-tick/next-tick.spec.js",
    "content": "modules.define('spec', ['next-tick'], function(provide, nextTick) {\n\ndescribe('next-tick', function() {\n    it('should call callback asynchronously', function(done) {\n        var isSync = true;\n        nextTick(function() {\n            isSync.should.be.false;\n            done();\n        });\n        isSync = false;\n    });\n\n    it('should call callbacks in the order of their originating calls', function(done) {\n        var order = [];\n        nextTick(function() { order.push(1); });\n        nextTick(function() { order.push(2); });\n        nextTick(function() { order.push(3); });\n        nextTick(function() {\n            order.should.be.eql([1, 2, 3]);\n            done();\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/next-tick/next-tick.vanilla.js",
    "content": "/**\n * @module next-tick\n */\n\n/**\n * Executes given function on next tick.\n * @type Function\n * @param {Function} fn\n */\n\nlet fns = []\nconst enqueueFn = fn => {\n    fns.push(fn)\n    return fns.length === 1\n  },\n  callFns = () => {\n    const fnsToCall = fns\n    fns = []\n    for(const fn of fnsToCall)\n      fn()\n  }\n\n/* global process */\nconst nextTick = typeof queueMicrotask === 'function'\n  ? fn => { enqueueFn(fn) && queueMicrotask(callFns) }\n  : typeof process === 'object' && process.nextTick\n    ? fn => { enqueueFn(fn) && process.nextTick(callFns) }\n    : fn => { enqueueFn(fn) && globalThis.setTimeout(callFns, 0) }\n\nexport default nextTick\n"
  },
  {
    "path": "common.blocks/objects/objects.en.md",
    "content": "# objects\n\nThis block provides an object with a set of methods for working with JavaScript objects.\n\n## Overview\n\n### Properties and methods of the object\n\n| Name | Return type | Description |\n| -------- | --- | -------- |\n| <a href=\"#fields-extend\">extend</a>(<br>`target {Object}`, <br>`source {Object}`) | `Object` | Extends an object with the properties of another object. |\n| <a href=\"#fields-isEmpty\">isEmpty</a>(`obj {Object}`) | `Boolean` |  Determines whether the passed object is empty. |\n| <a href=\"#fields-each\">each</a>(<br>`obj {Object}`, <br>`fn {Function}`, <br>`[ctx {Object}]`) | - | Iteratively traverses its own object properties. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `vanilla.js`\n\n## Description\n\n<a name=\"fields\"></a>\n\n### Properties and methods of the object\n\n<a name=\"fields-extend\"></a>\n\n#### `extend` method\n\nExtends an object with the properties of another object. It only copies its own properties that weren't taken from the prototype chain.\n\n**Accepted arguments:**\n\n* `target {Object}` – Target object. Required argument.\n* `source {Object}` – The object whose properties are added to the target object. Multiple objects can be passed. The properties of each of them will be added to the target object. Required argument.\n\n**Return value:** `Event`. The target object with the added properties.\n\nExample:\n\n```js\nmodules.require(['objects'], function(objects) {\n\nvar obj1 = { a : 1, b : 2 },\n    obj2 = { b : 3, c : 4 };\n\nconsole.log(objects.extend(obj1, obj2)); // { a : 1, b : 3, c : 4 }\n});\n```\n\n<a name=\"fields-isEmpty\"></a>\n\n#### `isEmpty` method\n\nDetermines whether the passed object is empty. In other words, whether the object has its own properties.\n\n**Accepted arguments:**\n\n* `obj {Object}` – The object to check. Required argument.\n\n**Return value:** `Boolean`. If the object doesn't have its own properties, `true`.\n\nExample:\n\n```js\nmodules.require(['objects'], function(objects) {\n\nvar obj1 = {},\n    obj2 = { foo : 'bar' };\n\nconsole.log(objects.isEmpty(obj1)); // true\nconsole.log(objects.isEmpty(obj2)); // false\n});\n```\n\n<a name=\"fields-each\"></a>\n\n#### `each` method\n\nUsed for iterating through an object's properties. The handler function is invoked for each of the object's own properties.\n\n**Accepted arguments:**\n\n* `obj {Object}` – The object whose properties are being traversed. Required argument.\n* `fn {Function}` – The handler function to call for each property. Required argument.\n* [`ctx {Object}`] – The handler context.\n\nNo return value.\n\nThe handler function receives arguments with the value and key of the object property that it was invoked for.\n\nExample:\n\n```js\nmodules.require(['objects'], function(objects) {\n    objects.each(\n        { a : 1, b : 2 },\n        function(val, key) {\n            console.log(key, val);\n        });\n    // a 1\n    // b 2\n});\n```\n"
  },
  {
    "path": "common.blocks/objects/objects.ru.md",
    "content": "# objects\n\nБлок предоставляет объект, содержащий набор методов для работы с объектами JavaScript.\n\n## Обзор\n\n### Свойства и методы объекта\n\n| Имя | Тип возвращаемого значения | Описание |\n| -------- | --- | -------- |\n| <a href=\"#fields-extend\">extend</a>(<br>`target {Object}`, <br>`source {Object}`) | `Object` | Расширяет объект свойствами другого объекта. |\n| <a href=\"#fields-isEmpty\">isEmpty</a>(`obj {Object}`) | `Boolean` |  Позволяет выяснить пуст ли переданный объект. |\n| <a href=\"#fields-each\">each</a>(<br>`obj {Object}`, <br>`fn {Function}`, <br>`[ctx {Object}]`) | - | Итеративно обходит собственные свойства объекта. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `vanilla.js`\n\n## Описание\n\n<a name=\"fields\"></a>\n\n### Свойства и методы объекта\n\n<a name=\"fields-extend\"></a>\n\n#### Метод `extend`\n\nРасширяет объект свойствами другого объекта. Копируются только собственные свойства, не полученные по цепочке прототипов.\n\n**Принимаемые аргументы:**\n\n* `target {Object}` – целевой объект. Обязательный аргумент.\n* `source {Object}` – объект, свойства которого добавляются к целевому. Может быть передано несколько объектов. Свойства каждого из них будут добавлены к целевому. Обязательный аргумент.\n\n**Возвращаемое значение:** `Object`. Целевой объект с добавленными свойствами.\n\nПример:\n\n```js\nmodules.require(['objects'], function(objects) {\n\nvar obj1 = { a : 1, b : 2 },\n    obj2 = { b : 3, c : 4 };\n\nconsole.log(objects.extend(obj1, obj2)); // { a : 1, b : 3, c : 4 }\n});\n```\n\n<a name=\"fields-isEmpty\"></a>\n\n#### Метод `isEmpty`\n\nПозволяет выяснить пуст ли переданный объект. Другими словами, имеет ли объект собственные свойства.\n\n**Принимаемые аргументы:**\n\n* `obj {Object}` – объект для проверки. Обязательный аргумент.\n\n**Возвращаемое значение:** `Boolean`. В случае, если объект не имеет собственных свойств – `true`.\n\nПример:\n\n```js\nmodules.require(['objects'], function(objects) {\n\nvar obj1 = {},\n    obj2 = { foo : 'bar' };\n\nconsole.log(objects.isEmpty(obj1)); // true\nconsole.log(objects.isEmpty(obj2)); // false\n});\n```\n\n<a name=\"fields-each\"></a>\n\n#### Метод `each`\n\nСлужит для итерации по собственным свойствам объекта. Для каждого собственного свойства вызывается функция-обработчик.\n\n**Принимаемые аргументы:**\n\n* `obj {Object}` – объект, обход свойств которого производится. Обязательный аргумент.\n* `fn {Function}` – функция-обработчик, вызываемая для каждого свойства. Обязательный аргумент.\n* [`ctx {Object}`] – контекст обработчика.\n\nНе имеет возвращаемого значения.\n\nФункция-обработчик получает в качестве аргументов значение и ключ свойства объекта, для которого была вызвана.\n\nПример:\n\n```js\nmodules.require(['objects'], function(objects) {\n    objects.each(\n        { a : 1, b : 2 },\n        function(val, key) {\n            console.log(key, val);\n        });\n    // a 1\n    // b 2\n});\n```\n"
  },
  {
    "path": "common.blocks/objects/objects.spec.js",
    "content": "modules.define('spec', ['objects'], function(provide, objects) {\n\ndescribe('objects', function() {\n    var undef;\n\n    /* jshint -W001 */\n    describe('extend', function() {\n        it('should returns target object', function() {\n            var target = {};\n            objects.extend(target).should.be.equal(target);\n        });\n\n        it('should copy properties to target object', function() {\n            objects.extend(\n                { p1 : 'v1', p2 : 'v2' },\n                { p2 : 'v2_2', p3 : false },\n                { p4 : null },\n                { p5 : 0 })\n                    .should.be.eql({\n                        p1 : 'v1',\n                        p2 : 'v2_2',\n                        p3 : false,\n                        p4 : null,\n                        p5 : 0\n                    });\n        });\n\n        it('should return new object if target is not a object', function() {\n            objects.extend(true, { p1 : 'v1' })\n                .should.be.eql({ p1 : 'v1' });\n        });\n\n        it('should return new object if target is null', function() {\n            objects.extend(null, { p1 : 'v1' })\n                .should.be.eql({ p1 : 'v1' });\n        });\n\n        it('should properly extend object with \"hasOwnProperty\" property', function() {\n            objects.extend(\n                { hasOwnProperty : '' },\n                { hasOwnProperty : 'has' })\n                    .should.be.eql({ hasOwnProperty : 'has' });\n        });\n    });\n\n    describe('isEmpty', function() {\n        it('should returns true for object with no properties', function() {\n            objects.isEmpty({}).should.be.true;\n        });\n\n        it('should returns false for object with properties', function() {\n            objects.isEmpty({ prop : '' }).should.be.false;\n        });\n\n        it('should properly checks object with \"hasOwnProperty\" property', function() {\n            objects.isEmpty({ hasOwnProperty : true }).should.be.false;\n        });\n    });\n\n    describe('each', function() {\n        it('should iterates over all properties', function() {\n            var res = [],\n                undef;\n\n            objects.each(\n                { a : 'str', b : false, c : null, d : undef },\n                function(val, key) {\n                    res.push({ val : val, key : key });\n                });\n\n            res.should.be.eql([\n                { val : 'str', key : 'a' },\n                { val : false, key : 'b' },\n                { val : null, key : 'c' },\n                { val : undef, key : 'd' }\n            ]);\n        });\n\n        it('should properly iterates over object with \"hasOwnProperty\" property', function() {\n            var res = [];\n            objects.each(\n                { hasOwnProperty : false },\n                function(val, key) {\n                    res.push({ val : val, key : key });\n                });\n            res.should.be.eql([{ val : false, key : 'hasOwnProperty' }]);\n        });\n\n        it('should call callback with given context', function() {\n            var ctx = {};\n            objects.each(\n                { key : 'val' },\n                function() {\n                    this.should.be.equal(ctx);\n                },\n                ctx);\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/objects/objects.vanilla.js",
    "content": "/**\n * @module objects\n * @description A set of helpers to work with JavaScript objects\n */\n\nexport default {\n  /**\n   * Extends a given target by\n   * @param {Object} target object to extend\n   * @param {Object} source\n   * @returns {Object}\n   */\n  extend(target, source) {\n    (typeof target !== 'object' || target === null) && (target = {})\n\n    for(let i = 1, len = arguments.length; i < len; i++) {\n      const obj = arguments[i]\n      if(obj) {\n        for(const [key, val] of Object.entries(obj))\n          target[key] = val\n      }\n    }\n\n    return target\n  },\n\n  /**\n   * Check whether a given object is empty (contains no enumerable properties)\n   * @param {Object} obj\n   * @returns {Boolean}\n   */\n  isEmpty(obj) {\n    return Object.keys(obj).length === 0\n  },\n\n  /**\n   * Generic iterator function over object\n   * @param {Object} obj object to iterate\n   * @param {Function} fn callback\n   * @param {Object} [ctx] callbacks's context\n   */\n  each(obj, fn, ctx) {\n    for(const [key, val] of Object.entries(obj))\n      ctx ? fn.call(ctx, val, key) : fn(val, key)\n  }\n}\n"
  },
  {
    "path": "common.blocks/page/__css/page__css.bemhtml.js",
    "content": "block('page').elem('css')(\n    bem()(false),\n    tag()('style'),\n    match(function() { return this.ctx.url; })(\n        tag()('link'),\n        attrs()(function() {\n            return this.extend(applyNext() || {}, { rel : 'stylesheet', href : this.ctx.url });\n        })\n    )\n);\n"
  },
  {
    "path": "common.blocks/page/__css/page__css.bh.js",
    "content": "module.exports = function(bh) {\n\n    bh.match('page__css', function(ctx, json) {\n        ctx.bem(false);\n\n        if(json.url) {\n            ctx\n                .tag('link')\n                .attr('rel', 'stylesheet')\n                .attr('href', json.url);\n        } else {\n            ctx.tag('style');\n        }\n\n    });\n\n};\n"
  },
  {
    "path": "common.blocks/page/__js/page__js.bemhtml.js",
    "content": "block('page').elem('js')(\n    bem()(false),\n    tag()('script'),\n    attrs()(function() {\n        var attrs = {};\n        if(this.ctx.url) {\n            attrs.src = this.ctx.url;\n        } else if(this._nonceCsp) {\n            attrs.nonce = this._nonceCsp;\n        }\n\n        return this.extend(applyNext() || {}, attrs);\n    })\n);\n"
  },
  {
    "path": "common.blocks/page/__js/page__js.bh.js",
    "content": "module.exports = function(bh) {\n\n    bh.match('page__js', function(ctx, json) {\n        var nonce = ctx.tParam('nonceCsp');\n        ctx\n            .bem(false)\n            .tag('script');\n\n        if(json.url) {\n            ctx.attr('src', json.url);\n        } else if(nonce) {\n            ctx.attr('nonce', nonce);\n        }\n    });\n\n};\n"
  },
  {
    "path": "common.blocks/page/page.bemhtml.js",
    "content": "block('page')(\n\n    mode('doctype')(function() {\n        return { html : this.ctx.doctype || '<!DOCTYPE html>' };\n    }),\n\n    wrap()(function() {\n        var ctx = this.ctx;\n        this._nonceCsp = ctx.nonce;\n\n        return [\n            apply('doctype'),\n            {\n                tag : 'html',\n                attrs : { lang : ctx.lang },\n                cls : 'ua_js_no',\n                content : [\n                    {\n                        elem : 'head',\n                        content : [\n                            { tag : 'meta', attrs : { charset : 'utf-8' } },\n                            ctx.uaCompatible === false? '' : {\n                                tag : 'meta',\n                                attrs : {\n                                    'http-equiv' : 'X-UA-Compatible',\n                                    content : ctx.uaCompatible || 'IE=edge'\n                                }\n                            },\n                            { tag : 'title', content : ctx.title },\n                            { block : 'ua', attrs : { nonce : ctx.nonce } },\n                            ctx.head,\n                            ctx.styles,\n                            ctx.favicon? { elem : 'favicon', url : ctx.favicon } : ''\n                        ]\n                    },\n                    ctx\n                ]\n            }\n        ];\n    }),\n\n    tag()('body'),\n\n    content()(function() {\n        return [\n            applyNext(),\n            this.ctx.scripts\n        ];\n    }),\n\n    elem('head')(\n        bem()(false),\n        tag()('head')\n    ),\n\n    elem('meta')(\n        bem()(false),\n        tag()('meta')\n    ),\n\n    elem('link')(\n        bem()(false),\n        tag()('link')\n    ),\n\n    elem('favicon')(\n        bem()(false),\n        tag()('link'),\n        attrs()(function() {\n            return this.extend(applyNext() || {}, { rel : 'shortcut icon', href : this.ctx.url });\n        })\n    )\n\n);\n"
  },
  {
    "path": "common.blocks/page/page.bh.js",
    "content": "module.exports = function(bh) {\n\n    bh.match('page', function(ctx, json) {\n        ctx\n            .tag('body')\n            .tParam('nonceCsp', json.nonce)\n            .content([\n                ctx.content(),\n                json.scripts\n            ], true);\n\n        return [\n            { html : json.doctype || '<!DOCTYPE html>', tag : false },\n            {\n                tag : 'html',\n                attrs : { lang : json.lang },\n                cls : 'ua_js_no',\n                content : [\n                    {\n                        elem : 'head',\n                        content : [\n                            { tag : 'meta', attrs : { charset : 'utf-8' } },\n                            json.uaCompatible === false? '' : {\n                                tag : 'meta',\n                                attrs : {\n                                    'http-equiv' : 'X-UA-Compatible',\n                                    content : json.uaCompatible || 'IE=edge'\n                                }\n                            },\n                            { tag : 'title', content : json.title },\n                            { block : 'ua',  attrs : { nonce : json.nonce } },\n                            json.head,\n                            json.styles,\n                            json.favicon? { elem : 'favicon', url : json.favicon } : '',\n                        ]\n                    },\n                    json\n                ]\n            }\n        ];\n    });\n\n    bh.match('page__head', function(ctx) {\n        ctx.bem(false).tag('head');\n    });\n\n    bh.match('page__meta', function(ctx) {\n        ctx.bem(false).tag('meta');\n    });\n\n    bh.match('page__link', function(ctx) {\n        ctx.bem(false).tag('link');\n    });\n\n    bh.match('page__favicon', function(ctx, json) {\n        ctx\n            .bem(false)\n            .tag('link')\n            .attr('rel', 'shortcut icon')\n            .attr('href', json.url);\n    });\n\n};\n"
  },
  {
    "path": "common.blocks/page/page.deps.js",
    "content": "({\n    shouldDeps : [\n        { block : 'i-bem-dom', elems : { elem : 'init', mods : { auto : true } } },\n        { elems : ['css', 'js'] }\n    ]\n})\n"
  },
  {
    "path": "common.blocks/page/page.en.md",
    "content": "# page\n\nThis block provides templates that create a set of top-level HTML elements for a page: `<html>`, `<head>`, and `<body>`.\n\n## Overview\n\n### Special fields of the block\n\n| Field | Type | Description |\n| ---- | --- | -------- |\n| <a href=\"#declfields-doctype\">doctype</a> | `String` | Use this field to redefine the DTD string for the current document. |\n| <a href=\"#declfields-title\">title</a> | `String` | Use this field to specify the content of `<title>`. |\n| <a href=\"#declfields-favicon\">favicon</a> | `String` | Use this field to specify the URL of the favicon for the page. |\n| <a href=\"#declfields-head\">head</a> | `BEMJSON` | Use this field to add content to `<head>`. |\n| <a href=\"#declfields-styles\">styles</a> | `BEMJSON` | Use this field to connect CSS style sheets to the document. |\n| <a href=\"#declfields-scripts\">scripts</a> | `BEMJSON` | Use this field to embed scripts in the body of the document. |\n| <a href=\"#declfields-content\">content</a> | `BEMJSON` | Use this field to set the page content. |\n\n### Elements of the block\n\n| Element | Usage | Description |\n| ------- | --------------------- | -------- |\n| <a href=\"#elems-css\">css</a> | `BEMJSON` | Connects CSS using a URL or a string. |\n| <a href=\"#elems-js\">js</a> | `BEMJSON` | Connects JS using a URL or a string. |\n| <a href=\"#elems-meta\">meta</a> | `BEMJSON` | Creates `<meta>` HTML elements. |\n\n### Special fields of block elements\n\n| Element | Field | Type | Description |\n| ------- | ---- | --- | -------- |\n| <a href=\"#elems-css\">css</a> | <a href=\"#elems-css-declfields-url\">url</a> | `String`  | Sets the URL for downloading styles. |\n|  | <a href=\"#elems-css-declfields-content\">content</a> | `String`  | Sets styles in string format. |\n| <a href=\"#elems-js\">js</a> | <a href=\"#elems-css-declfields-url\">url</a> | `String`  | Sets the URL for downloading a script. |\n|  | <a href=\"#elems-css-declfields-content\">content</a> | `String`  | Sets scripts in string format |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `bh.js`\n* `bemhtml`\n\n## Description\n\nThis block is responsible for creating top-level HTML elements, connecting CSS, JS, and `<meta>` elements to a page, and defining the title. The BEMJSON declaration for the block and its elements have special fields reserved for this purpose.\n\n<a name=\"declfields\"></a>\n\n### Special fields of the block\n\n<a name=\"declfields-doctype\"></a>\n\n#### `doctype` field\n\nType: `String`.\n\nUse this field to explicitly set the DTD (Document Type Definition) for the current document. If omitted, `<!DOCTYPE html>` is used by default.\n\n<a name=\"declfields-title\"></a>\n\n#### `title` field\n\nType: `String`.\n\nTitle of the page. It becomes the `<title>` HTML element.\n\n```js\n{\n    block : 'page',\n    title : 'title',\n    content : 'Block page'\n}\n```\n\n<a name=\"declfields-favicon\"></a>\n\n#### `favicon` field\n\nType: `String`.\n\nUse this field to specify the URL of the favicon for the page:\n\n```js\n{\n    block : 'page',\n    title : 'title',\n    favicon : 'favicon.ico',\n    content : 'Page with users favicon.ico'\n}\n```\n\n<a name=\"declfields-head\"></a>\n\n#### `head` field\n\nType: `BEMJSON`.\n\nUse this field to add content to the `<head>` `HTML` element that is defined in the block template:\n\n```js\n{\n    block : 'page',\n    title : 'title',\n    head : [\n        { elem : 'js', url : 'jquery-min.js' },\n        { elem : 'meta', attrs : { name : 'description', content : 'Yet another webdev blog' } }\n    ],\n    content : 'Page with JS and meta-data'\n}\n```\n\n<a name=\"declfields-styles\"></a>\n\n#### `styles` field\n\nType: `BEMJSON`.\n\nUse this field to connect `CSS`:\n\n```js\n{\n    block : 'page',\n    title : 'title',\n    styles : { elem : 'css', url : '_index.css' },\n    content : 'Page with CSS'\n}\n```\n\n<a name=\"declfields-scripts\"></a>\n\n#### `scripts` field\n\nType: `BEMJSON`.\n\nEmbeds JS in the body of the page, at the end of the `<body>` HTML element:\n\n```js\n{\n    block : 'page',\n    title : 'title',\n    scripts : { elem : 'js', url : '_index.js' },\n    content : 'Page with JS in body'\n}\n```\n\n<a name=\"declfields-content\"></a>\n\n#### `content` field\n\nType: `BEMJSON`.\n\nUse this field to set the page content.\n\n```js\n{\n    block : 'page',\n    title : 'title',\n    content : {\n        block : 'link',\n        mods : { pseudo : 'yes', togcolor : 'yes', color : 'green' },\n        url : '#',\n        target : '_blank',\n        title : 'Click me',\n        content : 'Pseudo link'\n    }\n}\n```\n\n<a name=\"elems\"></a>\n\n### Elements of the block\n\n<a name=\"elems-css\"></a>\n\n#### `css` element\n\nConnects CSS using a URL or a string. Depending on whether the `url` field is specified in the element declaration, an HTML element is created with the tag:\n\n* `<link>` and the `stylesheet` property, if `url` is specified.\n* `<style>`, if `url` is omitted. In this case, it is assumed that the element content is passed using the `content` property in the element's BEMJSON declaration.\n\n<a name=\"elems-css-declfields-content\"></a>\n\n##### Specialized `content` field\n\nType: `String`.\n\nUse this field for explicitly passing the content of the `<style>` HTML element:\n\n```js\n{\n    block : 'page',\n    title : 'Page title',\n    styles : {\n        elem : 'css',\n        content : '.page { color : #f00 }'\n    },\n    content : 'Page with tag <style>'\n }\n```\n\n<a name=\"elems-css-declfields-url\"></a>\n\n##### Specialized `url` field\n\nType: `String`.\n\nSets the URL for downloading CSS. The value of the `url` field in the BEMJSON declaration is passed to the `href` property in the created HTML element.\n\n<a name=\"elems-js\"></a>\n\n#### `js` element\n\nConnects JS using a URL or a string. Creates the `<script>` HTML element.\n\n<a name=\"elems-js-declfields-content\"></a>\n\n##### Specialized `content` field\n\nType: `String`.\n\nUse this field for explicitly passing the content of the `<script>` HTML element:\n\n```js\n{\n    block : 'page',\n    title : 'Page title',\n    scripts : {\n        elem : 'js',\n        content : 'console.log(document.title)'\n    },\n    content : 'Page with tag <script>'\n}\n```\n\n<a name=\"elems-js-declfields-url\"></a>\n\n##### Specialized `url` field\n\nType: `String`.\n\nSets the URL for downloading a script. The value of the `url` field in the BEMJSON declaration is passed to the `src` property in the created HTML element.\n\n```js\n{\n    block : 'page',\n    title : 'Page title',\n    styles : { elem : 'css', url : '_index.css' },\n    content : 'Page with tag style'\n}\n```\n\n<a name=\"elems-meta\"></a>\n\n#### `meta` element\n\nCreates `<meta>` HTML elements and defines user metadata for them. Metadata is passed as keys and values of attribute hashes, the `attrs` properties in the BEMJSON declaration of the element:\n\n```js\n{\n    block : 'page',\n    title : 'title',\n    head : [\n        { elem : 'css', url : 'example.css' },\n        { elem : 'meta', attrs : { name : 'keywords', content : 'js, css, html' } }\n    ],\n    content : 'Page with CSS и meta-data'\n}\n```\n\nFor more information, see the documentation for `<meta>` [at MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta).\n"
  },
  {
    "path": "common.blocks/page/page.en.title.txt",
    "content": "HTML skeleton (<html>, <head>, <body>)\n"
  },
  {
    "path": "common.blocks/page/page.examples/.bem/level.js",
    "content": "exports.baseLevelPath = require.resolve('../../../../.bem/levels/examples.js');\n"
  },
  {
    "path": "common.blocks/page/page.examples/10-simple.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'Простой пример html/head/body-обвязки страницы',\n    favicon : '//bem.info/favicon.ico',\n    head : [\n        { elem : 'css', url : '_10-simple.css' },\n        { elem : 'js', url : '_10-simple.js' },\n        { elem : 'meta', attrs : { name : 'keywords', content : 'BEM' } }\n    ],\n    content : { block : 'bla', content : 'Простой пример обвязки страницы' }\n})\n"
  },
  {
    "path": "common.blocks/page/page.examples/10-simple.ru.title.txt",
    "content": "Простой пример html/head/body-обвязки страницы"
  },
  {
    "path": "common.blocks/page/page.examples/10-simple.ru.wiki",
    "content": "﻿Пример иллюстрирует задание:\n  * favicon\n  * meta\n  * ссылок на css и js"
  },
  {
    "path": "common.blocks/page/page.examples/20-doctype.bemjson.js",
    "content": "({\n    block : 'page',\n    doctype : '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">',\n    title : 'Пример произвольного doctype',\n    content : 'Произвольный doctype'\n})\n"
  },
  {
    "path": "common.blocks/page/page.examples/20-doctype.ru.title.txt",
    "content": "Пример произвольного doctype"
  },
  {
    "path": "common.blocks/page/page.ru.md",
    "content": "# page\n\nБлок предоставляет шаблоны, создающие набор HTML-элементов верхнего уровня страницы: `<html>`, `<head>`, `<body>`.\n\n## Обзор\n\n### Специальные поля блока\n\n| Поле | Тип | Описание |\n| ---- | --- | -------- |\n| <a href=\"#declfields-doctype\">doctype</a> | `String` | Позволяет переопределить строку DTD текущего документа. |\n| <a href=\"#declfields-title\">title</a> | `String` | Позволяет указать содержимое `<title>`. |\n| <a href=\"#declfields-favicon\">favicon</a> | `String` | Позволяет указать URL значка страницы (фавиконки). |\n| <a href=\"#declfields-head\">head</a> | `BEMJSON` | Позволяет дополнить содержимое `<head>`. |\n| <a href=\"#declfields-styles\">styles</a> | `BEMJSON` | Позволяет подключать таблицы стилей CSS. |\n| <a href=\"#declfields-scripts\">scripts</a> | `BEMJSON` | Позволяет подключать скрипты в тело документа. |\n| <a href=\"#declfields-content\">content</a> | `BEMJSON` | Позволяет указать содержимое страницы. |\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| ------- | --------------------- | -------- |\n| <a href=\"#elems-css\">css</a> | `BEMJSON` | Служит для подключения CSS по ссылке или в виде строки. |\n| <a href=\"#elems-js\">js</a> | `BEMJSON` | Служит для подключения JS по ссылке или в виде строки. |\n| <a href=\"#elems-meta\">meta</a> | `BEMJSON` | Служит для создания HTML-элементов `<meta>`. |\n\n### Специальные поля элементов блока\n\n| Элемент | Поле | Тип | Описание |\n| ------- | ---- | --- | -------- |\n| <a href=\"#elems-css\">css</a> | <a href=\"#elems-css-declfields-url\">url</a> | `String`  | Позволяет задать URL для загрузки стилей. |\n|  | <a href=\"#elems-css-declfields-content\">content</a> | `String`  | Служит для задания стилей в виде строки |\n| <a href=\"#elems-js\">js</a> | <a href=\"#elems-js-declfields-url\">url</a> | `String`  | Позволяет задать URL для загрузки скрипта. |\n|  | <a href=\"#elems-js-declfields-content\">content</a> | `String`  | Служит для задания скриптов в виде строки |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `bh.js`\n* `bemhtml`\n\n## Описание\n\nБлок отвечает за создание HTML-элементов верхнего уровня, подключение к странице CSS, JS, элементов `<meta>` и указание заголовка. Для этого в BEMJSON-декларации блока и элементов блока зарезервированы специальные поля.\n\n<a name=\"declfields\"></a>\n\n### Специальные поля блока\n\n<a name=\"declfields-doctype\"></a>\n\n#### Поле  `doctype`\n\nТип: `String`.\n\nПозволяет явно указать строку с DTD (Document Type Definition) текущего документа. Если свойство не задано, по умолчанию будет использоваться `<!DOCTYPE html>`.\n\n<a name=\"declfields-title\"></a>\n\n#### Поле `title`\n\nТип: `String`.\n\nНазвание страницы. Становится HTML-элементом `<title>`.\n\n```js\n{\n    block : 'page',\n    title : 'Заголовок страницы',\n    content : 'Блок page'\n}\n```\n\n<a name=\"declfields-favicon\"></a>\n\n#### Поле `favicon`\n\nТип: `String`.\n\nПозволяет указать URL значка страницы (фавиконки):\n\n```js\n{\n    block : 'page',\n    title : 'Заголовок страницы',\n    favicon : 'favicon.ico',\n    content : 'Страница с пользовательской фавиконкой'\n}\n```\n\n<a name=\"declfields-head\"></a>\n\n#### Поле `head`\n\nТип: `BEMJSON`.\n\nПозволяет дополнить содержимое `HTML`-элемента `<head>`, определенное в шаблоне блока:\n\n```js\n{\n    block : 'page',\n    title : 'Заголовок страницы',\n    head : [\n        { elem : 'js', url : 'jquery-min.js' },\n        { elem : 'meta', attrs : { name : 'description', content : 'Yet another webdev blog' } }\n    ],\n    content : 'Страница с подключенным JS и meta-данными'\n}\n```\n\n<a name=\"declfields-styles\"></a>\n\n#### Поле `styles`\n\nТип: `BEMJSON`.\n\nПозволяет подключить `CSS`:\n\n```js\n{\n    block : 'page',\n    title : 'Заголовок страницы',\n    styles : { elem : 'css', url : '_index.css' },\n    content : 'Страница с подключенным CSS'\n}\n```\n\n<a name=\"declfields-scripts\"></a>\n\n#### Поле `scripts`\n\nТип: `BEMJSON`.\n\nПозволяет подключать JS в тело страницы в конец HTML-элемента `<body>`:\n\n```js\n{\n    block : 'page',\n    title : 'Заголовок страницы',\n    scripts : { elem : 'js', url : '_index.js' },\n    content : 'Страница со скриптом подключенным в body'\n}\n```\n\n<a name=\"declfields-content\"></a>\n\n#### Поле `content`\n\nТип: `BEMJSON`.\n\nПозволяет указать содержимое страницы.\n\n```js\n{\n    block : 'page',\n    title : 'Заголовок страницы',\n    content : {\n        block : 'link',\n        mods : { pseudo : 'yes', togcolor : 'yes', color : 'green' },\n        url : '#',\n        target : '_blank',\n        title : 'Кликни меня',\n        content : 'Псевдоссылка, меняющая цвет по клику'\n    }\n}\n```\n\n<a name=\"elems\"></a>\n\n### Элементы блока\n\n<a name=\"elems-css\"></a>\n\n#### Элемент `css`\n\nСлужит для подключения CSS по ссылке или в виде строки. В зависимости от того, указано ли в декларации элемента поле `url`, создается HTML-элемент с тегом:\n\n* `<link>` и свойством `stylesheet`, если `url` есть.\n* `<style>`, если поле `url` неуказано. В этом случае предполагается, что содержимое элемента передается с помощью свойства `content` BEMJSON-декларации элемента.\n\n<a name=\"elems-css-declfields-content\"></a>\n\n##### Специализированное поле `content`\n\nТип: `String`.\n\nСлужит для явной передачи содержимого HTML-элементу `<style>`:\n\n```js\n{\n    block : 'page',\n    title : 'Page title',\n    styles : {\n        elem : 'css',\n        content : '.page { color : #f00 }'\n    },\n    content : 'Страница с тэгом <style>'\n }\n```\n\n<a name=\"elems-css-declfields-url\"></a>\n\n##### Специализированное поле `url`\n\nТип: `String`.\n\nПозволяет задать URL для загрузки таблицы CSS. Значение поля `url` BEMJSON-декларации передается свойству `href` создаваемого HTML-элемента.\n\n<a name=\"elems-js\"></a>\n\n#### Элемент `js`\n\nСлужит для подключения JS по ссылке или в виде строки. Создает HTML-элемент `<script>`.\n\n<a name=\"elems-js-declfields-content\"></a>\n\n##### Специализированное поле `content`\n\nТип: `String`.\n\nСлужит для явной передачи содержимого HTML-элементу `<script>`:\n\n```js\n{\n    block : 'page',\n    title : 'Page title',\n    scripts : {\n        elem : 'js',\n        content : 'console.log(document.title)'\n    },\n    content : 'Страница с тэгом <script>'\n}\n```\n\n<a name=\"elems-js-declfields-url\"></a>\n\n##### Специализированное поле `url`\n\nТип: `String`.\n\nПозволяет задать URL для загрузки скрипта. Значение поля `url` BEMJSON-декларации передается свойству `src` создаваемого HTML-элемента.\n\n```js\n{\n    block : 'page',\n    title : 'Page title',\n    styles : { elem : 'css', url : '_index.css' },\n    content : 'Страница с тэгом style'\n}\n```\n\n<a name=\"elems-meta\"></a>\n\n#### Элемент `meta`\n\nСлужит для создания HTML-элементов `<meta>` и указания для них пользовательских метаданных. Метаданные передаются как ключи и значения хеша атрибутов – свойства `attrs` BEMJSON-декларации элемента:\n\n```js\n{\n    block : 'page',\n    title : 'Заголовок страницы',\n    head : [\n        { elem : 'css', url : 'example.css' },\n        { elem : 'meta', attrs : { name : 'keywords', content : 'js, css, html' } }\n    ],\n    content : 'Страница с подключенным CSS и meta-данными'\n}\n```\n\nПодробнее смотрите в документации к `<meta>` [на MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta).\n"
  },
  {
    "path": "common.blocks/page/page.ru.title.txt",
    "content": "HTML-обвязка (<html>, <head>, <body>)\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/00-empty.bemjson.js",
    "content": "({\n    block : 'page'\n})\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/00-empty.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n</head>\n<body class=\"page\"></body>\n</html>\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/10-simple.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'Простой пример html/head/body-обвязки страницы',\n    favicon : '//bem.info/favicon.ico',\n    head : [\n        { elem : 'css', url : '10-simple.css' },\n        { elem : 'js', url : '10-simple.js' },\n        { elem : 'meta', attrs : { name : 'keywords', content : 'BEM' } }\n    ],\n    content : { block : 'bla', content : 'bla' }\n})\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/10-simple.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n    <title>Простой пример html/head/body-обвязки страницы</title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <link rel=\"stylesheet\" href=\"10-simple.css\"/>\n    <script src=\"10-simple.js\"></script>\n    <meta name=\"keywords\" content=\"BEM\"/>\n    <link rel=\"shortcut icon\" href=\"//bem.info/favicon.ico\"/>\n</head>\n<body class=\"page\"><div class=\"bla\">bla</div></body>\n</html>\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/20-style.bemjson.js",
    "content": "({\n    block : 'page',\n    head : {\n        elem : 'css',\n        content : '.b-blah { color: #f00 }'\n    }\n})\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/20-style.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <style>.b-blah { color: #f00 }</style>\n</head>\n<body class=\"page\"></body></html>\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/25-styles.bemjson.js",
    "content": "({\n    block : 'page',\n    styles : [\n        { elem : 'css', content : '.b-blah { color: red }' },\n        { elem : 'css', content : '.b-blah2 { color: green }' }\n    ]\n})\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/25-styles.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <style>.b-blah { color: red }</style>\n    <style>.b-blah2 { color: green }</style>\n</head>\n<body class=\"page\"></body>\n</html>\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/30-scripts.bemjson.js",
    "content": "({\n    block : 'page',\n    scripts : [\n        { elem : 'js', url : 'https://yastatic.net/jquery/2.1.1/jquery.min.js' },\n        { elem : 'js', url : 'https://yastatic.net/jquery/easing/1.3/jquery.easing.min.js' }\n    ],\n    content : {\n        block : 'bla',\n        content : 'bla-bla'\n    }\n})\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/30-scripts.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n</head>\n<body class=\"page\">\n    <div class=\"bla\">bla-bla</div>\n    <script src=\"https://yastatic.net/jquery/2.1.1/jquery.min.js\"></script>\n    <script src=\"https://yastatic.net/jquery/easing/1.3/jquery.easing.min.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/40-nonce.bemjson.js",
    "content": "({\n    block : 'page',\n    scripts : [\n        { elem : 'js', url : 'https://yastatic.net/jquery/2.1.1/jquery.min.js' },\n        { elem : 'js', url : 'https://yastatic.net/jquery/easing/1.3/jquery.easing.min.js' },\n        { elem : 'js', content : 'var a = true;' }\n    ],\n    nonce : '123',\n    content : {\n        block : 'bla',\n        content : 'bla-bla'\n    }\n})\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/40-nonce.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n    <title></title>\n    <script nonce=\"123\">\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n</head>\n<body class=\"page\">\n    <div class=\"bla\">bla-bla</div>\n    <script src=\"https://yastatic.net/jquery/2.1.1/jquery.min.js\"></script>\n    <script src=\"https://yastatic.net/jquery/easing/1.3/jquery.easing.min.js\"></script>\n    <script nonce=\"123\">var a = true;</script>\n</body>\n</html>\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/60-x-ua-compatible.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'Remove x-ua-compatible',\n    uaCompatible : false\n})\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/60-x-ua-compatible.html",
    "content": "<!DOCTYPE HTML>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <title>Remove x-ua-compatible</title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n</head>\n<body class=\"page\"></body>\n</html>\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/70-lang.bemjson.js",
    "content": "({\n    block : 'page',\n    lang : 'en-us'\n})\n"
  },
  {
    "path": "common.blocks/page/page.tmpl-specs/70-lang.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\" lang=\"en-us\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n</head>\n<body class=\"page\"></body>\n</html>\n"
  },
  {
    "path": "common.blocks/strings/__escape/strings__escape.spec.js",
    "content": "modules.define('spec', ['strings__escape'], function(provide, escape) {\n\ndescribe('strings__escape', function() {\n    it('should properly escape XML', function() {\n        escape.xml('<x y=\"z\">a & b</x>')\n            .should.be.equal('&lt;x y=\"z\"&gt;a &amp; b&lt;/x&gt;');\n    });\n\n    it('should properly escape HTML', function() {\n        escape.html('<b class=\"bold\">Bold & bold</b>')\n            .should.be.equal('&lt;b class=\"bold\"&gt;Bold &amp; bold&lt;/b&gt;');\n    });\n\n    it('should properly escape attributes', function() {\n        escape.attr('some <> attr with different \"quo\" & \\'tes\\' inside')\n            .should.be.equal('some &lt;&gt; attr with different &quot;quo&quot; &amp; &apos;tes&apos; inside');\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/strings/__escape/strings__escape.vanilla.js",
    "content": "/**\n * @module strings__escape\n * @description A set of string escaping functions\n */\n\nconst symbols = {\n    '\"' : '&quot;',\n    '\\'' : '&apos;',\n    '&' : '&amp;',\n    '<' : '&lt;',\n    '>' : '&gt;'\n  },\n  mapSymbol = s => symbols[s] || s,\n  buildEscape = regexp => {\n    regexp = new RegExp(regexp, 'g')\n    return str => ('' + str).replace(regexp, mapSymbol)\n  }\n\nexport default {\n  /**\n   * Escape string to use in XML\n   * @type Function\n   * @param {String} str\n   * @returns {String}\n   */\n  xml : buildEscape('[&<>]'),\n\n  /**\n   * Escape string to use in HTML\n   * @type Function\n   * @param {String} str\n   * @returns {String}\n   */\n  html : buildEscape('[&<>]'),\n\n  /**\n   * Escape string to use in attributes\n   * @type Function\n   * @param {String} str\n   * @returns {String}\n   */\n  attr : buildEscape('[\"\\'&<>]')\n}\n"
  },
  {
    "path": "common.blocks/strings/strings.en.md",
    "content": "# strings\n\nThis block provides helpers for manipulating string data.\n\n## Overview\n\n### Elements of the block\n\n| Element | Usage | Description |\n| --------| --------------------- | -------- |\n| <a href=\"#elems-escape\">escape</a> | `JS` | A set of methods for escaping XML and HTML control characters. |\n\n### Properties and methods of the block elements\n\n| Element| Name | Type or return value | Description |\n| -------| --- | ----------------------------- | -------- |\n| <a href=\"#elems-escape\">escape</a> | <a href=\"#elems-escape-fields-xml\">xml</a>(`str {String}`) | `String` | Use for escaping XML control characters. |\n|  | <a href=\"#elems-escape-fields-html\">html</a>(`str {String}`) | `String` | Use for escaping HTML control characters. |\n|  | <a href=\"#elems-escape-fields-attr\">attr</a>(`str {String}`) | `String` | Use for escaping control characters in HTML and XML attributes. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `vanilla.js`\n\n## Description\n\n<a name=\"elems\"></a>\n\n### Elements of the block\n\n<a name=\"elems-escape\"></a>\n\n#### `escape` element\n\nThis element provides an object with a set of methods for escaping XML and HTML control characters.\n\n<a name=\"elems-name-fields\"></a>\n\n### Properties and methods of the object\n\n<a name=\"elems-escape-fields-xml\"></a>\n\n#### `xml` method\n\nUse for escaping XML control characters. Processes the symbols `&`, `<`, `>`.\n\n**Accepted arguments:**\n\n* `str {String}` – String to process. Required argument.\n\n**Return value:** `String`. The string with escaped control characters.\n\n<a name=\"elems-escape-fields-html\"></a>\n\n#### `html` method\n\nUse for escaping HTML control characters. It is a synonym of the `xml` method.\n\n<a name=\"elems-escape-fields-attr\"></a>\n\n#### `attr` method\n\nUse for escaping control characters in HTML and XML attributes. Processes the control characters `\"`, `\\`, `'`, `&`, `<`, `>`.\n\n**Accepted arguments:**\n\n* `str {String}` – String to process. Required argument.\n\n**Return value:** `String`. The string with escaped control characters.\n\nFor example, in the [`common.blocks/select`](https://github.com/bem/bem-components/blob/v2/common.blocks/select/select.js#L237) block in the `bem-components` library, `strings__escape` is used for escaping control characters in the `value` property of an HTML element:\n\n```js\n_createControlHTML : function(name, val) {\n    // Using string concatenation to not depend on template engines\n    return '<input ' +\n        'type=\"hidden\" ' +\n        'name=\"' + name + '\" ' +\n        'class=\"' + this.buildClass('control') + '\" ' +\n        'value=\"' + escape.attr(typeof val === 'object'? JSON.stringify(val) : val) + '\"/>';\n}\n```\n"
  },
  {
    "path": "common.blocks/strings/strings.ru.md",
    "content": "# strings\n\nБлок предоставляет хелперы для манипуляций с данными строчного типа.\n\n## Обзор\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| --------| --------------------- | -------- |\n| <a href=\"#elems-escape\">escape</a> | `JS` | Набор методов для экранирования (эскейпинга) управляющих символов XML и HTML. |\n\n### Свойства и методы элементов блока\n\n| Элемент| Имя | Тип или возвращаемое значение | Описание |\n| -------| --- | ----------------------------- | -------- |\n| <a href=\"#elems-escape\">escape</a> | <a href=\"#elems-escape-fields-xml\">xml</a>(`str {String}`) | `String` | Служит для экранирования управляющих символов XML. |\n|  | <a href=\"#elems-escape-fields-html\">html</a>(`str {String}`) | `String` | Служит для экранирования управляющих символов HTML. |\n|  | <a href=\"#elems-escape-fields-attr\">attr</a>(`str {String}`) | `String` | Служит для экранирования управляющих символов в HTML и XML атрибутах. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `vanilla.js`\n\n## Описание\n\n<a name=\"elems\"></a>\n\n### Элементы блока\n\n<a name=\"elems-escape\"></a>\n\n#### Элемент `escape`\n\nЭлемент предоставляет объект, содержащий набор методов для экранирования (эскейпинга) управляющих символов XML и HTML.\n\n<a name=\"elems-name-fields\"></a>\n\n### Свойства и методы объекта\n\n<a name=\"elems-escape-fields-xml\"></a>\n\n#### Метод `xml`\n\nСлужит для экранирования управляющих символов XML. Обрабатываются символы `&`, `<`, `>`.\n\n**Принимаемые аргументы:**\n\n* `str {String}` – строка для обработки. Обязательный аргумент.\n\n**Возвращаемое значение:** `String`. Строка с экранированными управляющими символами.\n\n<a name=\"elems-escape-fields-html\"></a>\n\n#### Метод `html`\n\nСлужит для экранирования управляющих символов HTML. Является синонимом метода `xml`.\n\n<a name=\"elems-escape-fields-attr\"></a>\n\n#### Метод `attr`\n\nСлужит для экранирования управляющих символов в HTML и XML атрибутах. Обрабатываются управляющие символы `\"`, `\\`, `'`, `&`, `<`, `>`.\n\n**Принимаемые аргументы:**\n\n* `str {String}` – строка для обработки. Обязательный аргумент.\n\n**Возвращаемое значение:** `String`. Строка с экранированными управляющими символами.\n\nНапример, в блоке [`common.blocks/select`](https://github.com/bem/bem-components/blob/v2/common.blocks/select/select.js#L237) библиотеки `bem-components`, `strings__escape` используется для экранирования управляющих символов в свойстве `value` HTML-элемента:\n\n```js\n_createControlHTML : function(name, val) {\n    // Using string concatenation to not depend on template engines\n    return '<input ' +\n        'type=\"hidden\" ' +\n        'name=\"' + name + '\" ' +\n        'class=\"' + this.buildClass('control') + '\" ' +\n        'value=\"' + escape.attr(typeof val === 'object'? JSON.stringify(val) : val) + '\"/>';\n}\n```\n"
  },
  {
    "path": "common.blocks/tick/_start/tick_start_auto.vanilla.js",
    "content": "/**\n * Automatically starts tick module\n */\n\nimport tick from 'bem:tick';\n\ntick.start();\n"
  },
  {
    "path": "common.blocks/tick/tick.deps.js",
    "content": "({\n    shouldDeps : ['events', 'inherit']\n})\n"
  },
  {
    "path": "common.blocks/tick/tick.en.md",
    "content": "# tick\n\nThis block provides an object for working with a regularly generated `tick` event (to implement the polling pattern).\n\n## Overview\n\n### Object events\n\n| Name | Description |\n| -------- | -------- |\n| <a href=\"#events-tick\">tick</a> | A regularly generated event. |\n\n### Properties and methods of the object\n\n| Name | Return type | Description |\n| -------- | --- | -------- |\n| <a href=\"#fields-start\">start</a>() | - | Starts generating `tick` events if the process hasn't started yet. |\n| <a href=\"#fields-stop\">stop</a>() | - | Stops generating `tick` events if the process hasn't stopped yet. |\n\n### Block modifiers\n\n| Modifier | Acceptable values | Usage | Description |\n| ----------- | ------------------- | --------------------- | -------- |\n| <a href=\"#modifiers-start\">start</a> | `'auto'` | `JS` | Automatically starts generating events |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `vanilla.js`\n\n## Description\n\n<a name=\"events\"></a>\n\n### Object events\n\n<a name=\"events-tick\"></a>\n\n#### `tick` event\n\nSubscribe to the event to use it to implement the polling pattern.\n\nAn event is generated every 50 milliseconds.\n\n<a name=\"fields\"></a>\n\n### Properties and methods of the object\n\nThe block is a descendant of the `Emitter` class in the `events` block, which allows it to call these classes.\n\n```js\nmodules.require('tick', function(tick) {\n\nvar update = function() { /* ... */ };\n\ntick\n    .on('tick', update) // subscribing to the tick event\n    .start(); // starting generation of tick events\n});\n```\n\n<a name=\"fields-start\"></a>\n\n#### `start` method\n\nStarts generating [tick](#fields-tick) events if the process hasn't started yet. A `tick` is generated with an interval of 50 milliseconds after invoking the method.\n\nDoesn't accept arguments.\n\nNo return value.\n\n<a name=\"fields-stop\"></a>\n\n#### `stop` method\n\nStops generating [tick](#fields-tick) events.\n\nDoesn't accept arguments.\n\nNo return value.\n\n<a name=\"modifiers\"></a>\n\n### Block modifiers\n\n<a name=\"modifiers-start\"></a>\n\n#### `start` modifier\n\nAcceptable values: `'auto'`.\n\nUsage: `JS`.\n\nUse the block with the `start` modifier set to `auto` in order to automatically start generating [tick](#fields-tick) events. The event starts being generated at the time of block initialization.\n"
  },
  {
    "path": "common.blocks/tick/tick.ru.md",
    "content": "# tick\n\nБлок предоставляет объект для работы с регулярно генерируемым событием `tick` (для реализации паттерна polling).\n\n## Обзор\n\n### События объекта\n\n| Имя | Описание |\n| -------- | -------- |\n| <a href=\"#events-tick\">tick</a> | Регулярно генерируемое событие. |\n\n### Свойства и методы объекта\n\n| Имя | Тп возвращаемого значения | Описание |\n| -------- | --- | -------- |\n| <a href=\"#fields-start\">start</a>() | - | Запускает генерацию события `tick`, если она еще не запущена. |\n| <a href=\"#fields-stop\">stop</a>() | - | Останавливает генерацию события `tick`, если она еще не остановлена. |\n\n### Модификаторы блока\n\n| Модификатор | Допустимые значения | Способы использования | Описание |\n| ----------- | ------------------- | --------------------- | -------- |\n| <a href=\"#modifiers-start\">start</a> | `'auto'` | `JS` | Автоматический запуск генерации события |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `vanilla.js`\n\n## Описание\n\n<a name=\"events\"></a>\n\n### События объекта\n\n<a name=\"events-tick\"></a>\n\n#### Событие `tick`\n\nПодписавшись на событие, можно использовать его для реализации паттерна polling.\n\nСобытие генерируется с интервалом в 50 миллисекунд.\n\n<a name=\"fields\"></a>\n\n### Свойства и методы объекта\n\nБлок наследуется от клсаса `Emitter` блока `events`, что позволяет вызывать методы класса.\n\n```js\nmodules.require('tick', function(tick) {\n\nvar update = function() { /* ... */ };\n\ntick\n    .on('tick', update) // подписываемся на событие tick\n    .start(); // запускаем генерацию события tick\n});\n```\n\n<a name=\"fields-start\"></a>\n\n#### Метод `start`\n\nЗапускает генерацию события [tick](#fields-tick), если они еще не запущены. Событие `tick` генерируется через интервал в 50 миллисекунд после вызова метода.\n\nНе принимает аргументов.\n\nНе имеет возвращаемого значения.\n\n<a name=\"fields-stop\"></a>\n\n#### Метод `stop`\n\nОстанавливает генерацию события [tick](#fields-tick).\n\nНе принимает аргументов.\n\nНе имеет возвращаемого значения.\n\n<a name=\"modifiers\"></a>\n\n### Модификаторы блока\n\n<a name=\"modifiers-start\"></a>\n\n#### Модификатор `start`\n\nДопустимые значения: `'auto'`.\n\nСпособ использования: `JS`.\n\nБлок с модификатором `start` в значении `auto` используется для автоматического запуска генерации события [tick](#fields-tick). Событие начинает генерироваться в момент инициализации блока.\n"
  },
  {
    "path": "common.blocks/tick/tick.spec.js",
    "content": "modules.define('spec', [\n    'tick',\n    'sinon'\n], function(provide,\n    tick,\n    sinon\n) {\n\ndescribe('tick', function() {\n    describe('start/stop', function() {\n        var TICK_INTERVAL = 50,\n            clock;\n\n        beforeEach(function() {\n            clock = sinon.useFakeTimers();\n        });\n\n        afterEach(function() {\n            clock.restore();\n        });\n\n        it('should emit tick event only if started', function(done) {\n            var spy = sinon.spy();\n\n            tick\n                .on('tick', spy)\n                .start();\n\n            setTimeout(function() {\n                tick.stop();\n                setTimeout(function() {\n                    spy.should.have.been.calledOnce;\n                    done();\n                }, TICK_INTERVAL);\n\n                clock.tick(TICK_INTERVAL);\n            }, TICK_INTERVAL);\n\n            clock.tick(TICK_INTERVAL);\n        });\n\n        it('should continue ticking after exception in callback', function(done) {\n            var spy = sinon.spy();\n\n            tick\n                .on('tick', function() {\n                    if(!spy.called) {\n                        spy();\n                        throw new Error('test error');\n                    }\n                    spy();\n                    tick.stop();\n                    done();\n                })\n                .start();\n\n            setTimeout(function() {\n                clock.tick(TICK_INTERVAL);\n            }, TICK_INTERVAL);\n\n            clock.tick(TICK_INTERVAL);\n        });\n\n        it('should not emit tick event after .stop() in callback', function(done) {\n            var spy = sinon.spy();\n\n            tick\n                .on('tick', function() {\n                    spy();\n                    tick.stop();\n                })\n                .start();\n\n            setTimeout(function() {\n                setTimeout(function() {\n                    spy.should.have.been.calledOnce;\n                    tick.stop();\n\n                    done();\n                }, TICK_INTERVAL);\n\n                clock.tick(TICK_INTERVAL);\n            }, TICK_INTERVAL);\n\n            clock.tick(TICK_INTERVAL);\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/tick/tick.vanilla.js",
    "content": "/**\n * @module tick\n * @description Helpers for polling anything\n */\n\nimport inherit from 'bem:inherit'\nimport events from 'bem:events'\n\nconst TICK_INTERVAL = 50,\n\n    /**\n     * @class Tick\n     * @augments events:Emitter\n     */\n    Tick = inherit(events.Emitter, /** @lends Tick.prototype */{\n        /**\n         * @constructor\n         */\n        __constructor : function() {\n            this._timer = null\n            this._isStarted = false\n        },\n\n        /**\n         * Starts polling\n         */\n        start : function() {\n            if(!this._isStarted) {\n                this._isStarted = true\n                this._scheduleTick()\n            }\n        },\n\n        /**\n         * Stops polling\n         */\n        stop : function() {\n            if(this._isStarted) {\n                this._isStarted = false\n                globalThis.clearTimeout(this._timer)\n            }\n        },\n\n        _scheduleTick : function() {\n            this._timer = globalThis.setTimeout(\n                () => this._onTick(),\n                TICK_INTERVAL)\n        },\n\n        _onTick : function() {\n            try {\n                this.emit('tick')\n            } finally {\n                this._isStarted && this._scheduleTick()\n            }\n        }\n    })\n\n/**\n * @type Tick\n */\nexport default new Tick()\n"
  },
  {
    "path": "common.blocks/ua/__svg/ua__svg.bemhtml.js",
    "content": "block('ua').content()(function() {\n    return [\n        applyNext(),\n        {\n            html : [\n                '(function(d,n){',\n                    'd.documentElement.className+=',\n                    '\" ua_svg_\"+(d[n]&&d[n](\"http://www.w3.org/2000/svg\",\"svg\").createSVGRect?\"yes\":\"no\");',\n                '})(document,\"createElementNS\");'\n            ].join('')\n        }\n    ];\n});\n"
  },
  {
    "path": "common.blocks/ua/__svg/ua__svg.bh.js",
    "content": "module.exports = function(bh) {\n    bh.match('ua', function(ctx, json) {\n        ctx.applyBase();\n        ctx.content([\n            json.content,\n            {\n                tag : false,\n                html : [\n                    '(function(d,n){',\n                        'd.documentElement.className+=',\n                        '\" ua_svg_\"+(d[n]&&d[n](\"http://www.w3.org/2000/svg\",\"svg\").createSVGRect?\"yes\":\"no\");',\n                    '})(document,\"createElementNS\");'\n                ].join('')\n            }\n        ], true);\n    });\n};\n"
  },
  {
    "path": "common.blocks/ua/__svg/ua__svg.deps.js",
    "content": "({\n    mustDeps : 'ua'\n})\n"
  },
  {
    "path": "common.blocks/ua/__svg/ua__svg.en.title.txt",
    "content": "SVG support detection\n"
  },
  {
    "path": "common.blocks/ua/__svg/ua__svg.ru.title.txt",
    "content": "Определение поддержки SVG\n"
  },
  {
    "path": "common.blocks/ua/__svg/ua__svg.tmpl-specs/00-simple.bemjson.js",
    "content": "({\n    block : 'ua'\n})\n"
  },
  {
    "path": "common.blocks/ua/__svg/ua__svg.tmpl-specs/00-simple.html",
    "content": "<script>(function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");(function(d,n){d.documentElement.className+=\" ua_svg_\"+(d[n]&&d[n](\"http://www.w3.org/2000/svg\",\"svg\").createSVGRect?\"yes\":\"no\");})(document,\"createElementNS\");</script>\n"
  },
  {
    "path": "common.blocks/ua/ua.bemhtml.js",
    "content": "block('ua')(\n    tag()('script'),\n    bem()(false),\n    content()([\n        '(function(e,c){',\n            'e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");',\n        '})(document.documentElement,\"className\");'\n    ])\n);\n"
  },
  {
    "path": "common.blocks/ua/ua.bh.js",
    "content": "module.exports = function(bh) {\n\n    bh.match('ua', function(ctx) {\n        ctx\n            .bem(false)\n            .tag('script')\n            .content([\n                '(function(e,c){',\n                    'e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");',\n                '})(document.documentElement,\"className\");'\n            ], true);\n    });\n\n};\n"
  },
  {
    "path": "common.blocks/ua/ua.en.md",
    "content": "# ua\n\nUse this block to collect data about the user's browser.\n\n## Overview\n\n### Elements of the block\n\n| Element | Usage | Description |\n| ------- | --------------------- | -------- |\n| <a href=\"#elems-svg\">svg</a> | `deps` | Checks whether the browser supports SVG format. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `bh.js`\n* `bemhtml`\n\n## Description\n\nThe block enables an inline script that adds `CSS` classes to the `<html>` tag to specify whether JavaScript is enabled – `ua_js_no` or `ua_js_yes`.\n\nIt doesn't have a visual representation on the page.\n\nUsed inside the [page](https://github.com/bem/bem-core/blob/v2/common.blocks/page/page.en.md) block. You normally don't need to connect it to the page yourself.\n\n<a name=\"elems\"></a>\n\n### Elements of the block\n\n<a name=\"elems-svg\"></a>\n\n#### `svg` element\n\nThis element enables an inline script that adds `CSS` classes to the `<html>` tag to specify whether SVG is supported – `ua_svg_no` or `ua_svg_yes`.\n\nIt doesn't have a visual representation on the page.\n\nTo use it, add the element to the `deps.js` dependencies file for the block that needs information about SVG support:\n\n```js\n({ shouldDeps : { block : 'ua', elem : 'svg' } })\n```\n"
  },
  {
    "path": "common.blocks/ua/ua.en.title.txt",
    "content": "Block for gathering and providing UserAgent information\n"
  },
  {
    "path": "common.blocks/ua/ua.ru.md",
    "content": "# ua\n\nБлок служит для сбора данных о браузере пользователя.\n\n## Обзор\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| ------- | --------------------- | -------- |\n| <a href=\"#elems-svg\">svg</a> | `deps` | Проверяет, поддерживает ли браузер формат SVG. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `bh.js`\n* `bemhtml`\n\n## Описание\n\nБлок подключает инлайновый скрипт, добавляющий тегу `<html>` `CSS`-классы, указывающие, включен ли JavaScript – `ua_js_no`/`ua_js_yes`.\n\nНе имеет визуального представления на странице.\n\nИспользуется внутри блока [page](https://github.com/bem/bem-core/blob/v2/common.blocks/page/page.ru.md) и самостоятельно подключать его к странице обычно не требуется.\n\n<a name=\"elems\"></a>\n\n### Элементы блока\n\n<a name=\"elems-svg\"></a>\n\n#### Элемент `svg`\n\nЭлемент подключает инлайновый скрипт, добавляющий тегу `<html>` `CSS`-классы, указывающие, поддерживается ли SVG – `ua_svg_no`/`ua_svg_yes`.\n\nНе имеет визуального представления на странице.\n\nДля использования включите элемент в файл зависимостей `deps.js` блока, которому требуются данные о поддержке SVG:\n\n```js\n({ shouldDeps : { block : 'ua', elem : 'svg' } })\n```\n"
  },
  {
    "path": "common.blocks/ua/ua.ru.title.txt",
    "content": "Сбор и провайдинг информации о UserAgent\n"
  },
  {
    "path": "common.blocks/ua/ua.tmpl-specs/00-simple.bemjson.js",
    "content": "({\n    block : 'ua'\n})\n"
  },
  {
    "path": "common.blocks/ua/ua.tmpl-specs/00-simple.html",
    "content": "<script>(function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");</script>\n"
  },
  {
    "path": "common.blocks/uri/__querystring/uri__querystring.deps.js",
    "content": "({\n    shouldDeps : 'uri'\n})\n"
  },
  {
    "path": "common.blocks/uri/__querystring/uri__querystring.spec.js",
    "content": "modules.define('spec', ['uri__querystring'], function(provide, qs) {\n\ndescribe('querystring', function() {\n    describe('parse()', function() {\n        it('should support the basics', function() {\n            qs.parse('0=foo').should.eql({ '0' : 'foo' });\n            qs.parse('foo').should.eql({ foo : '' });\n            qs.parse('foo=bar').should.eql({ foo : 'bar' });\n            qs.parse(' foo = bar = baz ').should.eql({ ' foo ' : ' bar = baz ' });\n            qs.parse('foo=bar=baz').should.eql({ foo : 'bar=baz' });\n            qs.parse('foo=bar&bar=baz').should.eql({ foo : 'bar', bar : 'baz' });\n            qs.parse('foo=bar&baz').should.eql({ foo : 'bar', baz : '' });\n            qs.parse('cht=p3&chd=t :60,40&chs=250x100&chl=Hello|World').should.eql({\n                cht : 'p3',\n                chd : 't :60,40',\n                chs : '250x100',\n                chl : 'Hello|World'\n            });\n            qs.parse('=').should.eql({ '' : '' });\n            qs.parse('==').should.eql({ '' : '=' });\n            qs.parse('_r=1&').should.eql({ _r : '1' });\n        });\n\n        it('should support encoded = signs', function() {\n            qs.parse('he%3Dllo=th%3Dere').should.eql({ 'he=llo' : 'th=ere' });\n        });\n\n        it('should expand to an array when dupliate keys are present', function() {\n            qs.parse('items=bar&items=baz&items=raz').should.eql({ items : ['bar', 'baz', 'raz'] });\n            qs.parse('=&=').should.eql({ '' : ['', ''] });\n        });\n\n        it('should support empty values', function(){\n            qs.parse('').should.eql({});\n            qs.parse(undefined).should.eql({});\n            qs.parse(null).should.eql({});\n        });\n\n        it('should support names of built-in Object properties', function() {\n            /* jshint -W001 */\n            qs.parse('hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz&constructor=1')\n                .should.eql({\n                    hasOwnProperty : 'x',\n                    toString : 'foo',\n                    valueOf : 'bar',\n                    __defineGetter__ : 'baz',\n                    constructor : '1'\n                });\n        });\n    });\n\n    describe('stringify()', function() {\n        function test(cases) {\n            cases.forEach(function(testCase) {\n                qs.stringify(testCase.obj).should.eq(testCase.str);\n            });\n        }\n\n        it('should support the basics', function() {\n            /* jshint quotmark: false */\n            test([\n                { str : 'foo=bar', obj : { 'foo' : 'bar' } },\n                { str : 'foo=%22bar%22', obj : { 'foo' : '\\\"bar\\\"' } },\n                { str : 'foo=', obj : { 'foo' : '' } },\n                { str : 'foo=1&bar=2', obj : { 'foo' : '1', 'bar' : '2' } },\n                {\n                    str : 'my%20weird%20field=q1!2%22\\'w%245%267%2Fz8)%3F',\n                    obj : { 'my weird field' : 'q1!2\"\\'w$5&7/z8)?' }\n                },\n                { str : 'foo%3Dbaz=bar', obj : { 'foo=baz' : 'bar' } },\n                { str : 'foo=bar&bar=baz', obj : { foo : 'bar', bar : 'baz' } },\n                { str : 'foo=bar&baz=&raz=', obj : { foo : 'bar', baz : null, raz : undefined } },\n                { str : 'foo=bar&=', obj : { foo : 'bar', '' : '' } }\n            ]);\n        });\n\n        it('should support escapes', function() {\n            test([\n                { str : 'foo=foo%20bar', obj : { foo : 'foo bar' } },\n                {\n                    str : 'cht=p3&chd=t%3A60%2C40&chs=250x100&chl=Hello%7CWorld',\n                    obj : {\n                        cht : 'p3',\n                        chd : 't:60,40',\n                        chs : '250x100',\n                        chl : 'Hello|World'\n                    }\n                }\n            ]);\n        });\n\n        it('should support arrays', function() {\n            test([\n                { str : 'limit=1&limit=2&limit=a', obj : { limit : [1, 2, 'a'] } }\n            ]);\n        });\n\n        it('should support others types', function() {\n            var date = new Date(0);\n\n            test([\n                { str : 'at=' + encodeURIComponent(date), obj : { at : date } }\n            ]);\n        });\n\n        it('should support names of built-in Object properties', function() {\n            /* jshint -W001 */\n            test([\n                {\n                    str : 'hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz&constructor=1',\n                    obj : {\n                        hasOwnProperty : 'x',\n                        toString : 'foo',\n                        valueOf : 'bar',\n                        __defineGetter__ : 'baz',\n                        constructor : '1'\n                    }\n                }\n            ]);\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/uri/__querystring/uri__querystring.vanilla.js",
    "content": "/**\n * @module uri__querystring\n * @description A set of helpers to work with query strings\n */\n\nimport uri from 'bem:uri'\n\nfunction addParam(res, name, val) {\n    res.push(encodeURIComponent(name) + '=' + (val == null? '' : encodeURIComponent(val)))\n}\n\nexport default {\n    /**\n     * Parse a query string to an object\n     * @param {String} str\n     * @returns {Object}\n     */\n    parse(str) {\n        if(!str) {\n            return {}\n        }\n\n        return str.split('&').reduce(\n            (res, pair) => {\n                if(!pair) {\n                    return res\n                }\n\n                const eq = pair.indexOf('=')\n                let name, val\n\n                if(eq >= 0) {\n                    name = pair.substr(0, eq)\n                    val = pair.substr(eq + 1)\n                } else {\n                    name = pair\n                    val = ''\n                }\n\n                name = uri.decodeURIComponent(name)\n                val = uri.decodeURIComponent(val)\n\n                Object.hasOwn(res, name)?\n                    Array.isArray(res[name])?\n                        res[name].push(val) :\n                        res[name] = [res[name], val] :\n                    res[name] = val\n\n                return res\n            },\n            {})\n    },\n\n    /**\n     * Serialize an object to a query string\n     * @param {Object} obj\n     * @returns {String}\n     */\n    stringify(obj) {\n        return Object.keys(obj)\n            .reduce(\n                (res, name) => {\n                    const val = obj[name]\n                    Array.isArray(val)?\n                        val.forEach(function(val) {\n                            addParam(res, name, val)\n                        }) :\n                        addParam(res, name, val)\n                    return res\n                },\n                [])\n            .join('&')\n    }\n}\n"
  },
  {
    "path": "common.blocks/uri/uri.en.md",
    "content": "# uri\n\nThis block provides an object with a set of methods for decoding a URI-encoded string.\n\n## Overview\n\n### Properties and methods of the object\n\n| Name | Return type | Description |\n| --- | -------------------------- | -------- |\n| <a href=\"#fields-decodeURI\">decodeURI</a>(`str {String}`) | `String` | Decodes a URI. |\n| <a href=\"#fields-decodeURIComponent\">decodeURIComponent</a>(`str {String}`) | `String` | Decodes a URI component. |\n\n### Elements of the block\n\n| Element | Usage | Description |\n| --------| --------------------- | -------- |\n| <a href=\"#elems-querystring\">querystring</a> | `JS` | This element provides an object with a set of methods for working with a URI query string. It decodes the string from URI format. |\n\n#### Properties and methods of the object\n\n| Element | Name | Returned value | Description |\n| ------- | --- | --------------------- | -------- |\n| <a href=\"#elems-querystring\">querystring</a> | <a href=\"#elems-querystring-fields-parse\">parse</a>(`str {String}`) | `Object` | Creates an object using the query parameters from the address bar. |\n| | <a href=\"#elems-querystring-fields-stringify\">stringify</a>(`obj {Object}`) | `String` | Creates a query string based on the object properties. |\n\n### Public block technologies\n\nThe block is implemented in:\n\n* `vanilla.js`\n\n<a name=\"fields\"></a>\n\n### Properties and methods of the object\n\nBoth of these methods function as wrappers for the standard JavaScript methods `decodeURI` and `decodeURIComponent`.\n\nAs they execute, the methods check whether the passed string is in UTF-8 format. If not, they generate an error.\n\n<a name=\"fields-decodeURI\"></a>\n\n#### `decodeURI` method\n\nDecodes a URI. This method is identical to the standard JavaScript method [decodeURI](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI), but it supports Cyrillic encoding `CP-1251`.\n\n**Accepted arguments:**\n\n* `str {String}` – A string with escape sequences. Required argument.\n\n**Return value:** `String`. If escape sequences are not found in the string, the method returns the string without any changes.\n\nExample:\n\n```js\nmodules.require('uri', function(uri){\n    uri.decodeURI(\"https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\");\n    // \"https://developer.mozilla.org/ru/docs/JavaScript_Shells\"\n})\n```\n\n<a name=\"fields-decodeURIComponent\"></a>\n\n#### `decodeURIComponent` method\n\nDecodes a URI component. This method is identical to the standard JavaScript method [decodeURIComponent](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent), but it supports Cyrillic encoding `CP-1251`.\n\n**Accepted arguments:**\n\n* `str {String}` – A string with escape sequences. Required argument.\n\n**Return value:** `String`. If escape sequences are not found in the string, the method returns the string without any changes.\n\nExample:\n\n```js\nmodules.require('uri', function(uri){\n    uri.decodeURIComponent(\"JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\");\n    // \"JavaScript_Shells\"\n})\n```\n\n<a name=\"elems\"></a>\n\n### Elements of the block\n\n<a name=\"elems-querystring\"></a>\n\n#### `querystring` element\n\nThis element provides an object with a set of methods for working with a URI query string.\n\n<a name=\"elems-name-fields\"></a>\n\n### Properties and methods of the object\n\n<a name=\"elems-querystring-fields-parse\"></a>\n\n#### `parse` method\n\nCreates an object using the parameters from a URI query string.\n\n**Accepted arguments:**\n\n* `str {String}` – A string with parameters as key-value pairs. The `=` symbol separates a key from its value. Pairs are separated by the `&` symbol. During parsing, keys and values are decoded from URI format. Required argument.\n\n**Return value:** `Object`. The object created from the parameters in the address bar.\n\n<a name=\"elems-querystring-fields-stringify\"></a>\n\n#### `stringify` method\n\nCreates a URI query string from an object.\n\n**Accepted arguments:**\n\n* `obj {Object}` – The object to create the string from. Required argument.\n\n**Return value:** `String`. Property names are separated from values by the `=` symbol, and the `&` symbol separates pairs in the string.\n"
  },
  {
    "path": "common.blocks/uri/uri.ru.md",
    "content": "# uri\n\nБлок предоставляет объект, содержащий набор методов для декодирования строки из формата URI.\n\n## Обзор\n\n### Свойства и методы объекта\n\n| Имя | Тип возвращаемого значения | Описание |\n| --- | -------------------------- | -------- |\n| <a href=\"#fields-decodeURI\">decodeURI</a>(`str {String}`) | `String` | Служит для декодирования URI. |\n| <a href=\"#fields-decodeURIComponent\">decodeURIComponent</a>(`str {String}`) | `String` | Служит для декодирования URI компонента. |\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| --------| --------------------- | -------- |\n| <a href=\"#elems-querystring\">querystring</a> | `JS` | Элемент предоставляет объект, содержащий набор методов для работы со строкой запроса формата URI. предназначен для декодирования строки из формата URI. |\n\n#### Свойства и методы объекта\n\n| Элемент | Имя | Возвращаемое значение | Описание |\n| ------- | --- | --------------------- | -------- |\n| <a href=\"#elems-querystring\">querystring</a> | <a href=\"#elems-querystring-fields-parse\">parse</a>(`str {String}`) | `Object` | Формирует объект на основании параметров запроса адресной строки. |\n| | <a href=\"#elems-querystring-fields-stringify\">stringify</a>(`obj {Object}`) | `String` | Формирует строку запроса на основании свойств объекта. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `vanilla.js`\n\n<a name=\"fields\"></a>\n\n### Свойства и методы объекта\n\nОба метода являются оберткой над соответствующими стандартными методами JavaScript `decodeURI` и `decodeURIComponent`.\n\nВ ходе работы методы проверяют соответствие формата переданной строки UTF-8. При несоответствии генерируется ошибка.\n\n<a name=\"fields-decodeURI\"></a>\n\n#### Метод `decodeURI`\n\nСлужит для декодирования URI. Метод идентичен стандартному методу JavaScript [decodeURI](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI), но поддерживает кириллическую кодировку `CP-1251`.\n\n**Принимаемые аргументы:**\n\n* `str {String}` – строка с последовательностями экранирования. Обязательный аргумент.\n\n**Возвращаемое значение:** `String`. В случае если последовательности экранирования в строке не найдены метод возвращают ее без изменений.\n\nПример:\n\n```js\nmodules.require('uri', function(uri){\n    uri.decodeURI(\"https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\");\n    // \"https://developer.mozilla.org/ru/docs/JavaScript_шеллы\"\n})\n```\n\n<a name=\"fields-decodeURIComponent\"></a>\n\n#### Метод `decodeURIComponent`\n\nСлужит для декодирование компонента URI. Метод идентичен стандартному методу JavaScript [decodeURIComponent](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent), но поддерживает кириллическую кодировку `CP-1251`.\n\n**Принимаемые аргументы:**\n\n* `str {String}` – строка с последовательностями экранирования. Обязательный аргумент.\n\n**Возвращаемое значение:** `String`. В случае если последовательности экранирования в строке не найдены метод возвращают ее без изменений.\n\nПример:\n\n```js\nmodules.require('uri', function(uri){\n    uri.decodeURIComponent(\"JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\");\n    // \"JavaScript_шеллы\"\n})\n```\n\n<a name=\"elems\"></a>\n\n### Элементы блока\n\n<a name=\"elems-querystring\"></a>\n\n#### Элемент `querystring`\n\nЭлемент предоставляет объект, содержащий набор методов для работы со строкой запроса формата URI.\n\n<a name=\"elems-name-fields\"></a>\n\n### Свойства и методы объекта\n\n<a name=\"elems-querystring-fields-parse\"></a>\n\n#### Метод `parse`\n\nФормирует объект на основании строки параметров в формате URI.\n\n**Принимаемые аргументы:**\n\n* `str {String}` – строка с параметрами в виде пар ключ-значение. Ключ отделяется от значения символом `=`. Пары разделяются символом `&`. В процессе обработки, ключи и значения декодируются из формата URI. Обязательный аргумент.\n\n**Возвращаемое значение:** `Object`. Объект, сформированный на основании параметров адресной строки.\n\n<a name=\"elems-querystring-fields-stringify\"></a>\n\n#### Метод `stringify`\n\nФормирует строку запроса в формате URI на основании объекта.\n\n**Принимаемые аргументы:**\n\n* `obj {Object}` – объект, на основании которого формируется строка. Обязательный аргумент.\n\n**Возвращаемое значение:** `String`. Имена свойств в строке отделяются от значений символом `=`, пары разделяются символом `&`.\n"
  },
  {
    "path": "common.blocks/uri/uri.spec.js",
    "content": "modules.define('spec', ['uri'], function(provide, uri) {\n\ndescribe('uri', function() {\n    describe('decodeURIComponent()', function() {\n        it('should be able to decode cp1251 encoded params', function() {\n            uri.decodeURIComponent('%F2%E0%E1%EB%EE').should.be.eql('табло');\n        });\n\n        it('should not fall on params encoded with unknown encoding', function() {\n            uri.decodeURIComponent('%COCO%C0C0').should.be.eql('%COCO%C0C0');\n        });\n    });\n\n    describe('decodeURI()', function() {\n        it('should be able to decode url with cp1251 encoded params', function() {\n            uri\n                .decodeURI('http://test.com/ololo/trololo.html?text=%F2%E0%E1%EB%EE')\n                .should.be.eql('http://test.com/ololo/trololo.html?text=табло');\n        });\n\n        it('should not fall on url with params encoded with unknown encoding', function() {\n            uri\n                .decodeURI('http://test.com/ololo/trololo.html?text=%COCO%C0C0')\n                .should.be.eql('http://test.com/ololo/trololo.html?text=%COCO%C0C0');\n        });\n    });\n});\n\nprovide();\n\n});\n"
  },
  {
    "path": "common.blocks/uri/uri.vanilla.js",
    "content": "/**\n * @module uri\n * @description A set of helpers to work with URI\n */\n\n// Equivalency table for cp1251 and utf8.\nconst map = { '%D0' : '%D0%A0', '%C0' : '%D0%90', '%C1' : '%D0%91', '%C2' : '%D0%92', '%C3' : '%D0%93', '%C4' : '%D0%94', '%C5' : '%D0%95', '%A8' : '%D0%81', '%C6' : '%D0%96', '%C7' : '%D0%97', '%C8' : '%D0%98', '%C9' : '%D0%99', '%CA' : '%D0%9A', '%CB' : '%D0%9B', '%CC' : '%D0%9C', '%CD' : '%D0%9D', '%CE' : '%D0%9E', '%CF' : '%D0%9F', '%D1' : '%D0%A1', '%D2' : '%D0%A2', '%D3' : '%D0%A3', '%D4' : '%D0%A4', '%D5' : '%D0%A5', '%D6' : '%D0%A6', '%D7' : '%D0%A7', '%D8' : '%D0%A8', '%D9' : '%D0%A9', '%DA' : '%D0%AA', '%DB' : '%D0%AB', '%DC' : '%D0%AC', '%DD' : '%D0%AD', '%DE' : '%D0%AE', '%DF' : '%D0%AF', '%E0' : '%D0%B0', '%E1' : '%D0%B1', '%E2' : '%D0%B2', '%E3' : '%D0%B3', '%E4' : '%D0%B4', '%E5' : '%D0%B5', '%B8' : '%D1%91', '%E6' : '%D0%B6', '%E7' : '%D0%B7', '%E8' : '%D0%B8', '%E9' : '%D0%B9', '%EA' : '%D0%BA', '%EB' : '%D0%BB', '%EC' : '%D0%BC', '%ED' : '%D0%BD', '%EE' : '%D0%BE', '%EF' : '%D0%BF', '%F0' : '%D1%80', '%F1' : '%D1%81', '%F2' : '%D1%82', '%F3' : '%D1%83', '%F4' : '%D1%84', '%F5' : '%D1%85', '%F6' : '%D1%86', '%F7' : '%D1%87', '%F8' : '%D1%88', '%F9' : '%D1%89', '%FA' : '%D1%8A', '%FB' : '%D1%8B', '%FC' : '%D1%8C', '%FD' : '%D1%8D', '%FE' : '%D1%8E', '%FF' : '%D1%8F' }\n\nfunction convert(str) {\n    // Symbol code in cp1251 (hex) : symbol code in utf8)\n    return str.replace(\n        /%.{2}/g,\n        function($0) {\n            return map[$0] || $0\n        })\n}\n\nfunction decode(fn,  str) {\n    // Try/catch block for getting the encoding of the source string.\n    // Error is thrown if a non-UTF8 string is input.\n    // If the string was not decoded, it is returned without changes.\n    try {\n        return fn(str)\n    } catch (e1) {\n        try {\n            return fn(convert(str))\n        } catch (e2) {\n            return str\n        }\n    }\n}\n\nexport default {\n    /**\n     * Decodes URI string\n     * @param {String} str\n     * @returns {String}\n     */\n    decodeURI(str) {\n        return decode(decodeURI,  str)\n    },\n\n    /**\n     * Decodes URI component string\n     * @param {String} str\n     * @returns {String}\n     */\n    decodeURIComponent(str) {\n        return decode(decodeURIComponent,  str)\n    }\n}\n"
  },
  {
    "path": "common.bundles/index/blocks/square/_color/square_color_green.css",
    "content": ".square_color_green\n{\n    background-color: green;\n}\n"
  },
  {
    "path": "common.bundles/index/blocks/square/square.css",
    "content": ".square\n{\n    background-color: red;\n    cursor: hand;\n    width: 100px;\n    height: 100px;\n}\n"
  },
  {
    "path": "common.bundles/index/blocks/square/square.deps.js",
    "content": "[{\n    mustDeps : ['i-bem', 'i-bem-dom'],\n    shouldDeps : { mods : { color : 'green' } }\n},\n{\n    tech : 'js',\n    mustDeps : { tech : 'bemhtml', block : 'i-bem' }\n}]\n"
  },
  {
    "path": "common.bundles/index/blocks/square/square.js",
    "content": "/**\n * The block's BEM declaration can state which block (a block with a modifier or a block\n * with a specific modifier value)\n * a given JavaScript component refers to.\n *\n * You can find various declarations on the i-bem block's wiki page, blocks/i-bem/i-bem.wiki\n */\nmodules.define('square', ['i-bem-dom', 'BEMHTML'], function(provide, bemDom, BEMHTML) {\nprovide(bemDom.declBlock(this.name, {\n    _onSquareClick : function() {\n        this.toggleMod('color', '', 'green');\n        bemDom.update(this.domElem, BEMHTML.apply({\n            block : 'test',\n            content : 'client BEMHTML test'\n        }));\n    }\n}, {\n    live : function() {\n        this._domEvents().on('click', this.prototype._onSquareClick);\n    }\n}));\n\n});\n"
  },
  {
    "path": "common.bundles/index/index.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'Обработчик события click',\n    styles : { elem : 'css', url : '_index.css' },\n    scripts : { elem : 'js', url : '_index.js' },\n    content : [{\n        block : 'square',\n        js : { id : 1 }\n    },{\n        block : 'square',\n        js : { id : 1 }\n    }]\n});\n"
  },
  {
    "path": "common.docs/bemjson/bemjson.en.md",
    "content": "# BEMJSON reference\n\n<a id=\"intro\"></a>\n## Introduction\n\n**This document** is a guide to the format for describing input data called BEMJSON.\n\nThe guide describes:\n\n* BEMJSON's main features distinguishing it from other formats;\n* BEMJSON syntax for data description.\n\n\n**The target audience for this guide**  are web developers and HTML coders who use the [BEM methodology](https://en.bem.info/method/).\n\nThe reader is assumed to be familiar with:\n\n* HTML\n* JavaScript\n* CSS\n* [BEM](https://en.bem.info/method/)\n\n\nThe description of tools for generating a BEM tree in BEMJSON format is **beyond the scope of this document**.\n\n\n<a id=\"common\"></a>\n## Key concepts\n\nTo describe web page markup in BEM terms, BEM projects introduce the concept of a **BEM tree**, named by analogy to the DOM tree data structure.\n\nA BEM tree is a data structure that describes:\n\n* web page structure – the order and nesting of the blocks;\n* names of BEM entities – the names of the blocks, elements, and their modifiers;\n* states of BEM entities – the occurrence of logical modifiers and their values;\n* arbitrary fields – custom data (hash keys, public API addresses, etc.)\n\nThe standard BEM tree format in the bem-core library (and many other BEM projects) is **BEMJSON**.\n\nBEMJSON is a JavaScript data structure (object) with a set of extra conventions on the representation of BEM entities.\n\n\n<a id=\"bemcore\"></a>\n## BEMJSON and data templating in bem-core\n\nA BEMJSON-formatted BEM tree is an integral part of the data templating mechanisms implemented in `bem-core`. BEMJSON is used as an input data format for these template engines:\n\n* [BEMTREE](https://en.bem.info/technology/bemtree/current/bemtree/)\n* [BEMHTML](https://en.bem.info/technology/bemhtml/current/intro/)\n\nFrom a BEMTREE and BEMHTML templates perspective, a portion of input data corresponding to the current BEM tree element (node) and its child elements is contained in the context field `this.ctx`.\n\n\n**NB** The BEMTREE template engine is used for generating BEMJSON from arbitrary data (the data normally comes in the form of a web page skeleton in BEMJSON format, which gets filled with content element by element as it is processed by the template engine).\n\n\n\n<a id=\"sbor\"></a>\n## BEMJSON and the build process\n\nCertain build systems, such as [bem-tools](https://en.bem.info/tools/bem/bem-tools/), use files that contain the literal record BEMJSON as a build **declaration**. In `bem-tools`, `bemjson.js`-suffixed files serve this purpose. Based on a BEM tree defined in such files, the build system determines a set of BEM entities whose implementations are to be built from block folders.\n\nIn practice, it works like this: first, based on the `bemjson.js` declaration and the build settings, the build tool creates a basic declaration file in `bemdecl.js` format. The latter is then used to build a file in `deps.js` format that describes build dependencies. The dependencies file is a flat list of BEM entities involved in the build, which looks like this:\n\n```js\nexports.deps = [\n    {\n        \"block\": \"page\",\n        \"elem\": \"css\"\n    },\n    {\n        \"block\": \"page\",\n        \"elem\": \"js\"\n    },\n    {\n        \"block\": \"page\",\n        \"elem\": \"meta\"\n    },\n    {\n        \"block\": \"header\"\n    },\n    {\n        \"block\": \"content\"\n    },\n    {\n        \"block\": \"footer\"\n    }\n];\n```\n\nThe dependencies file serves as the basis for the subsequent building of tech files from the folders of blocks, elements and modifiers targeted by the declaration. The files are grouped into technology bundles according to their **suffixes**.\n\nThe part of a filename that follows the first occurrence of the period is considered a suffix. For example, in the filename `index.bemjson.js`, the suffix is `bemjson.js`.\n\n**See also**:\n\n* [Dependencies in bem-tools](https://en.bem.info/technology/deps/)\n* [Building and connecting BEMTREE and BEMHTML technology bundles](https://ru.bem.info/technology/bemhtml/current/templating/#polymorph) (Russian version only)\n\n\n\n<a name=\"bemjson\"></a>\n\n## BEMJSON syntax\n\n<a id=\"datatype\"></a>\n\n### Data types\n\nData types in BEMJSON correspond to data types in JavaScript.\n\n* Strings and numbers:\n * **String** `` 'a' `` `\"a\"`;\n * **Number** `1` `0.1`;\n\n   A data structure consisting of a single string or number is valid BEMJSON.\n\n* **Boolean**. Values: `true`, `false`.\n\n* **Object** (associative array) '{key: value}' and other types except array.\n\n* **Array** – a list; can include elements of different types (strings, numbers, objects, arrays)\n  `[ \"a\", 1, {key: value}, [ \"b\", 2, ... ] ]`.\n\n<a id=\"fields_bemjson\"></a>\n\n### BEMJSON special fields\n\nFor the BEM domain data and HTML data representation, BEMJSON uses objects with special reserved field names.\n\n<a name=\"notionbem\"></a>\n\n#### Representation of BEM entities\n\nBEM entities are represented in BEMJSON as objects that can contain the following fields:\n\n<table>\n<tr>\n    <th>Field</th>\n    <th>Value</th>\n    <th>Value type</th>\n    <th>Example</th>\n</tr>\n<tr>\n    <td><code>block</code></td>\n    <td>Block name</td>\n    <td>String</td>\n    <td><code>{ block: 'menu' }</code></td>\n</tr>\n\n<tr>\n    <td><code>elem</code></td>\n    <td>Element name</td>\n    <td>String</td>\n    <td><code>{ elem: 'item' }</code></td>\n</tr>\n\n<tr>\n    <td><code>mods</code></td>\n    <td>Block modifiers</td>\n    <td>Object containing the names and values of block modifiers as key-value pairs:\n        <code>{modifier_name: 'modifier_value'}</code>\n    </td>\n    <td>\n        <pre><code>\n{\n  block: 'link',\n  mods: { pseudo: true, color: 'green' }\n}\n        </code></pre>\n    </td>\n</tr>\n\n<tr>\n    <td><code>elemMods</code></td>\n    <td>Element modifiers</td>\n    <td>Object containing the names and values of element modifiers as key-value pairs:\n        <code>{modifier_name: 'modifier_value'}</code>\n    </td>\n    <td>\n        <pre><code>\n{\n  elem: 'item',\n  elemMods: { selected: 'yes' }\n}\n        </code></pre>\n    </td>\n</tr>\n\n<tr>\n    <td><code>mix</code></td>\n    <td>Mixed blocks/elements</td>\n    <td>Array of objects that describe mixed blocks and elements or Object interpreted as an array consisting of a single element.</td>\n    <td>\n        <pre><code>\n{\n  block: 'link',\n  mix: [ { block: 'serp-item', elem: 'link' } ]\n}\n        </code></pre>\n    </td>\n</tr>\n</table>\n\n**See also**:\n\n* [Context-aided completion of BEM entities](https://ru.bem.info/technology/bemhtml/current/templating/#extensionbem) (Russian version only)\n\n<a name=\"notionhtml\"></a>\n\n#### HTML representation\n\nBEMJSON supports the ability to specify certain aspects of the resulting HTML directly in the input data. Admittedly, that is not recommended as common practice, considering that BEMJSON essentially describes data, while actual HTML layout is built at the BEMHMTL template engine level. Still there may be situations that warrant the use of HTML representation at BEMJSON level.\n\n\nThe following fields in BEMJSON are used to control HMTL rendering:\n\n<table>\n<tr>\n    <th>Field</th>\n    <th>Value</th>\n    <th>Value type</th>\n    <th>Example</th>\n</tr>\n<tr>\n    <td><code>tag</code></td>\n    <td>HTML tag for the current entity</td>\n    <td><code>String</code></td>\n    <td>\n        <pre><code>{\n  block: 'my-block',\n  tag: 'img'\n}</code></pre>\n    </td>\n</tr>\n<tr>\n    <td><code>attrs</code></td>\n    <td>HTML attributes for the current entity</td>\n    <td><code>Object</code></td>\n    <td>\n        <pre><code>{\n  block: 'my-block',\n  tag: 'img',\n  attrs: { src: '//yandex.ru/favicon.ico', alt: '' }\n}</code></pre>\n    </td>\n</tr>\n<tr>\n    <td><code>cls</code></td>\n    <td>Line added to the HTML attribute <code>class</code> (besides automatically generated classes)</td>\n    <td><code>String</code></td>\n    <td>\n        <pre><code>{\n  block: 'my-block',\n  cls: 'some-blah-class'\n}</code></pre>\n    </td>\n</tr>\n<tr>\n    <td><code>bem</code></td>\n    <td>Flag to cancel the generation of BEM classes in the HTML attribute <code>class</code> for the current entity</td>\n    <td><code>Boolean</code></td>\n    <td>\n        <pre><code>{\n  block: 'page',\n  tag: 'html',\n  bem: false\n}</code></pre>\n    </td>\n</tr>\n<tr>\n    <td><code>js</code></td>\n    <td>Either flag to indicate the presence of client JavaScript in the entity or JavaScript parameters</td>\n    <td><code>Boolean|Object</code></td>\n    <td>\n        <pre><code>{\n  block: 'form-input',\n  mods: { autocomplete: 'yes' },\n  js: {\n    dataprovider: { url: 'http://suggest.yandex.ru/...' }\n  }\n}</code></pre>\n    </td>\n</tr>\n</table>\n\nNote that the names and meanings of these HTML-specific BEMJSON fields are equivalent to those of the corresponding BEMHTML [standard modes](https://en.bem.info/technology/bemhtml/current/reference/#standardmoda) (tags, attributes, classes, etc.) If the same HTML aspects are specified **in both the input data and BEMHTML templates**, the values specified in the BEMHTML templates take priority.\n\nDuring the HTML generation process, the BEMHTML template engine will perform one of two actions:\n\n* **Merge** the values of the HTML parameters set in the BEMJSON with those specified in the BEMHTML template. Such merging is done only for those parameters where it makes obvious sense: `attrs`, `js`, `mix`.\n* **Override** the values of the HTML parameters set in the BEMJSON with those specified in the **BEMHTML template**. This is done for all other values: `tag`, `cls`, `bem`, `content`.\n\n\n<a name=\"nesting\"></a>\n\n#### Nesting: content\n\nThe field `content` is reserved in BEMJSON for the representation of nested BEM entities (BEM tree). The field can take arbitrary BEMJSON as its value:\n\n* A primitive data type (string, number) - the value is used as the content (text) of the HTML element that corresponds to the context entity.\n* An object describing a BEM tree - the value is used for generating HTML elements nested inside the HTML element that corresponds to the context entity.\n\nThere is no fixed limit on nesting depth for a tree of BEM entities that can be built from the `content` field.\n\n\n\n<a id=\"custom_fields\"></a>\n\n#### Custom fields\n\nIn addition to special fields that describe the BEM entity and its HTML representation, an object can contain any fields with custom data. The data will be available for use in BEMHTML and BEMTREE templates.\n\nAn example of a custom field is the field `url` in a link block:\n\n```js\n{\n  block: 'link',\n  url: '//yandex.ru'\n}\n```\n\nTo see how data from a custom field is used, refer to the section [Condition-based template selection](https://en.bem.info/technology/bemhtml/current/reference/#select_template) of the BEMHTML document.\n\n<a name=\"customjs\"></a>\n\n### Arbitrary JavaScript in BEMJSON\n\nAs a format, BEMJSON has fewer restrictions than JSON. Arbitrary JavaScript expressions are all valid BEMJSON.\n\nBEMJSON differs from other data formats in its adherence to the above listed naming conventions for fields in objects (in what concerns the representation of BEM entities and HTML) as well as the object nesting rules.\n"
  },
  {
    "path": "common.docs/bemjson/bemjson.ru.md",
    "content": "# Справочное руководство по BEMJSON\n\n<a id=\"intro\"></a>\n## Введение\n\n**Данный документ** представляет собой справочное руководство по формату описания входных данных BEMJSON.\n\nВ документе описаны:\n\n* основные особенности BEMJSON, отличающие его от других форматов;\n* синтаксис описания данных BEMJSON;\n\n\n**Целевая аудитория документа** — веб-разработчики и HTML-верстальщики, использующие\n[БЭМ-методологию](https://ru.bem.info/method/).\n\nПредполагается, что читатель знаком с:\n\n* HTML;\n* JavaScript;\n* CSS;\n* БЭМ.\n\n\n**В документе не описаны** средства гененерации БЭМ-дерева в формате BEMJSON.\n\n\n<a id=\"common\"></a>\n## Общие понятия\n\nВ БЭМ-проектах для описания разметки веб-страницы в БЭМ-терминах вводится специальное понятие – **БЭМ-дерево**. Название выбрано по аналогии с DOM-деревом.\n\nБЭМ-дерево – структура данных, которая описывает:\n\n* структуру страницы – порядок и вложенность блоков;\n* названия БЭМ-сущностей – имена блоков, элементов, модификаторов блока или элемента;\n* состояния БЭМ-сущностей – наличие логических модификаторов, значения модификаторов;\n* произвольные поля – вспомогательные данные (хеш-ключи, адреса публичных API и т.п.).\n\nВ библиотеке `bem-core` (и многих других БЭМ-проектах) стандартным форматом представления  БЭМ-дерева является **BEMJSON** .\n\nBEMJSON – структура данных (объект) JavaScript, с набором дополнительных соглашений о представлении БЭМ-сущностей.\n\n\n<a id=\"bemcore\"></a>\n## BEMJSON и шаблонизация данных в bem-core\nБЭМ-дерево в формате BEMJSON является неотъемлемой частью механизмов [шаблонизации данных](https://ru.bem.info/technology/bemhtml/current/templating/), реализованных в `bem-core`. BEMJSON используется в качестве входных данных для шаблонизаторов:\n* [BEMTREE](https://ru.bem.info/technology/bemtree/);\n* [BEMHTML](http://ru.bem.info/technology/bemhtml/current/intro/).\n\nВ рамках BEMTREE и BEMHTML шаблонов фрагмент входных данных, относящийся к текущему элементу BEMJSON-дерева и его потомкам,  содержится в поле контекста `this.ctx`.\n\n\n**NB** Шаблонизатор BEMTREE предназначен для генерации BEMJSON из произвольных данных.\n\n\n<a id=\"sbor\"></a>\n## BEMJSON и сборка\nНекоторые системы сборки, например, [bem-tools](https://ru.bem.info/tools/bem/bem-tools/), использует файлы, содержащие литеральную запись BEMJSON, в качестве **декларации** сборки. В `bem-tools` для этих целей служат файлы с суффиксом `bemjson.js`. На основе БЭМ-дерева, описанного в этих файлах, система сборки определяет набор БЭМ-сущностей, реализации которых должны быть собраны из папок блоков.\n\nНа практике это означает, что на основании декларации`bemjson.js` и настроек сборки строится файл базовой декларации в формате `bemdecl.js`. Затем из него - файл в формате `deps.js`, описывающий зависимости сборки. Файл зависимостей представляет собой плоский список БЭМ-сущностей, участвующих в сборке, вида:\n\n```js\nexports.deps = [\n    {\n        \"block\": \"page\",\n        \"elem\": \"css\"\n    },\n    {\n        \"block\": \"page\",\n        \"elem\": \"js\"\n    },\n    {\n        \"block\": \"page\",\n        \"elem\": \"meta\"\n    },\n    {\n        \"block\": \"header\"\n    },\n    {\n        \"block\": \"content\"\n    },\n    {\n        \"block\": \"footer\"\n    }\n];\n```\n\nНа основании файла зависимостей производится дальнейшая сборка файлов технологий из папок блоков, элементов и модификаторов, попадающих под декларацию. Файлы собираются в бандлы технологий на основании **суффиксов**.\n\nСуффиксом считается часть имени файла следующая за первой точкой. Например, в имени файла `index.bemjson.js` суффиксом является `bemjson.js`.\n\n**См. также**:\n* [Зависимости в bem-tools](https://ru.bem.info/tools/bem/bem-tools/depsjs/);\n* [Сборка и подключение бандла технологий BEMTREE и BEMHTML](https://ru.bem.info/technology/bemhtml/current/templating/#polymorph)\n\n\n\n<a name=\"bemjson\"></a>\n\n## Синтаксис BEMJSON\n\n<a id=\"datatype\"></a>\n\n### Типы данных\n\nТипы данных в BEMJSON соответствуют типам данных в JavaScript.\n\n* Строки и числа:\n * **Строка** `` 'a' `` `\"a\"`;\n * **Число** `1` `0.1`;\n\n   Структура данных, состоящая из строки или числа, является валидным BEMJSON.\n\n* **Boolean**. Значения: `true`, `false`.\n\n* **Объект** (ассоциативный массив) '{ключ: значение}' и остальные типы, кроме массива.\n\n* **Массив** — список, может содержать элементы различных типов (строки, числа, объекты, массивы)\n  `[ \"a\", 1, {ключ: значение}, [ \"b\", 2, ... ] ]`.\n\n<a id=\"fields_bemjson\"></a>\n\n### Специальные поля BEMJSON\n\nДля представления данных предметной области БЭМ и HTML в BEMJSON используются объекты, в которых зарезервированы\nспециальные имена полей.\n\n<a name=\"notionbem\"></a>\n\n#### Представление БЭМ-сущностей\n\nБЭМ-сущности представляются в BEMJSON в виде объектов, в которых могут присутствовать следующие поля:\n\n<table>\n<tr>\n    <th>Поле</th>\n    <th>Значение</th>\n    <th>Тип значения</th>\n    <th>Пример</th>\n</tr>\n<tr>\n    <td><code>block</code></td>\n    <td>Имя блока</td>\n    <td>Строка</td>\n    <td><code>{ block: 'menu' }</code></td>\n</tr>\n\n<tr>\n    <td><code>elem</code></td>\n    <td>Имя элемента</td>\n    <td>Строка</td>\n    <td><code>{ elem: 'item' }</code></td>\n</tr>\n\n<tr>\n    <td><code>mods</code></td>\n    <td>Модификаторы блока</td>\n    <td>Объект, содержащий имена и значения модификаторов в качестве пар ключ-значение:\n        <code>{имя_модификатора: 'значение_модификатора'}</code>\n    </td>\n    <td>\n        <pre><code>\n{\n  block: 'link',\n  mods: { pseudo: true, color: 'green' }\n}\n        </code></pre>\n    </td>\n</tr>\n\n<tr>\n    <td><code>elemMods</code></td>\n    <td>Модификаторы элемента</td>\n    <td>Объект, содержащий имена и значения модификаторов элемента в качестве пар ключ-значение:\n        <code>{имя_модификатора: 'значение_модификатора'}</code>\n    </td>\n    <td>\n        <pre><code>\n{\n  elem: 'item',\n  elemMods: { selected: 'yes' }\n}\n        </code></pre>\n    </td>\n</tr>\n\n<tr>\n    <td><code>mix</code></td>\n    <td>Подмешанные блоки/элементы</td>\n    <td>Массив, содержащий объекты, описывающие подмешанные блоки и элементы. В качестве значения может выступать\n    объект, который трактуется как массив, состоящий из одного элемента.</td>\n    <td>\n        <pre><code>\n{\n  block: 'link',\n  mix: [ { block: 'serp-item', elem: 'link' } ]\n}\n        </code></pre>\n    </td>\n</tr>\n</table>\n\n**См. также**:\n\n* [Достраивание БЭМ-сущностей по контексту](https://ru.bem.info/technology/bemhtml/current/templating/#extensionbem)\n\n<a name=\"notionhtml\"></a>\n\n#### Представление HTML\n\nBEMJSON предоставляет возможность задавать некоторые аспекты выходного HTML непосредственно во входных данных.\nЭтой возможностью не следует злоупотреблять, так как BEMJSON представляет собой уровень данных, а непосредственное\nоформление HTML должно выполняться на уровне шаблонизатора BEMHTML. Однако возможны ситуации, когда оправданно\nописание HTML-представления на уровне BEMJSON.\n\n\nВ BEMJSON предусмотрены следующие поля для непосредственного управления HTML-представлением:\n\n<table>\n<tr>\n    <th>Поле</th>\n    <th>Значение</th>\n    <th>Тип значения</th>\n    <th>Пример</th>\n</tr>\n<tr>\n    <td><code>tag</code></td>\n    <td>HTML-тег для данной сущности</td>\n    <td><code>String</code></td>\n    <td>\n        <pre><code>{\n  block: 'my-block',\n  tag: 'img'\n}</code></pre>\n    </td>\n</tr>\n<tr>\n    <td><code>attrs</code></td>\n    <td>HTML-атрибуты для данной сущности</td>\n    <td><code>Object</code></td>\n    <td>\n        <pre><code>{\n  block: 'my-block',\n  tag: 'img',\n  attrs: { src: '//yandex.ru/favicon.ico', alt: '' }\n}</code></pre>\n    </td>\n</tr>\n<tr>\n    <td><code>cls</code></td>\n    <td>Строка, добавляемая к HTML-атрибуту <code>class</code> (помимо автоматически генерируемых классов)</td>\n    <td><code>String</code></td>\n    <td>\n        <pre><code>{\n  block: 'my-block',\n  cls: 'some-blah-class'\n}</code></pre>\n    </td>\n</tr>\n<tr>\n    <td><code>bem</code></td>\n    <td>Флаг — отменить генерацию БЭМ-классов в HTML-атрибуте <code>class</code> для данной сущности</td>\n    <td><code>Boolean</code></td>\n    <td>\n        <pre><code>{\n  block: 'page',\n  tag: 'html',\n  bem: false\n}</code></pre>\n    </td>\n</tr>\n<tr>\n    <td><code>js</code></td>\n    <td>Либо флаг о наличии клиентского JavaScript у данной сущности, либо параметры JavaScript</td>\n    <td><code>Boolean|Object</code></td>\n    <td>\n        <pre><code>{\n  block: 'form-input',\n  mods: { autocomplete: 'yes' },\n  js: {\n    dataprovider: { url: 'http://suggest.yandex.ru/...' }\n  }\n}</code></pre>\n    </td>\n</tr>\n</table>\n\nОбратите внимание, что имена и смысл полей BEMJSON, управляющих HTML-представлением, совпадают с именами и смыслом соответствующих [стандартных мод](https://ru.bem.info/technology/bemhtml/current/reference/#standardmoda) BEMHTML (тег, атрибуты, класс и т.п.). В случае, если какие-то из аспектов выходного HTML заданы **и во входных данных, и в BEMHTML-шаблонах**, более высокий приоритет имеют значения, заданные в BEMHTML-шаблонах.\n\nПри генерации HTML будет выполнено одно из двух действий:\n\n* **Объединение** значений HTML-параметров, заданных в BEMJSON, cо значениями параметров, заданных в BEMHTML-шаблоне. Объединение значений производится только для тех параметров, для которых оно имеет очевидный смысл: `attrs`, `js`, `mix`.\n* **Замещение** значений HTML-параметров, заданных в BEMJSON, значениями, заданными в **BEMHTML-шаблоне**. Выполняется для всех прочих значений: `tag`, `cls`, `bem`, `content`.\n\n\n**NB:** Приоритет BEMHTML-шаблонов позволяет **автору шаблонов** принимать решение, какие HTML-параметры будут приоритетнее в каждом конкретном случае: заданные в BEMHTML или в BEMJSON. Значения HTML-параметров, заданных в BEMJSON, доступны в шаблонах при обращении к фрагменту входного BEMJSON-дерева в контексте (поле `this.ctx`).\n\n\n<a name=\"nesting\"></a>\n\n#### Вложенность: content\n\nДля представления вложенных БЭМ-сущностей (БЭМ-дерева) в BEMJSON зарезервировано поле `content`. В качестве значения\nданного поля может выступать произвольный BEMJSON:\n\n* Примитивный тип (строка, число). Значение используется в качестве содержимого (текста) HTML-элемента, соответствующего\n  контекстной сущности.\n* Объект, описывающий БЭМ-дерево. Значение используется для генерации HTML-элементов, вложенных в HTML-элемент,\n  соответствующий контекстной сущности.\n\nУровень вложенности дерева БЭМ-сущностей, построенного с помощью поля `content`, не ограничен.\n\n\n\n<a id=\"custom_fields\"></a>\n\n#### Произвольные поля\n\nПомимо специальных полей, описывающих БЭМ-сущность и ее HTML-представление, в том же объекте могут присутствовать\nлюбые поля с произвольными данными, которые будут доступны для использования в шаблонах BEMHTML или BEMTREE.\n\nПримером произвольного поля может служить поле `url` в блоке ссылки:\n\n```js\n{\n  block: 'link',\n  url: '//yandex.ru'\n}\n```\n\nПример использования данных из произвольного поля см. в разделе [Выбор шаблона по условию](https://ru.bem.info/technology/bemhtml/current/reference/#select_template) из документации по BEMHTML.\n\n<a name=\"customjs\"></a>\n\n### Произвольный JavaScript в BEMJSON\n\nBEMJSON является менее ограниченным форматом, чем JSON. Произвольные JavaScript-выражения будут валидным BEMJSON.\n\nСпецифика BEMJSON как формата данных заключается в соблюдении описанных в предшествующих разделах соглашений\nпо именованию полей в объектах (для представления БЭМ-сущностей и HTML-представления) и правил вложения объектов.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-collections.en.md",
    "content": "# Collections of blocks and elements\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-collections.ru.md",
    "content": "## Коллекции блоков и элементов\n\nОбщее описание\nСпособы получения\nAPI\n\nДля удобства работы одновременно с несколькими экземплярами блоков или элементов существует специальный класс **коллекции**, реализованный в элементах `collection` блоков `i-bem` и `i-bem-dom`.\n\n### Способы получения коллекции\n\n#### Создание экземпляра класса коллекции\n\nЭкземпляр класса коллекции создается базовыми средствами JavaScript,\nс помощью класса `BemCollection` модуля `i-bem__collection` или `BemDomCollection` модуля `i-bem-dom__collection`.\nКонструктор обоих классов принимает один аргумент в виде массива, либо несколько аргументов:\n\n* `entities` `{Array|...i-bem:Entity|...i-bem-dom:Entity}` — массив или несколько экземпляров БЭМ-сущностей.\n\n**Пример**\n\n```js\nmodules.define(\n    'my-form',\n    ['i-bem-dom', 'i-bem-dom__collection', 'button', 'input'],\n    function(provide, bemDom, BemDomCollection, Button, Input) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                var button = this.findChildBlock(Button),\n                    input = this.findChildBlock(Input);\n\n                this._controls = new BemDomCollection(button, input);\n            }\n        }\n    }\n}));\n\n});\n```\n\n#### Методы поиска\n\n[Методы поиска](./i-bem-js-dom.ru.md#Поиск-экземпляров-блоков-и-элементов-в-dom-дереве), способные найти несколько экземпляров блоков или элементов, возвращают коллекцию, состоящую из найденных экземпляров.\n\n**Пример**\n\n```js\nmodules.define('my-form', ['i-bem-dom', 'input'], function(provide, bemDom, Input) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._inputs = this.findChildBlocks(Input);\n            }\n        }\n    }\n}));\n\n});\n```\n\n### Методы коллекции\n\n* `setMod(modName, [modVal=true])`, `delMod(modName)`, `toggleMod(modName, modVal1, [modVal2], [condition])` — соответсвуют одноименным методам [управления модификаторами](./i-bem-js-states.ru.md#Управление-модификаторами) экземпляра блока и элемента.\n* `everyHasMod(modName, [modVal])`, `someHasMod(modName, [modVal])` — применяют метод `hasMod(modName, modVal)` для каждой сущности коллекции. Возвращает `true`, если все вызовы вернули `true` и если хотя бы один вызов вернул `true`, соответственно.\n* `get(i)` — возвращает элемент коллекции по индексу i.\n* `size()` — возвращает размер коллекции.\n* `forEach(fn, ctx)`, `map(fn, ctx)`, `reduce(fn, ctx)`, `reduceRight(fn, ctx)`, `filter(fn, ctx)`, `some(fn, ctx)`, `every(fn, ctx)`, `has(entity)`, `find(fn, ctx)`, `concat(...args)` — соответствует одноименным методам [объекта Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array).\n* `toArray()` — преобразовывает коллекцию в массив экземпляров блоков и элементов.\n\n**Пример**\n\n```js\nmodules.define('my-form', ['i-bem-dom', 'input'], function(provide, bemDom, Input) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                this._inputs = this.findChildBlocks(Input);\n            }\n        },\n\n        'disabled' : function(modName, modVal) {\n            this._inputs.setMod(modName, modVal);\n        }\n    },\n\n    getValue : function() {\n        return this._inputs\n            .filter(function(input) {\n                return !input.hasMod('disabled');\n            })\n            .map(function(input) {\n                return input.getValue();\n            });\n    }\n}));\n\n});\n```\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-common.en.md",
    "content": "<a name=\"intro\"></a>\n\n## Overview\n\n<a name=\"intro-bem\"></a>\n\n### The BEM methodology and JavaScript\n\nIn the BEM methodology, a web interface is built from independent\n**blocks**, which may have **elements**. Both blocks\nand elements can have states or characteristics described by **modifiers**.\n\nA web interface uses various **technologies**\n(HTML, CSS, JS, and others). Its implementation is divided into components by block. A block contains a set of **technology files** that represent aspects of its implementation:\n\n* `my-block.css` — The block appearance.\n* `my-block.bemhtml` — Templates for generating HTML representations of the block.\n* `my-block.js` — The block **dynamic behavior** in the browser.\n\nThe `i-bem.js` framework allows us to break down the client JavaScript into components in BEM terms:\n\n* **Block** — The JS component that describes the logic of same-type interface elements. For example, all buttons can be implemented as a `button` block. In this case, `button.css` defines how all buttons look, and `button.js` defines how they work.\n    Each page can have more than one **block instance** (such as buttons). Each block instance corresponds to a JS object in the browser memory that stores its state. The JS object contains a reference to the DOM node that this block instance is bound to.\n* **Elements** — DOM nodes nested in the block DOM node, with the `class` attribute pointing to their role in the BEM subject domain (the name of the block and element). Block elements are accessible via the block instance [JS-API] [dom].\n* **Modifiers** — Provide information about the state of a block and its elements. The state of modifiers is written in the `class` attribute on the DOM nodes of a block and elements. Modifiers are controlled using a block instance [JS-API](i-bem-js-states.en.md#js-api).\n\n<a name=\"intro-build\"></a>\n\n### Assembly\n\nIn the BEM methodology, development is modular — each block\nis programmed separately. The final source code of web pages is generated\nfrom the code of individual blocks using **assembly** procedures.\n\nIn the file system, it is convenient to represent a block as a directory, and the block implementation in each of the technologies as a separate file:\n\n```html\n    desktop.blocks/\n        my-block/\n            my-block.css\n            my-block.js\n            my-block.bemhtml\n            ...\n\n    desktop.blocks/\n        other-block/\n            other-block.css\n            other-block.js\n            other-block.bemhtml\n            ...\n```\n\nFor each web page, the code of the blocks used on it can be put in the same types of files:\n\n```html\n    desktop.bundles/\n        index/\n            index.html\n            index.css\n            index.js\n            ...\n```\n\nThere are two tools that support the BEM subject domain for assembling separate block descriptions into the code of resulting web pages:\n\n* [bem-tools](https://en.bem.info/tools/bem/)\n* [ENB](https://en.bem.info/tools/bem/enb-bem/)\n\nBoth tools automate the creation of HTML markup for [binding JS blocks](./i-bem-js-html-binding.en.md) and [passing parameters to a block instance](./i-bem-js-params.en.md).\n\n<a name=\"intro-name\"></a>\n\n### Why i-bem.js is named this way\n\nAccording to the BEM methodology, the base JS library of the BEM platform was originally developed\nas a special service block. This approach allows us to work with base libraries the same way as with\nregular blocks. In particular, it allows us to structure code in terms of elements and modifiers and flexibly\nconfigure the library behavior on various redefinition levels.\n\nService blocks in BEM were conventionally given names with the `i-` prefix. Thus, the name `i-bem.js`\nis read as *an implementation of the `i-bem` block in the `JS` technology*.\n\n<a name=\"intro-use\"></a>\n\n### How to use i-bem.js\n\nThe `i-bem.js` framework is a part of the [bem-core](https://en.bem.info/libs/bem-core/) library.\n\nThe implementation of `i-bem.js` consists of two modules:\n\n* **The [i-bem](https://en.bem.info/libs/bem-core/current/desktop/i-bem/jsdoc/) module**. Base implementation of the `i-bem` JS block, which all the blocks in `i-bem.js` inherit from. The `i-bem` block is written for use in any of the JS environments: both on the client and on the server (for example, in  Node.js).\n* **The [i-bem__dom](https://en.bem.info/libs/bem-core/current/desktop/i-bem/jsdoc/) module**. The base implementation of a block bound to a DOM node. Intended for use on the client, and relies on browsers working with DOM. Depends on `jQuery`.\n\nDependencies:\n\n* jQuery (only for the `i-bem__dom` module). When using `bem-core`, separate installation of jQuery is not necessary.\n* The [ym/modules](https://github.com/ymaps/modules) module system. When using [bem-tools](https://en.bem.info/tools/bem/) with `.browser.js` technology (and derivatives of it), this dependency is satisfied automatically.\n\nYou can use `i-bem.js` as a part of the full stack\nof BEM tools. In this case, it is convenient to base your project on the\n[project-stub](https://en.bem.info/tutorials/project-stub/) template repository, where automatic installation of dependent libraries and assembly is set up.\n\nIf you aren't planning to use other technologies of the BEM platform, you can just put the `bem-core` library code in an existing project.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-common.ru.md",
    "content": "## Общие сведения\n\n### БЭМ-методология и JavaScript\n\nВ БЭМ-методологии веб-интерфейс строится из независимых **блоков** у которых могут быть **элементы**. И блоки, и элементы могут иметь состояния или особенности, описываемые **модификаторами**.\n\nРабота веб-интерфейса обеспечивается несколькими **технологиями** (HTML, CSS, JS и т.д.). Его реализация разбита на компоненты по блокам. Блок содержит набор **файлов технологий**, составляющих аспекты его реализации:\n\n* `my-block.css` — внешний вид блока;\n* `my-block.bemhtml` — шаблоны для генерации HTML-представления блока;\n* `my-block.js` — **динамическое поведение** блока в браузере.\n\nФреймворк `i-bem.js` позволяет разложить клиентский JavaScript на компоненты в терминах БЭМ:\n\n* **Блок** — JS-компонент, описывающий логику работы однотипных элементов интерфейса. Например, все кнопки могут быть реализованы в виде блока `button`. В этом случае, `button.css` определяет внешний вид всех кнопок, а `button.js` — логику их работы. На каждой странице может размещаться более одного **экземпляра блока** (например, кнопки). Каждому экземпляру блока соответствует JS-объект, в памяти браузера, хранящий его состояние. JS-объект содержит ссылку на DOM-узел, к которому привязан данный экземпляр блока.\n* **Элементы** — DOM-узлы, вложенные в DOM-узел блока, с атрибутом `class`, указывающим на их роль в БЭМ-предметной области (имя блока и элемента). Элементы блока доступны через [JS-API](./i-bem-js-states.ru.md#Управление-модификаторами) экземпляра блока.\n* **Модификаторы** — предоставляют информацию о состоянии блока и его элементов. Состояние модификаторов записывается в атрибуте `class` на DOM-узлах блока и элементов. Управление модификаторами производится через [JS-API](./i-bem-js-states.ru.md#Управление-модификаторами) экземпляра блока.\n\n### Сборка\n\nРазработка в рамках БЭМ-методологии ведется модульно — каждый блок программируется отдельно. Финальный исходный код веб-страниц формируется из кода отдельных блоков с помощью процедур **сборки**.\n\nВ файловой системе блок удобно представлять в виде каталога, а реализацию блока в каждой из технологий — в виде отдельного файла:\n\n```html\n    desktop.blocks/\n        my-block/\n            my-block.css\n            my-block.js\n            my-block.bemhtml\n            ...\n\n    desktop.blocks/\n        other-block/\n            other-block.css\n            other-block.js\n            other-block.bemhtml\n            ...\n```\n\nДля каждой веб-страницы код использованных на ней блоков может быть собран в единые файлы:\n\n```html\n    desktop.bundles/\n        index/\n            index.html\n            index.css\n            index.js\n            ...\n```\n\nСуществует два инструмента, поддерживающих БЭМ-предметную область, для сборки кода результирующих веб-страниц из отдельных описаний блоков:\n\n* [bem-tools](https://ru.bem.info/toolbox/bem-tools/)\n* [ENB](https://ru.bem.info/toolbox/enb/)\n\nОба инструмента позволяют автоматизировать создание HTML-разметки для [привязки JS-блоков](./i-bem-js-html-binding.ru.md#Привязка-js-блоков-к-html) и [передачи параметров экземпляру блока](./i-bem-js-params.ru.md#Передача-параметров-экземпляру-блока-и-элемента).\n\n### Почему i-bem.js так называется\n\nВ соответствии с БЭМ-методологией, базовая JS-библиотека БЭМ-платформы изначально разрабатывалась как особый служебный блок. Такой подход позволяет работать с базовыми библиотеками так же, как и с обычными блоками. В частности, структурировать код в терминах элементов и модификаторов и гибко настраивать поведение библиотеки на разных уровнях переопределения.\n\nСлужебным блокам в БЭМ было принято давать имена с префиксом `i-`. Таким образом, имя `i-bem.js` читается как *реализация блока `i-bem` в технологии `JS`*.\n\n### Как использовать i-bem.js\n\nФреймворк `i-bem.js` входит в состав библиотеки [bem-core](https://ru.bem.info/platform/libs/bem-core/).\n\nРеализация `i-bem.js` состоит из двух модулей:\n\n* **Модуль [i-bem](https://ru.bem.info/platform/libs/bem-core/)**.\n\n  Предоставляет базовую реализацию JS-блока `i-bem`, от которой наследуются все блоки и элементы в `i-bem.js`. Блок `i-bem` написан с расчетом на использование в любом JS-окружении: как на клиенте, так и на сервере (например, в Node.js).\n\n* **Модуль [i-bem-dom](https://ru.bem.info/platform/libs/bem-core/)**.\n\n  Предоставляет базовую реализацию блока и элемента, привязанных к DOM-узлу. Рассчитан на использование на клиенте, опирается на работу браузеров с DOM. Зависит от `jQuery`.\n\nЗависимости:\n\n* jQuery (только для модуля `i-bem-dom`). При использовании `bem-core` отдельная установка jQuery не требуется.\n* Модульная система [ym/modules](https://github.com/ymaps/modules). При использовании [ENB](https://ru.bem.info/toolbox/enb/) с технологией `.browser.js` (и производных от нее) эта зависимость удовлетворяется автоматически.\n\nМожно использовать `i-bem.js` как часть полного стека БЭМ-инструментов. В этом случае свой проект удобно создавать на основе шаблонного репозитория [project-stub](https://ru.bem.info/platform/project-stub/), в котором настроена автоматическая установка зависимых библиотек и сборка.\n\nЕсли не планируется использование других технологий БЭМ-платформы, достаточно поместить код библиотеки `bem-core` в существующий проект.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-context.en.md",
    "content": "<a name=\"context\"></a>\n\nContext\n-------\n\n**A block instance methods** are executed in the context of the block instance JS object. The keyword `this` in the block instance methods references the JS object of the **block instance**.\n\n**Static methods** are executed in the context of the JS object that corresponds to the block class. The keyword `this` in a block static methods references the **block class**.\n\n\n> **Note** When developing blocks using `i-bem.js` in internal block methods that are not intended for use outside the block, it is customary to assign names that start with an underscore. For example, `_onClick`.\n\n### Properties of a block instance\n\n#### With DOM representation\n\n* `params` is a hash of parameters passed to the block instance during initialization.\n* `domElem` is a jQuery object containing references to DOM elements that the block is [bound](./i-bem-js-html-binding.en.md) to.\n\n#### Without DOM representation\n\n* `params` is a hash of parameters passed to the block instance during initialization.\n\n<a name=\"spec-fields\"></a>\n\n#### Helper properties\n\nA block instance provides a set of helper properties:\n\n* `__self` — For access to static properties and methods of the block and its instance.\n\n**Example**\n\nCalling `staticMethod` in the `onEvent` method of the `my-block` block instance.\n\n```js\nBEMDOM.decl('my-block', {\n    onEvent : function() {\n        this.__self.staticMethod(); // calling a static method\n        this.doMore();\n    }\n}, {\n    staticMethod : function() { /* ... */ }; // defining a static method\n});\n```\n\n* `__base` – For calling the implementation of the method with the same name from the base class that this one inherits from (`super call`).\n\n**Example**\n\nCalling the base implementation of the `_onClick` method of the `button` base class.\n\n```js\nBEMDOM.decl({ block : 'my-button', baseBlock : 'button' }, {\n    _onClick : function() {\n        this.__base(); // calling the base _onClick\n        this.doMore();\n    }\n});\n```\n\nHelper properties are provided by the [inherit](../../common.blocks/inherit) module, which implements the inheritance mechanism in `bem-core`.\n\n### Static block properties\n\n<a name=\"spec-fields-static\"></a>\n\n#### Helper properties\n\nHelper properties are available in the declaration of a block static methods:\n\n* `__base` – For calling the implementation of the method with the same name from the base class that this one inherits from (`super call`).\n\n```js\nBEMDOM.decl({ block : 'extra', baseBlock : 'my-block' },\n    { /* ... */ },\n    {\n        staticMethod: function() {\n            this.__base();\n            this.doMore();\n        }\n    }\n);\n```\n\n### Static properties of the BEMDOM module\n\n* `scope` — The root element of the DOM tree being processed. Allows executing several different versios of `i-bem.js` in the same runtime. By default, contains a reference to the `body` jQuery object.\n* `doc` — A reference to the `document` jQuery object.\n* `win` — A reference to the `window` jQuery object.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-context.ru.md",
    "content": "## Контекст\n\n**Методы экземпляра** исполняются в контексте JS-объекта экземпляра блока и элемента. Ключевое слово `this` в методах экземпляра блока ссылается на JS-объект **экземпляра**.\n\n**Статические методы** исполняются в контексте JS-объекта, соответствующего классу блока или элемента. Ключевое слово `this` в статических методах блока ссылается на **класс**.\n\n> **Примечание** При разработке с использованием `i-bem.js` внутренним методам блока и элемента, не предназначенным для использования извне, принято давать имена, начинающиеся с символа подчеркивания. Например, `_onClick`.\n\n### Свойства и методы экземпляра блока и элемента\n\n#### С DOM-представлением\n\n* `params` – [хеш параметров](./i-bem-js-params.ru.md), соответсвующий параметрам заданных в HTML-экземпляра.\n* `domElem` – объект jQuery, содержащий ссылки на DOM-элементы, к которым [привязан](./i-bem-js-html-binding.ru.md#Привязка-js-блоков-к-html) экземпляр.\n\n#### Без DOM-представления\n\n* `params` – хеш параметров, переданных экземпляру при инициализации.\n\n#### Вспомогательные свойства\n\nЭкземпляр предоставляет набор вспомогательных свойств:\n\n* `__self` – для доступа к статическим свойствам и методам из экземпляра.\n\n**Пример**\n\nВызов статического метода `staticMethod()` в методе `_onEvent()` экземпляра блока `my-block`.\n\n```js\nbemDom.declBlock('my-block', {\n    _onEvent : function() {\n        this.__self.staticMethod(); // вызов статического метода\n        this.doMore();\n    }\n}, {\n    staticMethod : function() { /* ... */ } // определение статического метода\n});\n```\n\n* `__base` – для вызова реализации одноименного метода из базового класса, от которого [наследуется](./i-bem-js-decl.ru.md#Наследование) данный («super call»).\n\n**Пример**\n\nВызов базовой реализации метода `_onClick` базового класса `button`.\n\n```js\nmodules.define('my-button', ['i-bem-dom', 'button'], function(provide, bemDom, Button) {\n\nprovide(bemDom.declBlock(this.name, Button, {\n    _onClick : function() {\n        this.__base(); // вызываем базовый _onClick\n        this.doMore();\n    }\n}));\n\n});\n```\n\nВспомогательные свойства предоставляются модулем [inherit](../../common.blocks/inherit/inherit.ru.md), реализующим механизм наследования в `bem-core`.\n\n#### Вспомогательные методы\n\n* `_nextTick(fn)` — производит асинхронный вызов функции `fn` `{Function}`, в следующем витке событийного цикла. Функция `fn` вызывается в контексте текущего экземпляра, при условии, что он ещё существует.\n\n**Пример**\n\nПодписываемся на событие `pointerclick` на документе в следующем витке событийного цикла, чтобы попап не закрывался (по клику во вне) до того, как обработается клик на нём.\n\n```js\nmodules.define('popup', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.decl(this.name, {\n    onSetMod : {\n        'visible' : {\n            'true' : function() {\n                this._nextTick(function() {\n                    this._domEvents(bemDom.doc).on('pointerclick', this._onDocPointerClick);\n                });\n            }\n        }\n    },\n\n    _onDocPointerClick : function() {\n        // ...\n        this.delMod('visible');\n    }\n}));\n\n});\n```\n\n### Статические свойства блока и элемента\n\n#### Вспомогательные свойства\n\nВ декларации статических методов доступны вспомогательные свойства:\n\n* `__base` – для вызова реализации одноименного метода из базового класса, от которого наследуется данный («super call»).\n\n```js\nmodules.define('my-button', ['i-bem-dom', 'button'], function(provide, bemDom, Button) {\n\nprovide(bemDom.declBlock(this.name, Button,\n    { /* ... */ },\n    {\n        staticMethod: function() {\n            this.__base();\n            this.doMore();\n        }\n    }\n));\n\n});\n```\n\n### Статические свойства модуля `i-bem-dom`\n\n* `scope` – корневой элемент обрабатываемого DOM-дерева. Позволяет выполнять несколько разных версий `i-bem.js` в одном рантайме. По умолчанию содержит ссылку на jQuery объект `body`;\n* `doc` – ссылка на jQuery объект `document`;\n* `win` – ссылка на jQuery объект `window`.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-decl.en.md",
    "content": "<a name=\"decl\"></a>\n\nBlock declaration\n-----------------\n\nA block JS implementation defines the behavior of a specific class of web interface elements. In the actual interfaces, each block can be represented by multiple instances.\nA block instance implements the functionality of its class and has its own independent state.\n\nIn **object-oriented programming** terms:\n\n* A block is a class\n* And a block instance is a class instance\n\nIn accordance with OOP, all the functionality of a block is implemented modularly in the methods of the class *(=block)*.\n\nThe block methods are divided into:\n\n* Block instance methods\n* Static methods\n\nThe code of a block in `i-bem.js` is called a **declaration** to emphasize the declarative programming style\nadopted in BEM.\n\nA block behavior is programmed in declarative style as statements: `set of conditions` — `block reaction`.\n\n<a name=\"decl-syntax\"></a>\n\n### Declaration syntax\n\n#### Blocks with DOM representation\n\n##### Declaring a new block without a parent\n\nTo declare a new JS block **with a DOM representation** (bound to an HTML element), use the `decl` method of the [ym](https://github.com/ymaps/modules) module in `i-bem__dom`.\n\nThe `decl` method accepts the following arguments:\n\n1.  A block description as `{String}` or `{Object}`.\n2.  Methods of the block instance — `{Object}`.\n3.  Static methods — `{Object}`.\n\nThe declared methods will be applied to all instances of the block, regardless of their states (modifiers).\n\n**Example**\n\nDeclaration of methods for the `button` block.\n\n```js\nmodules.define('button', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name,\n    {\n        /* instance's methods */\n    },\n    {\n        /* static methods */\n    })\n);\n\n});\n```\n\nThe `this.name` field of the `ym` context is passed to the `BEMDOM.decl` method as the first argument. It contains a reference to the name of the block specified as the first argument of `modules.define`.\n\n<a name=\"bem-decl\"></a>\n\n#### Blocks without DOM representation\n\nFor declaring blocks without DOM representation, use the `decl` method of the [ym](https://github.com/ymaps/modules) module in `i-bem`.\n\nThe method accepts the same parameters as the `decl` method of the `i-bem__dom` module:\n\n```js\nmodules.define('my-block', ['i-bem'], function(provide, BEM) {\n\nprovide(BEM.decl(this.name,\n    {\n        /*  instance's methods */\n    },\n    {\n        /* static methods */\n    })\n);\n\n});\n```\n\n> **Note** It is convenient to format infrastructure code as a block without DOM representation if you are planning to use BEM block APIs in it (states expressed as modifiers, BEM events, and so on). If you are not planning to use the BEM subject domain, you can format infrastructure code as a [ym](https://github.com/ymaps/modules) module.\n>\n> **Example**\n>\n> ```js\n> modules.define('router', function(provide) {\n>\n> provide({\n>     route : function() { /* ... */ }\n> });\n>\n> });\n> ```\n\n<a name=\"inher\"></a>\n\n### Block inheritance\n\nVarious blocks in a project often use identical functionality.\nFor example, several blocks might use AJAX to request data from the backend,\nperform the same operations with the DOM tree, and so on. To avoid unnecessary repetitions in the code, the shared functionality can be encapsulated as modules, then added to blocks.\n\nInheritance allows reusing block functionality by extending it with new logic.\nSeveral inheritance mechanisms are available in `i-bem.js`. The choice of a particular mechanism depends on the needs of the block being created.\n\n<a name=\"inher-simple\"></a>\n\n#### Simple inheritance\n\nWith simple inheritance, the block being created is declared as a descendant of an existing one. To do this:\n\n1.  Specify the base block in the module system dependencies.\n2.  Pass a reference to the base block in the special `baseBlock` field in the declaration.\n\nFor example, the `bblock` block inherits from the `ablock` block:\n\n```js\nmodules.define('ablock', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, {}));\n\n});\n\nmodules.define('bblock', ['i-bem__dom', 'ablock'], function(provide, BEMDOM, ABlock) {\n\nprovide(BEMDOM.decl({ block : this.name, baseBlock : ABlock }));\n\n});\n```\n\nThis mechanism allows using the methods of the base block inside a derived block.\nTo call base block methods of the same name, use the [helper property](i-bem-js-context.en.md#helper-property) `this.__base`.\n\n> **Note** You can create inheritance chains in `i-bem`, meaning that a block inherits from another one that, in turn, inherits from a third block, and so on.\n\n<a name=\"inher-over\"></a>\n\n#### Redefining a block\n\nTo create a variation of an existing block that alters or supplements its functionality, you can **redefine** a base block on the project *redefinition level*.\n\nIn the project, create a declaration of a new block with the same name as the base block. As a result, the block will have access to all the base block functionality. However, the implementation of methods and modifiers with the same name will be taken from the new declaration.\n\n```js\nmodules.define('ablock', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, {})); // Declaring the base block\n\n});\n\nmodules.define('ablock', function(provide, ABlock) {\n\nprovide(ABlock.decl({})); // Redefining the base block\n\n});\n```\n\nThis type of inheritance is often used when working with library blocks.\n\n<a name=\"inher-over-modifier\"></a>\n\n##### Adding a modifier to a block\n\nAccording to the BEM methodology, a block states must be defined by [modifers](i-bem-js-states.en.md#modifers).\nSo in order to extend a block functionality, you often need to implement support for new modifiers.\n\nTo add a modifier, pass the redefined block `decl` method:\n\n* A hash with the `modName` and `modVal` keys. The `modName` value is a string with the modifier name. The `modVal` value is a string with the modifier value.\n* A hash of methods that will be available for the block with the corresponding modifier. If there are methods and modifiers of the same name, their implementation from the hash is used.\n\n```js\nmodules.define('ablock', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, {})); // Declaring the base block\n\n});\n\nmodules.define('ablock', function(provide, ABlock) {\n\nprovide(ABlock.decl({ modName : 'm1', modVal : 'v1' }, {})); // Redefining the base block with the modifier _m1_v1\n\n});\n```\n\n> **Note** The block [static methods](./i-bem-js-context.en.md) will be available to all its instances, *regardless of modifier values*. Modifiers are properties of the block instance, but static methods belong to the block class and do not take the status of modifiers into account.\n\n<a name=\"inher-mixins\"></a>\n\n#### Mixed blocks\n\nIn `i-bem.js`, a special type of block is used for adding needed\nfunctionality to blocks — **mixed blocks**. The main feature of mixed blocks is that they do not participate in the inheritance chain. This means their functionality can be combined with other blocks, without risk of breaking their [relationships with parent blocks](i-bem-js-context.en.md#relationships-with-parent-blocks) (`this.__base`).\n\n<a name=\"inher-mixins-declwithmix\"></a>\n\n##### Adding mixed blocks\n\nTo add one or more mixed blocks to a block, assign a value to the optional `baseMix` field in the block declaration. The value is an array of strings — the names of mixed blocks to add in:\n\n```js\nmodules.define('my-block', ['i-bem__dom', 'foo', 'bar'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl({ block : this.name, baseMix : ['foo', 'bar']},\n    { /* instance's methods */ },\n    { /* static methods */ }\n}));\n\n});\n```\n\n<a name=\"inher-mixins-mixindecl\"></a>\n\n##### Mixed block declaration\n\nOnly blocks created using `declMix` can be used as mixed blocks.\nThe method accepts the block declaration in the same format as for the `decl` method.\n\n```js\nmodules.define('mymix', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.declMix('mymix', //only a string with the name\n    { /* instance's methods */ },\n    { /* static methods */ }\n}));\n\n});\n```\n\n> **Note** You can't instantiate a mixed block and use it as an independent block.\n\n<a name=\"trigger-decl\"></a>\n\n#### Trigger declaration\n\n[Triggers](./i-bem-js-states.en.md) that are executed when setting modifiers are described in the block declaration. The following properties are reserved for this purpose in the hash of the block instance methods:\n\n* `beforeSetMod` — Triggers called before setting **block modifiers**.\n* `beforeElemSetMod` — Triggers called before setting **element modifiers**.\n* `onSetMod` — Triggers called after setting **block modifiers**.\n* `onElemSetMod` — Triggers called after setting block **element modifiers**.\n\n```js\nmodules.define('block-name', function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name,\n    {\n        /* instance's methods */\n        beforeSetMod: { /* triggers before setting block modifiers*/}\n        beforeElemSetMod: { /* triggers before setting element modifiers*/}\n        onSetMod: { /* triggers after setting block modifiers */ }\n        onElemSetMod: { /* triggers after setting element modifiers */ }\n    },\n    {\n        /* static methods */\n    }\n));\n\n});\n```\n\nThe value of the `beforeSetMod` and `onSetMod` properties is a hash associating changes to modifiers with triggers. A trigger receives the following arguments:\n\n* `modName` – The modifier name.\n* `modVal` – The value of the modifier being set.\n* `prevModVal` – The previous modifier value. For `beforeSetMod`, this is the current value of the modifier, which will be changed to `modVal` if the trigger doesn't return `false`.\n\n```js\n{\n    'mod1': function(modName, modVal, prevModVal) { /* ... */ }, // setting mod1 to any value\n    'mod2': {\n        'val1': function(modName, modVal, prevModVal) { /* ... */ }, // trigger to set mod2 to the value val1\n        'val2': function(modName, modVal, prevModVal) { /* ... */ }, // trigger to set mod2 to the value val2\n        '': function(modName, modVal, prevModVal) { /* ... */ } // trigger to delete the mod2 modifier\n    }\n    'mod3': {\n        'true': function(modName, modVal, prevModVal) { /* ... */ }, // trigger to set the simple modifier mod3\n        '': function(modName, modVal, prevModVal) { /* ... */ }, // trigger to delete the simple modifier mod3\n    },\n    '*': function(modName, modVal, prevModVal) { /* ... */ } // trigger to set any modifier to any value\n}\n```\n\nThe shorthand for a trigger to set any block modifier to any value is:\n\n```js\nbeforeSetMod: function(modName, modVal, prevModVal) { /* ... */ }\nonSetMod: function(modName, modVal, prevModVal) { /* ... */ }\n```\n\nTriggers to set **element modifiers** are described in the `beforeElemSetMod` and `onElemSetMod` properties. The hash in the property values has an extra nesting level — the **element name**.\nThe trigger is passed the following as arguments:\n\n* `elem` — The element name.\n* `modName` – The modifier name.\n* `modVal` – The value of the modifier being set.\n* `prevModVal` – The previous modifier value. For `beforeSetMod`, this is the current value of the modifier, which will be changed to `modVal` if the trigger doesn't return `false`.\n\n```js\n{\n    'elem1': {\n        'mod1': function(elem, modName, modVal, prevModVal) { /* ... */ }, // trigger to set mod1 of elem 1 to any value\n        'mod2': {\n            'val1': function(elem, modName, modVal, prevModVal) { /* ... */ }, // trigger to set mod2 of elem1 to val1\n            'val2': function(elem, modName, modVal, prevModVal) { /* ... */ } // trigger to set mod2 of elem1 to val2\n            }\n        },\n    'elem2': function(elem, modName, modVal, prevModVal) { /* ... */ } // trigger to set any modifier of elem2 to any value\n}\n```\n\nShorthand for a trigger to set any modifier of the `elem` element to any value:\n\n```js\nbeforeElemSetMod: { 'elem1': function(elem, modName, modVal, prevModVal) { /* ... */ } }\nonElemSetMod: { 'elem1': function(elem, modName, modVal, prevModVal) { /* ... */ } }\n```\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-decl.ru.md",
    "content": "## Декларация блоков и элементов\n\nJS-реализация блока или элемента описывает поведение определенных частей веб-интерфейса. В конкретных интерфейсах каждый блок или элемент может быть представлен несколькими экземплярами. Экземпляр реализует функциональность своего класса и имеет собственное, независимое состояние.\n\nВ терминах парадигмы **объектно-ориентированного программирования**:\n\n* блок и элемент — классы;\n* экземпляры блока и элемента — экземпляры классов.\n\nВ соответствии с ООП, вся функциональность блока и элемента реализуется модульно в методах класса.\n\nМетоды блока и элемента подразделяются на:\n\n* методы экземпляра;\n* статические методы (методы класса).\n\nКод блока и элемента в `i-bem.js` принято называть **декларацией**, чтобы подчеркнуть принятый в БЭМ декларативный стиль программирования.\n\nПоведение блока программируется в декларативном стиле в виде утверждений: `набор условий` — `реакция блока`.\n\n### Синтаксис декларации\n\n#### Блоки и элементы с DOM-представлением\n\n##### Объявление нового блока без родителя\n\nЧтобы задекларировать новый JS-блок **с DOM-представлением** (привязанный к HTML-элементу), нужно воспользоваться методом `declBlock` [ym-модуля](https://github.com/ymaps/modules) `i-bem-dom`.\n\nМетод `declBlock` принимает аргументы:\n\n1. Имя `{String}` или класс `{Function}` блока.\n2. Методы экземпляра — `{Object}`.\n3. Статические методы (методы класса) — `{Object}`.\n\nОбъявленные методы будут применяться во всех экземплярах блока независимо от их состояний (модификаторов).\n\n**Пример**\n\nДекларация методов для блока `button`.\n\n```js\nmodules.define('button', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name,\n    {\n        /* методы экземпляра */\n    },\n    {\n        /* статические методы */\n    })\n);\n\n});\n```\n\nПоле контекста `ym` `this.name`, передаваемое первым аргументом методу `bemDom.declBlock`, содержит ссылку на имя блока, указанное первым аргументом `modules.define`.\n\n##### Объявление нового элемента без родителя\n\nЧтобы задекларировать новый JS-элемент **с DOM-представлением** (привязанный к HTML-элементу), нужно воспользоваться методом `declElem` [ym-модуля](https://github.com/ymaps/modules) `i-bem-dom`.\n\nМетод `declElem` принимает аргументы:\n\n1. Имя `{String}` или класс `{Function}` блока.\n2. Имя `{String}` или класс `{Function}` элемента.\n3. Методы экземпляра — `{Object}`.\n4. Статические методы (методы класса) — `{Object}`.\n\nОбъявленные методы будут применяться во всех экземплярах элемента независимо от их состояний (модификаторов).\n\n**Пример**\n\nДекларация методов для элемента `menu__item`.\n\n```js\nmodules.define('menu__item', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declElem('menu', 'item',\n    {\n        /* методы экземпляра */\n    },\n    {\n        /* статические методы */\n    })\n);\n\n});\n```\n\n#### Блоки и элементы без DOM-представления\n\nДля декларации блоков и элементов без DOM-представления служат аналогичные методы `declBlock` и `declElem` [ym-модуля](https://github.com/ymaps/modules) `i-bem`.\n\nМетоды принимают те же параметры, что и метод `declBlock` и `declElem` модуля `i-bem-dom`:\n\n```js\nmodules.define('my-block', ['i-bem'], function(provide, bem) {\n\nprovide(bem.declBlock(this.name,\n    {\n        /* методы экземпляра */\n    },\n    {\n        /* статические методы */\n    })\n);\n\n});\n```\n\n> **Примечание** Оформлять инфраструктурный код в виде блока без DOM-представления удобно, если в нем планируется использовать API БЭМ-блоков (состояния, выражаемые модификаторами, БЭМ-события и т. п.). Если использовать БЭМ-предметную область не планируется, инфраструктурный код можно оформлять в виде [ym-модуля](https://github.com/ymaps/modules).\n>\n> **Пример**\n>\n> ```js\n> modules.define('router', function(provide) {\n>\n> provide({\n>     route : function() { /* ... */ }\n> });\n>\n> });\n> ```\n\n### Наследование\n\nОдна и та же функциональность может быть востребована в нескольких блоках или элементах проекта.\nНапример, разные блоки могут обращаться за данными к бэкенду, используя AJAX, или совершать однотипные операции с DOM-деревом и т.д.\nЧтобы избежать ненужных повторов в коде, общую функциональность можно инкапсулировать в виде модулей, а затем добавлять к блокам.\n\nНаследование позволяет повторно использовать функциональность блока, расширяя её новой логикой.\nВ `i-bem.js` доступно несколько механизмов наследования. Выбор конкретного механизма зависит от специфики создаваемого блока.\n\n#### Простое наследование\n\nВ случае простого наследования создаваемый блок объявляется как наследник существующего. Для этого нужно:\n\n1. Указать базовый блок в зависимостях модульной системы.\n2. Передать ссылку на базовый блок во втором параметре декларации.\n\nНапример, блок `b-block` наследуется от блока `a-block`:\n\n```js\nmodules.define('a-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, {}));\n\n});\n\nmodules.define('b-block', ['i-bem-dom', 'a-block'], function(provide, bemDom, ABlock) {\n\nprovide(bemDom.declBlock(this.name, ABlock, {}));\n\n});\n```\n\nАналогично для элемента:\n\n```js\nmodules.define('block__a-elem', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declElem('block', 'a-elem', {}));\n\n});\n\nmodules.define('block__b-elem', ['i-bem-dom', 'block__a-elem'], function(provide, bemDom, BlockAElem) {\n\nprovide(bemDom.declElem('block', 'b-elem', BlockAElem, {}));\n\n});\n```\n\nТакой механизм позволяет использовать методы базового блока или элемента внутри производного. Для вызова одноименных методов базового блока служит [вспомогательный метод](./i-bem-js-context.ru.md#Вспомогательные-свойства) `this.__base`.\n\n> **Примечание** В `i-bem` можно создавать цепочки наследования – блок наследуется от другого, который, в свою очередь, наследуется от третьего и т.д.\n\n#### Доопределение блока или элемента\n\nЧтобы добавить и/или изменить поведение уже существующего блока или элемента, можно **доопределить** базовый класс на *уровне переопределения* проекта.\n\nДля этого в проекте создается декларация блока или элемента с тем же именем, что и у базового. В результате блоку будет доступна вся функциональностью базового. Реализация одноименных методов и модификаторов, при этом, будет взята из новой декларации.\n\n```js\nmodules.define('a-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, {})); // Объявляем базовый блок\n\n});\n\nmodules.define('a-block', ['i-bem-dom'], function(provide, bemDom, ABlock) {\n\nprovide(bemDom.declBlock(ABlock, {})); // Доопределяем базовый блок\n\n});\n```\n\nАналогично для элементов с использованием метода `declElem`.\n\nТакая схема наследования часто используется при работе с библиотечными блоками.\n\n##### Добавление модификатора\n\nВ соответствии с БЭМ-методологией состояния блока или элемента должны описываться [модификаторами](./i-bem-js-states.ru.md#Модификаторы). Поэтому чтобы расширить функциональность блока или элемента часто нужно реализовать поддержку новых модификаторов.\n\nДля декларации модификатора используется метод `declMod` у класса блока или элемента. В обоих случаях метод принимает аргументы:\n\n1. Хеш с ключами `modName` и `modVal`. Значением для `modName` – имя модификатора, `{String}`. Значением `modVal`, `{String|Boolean|Array}` — значение модификатора.\n2. Хеш методов, которые будут доступны для блока с соответствующим модификатором. При наличии одноименных методов и модификаторов, будет использована их реализация из хеша.\n\n```js\nmodules.define('a-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.decl(this.name, {})); // Объявляем базовый блок\n\n});\n\nmodules.define('a-block', function(provide, ABlock) {\n\nprovide(ABlock.declMod({ modName : 'm1', modVal : 'v1' }, {})); // Доопределяем базовый блок с модификтором _m1_v1\n\n});\n```\n\nАналогично для элементов.\n\n> **Примечание** [Cтатические методы](./i-bem-js-context.ru.md#Контекст) блока и элемента будут доступны всем их экземплярам *вне зависимости от значений модификаторов*. Модификаторы — это свойства экземпляра, а статические методы принадлежат классу и не учитывают состояния модификаторов.\n\n#### Миксины\n\nВ `i-bem.js` для добавления востребованной функциональности к блокам или элементам могут использоваться специальные сущности – **миксины**. Главная особенность миксинов состоит в том, что они не участвуют в цепочке наследования. Это позволяет примешивать реализованную в них функциональность к другим блокам или элементам без риска нарушить их [связи с родительскими классами](./i-bem-js-context.ru.md#Вспомогательные-свойства) `this.__base`.\n\n##### Декларация блока-микса\n\nДля декларации миксина используется метод `declMixin` модуля `i-bem-dom` (или `i-bem`).\n\nМетод принимает параметры:\n\n1. Методы экземпляра — `{Object}`.\n2. Статические методы (методы класса) — `{Object}`.\n\n```js\nmodules.define('my-mixin', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declMixin(\n    { /* методы экземпляра */ },\n    { /* статические методы */ }\n));\n\n});\n```\n\n> **Примечание** Миксины нельзя инстанцировать и использовать как самостоятельный блок.\n\n##### Примешивание миксина\n\nЧтобы примешать к блоку или элементу один или несколько миксинов, необходимо:\n\n1. Указать необходимые миксины в зависимостях модульной системы.\n2. Передать ссылку миксин (или миксины) во втором параметре декларации блока или элемента.\n\n```js\nmodules.define('my-block', ['i-bem-dom', 'my-mixin'], function(provide, bemDom, myMixin) {\n\nprovide(bemDom.declBlock(this.name, myMixin,\n    { /* методы экземпляра */ },\n    { /* статические методы */ }\n));\n\n});\n```\n\nВ случае нескольких миксинов:\n\n```js\nmodules.define('my-block', ['i-bem-dom', 'a-mixin', 'b-mixin'], function(provide, bemDom, aMixin, bMixin) {\n\nprovide(bemDom.declBlock(this.name, [aMixin, bMixin], {}));\n\n});\n```\n\nАналогично для элемента с использованием метода `declElem`.\n\n> **Примечание** В случае если нужно задекларировать наследование и миксины одновременно, во второй параметр следует передавать массив.Родительский класс обязательно должен быть первым элементом этого массива.\n>\n> ```js\n> modules.define('b-block', ['i-bem-dom', 'a-block', 'a-mixin'], function(provide, bemDom, ABlock, aMixin) {\n>\n> provide(bemDom.declBlock(this.name, [ABlock, aMixin], {}));\n>\n> });\n> ```\n\n#### Декларация триггеров\n\n[Триггеры](./i-bem-js-states.ru.md#Триггеры-на-установку-модификаторов), выполняемые при установке модификаторов, описываются в декларации блока или элемента. Для этого в хеше методов экземпляра зарезервированы свойства:\n\n* `beforeSetMod` — триггеры, вызываемые до установки **модификаторов**;\n* `onSetMod` — триггеры, вызываемые после установки **модификаторов**;\n\n```js\nmodules.define('my-block', function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name,\n    {\n        /* методы экземпляра */\n        beforeSetMod: { /* триггеры до установки модификаторов */},\n        onSetMod: { /* триггеры после установки модификаторов */ },\n    },\n    {\n        /* статические методы */\n    }\n));\n\n});\n```\n\nЗначение свойств `beforeSetMod` и `onSetMod` — хеш, связывающий изменения модификаторов с триггерами. Триггер получает аргументами:\n\n* `modName` – имя модификатора;\n* `modVal` – выставляемое значение модификатора;\n* `prevModVal` – предыдущее значение модификатора. Для `beforeSetMod` это текущее значение модификатора, которое будет заменено на `modVal`, если триггер не вернет `false`.\n\n```js\nbemDom.declBlock(this.name, {\n    onSetMod: {\n        'mod1': function(modName, modVal, prevModVal) { /* ... */ }, // установка mod1 в любое значение\n        'mod2': {\n            'val1': function(modName, modVal, prevModVal) { /* ... */ }, // триггер на установку mod2 в значение val1\n            'val2': function(modName, modVal, prevModVal) { /* ... */ }, // триггер на установку mod2 в значение val2\n            '': function(modName, modVal, prevModVal) { /* ... */ } // триггер на удаление модификатора mod2\n        },\n        'mod3': {\n            'true': function(modName, modVal, prevModVal) { /* ... */ }, // триггер на установку простого модификатора mod3\n            '': function(modName, modVal, prevModVal) { /* ... */ } // триггер на удаление простого модификатора mod3\n        },\n        'mod4': {\n            '!val1' : function() { /* ... */ } // декларация для изменения mod4 в любое значение, кроме val1\n            '~val2' : function() { /* ... */ } // декларация для изменения значения mod4 из val2 в любое другое значение\n        },\n        '*': function(modName, modVal, prevModVal) { /* ... */ } // триггер на установку любого модификатора в любое значение\n    }\n})\n```\n\nДля триггера на установку любого модификатора блока в любое значение существует сокращенная форма записи:\n\n```js\nbeforeSetMod: function(modName, modVal, prevModVal) { /* ... */ }\nonSetMod: function(modName, modVal, prevModVal) { /* ... */ }\n```\n\nАналогично для элементов с использованием метода `declElem`.\n\n> **Примечание** Если модификаторы были заданы в HTML-элементе блока или элемента до момента его инициализации, триггеры на установку данных модификаторов **не выполняются**. Экземпляр в этом случае получает начальное состояние, а не меняет его.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-dom.en.md",
    "content": "<a name=\"dom\"></a>\n\n## Working with the DOM tree\n\n<a name=\"domElem\"></a>\n\n### DOM node of a block instance\n\nThe `this.domElem` field is reserved in the context of a block instance with DOM representation. This field contains a jQuery object with references to all the DOM nodes that this instance is connected to.\n\n<a name=\"elem-api\"></a>\n\n### Elements\n\nBEM elements of blocks are represented in `i-bem.js` as DOM nodes nested in the DOM node of a block instance.\n\nTo access elements DOM nodes and work with their modifiers, use the block instance API:\n\n* Cached access: `elem(elems, [modName], [modVal])`. An element\n    obtained this way does not need to be stored in a variable.\n\n```js\nBEMDOM.decl('link', {\n    setInnerText: function() {\n        this.elem('inner').text('Link text');\n        /* ... */\n        this.elem('inner').text('Another text');\n    }\n});\n```\n\n* Uncached access: `findElem(elems, [modName], [modVal])`.\n\n```js\nBEMDOM.decl('link', {\n    setInnerText: function() {\n        var inner = this.findElem('inner');\n        inner.text('Link text');\n        /* ... */\n        inner.text('Another text');\n    }\n});\n```\n\nWhen [block elements are added and removed dynamically](#block-elements-are-added-and-removed-dynamically), the cache of elements\nmay need to be cleared. Use the `dropElemCache('elements')` method for this purpose. It accepts a string with a space-separated list of names of elements to drop the cache for.\n\n```js\nBEMDOM.decl('attach', {\n    clear: function() {\n        BEMDOM.destruct(this.elem('control'));\n        BEMDOM.destruct(this.elem('file'));\n        return this.dropElemCache('control file');\n    }\n});\n```\n\n<a name=\"api-find\"></a>\n\n### Searching for block instances in the DOM tree\n\nAccessing a different block in `i-bem.js` is performed from the current block\nlocated on a particular node of the DOM tree. The search for other blocks in\nthe DOM tree can be made in three directions (axes) relative to\nthe current block DOM node:\n\n* **Inside the block** — On DOM nodes nested in the DOM node of the current block. Helper methods: `findBlocksInside([elem], block)` and `findBlockInside([elem], block)`.\n* **Outside the block** — On DOM nodes that the current block DOM node\n    is a descendent of. Helper methods: `findBlocksOutside([elem], block)` and `findBlockOutside([elem], block)`.\n* **On itself** — On the same DOM node where the current block is located. This is relevant when [multiple JS blocks are located on a single DOM node](i-bem-js-html-binding.en.md#multiple-js-blocks-are-located-on-a-single-dom-node) (a mix). Helper methods: `findBlocksOn([elem], block)` and `findBlockOn([elem], block)`.\n\nThe signature of the helper methods is identical:\n\n* `[elem]` `{String|jQuery}` — The name or DOM node of the block element.\n* `block` `{String|Object}` – Name or description of the block being searched for. A description is a hash in the format `{ block : 'name', modName : 'foo', modVal : 'bar' }`.\n\nThe helper methods for searching are paired. They differ in the values they return:\n\n* `findBlocks<Direction>` – Returns an array of found blocks.\n* `findBlock<Direction>` – Returns the first block found.\n\n**Example**\n\nWhen the `disabled` modifier is toggled, the instance of the `attach` block finds the `button` block nested inside it and toggles its `disabled` modifer to the same value that it received itself:\n\n```js\nmodules.define('attach', ['i-bem__dom', 'button'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, {\n    onSetMod: {\n        'disabled': function(modName, modVal) {\n            this.findBlockInside('button').setMod(modName, modVal);\n        }\n    }\n}));\n\n});\n```\n\n> **Note** Don't use jQuery selectors to search for blocks and elements. `i-bem.js` provides a high-level API for accessing DOM nodes of blocks and elements. Accessing the DOM tree directly makes the code less robust to changes in the BEM libraries, and may cause errors that are difficult to detect.\n\n<a name=\"dynamic\"></a>\n\n### Dynamic changes to blocks and elements in the DOM tree\n\nIn modern interfaces, it is often necessary to create new\nfragments of the DOM tree and replace old ones as part of the workflow (using AJAX). The following functions\nare provided in `i-bem.js` for adding and replacing\nfragments of the DOM tree.\n\n* Add a DOM fragment:\n\n  * `append` — to the end of the specified context.\n  * `prepend` — to the beginning of the specified context.\n  * `before` — before the specified context.\n  * `after` — after the specified context.\n\n* Replace a DOM fragment:\n\n  * `update` — inside the specified context.\n  * `replace` — replace the specified context with a new DOM fragment.\n\nAll the functions automatically [initialize blocks on the updated fragment of the DOM tree](i-bem-js-init.en.md#initialize-blocks-on-the-updated-fragment-of-the-dom-tree).\n\nTo simplify the creation of BEM entities on updated fragments\nof the DOM tree, you can use the\n[BEMHTML](https://en.bem.info/technology/bemhtml/current/intro/)template engine by enabling\nit as a [ym](https://github.com/ymaps/modules) module. BEM entities are described in\n[BEMJSON](https://en.bem.info/technology/bemjson/current/bemjson/)\n format directly in the block code. The `BEMHTML.apply` function generates\nHTML elements for the BEMJSON declarations according to\nBEM naming conventions.\n\n**Example**\n\nThe `attach` block `_updateFileElem` method deletes the `file` element if it exists, and creates a new element using the `BEMHTML.apply` function:\n\n```js\nmodules.define(\n    'attach',\n    ['BEMHTML', 'strings__escape', 'i-bem__dom'],\n    function(provide, BEMHTML, escape, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, {\n    _updateFileElem : function() {\n        var fileName = extractFileNameFromPath(this.getVal());\n        this.elem('file').length && BEMDOM.destruct(this.elem('file'));\n        BEMDOM.append(\n            this.domElem,\n            BEMHTML.apply({\n                block : 'attach',\n                elem : 'file',\n                content : [\n                    {\n                        elem : 'icon',\n                        mods : { file : extractExtensionFromFileName(fileName) }\n                    },\n                    { elem : 'text', content : escape.html(fileName) },\n                    { elem : 'clear' }\n                ]\n            }));\n        return this.dropElemCache('file');\n    }\n}));\n\n});\n```\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-dom.ru.md",
    "content": "## Работа с DOM-деревом\n\n### DOM-узел экземпляра блока и элемента\n\nВ контексте экземпляра блока и элемента с DOM-представлением зарезервировано поле `this.domElem`, содержащее jQuery-объект со ссылками на все DOM-узлы, с которыми связан данный экземпляр.\n\n### Поиск экземпляров блоков и элементов в DOM-дереве\n\nОбращение к другому блоку в `i-bem.js` выполняется из текущего блока, размещенного на определенном узле DOM-дерева. Поиск других блоков в DOM-дереве может вестись по трём направлениям (осям) относительно DOM-узла текущего блока:\n\n* **Внутри блока** — на DOM-узлах, вложенных в DOM-узел текущего блока.\n\n  Вспомогательные методы:\n\n  * `findChildBlock(block)`;\n  * `findChildBlocks(block)`;\n  * `findChildElem(elem)`;\n  * `findChildElems(elem)`.\n\n\n* **Снаружи блока** — на DOM-узлах, потомком которых является DOM-узел текущего блока.\n\n  Вспомогательные методы:\n\n  * `findParentBlock(block)`;\n  * `findParentBlocks(block)`;\n  * `findParentElem(elem)`;\n  * `findParentElems(elem)`.\n\n\n* **На себе** — на том же DOM-узле, на котором размещен текущий блок. Это актуально в случае [размещения нескольких JS-блоков на одном DOM-узле](./i-bem-js-html-binding.ru.md#Один-html-элемент--несколько-js-блоков) (микс).\n\n  Вспомогательные методы:\n\n  * `findMixedBlock(block)`;\n  * `findMixedBlocks(block)`;\n  * `findMixedElem(elem)`;\n  * `findMixedElems(elem)`.\n\n  Методы `findMixedBlocks(block)` и `findMixedElems(elem)` могут возвращать больше одного экземпляра в случае, когда к [блоку или элементу с несколькими DOM-узлами](./i-bem-js-html-binding.ru.md#Один-js-блок-на-нескольких-html-элементах) примешаны несколько разных экземпляров одного и того же блока (`block`) или (`elem`).\n\nСигнатура вспомогательных методов поиска блоков идентична:\n\n* `block` `{Function|Object}` – класс или описание искомого блока. Описанием служит хеш вида `{ block : MyBlock, modName : 'my-mod', modVal : 'my-val' }`.\n\nДля методов поиска элементов:\n\n* `elem` `{String|Function|Object}` – имя, класс или описание искомого элемента. Описанием служит хеш вида `{ elem : MyElem, modName : 'my-mod', modVal : 'my-val' }` или `{ elem : 'my-elem', modName : 'my-mod', modVal : 'my-val' }`;\n* `[strictMode=false]` `{Boolean}` – нужно ли учитывать вложенность одноимённых блоков.\n\nВспомогательные методы для поиска парные. Различаются возвращаемым значением:\n\n* `find<Direction>Block` и `find<Direction>Elem` – возвращает первый найденный экземпляр\n* `find<Direction>Blocks` `find<Direction>Elems` – возвращает [коллекцию](./i-bem-js-collections.ru.md) найденных экземпляров\n\n**Пример**\n\n```js\nmodules.define('attach', ['i-bem-dom', 'button'], function(provide, bemDom, Button) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod: {\n        'js': {\n            'inited' : function(modName, modVal) {\n                this._button = this.findChildBlock(Button);\n            }\n        }\n    }\n}));\n\n});\n```\n\n> **Примечание** Не используйте jQuery-селекторы для поиска блоков и элементов. `i-bem.js` предоставляет высокоуровневое API для доступа к DOM-узлам блоков и элементов. Прямое обращение к DOM-дереву делает код менее устойчивым к изменениям БЭМ-библиотек и может привести к возникновению сложно обнаруживаемых ошибок.\n\n#### Кэширующие методы поиска экземпляров элементов\n\nДля оптимизации производительности для распространённых случаев поиска элементов одновременно по двум осям (**внутри** и **на себе**), служат кэширующие методы `_elem(elem)` и `_elems(elem)`. Оба метода принимают один параметр:\n\n* `elem` `{String|Function|Object}` – имя, класс или описание искомого элемента. Описанием служит хеш вида `{ elem : MyElem, modName : 'my-mod', modVal : 'my-val' }` или `{ elem : 'my-elem', modName : 'my-mod', modVal : 'my-val' }`.\n\nАналогично с некэширующими методами поиска кеширующие методы различаются возвращаемым значением:\n\n* `_elem()` – возвращает первый найденный экземпляр элемента\n* `_elems()` – возвращает [коллекцию](./i-bem-js-collections.ru.md) найденных экземпляров элементов\n\n**Пример**\n\n```js\nmodules.define('button', ['i-bem-dom', 'button__control'], function(provide, bemDom, ButtonControl) {\n\nprovide(bemDom.declBlock(this.name, {\n    setName : function(name) {\n        this._elem(ButtonControl).setName(name);\n    },\n\n    setValue : function(value) {\n        this._elem(ButtonControl).setValue(value);\n    }\n}));\n\n});\n```\n\n> **Примечание** Результат кеширующих методов нет необходимости сохранять в переменную (см. предыдущий пример). В то время как для некеширующих методов хорошей практикой является единоразовый поиск всего, что нужно, с сохранением в переменную или внутреннее поле.\n\n> **Примечание** В случае использования элементов без JS реализации и [динамического обновления DOM-дерева](#Динамическое-обновление-блоков-и-элементов-в-dom-дереве) может понадобиться метод инвалидации кеша элементов `_dropElemCache('elements')`.\nОн принимает опциональный параметр с одним или несколькими именами элементов через пробел.\n\n#### Кеширующий метод поиска экземпляра блока элемента\n\n* `_block()` — возвращает экземпляр блока для элемента.\n\n**Пример**\n\n```js\nmodules.define('my-form__submit-control', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declElem('my-form', 'submit-control', {\n    _onClick : function() {\n        this._block().submit();\n    }\n}));\n\n});\n```\n\n#### Проверка вложенности\n\n* `containsEntity(entity)` — проверяет вложен ли переданный экземпляр `entity` `{i-bem-dom:Entity}` в текущий экземпляр.\n\n### Динамическое обновление блоков и элементов в DOM-дереве\n\nВ модуле `i-bem-dom` предусмотрены следующие функции для добавления и замены фрагментов DOM-дерева.\n\n* Удалить DOM-фрагмент:\n\n  * `destruct(ctx, [excludeSelf])`\n\n  Сигнатура функции:\n\n  * `ctx` `{jQuery}` – корневой DOM-элемент. Удаляется со всем вложенными DOM-узлами.\n  * `[excludeSelf]` `{Boolean}` – не удалять корневой DOM-элемент, если значение `true`. По умолчанию `false`.\n\n* Добавить DOM-фрагмент:\n\n  * `append(ctx, content)` —  в конец указанного контекста;\n  * `prepend(ctx, content)` — в начало указанного контекста;\n  * `before(ctx, content)` — перед указанным контекстом;\n  * `after(ctx, content)` — после указанного контекста;\n\n* Заместить DOM-фрагмент:\n\n  * `update(ctx, content)` —  внутри указанного контекста;\n  * `replace(ctx, content)` — заменить указанный контекст новым DOM-фрагментом.\n\n  Сигнатура функций добавления и замены идентична:\n\n  * `ctx` `{jQuery}` – DOM-элемент\n  * `content` `{jQuery|String}` – содержимое\n\nВсе функции возвращают DOM-элемент с содержимым для которого была выполнена [инициализация для новых блоков и элементов](./i-bem-js-init.ru.md#Инициализация-блоков-и-элементов-на-фрагменте-dom-дерева).\n\nЧтобы упростить создание БЭМ-сущностей на обновляемых фрагментах DOM-дерева, можно использовать шаблонизатор [BEMHTML](https://ru.bem.info/platform/bem-xjst/), подключив его в качестве [ym-модуля](https://github.com/ymaps/modules). БЭМ-сущности описываются в формате [BEMJSON](https://ru.bem.info/platform/bemjson/) непосредственно в коде блока. Функция `BEMHTML.apply` генерирует HTML-элементы по BEMJSON-декларации в соответствии с правилами именования БЭМ.\n\n**Пример**\n\nМетод `_updateFileElem` блока `attach` удаляет элемент `file`, если он существовал, и создает новый элемент с помощью функции `BEMHTML.apply`:\n\n```js\nmodules.define( 'attach', ['BEMHTML', 'i-bem-dom'], function(provide, BEMHTML, bemDom) {\n\nprovide(bemDom.declBlock(this.name, {\n    _updateFileElem : function() {\n        bemDom.replace(\n            this._elem('file').domElem,\n            BEMHTML.apply({\n                block : 'attach',\n                elem : 'file',\n                content : this.getValue()\n            }));\n        return this;\n    }\n}));\n\n});\n```\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-events.en.md",
    "content": "<a name=\"events\"></a>\n\nEvents\n------\n\nIn `i-bem.js`, two types of events are supported:\n\n* **DOM events** — jQuery events on the DOM node connected with the block. They reflect the user's interaction with the interface (clicks, moving the mouse, entering text, and so on). DOM events are usually handled by the block instance of the DOM nodes where they occur.\n\n* **BEM events** — Private events generated by the block. They make it possible to form an API for [interaction with the block](./i-bem-js-interact.en.md). BEM events are usually handled by an instance of the block that monitors the state of other blocks where events are generated.\n\nDOM events should be used only in *internal* block procedures. Use BEM events for\na block interaction with the *external* environment (other blocks).\n\n<a name=\"delegated-events\"></a>\n\n### Delegating events\n\nHandling BEM events and DOM events can be **delegated** to a container\n(the entire document, or a specific DOM node). In this case, the container\nserves as a handling point for events that occur on any of its\nchild nodes, even if some of the child nodes didn't exist yet\nat the time of subscribing to events.\n\nFor example, a menu block can contain nested blocks — the menu items. Handling\nclicks on the menu items should logically be delegated to the menu\nblock. First, this saves resources on\nsubscribing to events (less resources are consumed by subscribing to a container single event\nthan by subscribing to many events on elements). Second, this makes it possible to add and remove menu items without subscribing to the events of added items or unsubscribing from the events of removed items.\n\nBoth BEM events and DOM events can be delegated.\n\n<a name=\"dom-events\"></a>\n\n### DOM events\n\nInteraction with DOM events in `i-bem.js` is fully implemented using the jQuery framework.\n\n#### Subscribing to DOM events\n\nA set of methods for subscribing to DOM events is reserved on the block instance object:\n\n* `bindTo([elem], event, handler)` — To events of the block main DOM node and its elements DOM nodes.\n* `bindToDoc(event, [data], handler)` – To events of the `document` DOM node.\n* `bindToWin(event, [data], handler)` – To events of the `window` DOM node.\n\n**Example**\n\nAt [initialization of the block instance](./i-bem-js-init.en.md) `my-block`, the `click` event is subscribed to. When this event occurs, the block sets its `size` [modifier](./i-bem-js-states.en.md) to `big`.\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited': function() {\n                this.bindTo('click', function(e) {\n                    this.setMod('size', 'big');\n                });\n            }\n        }\n    }\n});\n```\n\n**Example**\n\nAt [initialization of the block instance](./i-bem-js-init.en.md) `my-form`, it subscribes to the `click` event on the `submit` element. When the event occurs, the `_onSubmit` handler function will be invoked.\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited': function() {\n                this.bindTo('submit', 'click', this._onSubmit);\n            }\n        }\n    },\n\n    _onSubmit : function() { /* ... */ }\n});\n```\n\n> **Note** The handler function is executed in the context of the block instance where the event occurred.\n\n#### Removing subscriptions to DOM events\n\nSubscriptions to DOM events are removed automatically when a block instance is destroyed. However, the block instance object has a set of methods reserved for removing subscriptions manually while the block is working:\n\n* `unbindFrom([elem], event, [handler])` — Unsubscribing from events of the block main DOM node and its elements DOM nodes.\n* `unbindFromDoc(event, [handler])` – Unsubscribing from events of the `document` DOM node.\n* `unbindFromWin(event, [handler])` – Unsubscribing from events of the `window` DOM node.\n\nIf the handler function isn't specified when calling one of these methods, all the handlers are removed that were set by the block on the DOM node for this event.\n\n```js\n_stopKeysListening : function() {\n    this.unbindFromDoc('keydown');  // removing all the handlers of the 'keydown' event\n                                    // set by the block for the 'document' DOM node\n}\n```\n\n#### DOM event object\n\nThe first argument the handler function gets is a jQuery object for the DOM event — [`{jQuery.Event}`](https://api.jquery.com/category/events/event-object/).\n\nThis allows using the `stopPropagation` and `preventDefault` object methods for managing event propagation and the browser reaction to an event.\n\n```js\nBEMDOM.decl('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited': function() {\n                this.bindTo('click', function(e) {\n                    e.stopPropаgation(); // prevents the event from bubbling up\n                    this._onSubmit();\n                });\n            }\n        }\n    },\n\n    _onSubmit : function() {\n        /* ... */\n    }\n});\n```\n\nA DOM event can be generated manually, such as using the jQuery `trigger` function. After the event object, the handler function of the DOM event gets arguments with the parameters that were used to call `trigger` when the event was created.\n\n> **Note** Parameters for the environment and behavior of an event handler function are identical to the jQuery [handler function](http://api.jquery.com/on/#event-handler).\n\n<a name=\"dom-events-delegated\"></a>\n\n#### Delegating DOM events\n\nWe recommend using the `liveBindTo([elem], event, handler)` method to delegate handling DOM events. In the block [static declaration methods](./i-bem-js-decl.en.md), the `live` property is reserved for subscribing to delegated DOM events.\n\n**Example**\n\nAll instances of the `menu` block subscribe to the delegated `click` DOM event for their `item` elements. The `_onItemClick` method of the `menu` block instance will be invoked when any `item` in the menu is clicked. It doesn't matter whether this item existed when the instance was initialized.\n\n```js\nBEMDOM.decl('menu', {\n    _onItemClick : function(e) { /* ... */ }\n}, {\n    live : function() {\n        this.liveBindTo('item', 'click', function(e) {\n            this._onItemClick(e);\n        });\n    }\n});\n```\n\nIf the `live` property is set in the block declaration, the initialization of block instances will be *deferred* until the moment when the block instance is needed ([lazy initialization](./i-bem-js-init.en.md#lazy-initialization)). This moment could be a DOM event on the block instance that was delegated the subscription, or a request sent to the block instance [from another block](./i-bem-js-interact.en.md).\n\n> **Note** A handler function is executed in the context of the nearest block of this type in the direction of the DOM event bubbling (from bottom to top through the DOM tree).\n\nTo use delegated events in a block without deferring initialization, the function set in the `live` property should return `false`:\n\n```js\nmodules.define('my-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name,\n    {\n        _onClick: function() { /* ... */ }  // will be run each time\n                                            // the 'click' event occurs\n    },\n    {\n        live: function() {\n            this.liveBindTo('click', function() { this._onClick() });\n            return false; // block instances will be initialized automatically\n        }\n    }\n));\n\n});\n```\n\n<a name=\"bem-events\"></a>\n\n### BEM events\n\nIn contrast to DOM events, BEM events are not generated on DOM elements, but on **block instances**. Block elements can't generate BEM events.\n\n<a name=\"bem-events-subscribe\"></a>\n\n#### Generating BEM events\n\nThe `emit(event, [data])` method of a block instance is used for generating BEM events.\n\nDOM events occur when a user interacts with a block controls. BEM events can be created as part of their processing by the block. This allows a level of abstraction over DOM events. BEM events are generated as a reaction to DOM events, but subject to certain conditions, such as whether a modifier is present or has a specific value.\n\nFor example, a click on the `submit` button (the `click` DOM event) will generate the `click` **BEM event** only if the block doesn't have the `disabled` modifier set:\n\n```js\nBEMDOM.decl('submit', {\n    onSetMod: {\n        'js': {\n            'inited': function() {\n                this.bindTo('click', this._onClick); // subscribing to the \"click\" DOM event\n            }\n        }\n    },\n\n    _onClick: function() {\n        if(!this.hasMod('disabled')) {\n            this.emit('click'); // creating the \"click\" BEM event\n        }\n    }\n});\n```\n\nYou can pass any data as the second `emit` argument, which will be accessible as the second argument of the handler function.\n\n<a name=\"bem-events-subscribe\"></a>\n\n#### Subscribing to BEM events\n\nThe `on(event, [data], handler, [handlerCtx])` method of a block instance is used for subscribing to BEM events on block instances.\n\n**Example**\n\nAt initialization of an HTML form (an instance of the `my-form` block), a search is performed for the `submit` button embedded in the form, and its `click` BEM event is subscribed to. As a result, when the button (an instance of the `submit` block) is clicked, the `_onSubmit` method is executed for the form (the instance of the `my-form` block).\n\n```js\nBEMDOM.decl('my-form', {\n    onSetMod: {\n        'js': {\n            'inited': function() {\n                this.findBlockInside('submit').on(\n                    'click', // name of the BEM event\n                    this._onSubmit, // method of the 'my-form' block instance\n                    this); // context for executing _onSubmit — the my-form block\n            }\n        }\n    },\n\n    _onSubmit: function() { /* ... */ }\n});\n```\n\n> **Note** If you don't pass the `[handlerCtx]` argument, the context for the handler function will be the block where the BEM event occurred (in the example above, this is the `submit` block).\n\n<a name=\"bem-events-unsubscribe\"></a>\n\n#### Removing subscriptions to BEM events\n\nSubscriptions to BEM events are removed automatically when the block instance is destroyed. To remove a subscription manually, use the\n`un(event, [handler], [handlerCtx])` method of the block instance.\n\n<a name=\"bem-events-modchange\"></a>\n\n#### Events when modifiers are changed\n\nUse the `on(event, [data], handler, [handlerCtx])` block instance method for subscribing to BEM events for changes to a modifier of a block or element. The method accepts the arguments:\n\n* The properties object of the modifier that is being subscribed to.\n* The handler function that is executed when setting the corresponding modifier.\n\nThe object describing the modifier can contain the following reserved properties:\n\n* `modName` `{String}` – Modifier name. Required property.\n* `modVal` `{String}` – Modifier value. Required property. With the value `*`, the subscription is for setting the modifier to **any** value. With the value `''`, the subscription is for **deleting** the modifier. For more information, see the section [Triggers for setting modifiers](i-bem-js-states.en.md#triggers-for-setting-modifiers).\n* `elem` `{String}` – Element name (for element modifiers).\n\n**Example**\n\nAt initialization, the `form` block subscribes to the event of changing a modifier on the nested `submit` block. For example, subscriptions can be for:\n\n* Setting the `disabled` modifier to any value.\n\n    ```js\n    BEMDOM.decl('form', {\n    onSetMod: {\n        'js': {\n            'inited': function() {\n                var submit = findBlockInside('submit');\n                submit.on({ modName : 'disabled', modVal : '*' }, function() {});\n            }\n        }\n    },\n    });\n    ```\n\n* Setting the `'disabled'` modifier to `'true'`.\n\n    ```js\n    submit.on({ modName : 'disabled', modVal : 'true' }, function() {});\n    ```\n\n* Removing the `'disabled'` modifier.\n\n    ```js\n    submit.on({ modName : 'disabled', modVal : '' }, function() {});\n    ```\n\n* Removing the `m1` modifier from the `'control'` element.\n\n    ```js\n    submit.on({ elem : 'control', modName : 'm1', modVal : '' }, function() {});\n    ```\n\n<a name=\"bem-events-delegated\"></a>\n\n#### Delegating BEM events\n\nDelegating BEM events means that the block subscribes to a particular BEM event on **all instances** of the block with the specified name **in the scope of the specified context**.\n\nSubscribing to delegated BEM events is performed using the `MyBlock.on([ctx], event, [data], handler, [handlerCtx])` static method of the block class.\n\n* `{jQuery} [ctx]` — The DOM node where BEM events are monitored (the container). If omitted, the entire document is used as the container.\n* `{String} event` — Name of the BEM event.\n* `{Object} [data]` — Any data passed to the handler function.\n* `{Function} handler` — Event handler function.\n* `{Object} [handlerCtx]` — Context of the event handler function. If omitted, the handler function executes in the context of the block instance where the event occurred.\n\n**Example**\n\nDuring initialization of the `menu` block instance, it subscribes to the `click` BEM event on all links (instances of the `link` block) in the scope of the block DOM node (`this.domElem`). The current block instance is passed as the handler function context.\n\n```js\nmodules.define('menu', ['i-bem__dom', 'link'], function(provide, BEMDOM, Link) {\n\nprovide(BEMDOM.decl(this.name,\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                Link.on( // subscribing to BEM event\n                    this.domElem, // container — the DOM node of the 'menu' block instance\n                    'click', // BEM event\n                    this._onLinkClick, // handler\n                    this); // handler context — an instance of the 'menu' block\n            },\n\n            '' : function() {\n                Link.un( // unsubscribing from the BEM event\n                    this.domElem,\n                    'click',\n                    this._onLinkClick,\n                    this);\n            }\n        }\n    },\n\n    _onLinkClick : function(e) {\n        var clickedLink = e.target; // instance of the 'link' block\n                                    // where the 'click' BEM event occurred\n    }\n));\n\n});\n```\n\nAny BEM events can be delegated, including events for changes to modifiers.\n\n> **Note** **Unsubscribing** from delegated BEM events never happens automatically. Subscriptions should always be removed explicitly using the block static method `un([ctx], event, [handler], [handlerCtx])`.\n\n<a name=\"api\"></a>\n\n### BEM event object\n\nWhen invoked, a handler function gets an argument with an object describing the BEM event. The BEM event object class `events.Event` is defined in the [ym](https://github.com/ymaps/modules) module of [`events`](../../common.blocks/events/events.vanilla.js) in the bem-core library. This object contains the fields:\n\n* `target` — Instance of the block where the BEM event occurred.\n* `data` — Any additional data passed as the `data` argument when subscribing to a BEM event.\n* `result` — The last value returned by this event handler. The same as [jQuery.Event.result](https://api.jquery.com/event.result/).\n* `type` — The type of event. The same as [jQuery.Event.type](https://api.jquery.com/event.type/).\n\nFor more information about properties and methods of a BEM event object, see the [documentation for the 'events' block](../../common.blocks/events).\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-events.ru.md",
    "content": "## События\n\nВ `i-bem.js` поддерживается два вида событий:\n\n* **DOM-события** — события на DOM-узле, связанном с блоком или элементом. Отражают взаимодействие пользователя с интерфейсом (клик, наведение мыши, ввод текста и т.п.). DOM-события обычно обрабатывает тот экземпляр блока или элемента, на DOM-узлах которого они возникают.\n\n* **БЭМ-события** — собственные события, генерируемые блоком или элементом. Позволяют организовать API для [взаимодействия с блоком](./i-bem-js-interact.ru.md#Взаимодействие-блоков-и-элементов). БЭМ-события обычно обрабатывает экземпляр, отслеживающий состояние других блоков или элементов, на которых генерируются события.\n\nDOM-события следует использовать только во взаимодействиях экземпляра со своим DOM-узлом или блока со своими элементами. Для взаимодействия с другими блоками или элементами предназначены БЭМ-события.\n\n### DOM-события\n\nРабота с DOM-событиями в `i-bem-dom` полностью реализована средствами фреймворка jQuery.\n\n#### Подписка на DOM-события\n\n##### Из экземпляра\n\nДля подписки на DOM-события из экземпляра служит метод `_domEvents()`, создающий специальный объект [менеджера событий](#Объект-менеджера-событий).\n\nМетод принимает один опциональный параметр, задающий контекст, который может быть разных типов:\n\n* `elemInstance` `{Elem|BemDomCollection}` – экземпляр или коллекция элементов.\n* `elemClass` `{String|Function|Object}` — класс, имя или описание элемента. Описанием служит хеш вида `{ elem : MyElem, modName : 'my-mod', modVal : 'my-val' }` или `{ elem : 'my-elem', modName : 'my-mod', modVal : 'my-val' }`.\n* `document` `{Document|jQuery}` — документ\n* `window` `{Window|jQuery}` — окно\n\n[Менеджер событий](#Объект-менеджера-событий) обладает необходимым интерфейсом для подписки на события и отписки от них.\n\n**Пример**\n\nВ момент [инициализации экземпляра блока](./i-bem-js-init.ru.md#Инициализация) `my-block` выполняется подписка на событие `click`, при наступлении которого блок выставляет себе [модификатор](./i-bem-js-states.ru.md#Модификаторы) `size` в значение `big`.\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited': function() {\n                this._domEvents().on('click', function() {\n                    this.setMod('size', 'big');\n                });\n            }\n        }\n    }\n});\n```\n\n**Пример**\n\nПри [инициализации экземпляра блока](./i-bem-js-init.ru.md#Инициализация) `my-form` выполняется подписка на событие `click` элемента `button`, при наступлении которого будет вызвана функция-обработчик `_onSubmit`.\n\n```js\nbemDom.declBlock('my-form', {\n    onSetMod : {\n        'js' : {\n            'inited': function() {\n                this._domEvents('button').on('click', this._onSubmit);\n            }\n        }\n    },\n\n    _onSubmit : function() { /* ... */ }\n});\n```\n\n> **Примечание** Функция-обработчик выполняется в контексте того экземпляра, от которого создавался [менеджер событий](#Объект-менеджера-событий).\n\n##### Из класса\n\nДля подписки на DOM-события из класса служит статический метод `_domEvents()`, создающий специальный объект [менеджера событий](#Объект-менеджера-событий). Метод принимает один опциональный параметр, задающий контекст, который может быть разных типов:\n\n* `elemClass` `{String|Function|Object}` — класс, имя или описание элемента. Описанием служит хеш вида `{ elem : MyElem, modName : 'my-mod', modVal : 'my-val' }` или `{ elem : 'my-elem', modName : 'my-mod', modVal : 'my-val' }`.\n* `document` `{Document|jQuery}` — документ\n* `window` `{Window|jQuery}` — окно\n\n[Менеджер событий](#Объект-менеджера-событий) обладает необходимым интерфейсом для подписки на события и отписки от них.\n\n**Пример**\n\nПри [инициализации класса блока](./i-bem-js-init.ru.md#Инициализация-класса) `my-form` выполняется подписка на событие `click` всех элементов `button` внутри любого блока `my-form`, при наступлении которого выполнится инициализация блока `my-form` (если он уже не проинициализирован) и у полученного экземпляра будет вызвана функция-обработчик `_onSubmit`.\n\n```js\nbemDom.declBlock('my-form', {\n    _onSubmit : function() { /* ... */ }\n}, {\n    lazyInit : true,\n\n    onInit : function() {\n        this._domEvents('button').on('click', this.prototype._onSubmit);\n    }\n});\n```\n\n> **Примечание** Функция-обработчик выполняется в контексте того экземпляра, внутри которого случилось событие.\n\n#### Удаление подписки на DOM-событие\n\nУдаление подписки на DOM-события выполняется автоматически при уничтожении экземпляра. Тем не менее, с помощью [менеджера событий](#Объект-менеджера-событий) можно удалить подписки вручную во время работы экземпляра.\n\n```js\n_stopKeysListening : function() {\n    this._domEvents().un('keydown', this._onKeydown);  // удаляем обработчик события 'keydown'\n}\n```\n\n#### Объект DOM-события\n\nПервым аргументом функция-обработчик получает jQuery-объект DOM-события — [{jQuery.Event}](https://api.jquery.com/category/events/event-object/).\n\nЭто позволяет использовать методы объекта `stopPropagation` и `preventDefault` для управления всплытием события и реакцией на него браузера.\n\n```js\nbemDom.declBlock('my-form', {\n    onSetMod : {\n        'js' : {\n            'inited': function() {\n                this._domEvents('button').on('click', function(e) {\n                    e.stopPropagation(); // останавливаем всплытие события\n                    this._onSubmit();\n                });\n            }\n        }\n    },\n\n    _onSubmit : function() {\n        /* ... */\n    }\n});\n```\n\n### БЭМ-события\n\nВ отличие от DOM-событий, БЭМ-события генерируются не на DOM-элементах, а на **экземплярах** блоков и элементов.\n\n#### Генерация БЭМ-события\n\nДля генерации БЭМ-события используется метод экземпляра `_emit(event, [data])`.\n\n* `event` `{String|events:Event}` — имя или объект события.\n* `[data]` `{*}` — дополнительные данные для события, которые будут доступны во втором аргументе обработчика.\n\nПри взаимодействии пользователя с элементом управления блока возникают DOM-события. В ходе их обработки блоком можно создавать БЭМ-события. Это позволяет реализовать уровень абстракции над DOM-событиями.\n\nНапример, при клике по кнопке `button` (DOM-событие `click`) **БЭМ-событие** `click` генерируется только в том случае, если у блока в этот момент не установлен модификатор `disabled`:\n\n```js\nbemDom.declBlock('button', {\n    onSetMod: {\n        'js': {\n            'inited': function() {\n                this._domEvents().on('click', this._onClick); // подписка на DOM-событие \"click\"\n            }\n        }\n    },\n\n    _onClick: function() {\n        if(!this.hasMod('disabled')) {\n            this._emit('click'); // создание БЭМ-события \"click\"\n        }\n    }\n});\n```\n\n#### Подписка на БЭМ-события\n\n##### Из экземпляра\n\nДля подписки на БЭМ-события из экземпляра служит метод `_events()`, создающий специальный объект [менеджера событий](#Объект-менеджера-событий). Метод принимает один опциональный параметр, задающий контекст, который может быть разных типов:\n\n* `entityInstance` `{Elem|BemDomCollection}` – экземпляр или коллекция БЭМ-сущностей.\n* `entityClass` `{String|Function|Object}` — класс, имя или описание БЭМ-сущности. Описанием служит хеш вида `{ block : MyBlock, modName : 'my-mod', modVal : 'my-val' }`, `{ elem : MyElem, modName : 'my-mod', modVal : 'my-val' }` или `{ elem : 'my-elem', modName : 'my-mod', modVal : 'my-val' }`.\n\n[Менеджер событий](#Объект-менеджера-событий) обладает необходимым интерфейсом для подписки на события и отписки от них, в том числе для работы с [событиями на изменения модификатора](#События-при-изменении-модификаторов).\n\n**Пример**\n\nВ момент инициализации HTML-формы (экземпляра блока `my-form`) выполняется поиск вложенной в форму кнопки `button` и подписка на ее БЭМ-событие `click`. В результате при нажатии на кнопку (экземпляр блока `button`) будет выполнен метод `_onSubmit` формы (экземпляр блока `my-form`).\n\n```js\nmodules.define('my-form', ['i-bem-dom', 'button'], function(provide, bemDom, Button) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod: {\n        'js': {\n            'inited': function() {\n                this._events(this.findChildBlock(Button))\n                    .on('click', this._onSubmit);\n            }\n        }\n    },\n\n    _onSubmit: function() { /* ... */ }\n}));\n\n});\n```\n\n##### Из класса\n\nДля подписки на БЭМ-события из класса служит статический метод `_events()`, создающий специальный объект [менеджера событий](#Объект-менеджера-событий).\n\nМетод принимает один опциональный параметр, задающий контекст, который может быть разных типов:\n\n* `entityClass` `{String|Function|Object}` — класс, имя или описание БЭМ-сущности. Описанием служит хеш вида `{ block : MyBlock, modName : 'my-mod', modVal : 'my-val' }`, `{ elem : MyElem, modName : 'my-mod', modVal : 'my-val' }` или `{ elem : 'my-elem', modName : 'my-mod', modVal : 'my-val' }`.\n\n[Менеджер событий](#Объект-менеджера-событий) обладает необходимым интерфейсом для подписки на события и отписки от них, в том числе для работы с [событиями на изменения модификатора](#События-при-изменении-модификаторов).\n\n**Пример**\n\nПри [инициализации класса блока](./i-bem-js-init.ru.md#Инициализация-класса) `my-form` выполняется подписка на событие `click` любого блока `button` внутри любого `my-form`, при наступлении которого выполнится инициализация блока `my-form` (если он уже не проинициализирован) и у полученного экземпляра будет вызвана функция-обработчик `_onSubmit`.\n\n```js\nmodules.define('my-form', ['i-bem-dom', 'button'], function(provide, bemDom, Button) {\n\nprovide(bemDom.declBlock(this.name, {\n    _onSubmit: function() { /* ... */ }\n}, {\n    lazyInit : true,\n\n    onInit : function() {\n        this._events(Button).on('click', this.prototype._onSubmit);\n    }\n}));\n\n});\n```\n\n> **Примечание** Функция-обработчик выполняется в контексте того экземпляра класса, производящего подписку, внутри которого случилось событие.\n\n### Объект менеджера событий\n\nМенеджер событий служит для унификации работы со всеми видами событий.\n\nОбладает API:\n\nМетод `on(event, [data], fn)` служит для подписки на событие `event`, обработчика `fn`, с возможностью передачи опциональных данных `data`. Принимает аргументы:\n\n* `event` `String|Object` — имя события, хеш для [события при изменении модификатора](#События-при-изменении-модификаторов) или объект события (`jQuery.Event` для DOM-событий или `events:Event` для БЭМ-событий).\n* `[data]` `*` — дополнительные данные для события, которые будут доступны в поле `data` объекта события.\n* `fn` `Function` — функция-обработчик события.\n\nМетод `once(event, [data], fn)` служит для единоразовой подписки на событие `event`, обработчика `fn`, с возможностью передачи опциональных данных `data`. Аргументы аналогичны методу `on()`.\n\nМетод `un([event], [fn])` служит для удаления подписки на события.\n\nПринимает аргументы:\n\n* `[event]` `String|Object` — опциональное имя события, хеш для [события при изменении модификатора](#События-при-изменении-модификаторов), или объект события (`jQuery.Event` для DOM-событий или `events:Event` для БЭМ-событий). В случае, если аргумент не указан, происходит удаление подписок на все события.\n* `[fn]` `Function` — функция-обработчик события. В случае, если аргумент не указан, происходит удаление всех обработчиков события `event`.\n\n#### События при изменении модификаторов\n\nВ случае с БЭМ-событиями, существуют специальные события на изменение модификаторов, которые генерируются автоматически.\n\nДля работы с такими событиями используется специальный хеш с полями:\n\n* `modName` `{String}` – имя модификатора.\n* `modVal` `{String}` – значение модификатора. Со значением `*` производится подписка на установку модификатора в **любое** значение. Со значением `''` – на **удаление** модификатора.\n\n**Пример**\n\nВ момент инициализации блок `my-form` подписывается на событие изменения модификатора у вложенного блока `button`.\n\nК примеру, можно подписаться на:\n\n* установку модификатора `disabled` в любое значение;\n\n  ```js\n  modules.define('my-form', ['i-bem-dom', 'button'], function(provide, bemDom, Button) {\n\n  bemDom.declBlock('form', {\n      onSetMod: {\n          'js': {\n              'inited': function() {\n                  this._events(this.findChildBlock(Button))\n                      .on({ modName : 'disabled', modVal : '*' }, this._onButtonDisabledChange);\n              }\n          }\n      },\n\n      _onButtonDisabledChange() {}\n  });\n\n  });\n  ```\n\n* установку модификатора `'disabled'` в значение `'true'`;\n\n  ```js\n  this._events(this.findChildBlock(Button)).on({ modName : 'disabled', modVal : 'true' }, this._onButtonDisable);\n  ```\n\n* удаление модификатора `'disabled'`;\n\n  ```js\n  this._events(this.findChildBlock(Button)).on({ modName : 'disabled', modVal : '' }, this._onButtonEnable);\n  ```\n\n### Объект БЭМ-события\n\nПри вызове функция-обработчик получает аргументом объект, описывающий БЭМ-событие. Класс объекта БЭМ-события `events.Event` определен в [ym](https://github.com/ymaps/modules)-модуле [`events`](../../common.blocks/events/events.ru.md) библиотеки bem-core.\n\nОбъект содержит поля:\n\n* `type` `{String}` — тип события. Аналогично [jQuery.Event.type](https://api.jquery.com/event.type/).\n* `target` `{i-bem-dom:Entity}` — экземпляр блока или элемента, в котором произошло БЭМ-событие.\n* `data` `{*}` — произвольные дополнительные данные, переданные как аргумент `data` при подписке на БЭМ-событие.\n* `result` `{*}` — последнее значение, возвращенное обработчиком данного события. Аналогично [jQuery.Event.result](https://api.jquery.com/event.result/).\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-extras.en.md",
    "content": "## What next?\n\nFor information about the BEM methodology, tools, and news in the BEM world, visit the website [bem.info](https://en.bem.info/).\n\nFor complete information about all the `i-bem.js` API methods, see the section [JSDoc](https://en.bem.info/libs/bem-core/current/desktop/i-bem/jsdoc/) for the `i-bem` block.\n\nTo share your experience with BEM or submit questions for experienced users and `i-bem.js` developers, visit the [forum](https://en.bem.info/forum/).\n\nFor `i-bem.js` usage examples and additional information, see these articles:\n\n* [JavaScript for BEM: main terms](https://en.bem.info/articles/bem-js-main-terms/)\n* [i-bem.js tutorial](https://en.bem.info/tutorials/bem-js-tutorial/)\n* [Starting your own BEM project](https://en.bem.info/tutorials/start-with-project-stub/)\n* [Creating BEM application on Leaflet and 2GIS API](https://en.bem.info/articles/firm-card-story/)\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-extras.ru.md",
    "content": "## Что дальше?\n\nИнформацию о БЭМ-методологии, инструментарии и новостях в мире БЭМ смотрите на сайте [bem.info](https://ru.bem.info).\n\nПолную информацию обо всех методах API `i-bem.js` можно найти в разделе JSDoc [блока i-bem](https://ru.bem.info/platform/i-bem/) и [i-bem-dom](https://ru.bem.info/platform/i-bem/).\n\nОбменяться опытом и задать вопрос опытным пользователям и разработчикам `i-bem.js` можно на [форуме](https://ru.bem.info/forum/).\n\nПримеры использования `i-bem.js` и дополнительную информацию смотрите в статьях:\n\n* [JavaScript по БЭМ: основные понятия](https://ru.bem.info/articles/bem-js-main-terms/)\n* [Справочное руководство по i-bem.js](https://ru.bem.info/platform/tutorials/i-bem/)\n* [Создаем свой проект на БЭМ](https://ru.bem.info/platform/tutorials/start-with-project-stub/)\n* [БЭМ-приложение на Leaflet и API 2GIS](https://ru.bem.info/articles/firm-card-story/)\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-html-binding.en.md",
    "content": "## Binding JS blocks to HTML\n\nJavaScript components in `i-bem.js` are used for bringing a page HTML elements\nto life. The typical task of a JS block is to set reactions to events inside an HTML fragment.\n\nIn `i-bem.js`, the primary ”framework“ is the document HTML tree. Points are marked in it where interactive interface elements, the JS blocks, are connected.\nThe binding point for a JS block is an HTML element (DOM node) whose `class` attribute\nspecifies the name of the block, and the `data-bem` attribute specifies the [block parameters](./i-bem-js-params.en.md).\n\nWhen loading a page in the browser, [blocks are initialized](./i-bem-js-init.en.md). This creates instances of blocks — JS objects for all the blocks mentioned in classes of the page HTML elements. A JS object bound to an HTML element\nhandles the [DOM events](i-bem-js-events.en.md#dom-events) that occur on it and stores the states of this block instance.\n\nThis method of binding JavaScript components to HTML has the following advantages:\n\n* Natural degradation of the interface on clients with JavaScript disabled.\n* Progressive rendering — the ability to begin rendering interface elements before all the page data has finished loading (for example, images).\n\n### Mechanism for binding blocks\n\nTo bind a block to an HTML element (for example, `<div>...</div>`), it is necessary to:\n\n* **Declare the block in `i-bem`**.\n    Create the [ym](https://github.com/ymaps/modules) module containing the JS implementation of the block ([the declaration](./i-bem-js-decl.en.md)). To do this, pass a string with the block name as the first argument to the `modules.define` and `BEMDOM.decl` methods.\n\n```js\nmodules.define('my-block', ['i-bem__dom'], function(provide, BEMDOM){\n\nprovide(BEMDOM.decl(this.name,\n    {\n        /* instance methods */\n    },\n    {\n        /* static methods */\n    }\n));\n\n});\n```\n\nOn the project level, each `ym` module is usually stored as a separate `js` file. For example, the `my-block` declaration might be stored in the project as `my-block/my-block.js` – the file `my-block.js`, inside the folder `my-block`.\n\n* **Mark the block in the HTML tree**.\n\n  Add the `class` attribute with the block name to the HTML element.\n\n  ```html\n  <div class=\"my-block\">\n   ...\n  </div>\n  ```\n\n* **Allow initialization of a block instance**.\n\n  Include the `i-bem` class in the list of classes for an HTML element. The presence of this class will show the framework that the HTML element is connected to the JS block.\n\n  ```html\n  <div class=\"my-block i-bem\">\n   ...\n  </div>\n  ```\n\n* **Pass parameters to a block instance**.\n\n  Put block parameters in the `data-bem` attribute. Block parameters are written in JSON format as a hash of the format: `block name : hash of parameters`. The parameters will be passed to the block instance [at the time of initialization](./i-bem-js-init.en.md).\n\n  ```html\n  <div class=\"my-block i-bem\" data-bem=\"{ \" my-block=\"my-block\">\n   ...\n  </div>\n  ```\n\n### The relation of blocks to HTML elements\n\nA single HTML element doesn't have to correspond to a single block instance. The following relationships between blocks and HTML elements are possible:\n\n* [One HTML element to one JS block](#one-html-element-to-one-js-block)\n* [One HTML element to multiple JS blocks](#one-html-element-to-multiple-js-blocks)\n* [One JS block to multiple HTML elements](#one-js-block-to-multiple-html-elements)\n\n#### One HTML element to one JS block\n\nThe simplest and most common way of binding blocks to HTML.\n\n**Example**\n\nThe `div` HTML element with `my-block` placed on it. Block parameters: an empty list `{}`.\n\n```html\n<div class=\"my-block i-bem\" data-bem=\"{ \" my-block=\"my-block\">\n ...\n</div>\n```\n\n#### One HTML element to multiple JS blocks\n\nThe technique of placing multiple blocks on a single HTML element is called a [mix](i-bem-js-decl.en.md#mix) in BEM methodology.\n\n**Example**\n\nThe `div` HTML element, with the following blocks on it:\n\n* `user` with the parameter `name`: `pushkin`\n* `avatar` with the parameter `img`: `http:// ...`\n\n```html\n<div class=\"user avatar i-bem\" data-bem=\"{ \" user=\"user\">\n ...\n</div>\n```\n\n#### One JS block to multiple HTML elements\n\nThis design is convenient if you need to coordinate the states of multiple components of a block.\n\nTo bind a block instance to multiple HTML elements, you must set the same value for the `id` parameter in the `data-bem` attribute. The value of `id` can be any string.\n\n**Example**\n\nAn instance of the `notebook` block bound to the `div` and `span` HTML elements. The parameters specify the shared `id` — `maintab`.\n\n```html\n<div class=\"user avatar i-bem\"\n    data-bem='{\n        \"user\": { \"name\": \"pushkin\" },\n        \"avatar\": { \"img\": \"http://...\" }\n     }'>\n     ...\n</div>\n```\n\nAs a result, when the blocks are initialized, a single JS object is created, with a [`domElem`](./i-bem-js-dom.en.md) field that contains references to the jQuery objects of both DOM nodes.\n\nFor example, the ”tab“ widget, where a click on the tab title (the first HTML element) changes its content (the second HTML element).\nAnother example is a placemark that marks a point on a map (the first element), and the related description of the point in the list next to it (the second element).\n\nThe `id` is used *only at the time of initializing* the block instance. The `id` value must be unique for instances of the same block in the context of a single [wave of initialization](i-bem-js-init.en.md#wave-of-initialization).\n\n### Blocks without DOM representation\n\nInfrastructure code that performs general interface tasks (access to the backend, or helper methods) can be formatted as a block. This allows expressing block states using [modifiers](./i-bem-js-states.en.md), so that [other blocks can subscribe](i-bem-js-states.en.md#other-blocks-can-subscribe) to their changes.\n\nTo avoid binding these blocks to the HTML tree artificially, these blocks can be created in `i-bem.js` without DOM representation.\n\nBlocks without DOM representation:\n\n* Do not require binding to a page's HTML code.\n* Must be explicitly [initialized](i-bem-js-init.en.md#initialized) and destroyed.\n\n#### Access to block instances without DOM representation\n\nWhen creating a block instance without DOM representation, you must see to it that references to this instance are stored for blocks that need to interact with it.\n\nSee also:\n\n* [Initializing and deleting blocks without DOM representation](i-bem-js-init.en.md#initializing-and-deleting-blocks-without-dom-representation)\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-html-binding.ru.md",
    "content": "## Привязка JS-блоков к HTML\n\nJavaScript-компоненты в `i-bem.js` служат для «оживления» HTML-элементов страницы. Типовая задача JS-блока — установка реакции на события внутри HTML-фрагмента.\n\nВ `i-bem.js` первичным «каркасом» является HTML-дерево документа. В нем размечаются точки, к которым привязаны интерактивные элементы интерфейса — JS-блоки. Точка привязки JS-блока — HTML-элемент (DOM-узел), в атрибуте `class` которого указано имя блока, а в атрибуте `data-bem` — [параметры блока](./i-bem-js-params.ru.md#Передача-параметров-экземпляру-блока-и-элемента).\n\nПри загрузке страницы в браузере выполняется [инициализация блоков](./i-bem-js-init.ru.md#Инициализация). В ходе нее создаются экземпляры блоков — JS-объекты всех блоков, упомянутых в классах HTML-элементов страницы. JS-объект, привязанный к HTML-элементу, обрабатывает происходящие на нем [DOM-события](./i-bem-js-events.ru.md#dom-события) и хранит состояния данного экземпляра блока.\n\nТакой способ привязки JavaScript-компонентов к HTML имеет следующие преимущества:\n\n* естественная деградация интерфейса на клиентах с отключенным JavaScript;\n* прогрессивный рендеринг — возможность начинать отрисовку элементов интерфейса до окончания загрузки всех данных страницы (например, изображений).\n\n> **Примечание** Начиная с версии `bem-core@v4.0.0` всё описанное ниже для блоков так же справедливо для элементов.\n\n### Механизм привязки блоков\n\nЧтобы привязать блок к HTML-элементу (например, `<div>...</div>`), необходимо:\n\n* **Декларировать блок в `i-bem`**.\n\nCоздать модуль [ym](https://github.com/ymaps/modules), содержащий JS-реализацию блока ([декларацию](./i-bem-js-decl.ru.md#Декларация-блоков-и-элементов)). Для этого строка с именем блока передается первым аргументом методам `modules.define` и `bemDom.declBlock`.\n\n```js\nmodules.define('my-block', ['i-bem-dom'], function(provide, bemDom){\n\nprovide(bemDom.declBlock(this.name,\n    {\n        /* методы экземпляра */\n    },\n    {\n        /* статические методы */\n    }\n));\n\n});\n```\n\nНа уровне проекта каждый модуль `ym` обычно хранится как отдельный файл технологии `js`. Например, декларация `my-block` в проекте может храниться как `my-block/my-block.js` – файл `my-block.js`, вложенный в папку `my-block`.\n\n* **Отметить блок в HTML-дереве**.\n\n  Добавить HTML-элементу атрибут `class` с именем блока.\n\n  ```html\n   <div class=\"my-block\">...</div>\n  ```\n\n* **Разрешить инициализацию экземпляра блока**.\n\n  Включить класс `i-bem` в список классов HTML-элемента. Наличие этого класса укажет фреймворку, что HTML-элемент связан с JS-блоком.\n\n  ```html\n  <div class=\"my-block i-bem\">...</div>\n  ```\n\n* **Передать параметры экземпляру блока**.\n\n  Поместить параметры блока в атрибут `data-bem`. Параметры блока записываются в формате JSON и представляют собой хеш вида: `имя блока : хэш параметров`. Параметры будут переданы экземпляру блока [в момент инициализации](./i-bem-js-init.ru.md#Инициализация).\n\n  ```html\n  <div class=\"my-block i-bem\" data-bem='{ \"my-block\": { \"name\": \"ya\" } }'>...</div>\n  ```\n\n### Связь блоков с HTML-элементами\n\nОдному HTML-элементу не обязательно должен соответствовать один экземпляр блока. Возможны следующие типы связи между блоками и HTML-элементами:\n\n* [Один HTML-элемент — один JS-блок](#Один-html-элемент--один-js-блок)\n* [Один HTML-элемент — несколько JS-блоков](#Один-html-элемент--несколько-js-блоков)\n* [Один JS-блок на нескольких HTML-элементах](#Один-js-блок-на-нескольких-html-элементах)\n\n#### Один HTML-элемент — один JS-блок\n\nСамый простой и распространенный способ привязки блоков к HTML.\n\n**Пример**\n\nHTML-элемент `div`, на котором размещен блок `my-block`. Параметры блока: пустой список `{}`.\n\n```html\n<div class=\"my-block i-bem\" data-bem='{ \"my-block\": {} }'>\n    ...\n</div>\n```\n\n#### Один HTML-элемент — несколько JS-блоков\n\nТехника размещения нескольких блоков на одном HTML-элементе в БЭМ-методологии называется [микс](./i-bem-js-decl.ru.md#Миксины).\n\n**Пример**\n\nHTML-элемент `div`, на котором размещены:\n\n* блок `user` с параметром `name`: `pushkin`;\n* блок `avatar` с параметром `img`: `http://...`.\n\n```html\n<div class=\"user avatar i-bem\"\n    data-bem='{\n        \"user\": { \"name\": \"pushkin\" },\n        \"avatar\": { \"img\": \"http://...\" }\n     }'>\n     ...\n</div>\n```\n\n#### Один JS-блок на нескольких HTML-элементах\n\nТакой дизайн удобен, если нужно согласовать состояния нескольких компонентов блока.\n\nЧтобы привязать экземпляр блока к нескольким HTML-элементам, нужно указать им в атрибуте `data-bem` одинаковое значение параметра `id`. Значением `id` может быть произвольная строка.\n\n**Пример**\n\nЭкземпляр блока `notebook` привязан к HTML-элементам `div` и `span`. В параметрах блока указан общий `id` — `maintab`.\n\n```html\n<div class=\"notebook i-bem\" data-bem='{ \"notebook\": { \"id\": \"maintab\" }}'>\n</div>\n...\n<span class=\"notebook i-bem\" data-bem='{ \"notebook\": { \"id\": \"maintab\" }}'>\n</span>\n```\n\nВ результате при инициализации блоков создается один JS-объект, поле [domElem](./i-bem-js-dom.ru.md#Работа-с-dom-деревом) которого содержит ссылки на jQuery-объекты обоих DOM-узлов.\n\nНапример, виджет «вкладка», где клик по заголовку вкладки (первый HTML-элемент), меняет ее содержимое (второй HTML-элемент).\n\nДругой пример: маркер, обозначающий точку на карте (первый элемент), и связанное с ним описание точки в списке рядом (второй элемент).\n\nИдентификатор `id` используется *только в момент инициализации* экземпляра блока. Значение `id` должно быть уникальным для экземпляров одного блока в рамках одной [волны инициализации](./i-bem-js-init.ru.md#Волны-инициализации).\n\n### Блоки без DOM-представления\n\nИнфраструктурный код, решающий общие задачи интерфейса (связь с бэкэндом, вспомогательные методы), можно оформить в виде блока. Это позволит выражать состояния блока с помощью [модификаторов](./i-bem-js-states.ru.md#Модификаторы), на изменение которых смогут [подписаться другие блоки](./i-bem-js-states.ru.md#Триггеры-на-установку-модификаторов).\n\nЧтобы не привязывать такие блоки к HTML-дереву искусственно в `i-bem.js` можно создавать блоки без DOM-представления.\n\nБлоки без DOM-представления:\n\n* не требуют привязки к HTML-коду страницы;\n* должны быть явно [инициализированы](./i-bem-js-init.ru.md#Инициализация-и-удаление-блоков-без-dom-представления) и уничтожены.\n\n#### Доступ к экземплярам блоков без DOM-представления\n\nПри создании экземпляра блока без DOM-представления необходимо позаботиться о сохранении ссылки на этот экземпляр для блоков, которым нужно с ним взаимодействовать.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-init.en.md",
    "content": "<a name=\"init\"></a>\n\nInitialization\n--------------\n\nBlock initialization creates a JS object corresponding to the block instance\nin the browser memory. Initialization of block instances is performed by the\n`init()` method of the `i-bem__dom` module on the specified fragment of the DOM tree.\n\nEach instance of a block can be assigned three states:\n\n* The block instance is not initialized (the JS object has not been created).\n* The block instance is initialized (the JS object has been created in the browser memory).\n* The block instance was destroyed (all references to the block instance\n    were deleted, and it may be removed by the garbage collector).\n\nIn `i-bem.js`, these states of the block instance are described using the\nauxiliary `js` modifier.\n\n* Before initialization, the block instance does not have a `js` modifier.\n\n  ```html\n  <div class=\"my-block i-bem\" data-bem=\"...\">\n   ...\n  </div>\n  ```\n\n* At the time of the block instance initialization, the `js` modifier is set to `inited`.\n\n  ```html\n  <div class=\"my-block i-bem my-block_js_inited\" data-bem=\"...\">\n   ...\n  </div>\n  ```\n\n* If a fragment of the DOM tree is deleted during workflow (using the `destruct` method of the `i-bem__dom` module), block instances are also deleted with it if their HTML elements are all located in this fragment. Before deleting a block instance, the `js` modifier is deleted so that the block [instance destructors](#instance-destructors) are executed.\n\n> **Note** If a block instance was [bound to multiple HTML elements](i-bem-js-html-binding.en.md#bound-to-multiple-html-elements), the block will exist as long as at least one element it is connected to remains in the HTML tree.\n\nIf multiple instances of other blocks are located on an HTML element, the\ninitialization of one of them (the appearance of the `js_inited` modifier)\ndoesn't affect the initialization of the rest of them.\n\n**Example**\n\nOnly the `my-block` instance is initialized on the HTML element.\n\nThe `lazy-block` instance is not initialized:\n\n```html\n<div class=\"my-block my-block_js_inited lazy-block i-bem\" data-bem=\"{ \" my-block=\"my-block\">\n ...\n</div>\n```\n\n> **Note** The presence of the `js` modifier makes it possible to write various CSS styles for a block that depend on whether it is initialized or not.\n\n<a name=\"constructor\"></a>\n\n### Block instance constructor\n\n[Triggers](i-bem-js-states.en.md#triggers) can be assigned for changing the values of the `js` modifier, the same way as for other block modifiers.\n\nThe trigger to set the `js` modifier to the `inited` value is executed\nduring block creation. This trigger can be considered a **block instance constructor**:\n\n```js\nonSetMod: {\n    'js': {\n        'inited': function() { /* ... */ } // block instance constructor\n    }\n}\n```\n\n<a name=\"destruct\"></a>\n\n### Block instance destructor\n\nThe moment of block deletion is the moment when all references to\nthe block JS object are destroyed. After this, the garbage collector can delete it from\nbrowser memory.\n\nThe trigger to delete the `js` modifier (set it to an empty value\n`''`) is executed before deleting the block. This trigger can be considered a\n**block instance destructor**.\n\n```js\nonSetMod: {\n    'js': {\n        '': function() { /* ... */ } // block instance destructor\n    }\n}\n```\n\n<a name=\"init-wave\"></a>\n\n### Waves of initialization\n\nThe instances of blocks that are present on a page do not have\nto be initialized simultaneously. The blocks can be added dynamically\nand initialized on request or on an event.\nInitialization of a consecutive group of blocks is called a **wave of initialization**.\n\nA new wave of initialization is created in the following cases:\n\n* [Automatic initialization of blocks when the `domReady` event occurs](#automatic-initialization-of-blocks-when-the-)\n* [Initialization of a block when an event occurs](#initialization-of-a-block-when-an-event-occurs) (lazy initialization)\n* [Directly calling block initialization on a specified fragment of the DOM tree](#directly-calling-block-initialization-on-a-specified-fragment-of-the-dom-tree)\n\n<a name=\"init-auto\"></a>\n\n### Automatic initialization\n\nThe *i-bem.js* framework allows automatically initializing blocks with DOM representation when the `domReady` event occurs.\n\nFor automatic initialization, JS objects will be created in browser memory for all the DOM nodes containing `i-bem` in the `class` attribute. Initialization is performed by the `init` function of the [i-bem__dom](https://en.bem.info/libs/bem-core/current/desktop/i-bem/jsdoc/) module.\n\nTo enable automatic initialization, specify the `i-bem` block with the `init` modifier set to the `auto` value in the `.deps.js` dependencies file.\n\n**Example of** `.deps.js`:\n\n```js\n({\n    shouldDeps: [\n        {\n            block: 'i-bem',\n            elem: 'dom',\n            mods: { 'init': 'auto' }\n        }\n    ]\n})\n```\n\nThe [page](../../common.blocks/page/) block already contains `i-bem__dom_init_auto` in dependencies, so if it is used in the project, nothing else needs to be enabled.\n\n> **Note** Blocks that have lazy initialization set will not be initialized automatically.\n\n<a name=\"init-live\"></a>\n\n### Initialization on event (lazy initialization)\n\nIf a page has many instances of blocks, automatic initialization of\nall the blocks at the time of loading is undesirable, since this increases the loading time\nand the amount of memory consumed by the browser.\n\nIt is more convenient to initialize JS objects only when their functionality is needed by the user,\nsuch as when the block is clicked. This is called **lazy** or **live** initialization.\n\nThe static property `live` is reserved in the declaration for describing conditions for lazy initialization. The `live` property can have the following types of values:\n\n`Boolean`\n\n* `true` — Instances of blocks in this class will be initialized only when attempting to get the corresponding instance (see the section [Interaction of blocks](./i-bem-js-interact.en.md)).\n\n```js\nmodules.define('my-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name,\n    {\n        onSetMod: {\n            'js': {\n                'inited': function() { /* ... */ } // this code will be executed\n                                                   // the first time the block instance is accessed\n            }\n        }\n    },\n    { live: true } // static methods and properties\n));\n\n});\n```\n\n* `false` — Allows cancelling lazy initialization of blocks that is set on another redefinition level.\n\n`Function` – a function that is executed before initializing the **first instance** of a block of the specified class. If the function returns `false`, instances of the block will be initialized [automatically](#automatically).\n\n```js\nmodules.define('my-block', ['i-bem__dom', 'ua'], function(provide, BEMDOM, ua) {\n\nprovide(BEMDOM.decl(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                // executed when the block instance is first accessed\n            }\n        }\n    }\n}, {\n    live : function() { // executed before initialization of the first instance of the block\n        if(ua.msie && ua.version < 9) {\n                          // disables lazy initialization of the block\n            return false; // for old versions of Internet Explorer\n        }\n    }\n}));\n\n});\n```\n\n> **Note** Lazy initialization can be canceled for a specific instance of a block. To do this, specify `data-bem='{\"live\": false}'` in the [parameters](./i-bem-js-params.en.md) of the HTML element that the block instance is bound to.\n\nTo initialize block instances as DOM events or BEM events occur, subscribe to [delegated events](i-bem-js-events.en.md#delegated-events) in the function body or use a [helper](#helper).\n\n**Example**\n\nInstances of `my-block` will be initialized on the `click` DOM event on the block DOM node. For each `click` DOM event, the `_onClick` method of the block instance is called:\n\n```js\nmodules.define('my-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, {\n    onSetMod: {\n        'js': {\n            'inited': function() { /* ... */ } // executed on the first \"click\" DOM event\n        }\n    },\n\n    _onClick: function(e) { /* ... */ } // executed on every ”click“ DOM event\n}, {\n    live: function() {\n        this.liveBindTo('click', function(e) {\n            this._onClick(e);   // block instance will be created when a click occurs\n                                // and its _onClick method will be called\n        });\n    }\n}));\n\n});\n```\n\n> **Note** The `live` property applies to static methods of a block class. So even if it is set in the block declaration with a particular modifier, `live` will be applied to all the blocks in this class, regardless of the modifiers.\n\n<a name=\"init-live-helpers\"></a>\n\n### Helpers for initialization on an event\n\nTo simplify initialization on events in the context of a block instance, a set of helper methods is reserved for subscribing to the following types of events:\n\n* DOM events:\n  * `liveBindTo([elemName], event, [callback])` — Subscribes to an event on the block DOM node or its elements, with deferred initialization. The block will be initialized on the first `event`. The `callback` handler function will be called on `event` and after block initialization.\n  * `liveUnbindFrom([elemName], event, [callback])` — Deletes the subscription with deferred initialization on an event on the block DOM node or its elements.\n  * `liveInitOnEvent([elemName], event, callback)` — Initialization on an event on the block DOM node or its elements.\n* BEM events:\n  * `liveInitOnBlockEvent(event, blockName, callback)` — Initialization on a BEM event of an instance of a different block placed on the DOM node of the current block instance.\n  * `liveInitOnBlockInsideEvent(event, blockName, [callback])` — Initialization on a BEM event of an instance of a different block nested in the DOM node of the current block instance.\n\nFor example, the `menu` block is initialized on the `click` **BEM event** of the nested `menu-item` block.\n\n```js\nmodules.define('menu', ['i-bem__dom', 'menu-item'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, {\n    _onItemClick : function(e, data) {\n        // handler function for the ”click“ BEM event on nested ”menu-item“ instances\n    }\n}, {\n    live : function() {\n        this.liveInitOnBlockInsideEvent('click', 'menu-item', function(e, data) {\n            this._onItemClick(e, data);\n        });\n    }\n}));\n\n});\n```\n\n<a name=\"init-ajax\"></a>\n\n### Initialization of blocks on a fragment of the DOM tree\n\nThe initialization of JS objects can be called\ndirectly for a specified fragment of the DOM tree. This is often necessary when developing AJAX interfaces,\nwhen new instances of blocks have to be [dynamically added](i-bem-js-dom.en.md#dynamically-added) to a page or existing ones have to be updated.\n\nIn `i-bem.js`, the following functions perform dynamic initialization of blocks:\n\n* `init`, `destruct` – Initialization/destruction of blocks on a specified fragment of the DOM tree.\n* `update`, `replace`, `append`, `prepend`, `before`, `after` – Adding/replacing a fragment of the DOM tree with simultaneous initialization of blocks on the updated fragment.\n\nFor an example of using functions that perform dynamic initialization, see [Dynamically updating blocks and elements in the DOM tree](i-bem-js-dom.en.md#dynamically-updating-blocks-and-elements-in-the-dom-tree).\n\n<a name=\"destruct-dom\"></a>\n\n### Deleting blocks on a fragment of the DOM tree\n\nLike the block initialization process, the deletion process can be called directly for a specified fragment of the DOM tree. For example, you may use this for dynamically deleting instances of blocks from a page when developing AJAX interfaces.\n\nExplicitly invoking this procedure guarantees correct deletion of:\n\n* Nested DOM nodes.\n* Blocks mixed into other blocks.\n\nUse the `BEMDOM.destruct` static method to explicitly invoke deletion.\n\nThe method accepts:\n\n* `ctx` `{jQuery}` – The root DOM element. Deleted together with all the nested DOM nodes.\n* `excludeSelf` `{Boolean}` – Doesn't delete the root DOM element if set to `true`. By default, `false`.\n\n<a name=\"init-bem\"></a>\n\n### Initializing and deleting blocks without DOM representation\n\nUse the `BEM.create` method for creating JS objects of a block without DOM representation (that aren't bound to an HTML element).\n\nThe method accepts:\n\n* `name` `{String|Object}` – The name of the block.\n\nReturns an instance of a block of the specified class.\n\n**Deletion** of instances of blocks without DOM representation can't be\nperformed automatically. Blocks without DOM representation are normal JS objects and are deleted when\nall the references to the block object are deleted.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-init.ru.md",
    "content": "## Инициализация\n\nИнициализация — это создание в памяти браузера JS-объекта, соответствующего экземпляру блока или элемента. Инициализация экземпляров блоков или элементов выполняется функцией `init([ctx])` из модуля `i-bem-dom` на заданном фрагменте DOM-дерева `ctx`.\n\nКаждому экземпляру можно приписать три состояния:\n\n* не инициализирован — JS-объект не создан\n* инициализирован — JS-объект создан в памяти браузера\n* уничтожен — удалены все ссылки на JS-объект экземпляра, и он может быть удален сборщиком мусора.\n\nВ `i-bem.js` эти состояния описываются с помощью служебного модификатора `js`.\n\n* До инициализации экземпляр не имеет модификатора `js`.\n\n```html\n<div class=\"my-block i-bem\" data-bem='{ \"my-block\" : {} }'>...</div>\n```\n\n* В момент инициализации экземпляру устанавливается модификатор `js` в значении `inited`.\n\n```html\n<div class=\"my-block i-bem my-block_js_inited\" data-bem='{ \"my-block\" : {} }'>...</div>\n```\n\n* Если в процессе работы удаляется фрагмент DOM-дерева (при помощи метода `destruct()` модуля `i-bem-dom`), то вместе с ним удаляются экземпляры, все HTML-элементы которых находятся в этом фрагменте. Перед удалением экземпляра модификатор `js` удаляется, чтобы выполнились [деструкторы экземпляра](#Деструктор-экземпляра-блока-и-элемента).\n\n> **Примечание** Если экземпляр блока или элемента был [привязан к нескольким HTML-элементам](./i-bem-js-html-binding.ru.md#Один-js-блок-на-нескольких-html-элементах), экземпляр будет существовать, пока в HTML-дереве сохраняется хотя бы один HTML-элемент, с которым он связан.\n\nЕсли на HTML-элементе размещено несколько экземпляров других блоков или элементов, то инициализация одного из них (появление модификатора `js` со значением `inited`) не влияет на инициализацию остальных.\n\n**Пример**\n\nНа HTML-элементе инициализирован только экземпляр блока `my-block`. Экземпляр блока `lazy-block` не инициализирован:\n\n```html\n<div class=\"my-block my-block_js_inited lazy-block i-bem\" data-bem='{ \"my-block\" : {}, \"lazy-block\" : {} }'>\n    ...\n</div>\n```\n\n> **Примечание** Наличие модификатора `js` позволяет писать разные CSS-стили для блока или элемента в зависимости от того, инициализирован он или нет.\n\n### Конструктор экземпляра блока и элемента\n\nНа изменение значений модификатора `js` можно назначать [триггеры](./i-bem-js-states.ru.md#Триггеры-на-установку-модификаторов) так же, как и для любых других модификаторов.\n\nТриггер на установку модификатора `js` в значение `inited` выполняется при инициализации экземпляра.\n\nЭтот триггер можно считать **конструктором**:\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            'inited' : function() { /* ... */ } // конструктор экземпляра\n        }\n    }\n});\n```\n\n### Деструктор экземпляра блока и элемента\n\nМоментом удаления экземпляра является момент уничтожения всех ссылок на его JS-объект, после чего он может быть удален из памяти браузера сборщиком мусора.\n\nТриггер на удаление модификатора `js` (установку в пустое значение `''`) выполняется перед удалением блока. Такой триггер можно считать **деструктором**.\n\n```js\nbemDom.declBlock('my-block', {\n    onSetMod : {\n        'js' : {\n            '' : function() { /* ... */ } // деструктор экземпляра\n        }\n    }\n});\n```\n\n### Волны инициализации\n\nИнициализация экземпляров блоков и элементов, присутствующих на странице, не обязательно происходит одновременно. Они могут динамически добавляться в ходе работы, инициализироваться по запросу или событию. Инициализация очередной группы блоков или элементов называется **волной инициализации**.\n\nНовая волна инициализации создается в следующих случаях:\n\n* [Автоматическая инициализация блоков и элементов по событию `domReady`](#Автоматическая-инициализация).\n* [Ленивая инициализация](#Ленивая-инициализация).\n* [Явный вызов инициализации на указанном фрагменте DOM-дерева](#Инициализация-блоков-и-элементов-на-фрагменте-dom-дерева).\n\n### Автоматическая инициализация\n\n`i-bem.js` позволяет автоматически инициализировать блоки и элементы с DOM-представлением в момент наступления DOM-события `domReady`.\n\nВключить автоматическую инициализацию можно, указав блок `i-bem` с модификатором `init` в значении `auto` в файле зависимостей `.deps.js`.\n\n**Пример файла** `.deps.js`:\n\n```js\n({\n    shouldDeps : [\n        {\n            block : 'i-bem',\n            elem : 'dom',\n            mods : { init : 'auto' }\n        }\n    ]\n})\n```\n\nБлок [page](../../common.blocks/page/page.ru.md) уже содержит в зависимостях `i-bem-dom_init_auto`, поэтому если он используется в проекте, не требуется ничего дополнительно подключать.\n\n> **Примечание** Блоки и элементы, для которых задекларирована [ленивая инициализация](#Ленивая-инициализация), не будут инициализированы автоматически.\n\n### Ленивая инициализация\n\nЕсли на странице размещено много экземпляров блоков и элементов, их автоматическая инициализация в момент загрузки страницы нежелательна, так как она увеличивает время загрузки и объем памяти, затрачиваемой браузером.\n\nРекомендуется инициализировать блоки и элементы только в тот момент, когда их функциональность потребуется пользователю, например, по клику на блок. Такая инициализация называется **ленивой**.\n\nДля декларации ленивой инициализации, в декларации зарезервировано статическое свойство `lazyInit` типа `Boolean`.\n\nПри `lazyInit : true`, блоки или элемнеты данного класса будут инициализированы только при попытке получить соответствующий экземпляр (см. раздел «[Взаимодействие блоков](./i-bem-js-interact.ru.md#Взаимодействие-блоков-и-элементов)»).\n\n```js\nmodules.define('my-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                // этот код будет выполняться при первом обращении к экземпляру блока\n            }\n        }\n    }\n}, {\n    lazyInit : true\n}));\n\n});\n```\n\nДекларация с `lazyInit : false` позволяет отменить ленивую инициализацию, заданную на другом уровне переопределения.\n\n> **Примечание** Ленивая инициализация может быть отменена для конкретного экземпляра. Для этого  нужно указать в [параметрах](./i-bem-js-params.ru.md#Передача-параметров-экземпляру-блока-и-элемента) HTML-элемента, к которому привязан экземпляр `data-bem='{ \"my-block\" : { \"lazyInit\" : false } }'`.\n\n#### Инициализация класса\n\nВ терминах `i-bem-dom` существует понятие **инициализация класса**. Она происходит в момент прохождения [волны инициализации](#Волны-инициализации) на HTML-фрагменте, когда в нем впервые за время жизни приложения встречается блок или элемент данного класса.\n\nИнициализация класса необходима для реализации ленивой инициализации по DOM- или БЭМ-событию. Для этого в декларации зарезервирован статический метод `onInit`, внутри которого можно [подписаться на нужные события](./i-bem-js-events.ru.md#События).\n\n**Пример**\n\nБлок `button` будет инициализирован по DOM-событию `click` на DOM-узле блока.\n\n```js\nmodules.define('button', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited' : function() {\n                // выполняется при первом DOM-событии \"click\"\n            }\n        }\n    },\n\n    _onClick: function(e) {\n        // выполняется при каждом DOM-событии \"click\"\n    }\n}, {\n    lazyInit : true,\n\n    onInit : function() {\n        this._domEvents().on(\n            'click',\n            this.prototype._onClick);  // в момент клика будет создан экземпляр блока и вызван его метод _onClick\n    }\n}));\n\n});\n```\n\nБлок `my-form` инициализируется по БЭМ-событию `click` вложенного в него блока `button`.\n\n```js\nmodules.define('my-form', ['i-bem-dom', 'button'], function(provide, bemDom, Button) {\n\nprovide(bemDom.declBlock(this.name, {\n    _onButtonClick : function(e, data) {\n        // функция-обработчик БЭМ-события click на вложенных блоках button\n    }\n}, {\n    lazyInit : true,\n\n    onInit : function() {\n        this._events(Button).on('click', this.prototype._onButtonClick);\n    }\n}));\n\n});\n```\n\n> **Примечание** Свойства `lazyInit` и `onInit` относятся к статическим свойствам класса. Поэтому даже если оно задано в декларации блока или элемента с определенным модификатором, они будут применены ко всем экземплярам данного класса, вне зависимости от модификаторов.\n\n### Инициализация блоков и элементов на фрагменте DOM-дерева\n\nПроцедура инициализации JS-объектов может быть вызвана явно для указанного фрагмента DOM-дерева. Такая необходимость возникает при [динамическом обновлении](./i-bem-js-dom.ru.md#Динамическое-обновление-блоков-и-элементов-в-dom-дереве) блоков или элементов.\n\nСледующие функции выполняют динамическую инициализацию блоков и элементов:\n\n* `init()`, `destruct()` – инициализация и уничтожение экземпляров на указанном фрагменте DOM-дерева.\n* `update()`, `replace()`, `append()`, `prepend()`, `before()`, `after()` – обновление фрагмента DOM-дерева\n  с одновременной инициализацией на обновленном фрагменте.\n\nПример использования функций, выполняющих динамическую инициализацию см. в разделе «[Динамическое обновление блоков и элементов в DOM-дереве](./i-bem-js-dom.ru.md#Динамическое-обновление-блоков-и-элементов-в-dom-дереве)».\n\n### Инициализация и удаление блоков без DOM-представления\n\nДля создания JS-объектов блока или элемента без DOM-представления (не привязанного к HTML-элементу) служит статический метод `create()` классов `Block` или `Elem` из модуля `i-bem`.\n\nМетод принимает аргументы:\n\n* `mods` `{Object}` – модификаторы  создаваемого блока или элемента.\n* `params` `{Object}` – параметры блока или элемента.\n\nВозвращает экземпляр указанного класса.\n\n**Удаление** экземпляров блоков и элементов без DOM-представления не может быть выполнено автоматически. Блоки и элементы без DOM-представления представляют собой обычные JS-объекты и удаляются в момент удаления всех ссылок на объект.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-interact.en.md",
    "content": "<a name=\"ibc\"></a>\n\nInteraction of blocks\n---------------------\n\nIn the scope of the BEM methodology, blocks should be developed in a way that minimizes their dependency on each others' states. However, the ideal of fully independent blocks is not achievable in practice.\n\nBlock interaction can be implemented in the following ways:\n\n* By subscribing to [BEM events](i-bem-js-events.en.md#bem-events) on other block instances\n     or subscribing to [delegated BEM events](i-bem-js-events.en.md#delegated-bem-events).\n* By directly calling methods of other block instances\n     or static methods of another block class.\n* By checking the states of one of the blocks.\n* Through *event channels* (for example, using the [channels](../../common.blocks/events/__channels) element in the `events` block).\n\n> **Note** Don't use [DOM events](i-bem-js-events.en.md#dom-events) for arranging interaction between blocks. DOM events are intended only for implementing internal procedures of a block.\n\nThe following `i-bem.js` APIs are provided for implementing interaction between blocks:\n\n* [Searching for block instances in the DOM tree](i-bem-js-dom.en.md#searching-for-block-instances-in-the-dom-tree)\n* [Access to block instances without DOM representation](i-bem-js-html-binding.en.md#access-to-block-instances-without-dom-representation)\n* [Access to block classes](#access-to-block-classes)\n\n<a name=\"api-class\"></a>\n\n### Access to block classes\n\nYou can get JS components corresponding to block classes via the [module system](https://github.com/ymaps/modules). This is also true for blocks [without DOM representation](i-bem-js-html-binding.en.md#without-dom-representation).\n\nAccess to block classes is needed for:\n\n* [Delegating BEM events](i-bem-js-events.en.md#delegating-bem-events).\n* [Redefining](i-bem-js-decl.en.md#redefining) a block declaration.\n* Calling static methods of a class.\n\n**Example**\n\nCalling the `close` static method for the `popup` block will close all popups on the page.\n\n```js\nmodules.define('switcher', ['i-bem__dom', 'popup'], function(provide, BEMDOM, Popup) {\n\nprovide(BEMDOM.decl(this.name,\n    {\n        onSetMod : {\n            'popup' : {\n                'disabled' : function() {\n                    Popup.close();\n                }\n            }\n        }\n    }\n));\n\n});\n```\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-interact.ru.md",
    "content": "## Взаимодействие блоков и элементов\n\nВ рамках БЭМ-методологии блоки и элементы следует разрабатывать так, чтобы свести к минимуму зависимость состояний одних сущностей от других. Однако на практике идеал полной независимости блоков недостижим.\n\nВзаимодействие блоков и элементов может быть реализовано:\n\n* с помощью подписки на [БЭМ-события](./i-bem-js-events.ru.md#БЭМ-события);\n* с помощью непосредственного вызова методов других экземпляров или статических методов класса;\n* через проверку [состояний](./i-bem-js-states.ru.md) одного экземпляра из другого.\n\n> **Примечание** Не используйте [DOM-события](./i-bem-js-events.ru.md#dom-события) для организации взаимодействия между экземплярами. DOM-события следует использовать только во взаимодействиях экземпляра со своим DOM-узлом.\n\nДля реализации взаимодействия блоков или элементов `i-bem.js` предоставляет API:\n\n* [Поиск экземпляров в DOM-дереве](./i-bem-js-dom.ru.md#Поиск-экземпляров-блоков-и-элементов-в-dom-дереве).\n* [Доступ к экземплярам без DOM-представления](./i-bem-js-html-binding.ru.md#Блоки-без-dom-представления).\n* [Доступ к классам блоков и элементов](#Доступ-к-классам-блоков-и-элементов).\n\n### Доступ к классам блоков и элементов\n\nКлассы блоков и элементов, можно получить через [модульную систему ym](https://github.com/ymaps/modules). Это же верно и для блоков и элементов [без DOM-представления](./i-bem-js-html-binding.ru.md#Блоки-без-dom-представления).\n\nДоступ к классам блоков и элементов необходим для:\n\n* [Доопределения](./i-bem-js-decl.ru.md#Доопределение-блока-или-элемента) декларации блока и [наследования](./i-bem-js-decl.ru.md#Наследование).\n* [Поиска](./i-bem-js-dom.ru.md#Поиск-экземпляров-блоков-и-элементов-в-dom-дереве) их экземпляров в DOM-дереве.\n* [Для работы с событиями в контексте класса](./i-bem-js-events.ru.md).\n* Вызова статических методов класса.\n\n**Пример**\n\nБлок `button` наследуется от базового блока `control`:\n\n```js\nmodules.define('button', ['i-bem-dom', 'control'], function(provide, bemDom, Control) {\n\nprovide(bemDom.declBlock(this.name, Control, {}));\n\n});\n```\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-params.en.md",
    "content": "<a name=\"data-bem\"></a>\n\nPassing parameters to a block instance\n--------------------------------------\n\n### Syntax for passing parameters\n\nBlock parameters are stored in the `data-bem` attribute of an HTML element, and are passed to the block at the time of initialization. Use parameters to control the behavior of a specific block instance that is bound to a given HTML element.\n\nThe value of the `data-bem` attribute must contain valid JSON describing a hash in the format:\n\n* key — `{String}`, name of the block.\n* value — `{Object}`, parameters of the block. If this instance of the block does not need\n    parameters, specify an empty hash `{}`.\n\n```html\n<div class=\"my-block i-bem\" data-bem=\"{ \" my-block=\"my-block\">\n</div>\n```\n\nIf an HTML element has [multiple JS blocks bound to it](./i-bem-js-html-binding.en.md#multiple-js-blocks-bound-to-it), the value of the `data-bem` attribute must contain the parameters for each of them:\n\n```html\n<div class=\"my-block another-block i-bem\" data-bem=\"{ \" my-block=\"my-block\">\n</div>\n```\n\n**Element parameters** are passed via the `data-bem` attribute of the element DOM node. For example, you can pass parameters to the `my-elem` element in the `my-block` block like this:\n\n```html\n<div class=\"my-block i-bem\" data-bem=\"{ \" my-block=\"my-block\">\n <div class=\"my-block__my-elem\" data-bem=\"{ \" my-block__my-elem=\"my-block__my-elem\">\n </div>\n</div>\n```\n\nSpecifying the block name in the parameters provides the following advantages:\n\n* Blocks are initialized faster, since the value of the `class` attribute doesn't have to be parsed.\n* Multiple blocks can be put on the same HTML element without having to multiply its attributes.\n\n### Accessing parameters from a block instance\n\nYou can access parameters from a block instance via the `this.params` field. Its value is a hash of parameters from the `data-bem` attribute of the block DOM element (`this.domElem`).\n\nFor example, you can access parameters of the `my-block` block like this:\n\n```html\n<div class=\"my-block i-bem\" data-bem=\"{ \" my-block=\"my-block\">\n</div>\n```\n\n```js\nmodules.define('my-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited': function() {\n                console.log(this.params); // { foo : 'bar' }\n            }\n        }\n    }\n}));\n\n});\n```\n\nTo get element parameters, use the `elemParams` method of the block instance. It accepts a string argument with the element name or its jQuery object. It returns a hash of element parameters.\n\n```html\n<div class=\"my-block i-bem\" data-bem=\"{ \" my-block=\"my-block\">\n <div class=\"my-block__my-elem\" data-bem=\"{ \" my-block__my-elem=\"my-block__my-elem\">\n </div>\n</div>\n```\n\n```js\nmodules.define('my-block', ['i-bem__dom'], function(provide, BEMDOM) {\n\nprovide(BEMDOM.decl(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited': function() {\n                    console.log(this.elemParams('my-elem')); // { foo : 'bar' }\n            }\n        }\n    }\n}));\n\n});\n```\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-params.ru.md",
    "content": "## Передача параметров экземпляру блока и элемента\n\n### Синтаксис передачи параметров\n\nПараметры блока и элемента хранятся в атрибуте `data-bem` HTML-элемента и передаются экземпляру в момент инициализации. Параметры позволяют влиять на поведение конкретного экземпляра, привязанного к данному HTML-элементу.\n\nЗначение атрибута `data-bem` должно содержать валидный JSON описывающий хеш вида:\n\n* ключ — `{String}`, имя блока;\n* значение — `{Object}`, параметры данного блока. Если данному экземпляру не требуются параметры, указывается пустой хеш `{}`.\n\n```html\n<div class=\"my-block i-bem\" data-bem='{ \"my-block\" : {} }'></div>\n<div class=\"my-block__my-elem i-bem\" data-bem='{ \"my-block__my-elem\" : {} }'></div>\n```\n\nЕсли к HTML-элементу [привязано несколько блоков или элементов в технологии JS](./i-bem-js-html-binding.ru.md#Один-html-элемент--несколько-js-блоков),\nто в значении атрибута `data-bem` должны содержаться параметры для каждого из них:\n\n```html\n<div class=\"a-block b-block i-bem\" data-bem='{ \"a-block\" : {}, \"b-block\" : {} }'></div>\n```\n\nУказание имени блока в параметрах позволяет:\n\n* размещать несколько блоков на одном HTML-элементе без необходимости множить его атрибуты\n* ускорить инициализацию блоков – не нужно парсить значение атрибута `class`\n\n### Задание параметров по умолчанию\n\nДля задания параметров по умолчанию в декларации блока или элемента необходимо переопределить метод `_getDefaultParams()`. Его результат будет объединён со значениями параметров из атрибута `data-bem` DOM-элемента, при этом параметры из атрибута будут иметь приоритет.\n\n**Пример**\n\n```js\nmodules.define('my-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, {\n    _getDefaultParams : function() {\n        return {\n            param1 : 'val1'\n            param2 : 'val2'\n        }\n    }\n}));\n\n});\n```\n\n```html\n<div class=\"my-block i-bem\" data-bem='{ \"my-block\" : { \"param1\" : \"val2\", \"param3\" : \"val3\" } }'></div>\n```\n\nИтоговые параметры:\n\n```js\n{\n    param1 : 'val2',\n    param2 : 'val2',\n    param3 : 'val3'\n}\n```\n\n### Доступ к параметрам из экземпляра\n\nДоступ к параметрам из экземпляра блока и элемента можно получить через поле `this.params`.\n\n**Пример**\n\n```html\n<div class=\"my-block i-bem\" data-bem='{ \"my-block\" : { \"param1\" : \"val1\" } }'></div>\n```\n\n```js\nmodules.define('my-block', ['i-bem-dom'], function(provide, bemDom) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod : {\n        'js' : {\n            'inited': function() {\n                console.log(this.params); // { param1 : 'val1' }\n            }\n        }\n    }\n}));\n\n});\n```\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-states.en.md",
    "content": "<a name=\"states\"></a>\n\nStates of a block\n-----------------\n\nWhen designing a dynamic block in BEM style, you need to provide the complete logic of\nchanges that occur in it as a set of **states** for the block. Then the block behavior is determined by\n**triggers** — callback functions that are performed when the block switches\nfrom one state to another.\n\nThis allows you to write the block code declaratively as a set of statements in the format: `state description` — `action performed when switching to this state`.\n\n<a name=\"modifiers\"></a>\n\n### Modifiers\n\nAccording to the BEM methodology,\n**modifiers** describe the state of a block and its elements.\n\nA modifier indicates which of the possible states the block is in. A modifier is a **name** — **value** pair. The list of acceptable modifier values describes the set of block states. For example,\nto describe a block size, you can use the `size` modifier with the possible values `s`, `m` and `l`.\n\nA **simple modifier** is a special case when only the presence or absence\nof the modifier on the block is important, and its value is insignificant. An example is the modifier describing the ”disabled“ state: `disabled`. A modifier with an unspecified `i-bem.js` value is interpreted as boolean and automatically assigned the value `true`.\n\nEach block can have one or more modifiers set. A block isn't required to have\nany modifiers. The block developer defines the list of acceptable modifiers and their\nvalues.\n\nModifiers are set during [initialization of a block instance](./i-bem-js-init.en.md) (if modifiers and their values are specified in the `class` attribute of the corresponding HTML element).\n\nModifiers can change as part of the block functioning (for example, as a reaction to a [DOM event](i-bem-js-events.en.md#dom-event) of the block), or at the request of other blocks (see [Interaction of blocks](./i-bem-js-interact.en.md)).\n\nWhen setting, deleting, and changing modifier values, [triggers](#triggers) are executed.\n\n> **Note** If modifiers were set in a block HTML element before its initialization, the triggers to set these modifiers **are not executed**. In this case, the block instance gets its original state, and doesn't change it.\n\n<a name=\"mods-api\"></a>\n\n#### Managing modifiers\n\nMethods of a block instance for working with modifiers:\n\n* `hasMod([elem], modName, [modVal])` – Checks for the presence of a modifier. Returns `true` if the `modName` modifier is set.\n* `getMod([elem], modName)` – Returns the value of `modName`.\n* `getMods([elem], [...modNames])` – Returns a hash with the values of all modifiers. You can get the values of multiple modifiers by passing their names in separate arguments (`[...modNames]`). To get the modifiers of an element, you can specify the `[elem]` argument.\n* `setMod([elem], modName, [modVal=true])` – Sets the `modName` modifier. If the value of `modVal` isn't specified, a *simple modifier* will be set.\n* `toggleMod([elem], modName, modVal1, [modVal2], [condition])` – Toggles a modifier's value. If the `[modVal2]` argument is passed, it switches between `modVal1` and `modVal2`. If not, `modVal1` will be set and removed in turn. The `condition` argument with the `true` value allows inverting the order for toggling modifier values.\n* `delMod([elem], modName)` – Deletes `modName`.\n\n**Example**\n\nThe `changeColor` method of the `square` block toggles the `color` modifier between the values `green` and `red`, if the block has the `has-color` modifier set:\n\n```js\nBEMDOM.decl('square', {\n    changeColor : function(e) {\n        if(this.hasMod('has-color')) {\n            this.toggleMod('color', 'green', 'red');\n        }\n    }\n});\n```\n\nThe same methods allow managing modifiers of the block elements. To do this, a reference to the **element DOM node** (not the element name) is passed as the first argument.\n\n**Example**\n\nOn a click, the `searchbox` block can assign its `input` element the simple modifier `clean` (the assumed value is `true`):\n\n```js\nBEMDOM.decl('searchbox', {\n    _onClick: function() {\n        this.setMod(this.elem('input'), 'clean');\n    }\n});\n```\n\n> **Note** Use the API for changing the values of modifiers. Don't set modifiers by altering the CSS classes of the corresponding DOM node yourself.\n\nFor complete documentation of the API for managing modifiers, see the [JSDoc](https://en.bem.info/libs/bem-core/current/desktop/i-bem/jsdoc/) section for the `i-bem` block.\n\n<a name=\"mods-api-trigger\"></a>\n\n### Triggers to set modifiers\n\nTriggers to set modifiers are executed in two phases:\n\n1.  **Before setting the modifier**. This phase is reserved for the ability to\n    **cancel** setting modifiers. If at least one of the triggers executed in this phase returns\n    `false`, modifiers are not set.\n2.  **After setting the modifier**. Triggers executed in this phase\n    can't cancel setting modifiers.\n\nTriggers can be bound to the following types of changes to modifier values:\n\n1.  Setting *any* modifier to *any* value.\n2.  Setting a *specific* `modName` modifier to *any* value (including\n    setting a simple modifier to `true`).\n3.  Setting a *specific* `modName` modifier to a *specific* `modVal` value.\n4.  Setting a modifier to the value `''` (empty string), which is\n    equivalent to deleting the modifier or setting a simple modifier\n    to `false`.\n\nWhen setting the `modName` modifier to the `modVal` value, triggers in\neach phase (if they are defined) are fired in the same order as they are\nlisted in the list of events above (from general to specific).\n\nThus, when defining a trigger, the user specifies:\n\n* The execution phase (before or after setting a modifier).\n* The event type (the modifier name and value to set).\n\n<a name=\"mods-api-trigger-phase\"></a>\n\n#### Execution phases\n\nAn additional phase prior to setting a modifier allows performing\ncertain checks without risk of affecting the logic for setting the modifier. For example, if there are mutually exclusive modifiers, it makes sense before setting one of them to check whether the other is already set.\n\n**Example**\n\nThe `focused` modifier won't be set on the `searchbox` block if it has the `disabled` modifier.\n\n```js\nBEMDOM.decl('searchbox', {\n    beforeSetMod : {\n        'focused' : {\n            'true' : function() {\n                return !this.hasMod('disabled');\n            }\n        }\n    },\n\n    onSetMod : {\n        'focused' : {\n            'true' : function() { /* ... */ }\n        }\n    }\n});\n```\n\nIf the trigger for the phase prior to setting (`beforeSetMod`) returns `false`, the modifier is not set.\n\nFor more information about using triggers, see [Declaring triggers](i-bem-js-decl.en.md#declaring-triggers).\n\n> **Note** The trigger to set the `js` modifier to `inited` is a constructor of a block instance, but with the value `''` it is a destructor of a block instance. For more information, see [Initialization](./i-bem-js-init.en.md).\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js-states.ru.md",
    "content": "## Состояния блока и элемента\n\nПроектируя динамический блок или элемент в стиле БЭМ, нужно представить всю логику изменений, происходящих в нем, как набор **состояний**. Тогда поведение блока и элемента определяется **триггерами** — callback-функциями, которые выполняются при переходе из одного состояния в другое.\n\nЭто позволяет писать код блока в декларативном стиле как набор утверждений вида:\n\n* «описание состояния» — «действия, выполняемые при переходе в данное состояние».\n\n### Модификаторы\n\nСогласно БЭМ-методологии, состояние блока и его элементов описывается **модификаторами**.\n\nМодификатор указывает, в каком из возможных состояний находится блок или элемент. Модификатор представляет собой пару: **ключ-значение**. Список допустимых значений модификатора описывает набор состояний блока и элемента. Например, для описания размеров блока можно использовать модификатор `size` с допустимыми значениями `s`, `m` и `l`.\n\n**Простой модификатор** — частный случай, когда важно только наличие или отсутствие модификатора у блока или элемента, а его значение несущественно. Например, модификатор, описывающий состояние «отключен»: `disabled`. Модификатор с неуказанным значением `i-bem.js` интерпретирует как булев и автоматически присваивает ему значение `true`.\n\nКаждому блоку и элементу можно установить один или несколько модификаторов. Блок и элемент могут не иметь модификаторов. Список допустимых модификаторов и их значений определяет разработчик.\n\nМодификаторы устанавливаются при [инициализации экземпляра](./i-bem-js-init.ru.md#Инициализация) (если модификаторы и их значения указаны в атрибуте `class` соответствующего HTML-элемента).\n\nМодификаторы могут изменяться как в процессе работы блока и элемента (например, как реакция на [DOM-события](./i-bem-js-events.ru.md#dom-события) блока), так и по запросу из других блоков и элементов (см. раздел [Взаимодействие блоков](./i-bem-js-interact.ru.md#Взаимодействие-блоков-и-элементов)).\n\nПри установке, удалении и изменении значений модификаторов, выполняются [триггеры](#Триггеры-на-установку-модификаторов).\n\n> **Примечание** Если модификаторы были заданы в HTML-элементе блока или элемента до момента его инициализации, триггеры на установку данных модификаторов **не выполняются**. Экземпляр в этом случае получает начальное состояние, а не меняет его.\n\n#### Управление модификаторами\n\nМетоды экземпляра для работы с модификаторами:\n\n* `hasMod(modName, [modVal])` – проверяет наличие модификатора. Возвращает `true`, если модификатор `modName` установлен.\n* `getMod(modName)` – возвращает значение модификатора `modName`.\n* `setMod(modName, [modVal=true])` – устанавливает модификатор `modName`. Если значение `modVal` не задано, будет установлен *простой модификатор*.\n* `toggleMod(modName, modVal1, [modVal2], [condition])` – переключает значения модификатора. Если передан аргумент `[modVal2]`, переключение происходит между `modVal1` и `modVal2`, если нет, `modVal1` будет поочередно устанавливаться и удаляться. Аргумент `condition` в значении `true` позволяет инвертировать порядок переключения значений модификатора.\n* `delMod(modName)` – удаляет модификатор `modName`.\n\n**Пример**\n\n```js\nbemDom.declBlock('link', {\n    // ...\n\n    _onClick : function() {\n        if(!this.hasMod('disabled')) {\n            this._emit('click');\n        }\n    },\n\n    _onFocus : function() {\n        this.setMod('focused');\n    },\n\n    _onBlur : function() {\n        this.delMod('focused');\n    }\n\n    // ...\n});\n```\n\n> **Примечание** Для изменения значений модификаторов используйте API. Не следует устанавливать модификаторы, самостоятельно изменяя CSS-классы соответствующего DOM-узла.\n\nПолное описание API для управления модификаторами приведено в разделе [JSDoc](https://ru.bem.info/platform/i-bem/) блока `i-bem`.\n\n### Триггеры на установку модификаторов\n\nВыполнение триггеров на установку модификаторов разбито на две фазы:\n\n1. **До установки модификатора**. Эта фаза зарезервирована для возможности **отменить** установку модификатора. Если хотя бы один из триггеров, выполняемых в этой фазе, вернет `false`, установки модификатора не произойдет.\n2. **После установки модификатора**. Триггеры, выполняемые в этой фазе, уже не могут отменить установку модификаторов.\n\nТриггеры могут быть привязаны к следующим типам изменений значений модификаторов:\n\n1. Установка *любого* модификатора в *любое* значение.\n2. Установка *конкретного* модификатора `modName` в *любое* значение (в том числе установка простого модификатора в значение `true` и удаление модификатора).\n3. Установка *конкретного* модификатора `modName` в *конкретное* значение `modVal`.\n4. Установка модификатора в значение `''` (пустая строка), что эквивалентно удалению модификатора или установке простого модификатора в значение `false`.\n5. Установка *конкретного* модификатора `modName` в *любое, отличное от* конкретного значения `modVal`.\n6. Установка *конкретного* модификатора `modName` из *конкретного* значения `modVal` в любое другое.\n\nПри установке модификатора `modName` в значение `modVal` триггеры каждой фазы (если они определены) вызываются в том порядке, в котором они перечислены в приведенном выше списке событий (от общего к частному).\n\nТаким образом, при определении триггера пользователь указывает:\n\n* фазу выполнения (до или после установки модификатора);\n* тип действия (имя и устанавливаемое значение модификатора).\n\n#### Фазы выполнения\n\nДополнительная фаза, предшествующая установке модификатора, позволяет\nпроизвести некоторые проверки без риска повлиять на логику, связанную с установкой модификатора.\nНапример, если существуют взаимоисключающие модификаторы, перед установкой одного из них логично проверить, не установлен ли другой.\n\n**Пример**\n\nМодификатор `focused` не будет установлен блоку `searchbox`, если у него есть модификатор `disabled`.\n\n```js\nbemDom.declBlock('searchbox', {\n    beforeSetMod : {\n        'focused' : {\n            'true' : function() {\n                return !this.hasMod('disabled');\n            }\n        }\n    },\n\n    onSetMod : {\n        'focused' : {\n            'true' : function() { /* ... */ }\n        }\n    }\n});\n```\n\nЕсли триггер для фазы, предшествующей установке (`beforeSetMod`), возвращает `false`, установка модификатора не производится.\n\nПодробнее об использовании триггеров читайте в разделе [Декларация триггеров](./i-bem-js-decl.ru.md#Декларация-триггеров).\n\n> **Примечание** Триггер на установку модификатора `js` в значение `inited` является конструктором экземпляра блока, а в значение `''` – деструктором экземпляра блока. Подробности смотрите в разделе [Инициализация](./i-bem-js-init.ru.md#Инициализация).\n\n[ym]: https://github.com/ymaps/modules\n\n[bem-tools]: https://ru.bem.info/tools/bem/\n\n[i-bem]: https://ru.bem.info/libs/bem-core/current/desktop/i-bem/jsdoc/\n\n[i-bem__dom]: https://ru.bem.info/libs/bem-core/current/desktop/i-bem/jsdoc/\n\n[html]: ./i-bem-js-html-binding.ru.md\n\n[decl]: ./i-bem-js-decl.ru.md\n\n[dom]: ./i-bem-js-dom.ru.md\n\n[states]: ./i-bem-js-states.ru.md\n\n[events]: ./i-bem-js-events.ru.md\n\n[init]: ./i-bem-js-init.ru.md\n\n[interact]: ./i-bem-js-interact.ru.md\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js.en.md",
    "content": "# i-bem.js: User's guide\n\n## i-bem.js: JavaScript framework for BEM\n\n`i-bem.js` is a specialized JavaScript framework for web development using the [BEM methodology](https://en.bem.info/method/).\n\n`i-bem.js` makes it possible to:\n\n* Develop a web interface in terms of blocks, elements, and modifiers.\n* Describe a block logic in declarative style, as a set of states.\n* Easily integrate JavaScript code with BEMHTML or BH templates and CSS in BEM style.\n* Flexibly redefine the behavior of library blocks.\n\n`i-bem.js` is not meant to replace the general-purpose framework, like jQuery.\n\n**What this document covers**:\n\n* [Overview](./i-bem-js-common.en.md) of the framework: its relationship to the BEM subject domain, and a summary of the framework modular structure, a template project, and assembly tools written using `i-bem.js`.\n* [Binding JS blocks to HTML](./i-bem-js-html-binding.en.md) — Markup for JS blocks in a page HTML code and the possible relationships of HTML elements to JS blocks.\n* [Block declaration](./i-bem-js-decl.en.md) — Syntax for describing JS blocks.\n* [Passing parameters](./i-bem-js-params.en.md) — Passing parameters to a block instance and accessing block parameters from an instance.\n* [Working with the DOM tree](./i-bem-js-dom.en.md) — The API for working with DOM nodes of blocks: elements, dynamic changes to the DOM tree (using AJAX), and searching DOM nodes.\n* [Block states](./i-bem-js-states.en.md) — Modifiers and triggers for state changes (setting modifiers).\n* [Events](./i-bem-js-events.en.md) — The `i-bem.js` event model: DOM and BEM events and event delegation.\n* [Initialization](./i-bem-js-init.en.md) — Initializing and deleting block instances; deferred and automatic initialization.\n* [Interaction of blocks](./i-bem-js-interact.en.md) — Calls from a block to other blocks and classes of blocks.\n* [Context](./i-bem-js-context.en.md) — Private and static properties of a block. BEMDOM static properties.\n* [What next?](./i-bem-js-extras.en.md) — Links to documentation and supplemental materials.\n"
  },
  {
    "path": "common.docs/i-bem-js/i-bem-js.ru.md",
    "content": "# i-bem.js: руководство пользователя\n\n## i-bem.js: JavaScript-фреймворк для БЭМ\n\n`i-bem.js` — специализированный JavaScript-фреймворк для веб-разработки в рамках [БЭМ-методологии](https://ru.bem.info/methodology/).\n\n`i-bem.js` позволяет:\n\n* разрабатывать веб-интерфейс в терминах блоков, элементов, модификаторов;\n* описывать логику работы блока в декларативном стиле — как набор состояний;\n* легко интегрировать JavaScript-код с BEMHTML- или BH-шаблонами и CSS в стиле БЭМ;\n* гибко переопределять поведение библиотечных блоков.\n\n`i-bem.js` не предназначен для замены фреймворка общего назначения, подобного jQuery.\n\n**Краткий обзор содержания документа**:\n\n* [Общие сведения](./i-bem-js-common.ru.md) о фреймворке: связь с предметной областью БЭМ, краткое описание модульной структуры фреймворка, шаблонного проекта и инструментов для сборки кода, написанного с использованием `i-bem.js`.\n* [Привязка JS-экземпляров к HTML](./i-bem-js-html-binding.ru.md) — разметка JS-блоков в HTML-коде страницы, варианты соотношения HTML-элементов и JS-экземпляров.\n* [Декларация](./i-bem-js-decl.ru.md) — синтаксис описания класса блока и элемента.\n* [Передача параметров](./i-bem-js-params.ru.md) — передача параметров экземпляру блока и элемента, получение доступа к параметрам из экземпляра.\n* [Работа с DOM-деревом](./i-bem-js-dom.ru.md) — API для работы с DOM-узлами блоков и элементов.\n* [Состояния](./i-bem-js-states.ru.md) — модификаторы, триггеры на изменение модификаторов.\n* [Коллекции](./i-bem-js-collections.ru.md) — работа с несколькими экземплярами блоков или элементов.\n* [События](./i-bem-js-events.ru.md) — событийная модель `i-bem.js`: DOM- и БЭМ-события.\n* [Инициализация](./i-bem-js-init.ru.md) — инициализация экземпляров блоков и элементов.\n* [Взаимодействие блоков](./i-bem-js-interact.ru.md) — обращение из экземпляра блока или элемента к другим экземплярам и классам.\n* [Контекст](./i-bem-js-context.ru.md) —  собственные и статические свойства блока и элемента. Статические свойства `i-bem-dom`.\n* [Что дальше?](./i-bem-js-extras.ru.md) — ссылки на документацию и дополнительные материалы.\n"
  },
  {
    "path": "desktop.blocks/jquery/__config/jquery__config.deps.js",
    "content": "({\n    shouldDeps : ['ua', 'objects']\n})\n"
  },
  {
    "path": "desktop.blocks/jquery/__config/jquery__config.js",
    "content": "/**\n * @module jquery__config\n * @description Configuration for jQuery (desktop override).\n * Previously downgraded jQuery for IE < 9, no longer needed.\n */\n\nexport default function(base) {\n    return base;\n};\n"
  },
  {
    "path": "desktop.blocks/jquery/__event/_type/jquery__event_type_winresize.deps.js",
    "content": "({\n    shouldDeps : ['jquery', 'ua']\n})\n"
  },
  {
    "path": "desktop.blocks/jquery/__event/_type/jquery__event_type_winresize.js",
    "content": "/**\n * @module jquery\n */\n\nimport ua from 'bem:ua';\nimport $ from 'bem:jquery';\n\n// IE8 and below, https://msdn.microsoft.com/en-us/library/ie/ms536959%28v=vs.85%29.aspx\nif(ua.msie && document.documentMode < 9) {\n    const win = window,\n        $win = $(window);\n    let winWidth = $win.width(),\n        winHeight = $win.height();\n\n    ($.event.special.resize || ($.event.special.resize = {})).preDispatch = function(e) {\n        if(e.target === win) {\n            const curWinWidth = $win.width(),\n                curWinHeight = $win.height();\n\n            if(curWinWidth === winWidth && curWinHeight === winHeight) {\n                return false;\n            } else {\n                winWidth = curWinWidth;\n                winHeight = curWinHeight;\n            }\n        }\n    };\n}\n\nexport default $;\n"
  },
  {
    "path": "desktop.blocks/page/__conditional-comment/page__conditional-comment.bemhtml.js",
    "content": "block('page').elem('conditional-comment')(\n    tag()(false),\n\n    content()(function() {\n        var ctx = this.ctx,\n            cond = ctx.condition\n                .replace('<', 'lt')\n                .replace('>', 'gt')\n                .replace('=', 'e'),\n            hasNegation = cond.indexOf('!') > -1,\n            includeOthers = ctx.msieOnly === false,\n            hasNegationOrIncludeOthers = hasNegation || includeOthers;\n\n        return [\n            { html : '<!--[if ' + cond + ']>' },\n            includeOthers? { html : '<!' } : '',\n            hasNegationOrIncludeOthers? { html : '-->' } : '',\n            applyNext(),\n            hasNegationOrIncludeOthers? { html : '<!--' } : '',\n            { html : '<![endif]-->' }\n        ];\n    })\n);\n"
  },
  {
    "path": "desktop.blocks/page/__conditional-comment/page__conditional-comment.bh.js",
    "content": "module.exports = function(bh) {\n\n    bh.match('page__conditional-comment', function(ctx, json) {\n        ctx.tag(false);\n\n        var cond = json.condition\n                .replace('<', 'lt')\n                .replace('>', 'gt')\n                .replace('=', 'e'),\n            hasNegation = cond.indexOf('!') > -1,\n            includeOthers = json.msieOnly === false,\n            hasNegationOrIncludeOthers = hasNegation || includeOthers;\n\n        return [\n            { html : '<!--[if ' + cond + ']>', tag : false },\n            includeOthers? { html : '<!', tag : false } : '',\n            hasNegationOrIncludeOthers? { html : '-->', tag : false } : '',\n            json,\n            hasNegationOrIncludeOthers? { html : '<!--', tag : false } : '',\n            { html : '<![endif]-->', tag : false }\n        ];\n    });\n\n};\n"
  },
  {
    "path": "desktop.blocks/page/__conditional-comment/page__conditional-comment.ru.md",
    "content": "# Элемент `conditional-comments` блока `page`\n\n\n```javascript\n({\n    block : 'page',\n    title : 'page__conditional-comments',\n    head : [\n        {\n            elem : 'conditional-comment',\n            condition : '<= IE 8',\n            content : { elem : 'css', url : '_page.ie.css' }\n        },\n        {\n            elem : 'conditional-comment',\n            condition : '! IE',\n            content : 'Not for IE'\n        },\n        {\n            elem : 'conditional-comment',\n            condition : '> IE 8',\n            msieOnly : false,\n            content : 'For IE9+ and all other browsers'\n        }\n    ],\n    scripts : [\n        {\n            elem : 'conditional-comment',\n            condition : 'lte IE 8',\n            content : { elem : 'js', url : 'https://yastatic.net/es5-shims/0.0.1/es5-shims.min.js' }\n        }\n    ]\n})\n```\n"
  },
  {
    "path": "desktop.blocks/page/page.deps.js",
    "content": "({\n    tech : 'tmpl-spec.js',\n    shouldDeps : {\n        elems : ['conditional-comment']\n    }\n})\n"
  },
  {
    "path": "desktop.blocks/page/page.examples/.bem/level.js",
    "content": "exports.baseLevelPath = require.resolve('../../../../.bem/levels/examples.js');\n"
  },
  {
    "path": "desktop.blocks/page/page.examples/40-es5-shims.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'Пример подключения es5-shims для IE',\n    head : [\n        '<!--[if lt IE 9]>',\n        { elem : 'js', url : 'https://yastatic.net/es5-shims/0.0.1/es5-shims.min.js' },\n        '<![endif]-->'\n    ],\n    content : 'Подключение es5-shims для IE'\n})\n"
  },
  {
    "path": "desktop.blocks/page/page.examples/40-es5-shims.ru.title.txt",
    "content": "Пример подключения es5-shims для IE\n"
  },
  {
    "path": "desktop.blocks/page/page.ru.md",
    "content": "﻿# page\n\nНа уровне переопределения `desktop.blocks` блок предоставляет шаблон, создающий дополнительный HTML-элемент `<meta>`. \n\n## Обзор\n\n### Специализированные поля блока\n\n| Поле | Тип | Описание |\n| ---- | --- | -------- |\n| <a href=\"#declfields-x-ua-compatible\">x-ua-compatible</a> | `{String}`&#124;`{Boolean}` | Управляет поведением создаваемого блоком HTML-элемента `<meta>` с атрибутом `http-equiv` `X-UA-Compatible`. |\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| ------- | --------------------- | -------- |\n| <a href=\"#elems-css\">css</a> | `BEMJSON` | Элемент служит для подключения CSS. |\n| <a href=\"#elems-conditional-comment\">conditional-comment</a> | `BEMJSON` | Помогает использовать условные комментарии. |\n\n### Специализированные поля элементов блока\n\n| Элемент | Поле | Тип | Описание |\n| ------- | ---- | --- | -------- |\n| <a href=\"#elems-css\">css</a> | <a href=\"#elems-css-declfields-ie\">`ie`</a> | `{String}`&#124;`{Boolean}` | Используется для указания применимости стилей к Internet Explorer версий 6-9 и подключения специальных стилей для Internet Explorer.  |\n| <a href=\"#elems-conditional-comment\">conditional-comment</a> | <a href=\"#elems-conditional-comment-declfields-condition\">`condition`</a> | `{String}` | Позволяет указать условие, при выполнении которого содержимое поля `content` декларации элемента, будет доступно.  |\n|  | <a href=\"#elems-conditional-comment-declfields-msieOnly\">`msieOnly`</a> | `{Boolean}` | Указывает, предназначен ли данный условный комментарий для использования исключительно в Internet Explorer.  |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `bh.js`\n* `bemhtml`\n\n## Подробности\n\nСоздает HTML-элемент `<meta>` с атрибутом `http-equiv` `X-UA-Compatible`, определяющий совместимость с юзер-агентами. По умолчанию, значением атрибута `content` элемента является `IE=edge` (совместим с последними версиями Internet Explorer).\n\n<a name=\"declfields\"></a>\n### Специализированные поля блока\n\n<a name=\"declfields-x-ua-compatible\"></a>\n#### Поле `x-ua-compatible`\n\nТип: `{String}`|`{Boolean}`.\n\nУправляет поведением создаваемого блоком HTML-элемента `<meta>` с атрибутом `http-equiv` `X-UA-Compatible`:\n\n* со значением `false` HTML-элемент `<meta>` не будет создаваться.\n* \n```js\n{\n    block : 'page',\n    title : 'Hello, World!',\n    'x-ua-compatible' : false,\n    content : 'Отмена создания HTML-элемента <meta>'\n}\n```\n\n* строчное значение будет присвоено свойству `content` HTML-элемента `<meta>`.\n\n```js\n{\n    block : 'page',\n    title : 'Hello, World!',\n    'x-ua-compatible' : 'IE=6',\n    content : 'Совместим с Internet Explorer 6'\n}\n```\n\n\n<a name=\"elems\"></a>\n### Элементы блока\n\n<a name=\"elems-css\"></a>\n#### Элемент `css` \n\n<a name=\"elems-css-declfields-ie\"></a>\n##### Специализированное поле `ie` \n\nТип: `{String}`|`{Boolean}`.\n\nИспользуется для указания применимости стилей к Internet Explorer версий 6-9 и подключения специальных стилей Internet Explorer. \n\nДопустимы следующие значения:\n\n* строка вида `'lt IE 8'` – элемент `<link>` будет обернут в условные комментарии, для использования в соответствующих версиях Internet Explorer (для текущего примера `lt IE 8` – ниже восьмой версии).\n* `false` – будут использоваться условные комментарии, предотвращающие использование стилей в IE 9 и ниже. \n* `true` – используется в случае, если в проекте есть отдельный CSS для каждой версии Internet Explorer. Значением свойства `url`, при этом, должна быть строка с путем и именем файла без суффикса. Во время подключения создаются элементы `<link>` с отдельным суффиксом для каждой версии. Другими словами, при значении `url` равном `foo.com/index` будут подключены стили `foo.com/index.ie6.css`, `foo.com/index.ie7.css` и т.д. до `...ie9.css`. При этом каждый HTML-элемент будет обернут в условный комментарий, обеспечивающий его подключение только в соответствующей версии Internet Explorer.\n\n```js\n{\n    block : 'page',\n    title : 'Page title',\n    head : [\n        { elem : 'css', url : 'example.css', ie : false },\n        { elem : 'css', url : 'example.ie.css', ie : 'lt IE 8' }\n    ],\n    content : 'Страница с отдельными CSS правилами для IE'\n}\n```\n\n\n<a name=\"elems-conditional-comment\"></a>\n#### Элемент `conditional-comment`\n\nПозволяет обернуть содержимое поля `content`, определенное в BEMJSON-декларации элемента, в условные комментарии. Условие, при котором содержимое поля будет доступно, определяется специализированным полем `condition`.\n\n```js\n({\n    block : 'page',\n    title : 'page__conditional-comments',\n    styles : \n        {\n            elem : 'conditional-comment',\n            condition : '<= IE 8',\n            content : { elem : 'css', url : '_page.ie.css' }\n        },\n    scripts : \n        {\n            elem : 'conditional-comment',\n            condition : 'lte IE 8',\n            content : { elem : 'js', url : 'https://yastatic.net/es5-shims/0.0.1/es5-shims.min.js' }\n        }\n})\n```\n\n\n<a name=\"elems-conditional-comment-declfields-condition\"></a>\n##### Специализированное поле `condition`\n\nТип: `{String}`.\n\nУсловие, при выполнении которого содержимое поля `content` декларации элемента, будет доступно. Например, определенная версия Internet Explorer.\n\nЗначение поля составляется из:\n\n* квантора – `>`, `<`, `=`, `<=`, `>=`, `lt`, `gt`, `e` или `!` (логическое «не»);\n* слова `IE` отделенного с обеих сторон пробелами;\n* номера версии (6, 7, 8, 9). Может отсутствовать, если указан квантор `!`. Тогда, значение поля `content` будет доступно для всех браузеров, кроме Internet Explorer.\n\n```js\n({\n    block : 'page',\n    head :\n        {\n            elem : 'conditional-comment',\n            condition : '! IE',\n            content : 'Not for IE'\n        }\n})\n```\n\n\n<a name=\"elems-conditional-comment-declfields-msieOnly\"></a>\n##### Специализированное поле `msieOnly`\n\nТип: `{Boolean}`.\n\nУказывает на то, предназначен ли данный условный комментарий для использования исключительно в Internet Explorer. Со значением `true` поле можно не указывать.\n\n```js\n({\n    block : 'page',\n    head :\n        {\n            elem : 'conditional-comment',\n            condition : '> IE 8',\n            msieOnly : false,\n            content : 'For IE9+ and all other browsers'\n        }\n})\n```\n"
  },
  {
    "path": "desktop.blocks/page/page.tmpl-specs/50-conditions.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'Пример подключения es5-shims для IE',\n    head : [\n        { html : '<!--[if lt IE 9]>', tag : false },\n        { elem : 'js', url : 'https://yastatic.net/es5-shims/0.0.1/es5-shims.min.js' },\n        { html : '<![endif]-->', tag : false }\n    ]\n})\n"
  },
  {
    "path": "desktop.blocks/page/page.tmpl-specs/50-conditions.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n    <title>Пример подключения es5-shims для IE</title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <!--[if lt IE 9]><script src=\"https://yastatic.net/es5-shims/0.0.1/es5-shims.min.js\"></script><![endif]-->\n</head>\n<body class=\"page\"></body>\n</html>\n"
  },
  {
    "path": "desktop.blocks/page/page.tmpl-specs/60-conditional-comments.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'page__conditional-comments',\n    head : [\n        {\n            elem : 'conditional-comment',\n            condition : '<= IE 8',\n            content : { elem : 'css', url : '60-conditional-comment.ie.css' }\n        },\n        {\n            elem : 'conditional-comment',\n            condition : '! IE',\n            content : 'Not for IE'\n        },\n        {\n            elem : 'conditional-comment',\n            condition : '> IE 8',\n            msieOnly : false,\n            content : 'For IE9+ and all other browsers'\n        }\n    ],\n    scripts : [\n        {\n            elem : 'conditional-comment',\n            condition : 'lte IE 8',\n            content : { elem : 'js', url : 'https://yastatic.net/es5-shims/0.0.1/es5-shims.min.js' }\n        }\n    ]\n})\n"
  },
  {
    "path": "desktop.blocks/page/page.tmpl-specs/60-conditional-comments.html",
    "content": "<!DOCTYPE HTML>\n<html class=\"ua_js_no\">\n<head>\n<meta charset=\"utf-8\"/>\n<meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n<title>page__conditional-comments</title>\n<script>(function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");</script>\n<!--[if lte IE 8]><link rel=\"stylesheet\" href=\"60-conditional-comment.ie.css\"/><![endif]-->\n<!--[if ! IE]>-->Not for IE<!--<![endif]-->\n<!--[if gt IE 8]><!-->For IE9+ and all other browsers<!--<![endif]-->\n</head>\n<body class=\"page\">\n<!--[if lte IE 8]><script src=\"https://yastatic.net/es5-shims/0.0.1/es5-shims.min.js\"></script><![endif]-->\n</body>\n</html>\n"
  },
  {
    "path": "desktop.blocks/page/page.tmpl-specs/70-custom-x-ua-compatible.bemjson.js",
    "content": "({\n    block : 'page',\n    title : 'X-UA-Compatible',\n    uaCompatible : 'IE=EmulateIE8'\n})\n"
  },
  {
    "path": "desktop.blocks/page/page.tmpl-specs/70-custom-x-ua-compatible.html",
    "content": "<!DOCTYPE HTML>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=EmulateIE8\" http-equiv=\"X-UA-Compatible\"/>\n    <title>X-UA-Compatible</title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n</head>\n<body class=\"page\"></body>\n</html>\n"
  },
  {
    "path": "desktop.blocks/ua/ua.js",
    "content": "/**\n * @module ua\n * @description Detect some user agent features (works like jQuery.browser in jQuery 1.8)\n * @see http://code.jquery.com/jquery-migrate-1.1.1.js\n */\n\nconst ua = navigator.userAgent.toLowerCase(),\n    match = /(chrome)[ /]([\\w.]+)/.exec(ua) ||\n        /(webkit)[ /]([\\w.]+)/.exec(ua) ||\n        /(opera)(?:.*version|)[ /]([\\w.]+)/.exec(ua) ||\n        /(msie) ([\\w.]+)/.exec(ua) ||\n        ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\\w.]+)|)/.exec(ua) ||\n        [],\n    matched = {\n        browser : match[1] || '',\n        version : match[2] || '0'\n    },\n    browser = {};\n\nif(matched.browser) {\n    browser[matched.browser] = true;\n    browser.version = matched.version;\n}\n\nif(browser.chrome) {\n    browser.webkit = true;\n} else if(browser.webkit) {\n    browser.safari = true;\n}\n\n/**\n * @type Object\n */\nexport default browser;\n"
  },
  {
    "path": "desktop.blocks/ua/ua.ru.md",
    "content": "# ua\n\nНа уровне `desktop`, блок предоставляет объект, содержащий набор свойств, указывающих особенности браузера.\n\n## Обзор\n\n### Свойства и методы объекта\n\n| Имя | Тип | Описание |\n| --- | -------------- | -------- |\n| <a href=\"#fields-chrome\">chrome</a> | `{Boolean}` | Тип браузера: Google Chrome. |\n| <a href=\"#fields-opera\">opera</a> | `{Boolean}` | Тип браузера: Opera. |\n| <a href=\"#fields-msie\">msie</a> | `{Boolean}` | Тип браузера: Microsoft Internet Explorer. |\n| <a href=\"#fields-mozilla\">mozilla</a> | `{Boolean}` | Тип браузера: Mozilla Firefox. |\n| <a href=\"#fields-safari\">safari</a> | `{Boolean}` | Тип браузера: Safari. |\n| <a href=\"#fields-webkit\">webkit</a> | `{Boolean}` | Браузер построен на движке WebKit. |\n| <a href=\"#fields-version\">version</a> | `{String}` | Версия браузера. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `js`\n\n## Подробности\n\nБлок позволяет определить:\n\n* Тип браузера.\n* Совместимость с WebKit.\n* Версию браузера.\n\n```js\nmodules.require('ua', function(ua) {\n\nconsole.dir(ua);\n\n});\n```\n\n\n<a name=\"fields\"></a>\n### Свойства и методы объекта\n\n<a name=\"fields-chrome\"></a>\n#### Свойство `chrome`\n\nТип: `{Boolean}`.\n\nТип браузера. `true`, если Google Chrome.\n\n<a name=\"fields-opera\"></a>\n#### Свойство `opera`\n\nТип: `{Boolean}`.\n\nТип браузера. `true`, если Opera.\n\n<a name=\"fields-msie\"></a>\n#### Свойство `msie`\n\nТип: `{Boolean}`.\n\nТип браузера. `true`, если Microsoft Internet Explorer.\n\n<a name=\"fields-mozilla\"></a>\n#### Свойство `mozilla`\n\nТип: `{Boolean}`.\n\nТип браузера. `true`, если Mozilla Firefox.\n\n<a name=\"fields-safari\"></a>\n#### Свойство `safari`\n\nТип: `{Boolean}`.\n\nТип браузера. `true`, если Safari.\n\n<a name=\"fields-webkit\"></a>\n#### Свойство `webkit`\n\nТип: `{Boolean}`.\n\n`true`, если браузер построен на движке WebKit.\n\n<a name=\"fields-version\"></a>\n#### Свойство `version`\n\nТип: `{String}`.\n\nЗначение – строка с версией браузера вида `'600.2.5'` (для Safari). Если определить версию браузера не удается, в качестве значения устанавливается `'0'`.\n"
  },
  {
    "path": "eslint.config.js",
    "content": "import js from '@eslint/js';\nimport globals from 'globals';\n\nexport default [\n    {\n        ignores: [\n            'dist/',\n            'docs/',\n            'node_modules/',\n            'libs/',\n            'test/',\n            'common.blocks/inherit/',\n            '**/*.spec.js',\n            '**/*.tests/**',\n            '**/*.tmpl-specs/**',\n            '**/*.examples/**',\n            '**/*.bemhtml.js',\n            '**/*.bh.js',\n            'common.bundles/',\n            'docs/',\n        ],\n    },\n    js.configs.recommended,\n    {\n        languageOptions: {\n            ecmaVersion: 2024,\n            sourceType: 'module',\n            globals: {\n                ...globals.browser,\n            },\n        },\n        rules: {\n            'no-var': 'error',\n            'prefer-const': 'error',\n            'no-unused-vars': ['error', { args: 'none', caughtErrors: 'none' }],\n            'no-prototype-builtins': 'error',\n            'no-empty': ['error', { allowEmptyCatch: true }],\n            'no-cond-assign': 'off',\n        },\n    },\n    {\n        files: ['build/**/*.js', '**/*.test.js'],\n        languageOptions: {\n            globals: {\n                ...globals.node,\n            },\n        },\n    },\n];\n"
  },
  {
    "path": "jsdoc.config.json",
    "content": "{\n    \"source\": {\n        \"include\": [\n            \"common.blocks\",\n            \"desktop.blocks\",\n            \"touch.blocks\"\n        ],\n        \"includePattern\": \".+\\\\.(vanilla\\\\.js|js)$\",\n        \"excludePattern\": \"(spec|test|__tests__)\\\\.js$\"\n    },\n    \"opts\": {\n        \"destination\": \"docs/jsdoc\",\n        \"recurse\": true,\n        \"readme\": \"README.md\"\n    },\n    \"plugins\": [\"plugins/markdown\"],\n    \"templates\": {\n        \"cleverLinks\": true,\n        \"monospaceLinks\": false\n    }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"bem-core\",\n  \"version\": \"5.0.0\",\n  \"type\": \"module\",\n  \"description\": \"bem-core Library\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/bem/bem-core.git\"\n  },\n  \"keywords\": [\n    \"bem\",\n    \"core\"\n  ],\n  \"author\": \"\",\n  \"license\": \"MPL-2.0\",\n  \"engines\": {\n    \"node\": \">=20\"\n  },\n  \"exports\": {\n    \".\": \"./dist/desktop/bem-core.mjs\",\n    \"./build/plugins/*\": \"./build/plugins/*\",\n    \"./*\": \"./*\"\n  },\n  \"peerDependencies\": {\n    \"jquery\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^10.0.1\",\n    \"@playwright/test\": \"^1.58.2\",\n    \"chai\": \"^6.2.2\",\n    \"eslint\": \"^10.0.1\",\n    \"globals\": \"^16.5.0\",\n    \"husky\": \"^9.1.7\",\n    \"jquery\": \"^4.0.0\",\n    \"jsdoc\": \"^4.0.0\",\n    \"lint-staged\": \"^16.2.7\",\n    \"mocha\": \"^11.7.5\",\n    \"sinon\": \"^21.0.1\",\n    \"sinon-chai\": \"^4.0.1\",\n    \"vite\": \"^8.0.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"test\": \"node --test build/plugins/vite-plugin-bem-levels.test.js common.blocks/i18n/i18n.test.js\",\n    \"test:browser\": \"playwright test\",\n    \"test:browser:ui\": \"playwright test --ui\",\n    \"test:all\": \"npm test && npm run test:browser\",\n    \"build\": \"BEM_PLATFORM=desktop vite build --config build/vite.config.js && BEM_PLATFORM=touch vite build --config build/vite.config.js\",\n    \"build:desktop\": \"BEM_PLATFORM=desktop vite build --config build/vite.config.js\",\n    \"build:touch\": \"BEM_PLATFORM=touch vite build --config build/vite.config.js\",\n    \"docs\": \"jsdoc -c jsdoc.config.json\",\n    \"prepare\": \"husky\"\n  },\n  \"lint-staged\": {\n    \"*.js\": \"eslint --fix\"\n  }\n}\n"
  },
  {
    "path": "playwright.config.js",
    "content": "/* global process */\nimport { defineConfig } from '@playwright/test';\n\nexport default defineConfig({\n    testDir: './test',\n    testMatch: ['browser.spec.js'],\n    timeout: 60_000,\n    retries: 1,\n\n    use: {\n        browserName: 'chromium',\n        headless: true,\n        baseURL: 'http://localhost:5174',\n    },\n\n    webServer: {\n        command: 'node node_modules/.bin/vite --config build/vite.test.config.js --port 5174',\n        url: 'http://localhost:5174/test/browser/index.html',\n        reuseExistingServer: !process.env.CI,\n        timeout: 30_000,\n    },\n});\n"
  },
  {
    "path": "test/browser/bemhtml-shim.js",
    "content": "/**\n * Minimal BEMJSON-to-HTML converter for browser tests.\n *\n * Handles the BEMJSON patterns used in bem-core spec files.\n * Produces HTML compatible with bemDom.init() expectations:\n *   – correct BEM CSS class names\n *   – 'i-bem' CSS class for JS-enabled blocks (so bemDom.init finds them)\n *   – data-bem attribute with JSON params\n *   – block context propagation to child elements\n */\n\nconst BEM_JS_CLASS = 'i-bem';\n\nconst BEMHTML = {\n    apply(bemjson, ctx) {\n        if (bemjson === null || bemjson === undefined) return '';\n        if (typeof bemjson === 'string' || typeof bemjson === 'number') {\n            return String(bemjson);\n        }\n        if (Array.isArray(bemjson)) {\n            return bemjson.map(item => this.apply(item, ctx)).join('');\n        }\n        return this._render(bemjson, ctx);\n    },\n\n    _render(node, ctx) {\n        if (!node || typeof node !== 'object') return String(node ?? '');\n\n        let {\n            block,\n            elem,\n            mods,\n            elemMods,\n            mix,\n            content,\n            js,\n            tag = 'div',\n            attrs = {},\n            cls,\n        } = node;\n\n        // Inherit block context from parent when only elem is specified\n        if (!block && elem && ctx) {\n            block = ctx;\n        }\n\n        // Determine block context for children\n        const childCtx = block || ctx;\n\n        const classes = [];\n        let dataBem = null;\n        let needBemClass = false;\n\n        if (block && !elem) {\n            // Block\n            classes.push(block);\n            if (mods) {\n                for (const [m, v] of Object.entries(mods)) {\n                    if (v === true) classes.push(`${block}_${m}`);\n                    else if (v) classes.push(`${block}_${m}_${v}`);\n                }\n            }\n            if (js) {\n                needBemClass = true;\n                dataBem = dataBem || {};\n                dataBem[block] = typeof js === 'object' ? js : {};\n            }\n        } else if (block && elem) {\n            // Element\n            const elemName = `${block}__${elem}`;\n            classes.push(elemName);\n            if (elemMods) {\n                for (const [m, v] of Object.entries(elemMods)) {\n                    if (v === true) classes.push(`${elemName}_${m}`);\n                    else if (v) classes.push(`${elemName}_${m}_${v}`);\n                }\n            }\n            // Elements get data-bem and i-bem if js is explicitly provided\n            if (js) {\n                needBemClass = true;\n                dataBem = dataBem || {};\n                dataBem[elemName] = typeof js === 'object' ? js : {};\n            }\n        }\n\n        // Mix\n        if (mix) {\n            const mixes = Array.isArray(mix) ? mix : [mix];\n            for (const m of mixes) {\n                if (!m) continue;\n                const mixBlock = m.block || block || ctx;\n                if (!mixBlock) continue;\n                const mixClass = m.elem\n                    ? `${mixBlock}__${m.elem}`\n                    : mixBlock;\n                classes.push(mixClass);\n                // Add modifier classes for mix\n                if (m.mods) {\n                    for (const [mm, mv] of Object.entries(m.mods)) {\n                        if (mv === true) classes.push(`${mixClass}_${mm}`);\n                        else if (mv) classes.push(`${mixClass}_${mm}_${mv}`);\n                    }\n                }\n                if (m.elemMods) {\n                    for (const [mm, mv] of Object.entries(m.elemMods)) {\n                        if (mv === true) classes.push(`${mixClass}_${mm}`);\n                        else if (mv) classes.push(`${mixClass}_${mm}_${mv}`);\n                    }\n                }\n                if (!m.elem && m.js !== false) {\n                    needBemClass = true;\n                    dataBem = dataBem || {};\n                    dataBem[mixBlock] =\n                        m.js && typeof m.js === 'object' ? m.js : {};\n                } else if (m.elem && m.js) {\n                    needBemClass = true;\n                    dataBem = dataBem || {};\n                    dataBem[mixClass] = typeof m.js === 'object' ? m.js : {};\n                }\n            }\n        }\n\n        // Add i-bem class AFTER all block/mix classes (matching real BEMHTML order)\n        if (needBemClass) {\n            classes.push(BEM_JS_CLASS);\n        }\n\n        // Extra CSS class\n        if (cls) {\n            classes.push(cls);\n        }\n\n        // Build attributes string\n        let attrsStr = '';\n        if (classes.length) {\n            attrsStr += ` class=\"${[...new Set(classes)].join(' ')}\"`;\n        }\n        if (dataBem) {\n            attrsStr += ` data-bem='${JSON.stringify(dataBem)}'`;\n        }\n        if (attrs) {\n            for (const [k, v] of Object.entries(attrs)) {\n                attrsStr += ` ${k}=\"${String(v).replace(/\"/g, '&quot;')}\"`;\n            }\n        }\n\n        const inner =\n            content !== undefined ? this.apply(content, childCtx) : '';\n        return `<${tag}${attrsStr}>${inner}</${tag}>`;\n    },\n};\n\nexport default BEMHTML;\n"
  },
  {
    "path": "test/browser/entry.js",
    "content": "/**\n * Browser test entry point.\n *\n * 1. Sets up mocha (BDD globals: describe, it, before, after, beforeEach, afterEach)\n * 2. Imports all bem-core blocks and registers them via the modules shim\n * 3. Imports all *.spec.js files (they call modules.define('spec', …))\n * 4. Resolves the 'spec' module (runs all spec factories, registering mocha tests)\n * 5. Runs mocha and writes window.__testResults\n */\n\n// ── 1. Mocha browser setup ────────────────────────────────────────────────────\n// mocha.js is loaded as a classic <script> tag in index.html (UMD build).\n// It sets up window.Mocha and window.mocha. We only need to call setup() here.\nmocha.setup({ ui: 'bdd', reporter: 'html' });\n\n// ── 2. Test helpers ───────────────────────────────────────────────────────────\n// chai/sinon-chai don't have default exports — use namespace imports.\nimport * as chai from 'chai';\nimport sinon from 'sinon';\nimport * as sinonChai from 'sinon-chai';\n\nconst sinonChaiPlugin = sinonChai.default ?? sinonChai;\nchai.use(sinonChaiPlugin);\nchai.should();\n\n// ── 3. bem-core blocks ────────────────────────────────────────────────────────\n// Import each block to capture its default export for the modules shim.\n// Side-effect-only imports (jQuery event extensions, dom event types) are imported last.\n\nimport identify from 'bem:identify';\nimport inherit from 'bem:inherit';\nimport objects from 'bem:objects';\nimport functions from 'bem:functions';\nimport functionsDebounce from 'bem:functions__debounce';\nimport functionsThrottle from 'bem:functions__throttle';\nimport nextTick from 'bem:next-tick';\nimport stringsEscape from 'bem:strings__escape';\n\nimport events from 'bem:events';\nimport eventsObservable from 'bem:events__observable';\n\nimport $ from 'jquery';\nimport 'bem:jquery__config';\n\nimport dom from 'bem:dom';\nimport cookie from 'bem:cookie';\nimport tick from 'bem:tick';\nimport uri from 'bem:uri';\nimport uriQuerystring from 'bem:uri__querystring';\nimport loaderTypeJs from 'bem:loader_type_js';\n\nimport bemInternal from 'bem:i-bem__internal';\nimport bem from 'bem:i-bem';\nimport bemCollection from 'bem:i-bem__collection';\n\nimport 'bem:i-bem-dom__events';\nimport 'bem:i-bem-dom__events_type_dom';\nimport 'bem:i-bem-dom__events_type_bem';\nimport bemDomCollection from 'bem:i-bem-dom__collection';\nimport bemDom from 'bem:i-bem-dom';\nimport bemDomInit from 'bem:i-bem-dom__init';\n// NOTE: i-bem-dom__init_auto is intentionally NOT imported in tests\n//       to avoid auto-initialization on DOMContentLoaded.\n\nimport eventsObservableBemDom from 'bem:events__observable_type_bem-dom';\n\n// ── 4. BEMHTML shim ───────────────────────────────────────────────────────────\nimport BEMHTML from './bemhtml-shim.js';\n\n// ── 5. Modules shim ───────────────────────────────────────────────────────────\nimport { createModulesShim } from './modules-shim.js';\n\nwindow.modules = createModulesShim({\n    identify,\n    inherit,\n    objects,\n    functions,\n    'functions__debounce': functionsDebounce,\n    'functions__throttle': functionsThrottle,\n    'next-tick': nextTick,\n    'strings__escape': stringsEscape,\n    events,\n    'events__observable': eventsObservableBemDom,\n    'events__observable_type_bem-dom': eventsObservableBemDom,\n    dom,\n    cookie,\n    tick,\n    uri,\n    'uri__querystring': uriQuerystring,\n    'loader_type_js': loaderTypeJs,\n    'i-bem__internal': bemInternal,\n    'i-bem': bem,\n    'i-bem__collection': bemCollection,\n    'i-bem-dom': bemDom,\n    'i-bem-dom__collection': bemDomCollection,\n    'i-bem-dom__init': bemDomInit,\n    jquery: $,\n    chai,\n    sinon,\n    'sinon-chai': sinonChaiPlugin,\n    BEMHTML,\n});\n\n// Register mocha as a module (some internal wiring may require it)\nwindow.modules.define('mocha', function(provide) {\n    provide(window.mocha);\n});\n\n// Bootstrap 'spec': sets up chai.should() and sinon-chai for the test suite.\n// Individual spec files will add more 'spec' definitions after this one.\nwindow.modules.define('spec', function(provide) {\n    provide();\n});\n\n// ── 6. Import all spec files (dynamic, so window.modules is already set up) ──\n// We use lazy glob (without eager:true) so spec files are imported AFTER this\n// module's top-level code has executed and window.modules is available.\n// Each spec file calls modules.define('spec', …) as a side effect.\nconst specLoaders = import.meta.glob('/common.blocks/**/*.spec.js');\n\nPromise.all(Object.values(specLoaders).map(load => load())).then(() => {\n    // ── 7. Run mocha ─────────────────────────────────────────────────────────\n    // Resolve 'spec' module → runs all spec factories → registers describe/it.\n    window.modules.require(['spec'], function() {\n        const runner = mocha.run(function(failures) {\n            window.__testResults = {\n                failures,\n                total: runner.stats.tests,\n                passed: runner.stats.passes,\n                pending: runner.stats.pending,\n            };\n        });\n        window.__testFailures = [];\n        runner.on('fail', function(test, err) {\n            window.__testFailures.push({\n                title: test.fullTitle(),\n                err: err.message,\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "test/browser/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <title>bem-core browser tests</title>\n    <link rel=\"stylesheet\" href=\"/node_modules/mocha/mocha.css\">\n    <!-- mocha.js is the pre-compiled browser UMD bundle — load as classic script -->\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <style>\n        body { padding: 20px; }\n        #mocha { margin: 0; }\n    </style>\n</head>\n<body>\n    <div id=\"mocha\"></div>\n    <script type=\"module\" src=\"./entry.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "test/browser/modules-shim.js",
    "content": "/**\n * Minimal synchronous ym-compatible modules shim for browser tests.\n *\n * Supports:\n *   modules.define(name, deps, factory)   — normal definition\n *   modules.define(name, factory)          — redefinition (factory receives prev value as 2nd arg)\n *   modules.require(deps, factory)         — resolve deps synchronously, call factory\n *\n * Eagerly resolves first definitions when all deps are available (ym behavior).\n * Supports incremental resolution: previously resolved entries are skipped on re-resolve.\n */\nexport function createModulesShim(preRegistered) {\n    // name → [{deps, factory, isRedef, resolved}]\n    const registry = new Map();\n    // name → resolved value (cache)\n    const resolved = new Map();\n\n    for (const [name, value] of Object.entries(preRegistered)) {\n        resolved.set(name, value);\n    }\n\n    function resolve(name) {\n        const entries = registry.get(name);\n        const hasUnresolved = entries && entries.some(e => !e.resolved);\n\n        if (resolved.has(name) && !hasUnresolved) return resolved.get(name);\n\n        if (!entries || !entries.length) {\n            if (resolved.has(name)) return resolved.get(name);\n            throw new Error(\n                `Module \"${name}\" is not defined.\\nKnown: ${[\n                    ...resolved.keys(),\n                    ...registry.keys(),\n                ].join(', ')}`\n            );\n        }\n\n        let value = resolved.get(name);\n        let runCount = 0;\n        for (const entry of entries) {\n            if (entry.resolved) continue;\n            try {\n                const depVals = entry.deps.map(d => resolve(d));\n                const thisCtx = { name };\n                if (entry.isRedef) {\n                    entry.factory.call(thisCtx, v => { value = v; }, value, ...depVals);\n                } else {\n                    entry.factory.call(thisCtx, v => { value = v; }, ...depVals);\n                }\n                entry.resolved = true;\n                runCount++;\n            } catch (err) {\n                // Log but continue — one failing spec factory must not block others\n                console.error(`[modules] factory #${runCount} for \"${name}\" threw:`, err);\n            }\n        }\n\n        resolved.set(name, value);\n        return value;\n    }\n\n    const shim = {\n        define(name, depsOrFactory, factoryArg) {\n            const isRedef = typeof depsOrFactory === 'function';\n            const deps = isRedef ? [] : depsOrFactory;\n            const factory = isRedef ? depsOrFactory : factoryArg;\n\n            const isFirst = !registry.has(name);\n            if (isFirst) registry.set(name, []);\n            registry.get(name).push({ deps, factory, isRedef, resolved: false });\n\n            // Eagerly resolve first non-redef definitions when all deps are available\n            if (isFirst && !isRedef && deps.every(d => resolved.has(d))) {\n                try { resolve(name); } catch (e) { /* ignore — will be resolved on require */ }\n            }\n        },\n\n        require(deps, factory) {\n            const depVals = deps.map(d => resolve(d));\n            factory(...depVals);\n        },\n    };\n\n    return shim;\n}\n"
  },
  {
    "path": "test/browser.spec.js",
    "content": "import { test, expect } from '@playwright/test';\n\nconst MAX_ALLOWED_FAILURES = 0;\n\ntest('bem-core browser spec tests', async ({ page }) => {\n    const pageErrors = [];\n    page.on('pageerror', err => {\n        pageErrors.push(`[pageerror] ${err.message}`);\n    });\n    page.on('console', msg => {\n        if (msg.type() === 'warn' || msg.type() === 'error') {\n            console.log(`[browser:${msg.type()}] ${msg.text()}`);\n        }\n    });\n\n    await page.goto('/test/browser/index.html');\n\n    // Wait for mocha to finish (window.__testResults is set in the run callback)\n    await page.waitForFunction(\n        () => window.__testResults !== undefined,\n        { timeout: 50_000 }\n    ).catch(async () => {\n        const errors = await page.evaluate(() => window.__testFailures ?? []);\n        const info = [\n            ...pageErrors,\n            ...errors.map(f => `FAIL: ${f.title}\\n  ${f.err}`),\n        ].join('\\n');\n        throw new Error(`Mocha did not finish within timeout.\\n${info}`);\n    });\n\n    const { failures, total, passed, pending } =\n        await page.evaluate(() => window.__testResults);\n\n    // Ensure tests actually ran\n    expect(total, 'mocha should have run tests').toBeGreaterThan(400);\n\n    if (failures > MAX_ALLOWED_FAILURES) {\n        const failDetails = await page.evaluate(() => window.__testFailures ?? []);\n        const details = failDetails\n            .map(f => `  ✗ ${f.title}\\n    ${f.err}`)\n            .join('\\n');\n        expect(\n            failures,\n            `Too many failures: ${failures}/${total} (max allowed: ${MAX_ALLOWED_FAILURES}).\\n` +\n            `Passed: ${passed}, pending: ${pending}\\n${details}`\n        ).toBeLessThanOrEqual(MAX_ALLOWED_FAILURES);\n    }\n\n    console.log(`Browser tests: ${passed} passed, ${failures} failed, ${total} total`);\n    expect(failures).toBeLessThanOrEqual(MAX_ALLOWED_FAILURES);\n});\n"
  },
  {
    "path": "test/dist/assets/test.html",
    "content": "<html>\n    <head>\n        <meta charset=\"utf-8\">\n        <link href=\"mocha.css\" rel=\"stylesheet\" />\n        <link href=\"../../../dist/${ platform }/bem-core.css\" rel=\"stylesheet\" />\n    </head>\n    <body>\n        <div id=\"mocha\"></div>\n        <script src=\"https://yastatic.net/es5-shims/0.0.2/es5-shims.min.js\"></script>\n        <script src=\"../../../dist/${ platform }/bem-core.js+bemhtml.js\"></script>\n        <script src=\"mocha.js\"></script>\n        <script src=\"sinon.js\"></script>\n        <script src=\"chai.js\"></script>\n        <script src=\"sinon-chai.js\"></script>\n        <script>\n            (function() {\n                modules.define('spec', ['mocha', 'chai', 'sinon', 'sinon-chai'],\n                    function(provide, mocha, chai, sinon, sinonChai) {\n                        mocha.ui('bdd');\n\n                        chai.use(sinonChai);\n                        chai.should();\n\n                        provide();\n                    });\n            }());\n        </script>\n${ scripts }\n        <script>\n            (function() {\n                var global = this;\n\n                modules.require(['jquery', 'mocha', 'spec'], function($, mocha) {\n                    (global.mochaPhantomJS || mocha).run(done);\n\n                    function done() {\n                         $('#mocha').show();\n                    }\n                });\n            }());\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "test/dist/build-fixtures.js",
    "content": "var EOL = require('os').EOL,\n    fs = require('fs'),\n    path = require('path'),\n    walk = require('bem-walk'),\n    borschik = require('borschik').api,\n    sets = require('./config').sets,\n    platforms = Object.keys(sets),\n    fixturesDir = path.join(__dirname, 'fixtures'),\n    blocksDir = path.resolve('libs/bem-pr/spec.blocks'),\n    asset = fs.readFileSync(path.join(__dirname, 'assets', 'test.html'), 'utf-8');\n\nif(!fs.existsSync(fixturesDir)) {\n    fs.mkdirSync(fixturesDir);\n}\n\n[\n    'mocha.css',\n    'mocha.js',\n    'chai.js',\n    'sinon.js',\n    'sinon-chai.js'\n].map(function (basename) {\n    var name = basename.split('.')[0];\n\n    borschik({\n        input : path.join(blocksDir, name, basename),\n        output : path.join(fixturesDir, basename)\n    });\n});\n\nplatforms.forEach(function (platform) {\n    var levels = sets[platform],\n        config = {\n            levels : levels.map(function (levelname) {\n                return { path : levelname };\n            })\n        },\n        walker = walk(levels, config),\n        specs = [];\n\n    walker.on('data', function (data) {\n        if(data.tech === 'spec.js') {\n            specs.push(data.path);\n        }\n    });\n\n    walker.on('end', function () {\n        html = buildHTML(platform, specs);\n\n        fs.writeFileSync(path.join(fixturesDir, platform + '.html'), html, 'utf-8');\n    });\n});\n\nfunction buildHTML(platform, specs) {\n    return asset\n        .replace(/\\${ platform }/g, platform)\n        .replace(/\\${ scripts }/g, specs.map(function (url) {\n            return '        <script src=\"../../../' + url + '\"></script>';\n        }).join(EOL));\n}\n"
  },
  {
    "path": "test/dist/config.js",
    "content": "module.exports = {\n    sets : {\n        desktop : [\n            'common.blocks',\n            'desktop.blocks'\n        ],\n        'touch' : [\n            'common.blocks',\n            'touch.blocks'\n        ]\n    }\n};\n"
  },
  {
    "path": "test/dist/fixtures/chai.js",
    "content": "!function(e,t){var i=t.call(e);\"object\"==typeof modules?modules.define(\"chai\",function(e){e(i)}):e.chai=i}(this,function(){return function(){function require(e){var t=require.modules[e];if(!t)throw new Error('failed to require \"'+e+'\"');return\"exports\"in t||\"function\"!=typeof t.definition||(t.client=t.component=!0,t.definition.call(this,t.exports={},t),delete t.definition),t.exports}require.loader=\"component\",require.helper={},require.helper.semVerSort=function(e,t){for(var i=e.version.split(\".\"),r=t.version.split(\".\"),n=0;n<i.length;++n){var o=parseInt(i[n],10),s=parseInt(r[n],10);if(o!==s)return o>s?1:-1;var a=i[n].substr((\"\"+o).length),c=r[n].substr((\"\"+s).length);if(\"\"===a&&\"\"!==c)return 1;if(\"\"!==a&&\"\"===c)return-1;if(\"\"!==a&&\"\"!==c)return a>c?1:-1}return 0},require.latest=function(e,t){function i(e){throw new Error('failed to find latest module of \"'+e+'\"')}var r=/(.*)~(.*)@v?(\\d+\\.\\d+\\.\\d+[^\\/]*)$/,n=/(.*)~(.*)/;n.test(e)||i(e);for(var o=Object.keys(require.modules),s=[],a=[],c=0;c<o.length;c++){var u=o[c];if(new RegExp(e+\"@\").test(u)){var h=u.substr(e.length+1),l=r.exec(u);null!=l?s.push({version:h,name:u}):a.push({version:h,name:u})}}if(0===s.concat(a).length&&i(e),s.length>0){var f=s.sort(require.helper.semVerSort).pop().name;return t===!0?f:require(f)}var f=a.pop().name;return t===!0?f:require(f)},require.modules={},require.register=function(e,t){require.modules[e]={definition:t}},require.define=function(e,t){require.modules[e]={exports:t}},require.register(\"chaijs~assertion-error@1.0.0\",function(e,t){function i(){function e(e,i){Object.keys(i).forEach(function(r){~t.indexOf(r)||(e[r]=i[r])})}var t=[].slice.call(arguments);return function(){for(var t=[].slice.call(arguments),i=0,r={};i<t.length;i++)e(r,t[i]);return r}}function r(e,t,r){var n=i(\"name\",\"message\",\"stack\",\"constructor\",\"toJSON\"),o=n(t||{});this.message=e||\"Unspecified AssertionError\",this.showDiff=!1;for(var s in o)this[s]=o[s];r=r||arguments.callee,r&&Error.captureStackTrace&&Error.captureStackTrace(this,r)}t.exports=r,r.prototype=Object.create(Error.prototype),r.prototype.name=\"AssertionError\",r.prototype.constructor=r,r.prototype.toJSON=function(e){var t=i(\"constructor\",\"toJSON\",\"stack\"),r=t({name:this.name},this);return!1!==e&&this.stack&&(r.stack=this.stack),r}}),require.register(\"chaijs~type-detect@0.1.1\",function(e,t){function i(e){var t=Object.prototype.toString.call(e);return n[t]?n[t]:null===e?\"null\":void 0===e?\"undefined\":e===Object(e)?\"object\":typeof e}function r(){this.tests={}}var e=t.exports=i,n={\"[object Array]\":\"array\",\"[object RegExp]\":\"regexp\",\"[object Function]\":\"function\",\"[object Arguments]\":\"arguments\",\"[object Date]\":\"date\"};e.Library=r,r.prototype.of=i,r.prototype.define=function(e,t){return 1===arguments.length?this.tests[e]:(this.tests[e]=t,this)},r.prototype.test=function(e,t){if(t===i(e))return!0;var r=this.tests[t];if(r&&\"regexp\"===i(r))return r.test(e);if(r&&\"function\"===i(r))return r(e);throw new ReferenceError('Type test \"'+t+'\" not defined or invalid.')}}),require.register(\"chaijs~deep-eql@0.1.3\",function(e,t){function i(e,t,i){return r(e,t)?!0:\"date\"===d(e)?o(e,t):\"regexp\"===d(e)?s(e,t):p.isBuffer(e)?h(e,t):\"arguments\"===d(e)?a(e,t,i):n(e,t)?\"object\"!==d(e)&&\"object\"!==d(t)&&\"array\"!==d(e)&&\"array\"!==d(t)?r(e,t):f(e,t,i):!1}function r(e,t){return e===t?0!==e||1/e===1/t:e!==e&&t!==t}function n(e,t){return d(e)===d(t)}function o(e,t){return\"date\"!==d(t)?!1:r(e.getTime(),t.getTime())}function s(e,t){return\"regexp\"!==d(t)?!1:r(e.toString(),t.toString())}function a(e,t,r){return\"arguments\"!==d(t)?!1:(e=[].slice.call(e),t=[].slice.call(t),i(e,t,r))}function c(e){var t=[];for(var i in e)t.push(i);return t}function u(e,t){if(e.length!==t.length)return!1;for(var i=0,r=!0;i<e.length;i++)if(e[i]!==t[i]){r=!1;break}return r}function h(e,t){return p.isBuffer(t)?u(e,t):!1}function l(e){return null!==e&&void 0!==e}function f(e,t,r){if(!l(e)||!l(t))return!1;if(e.prototype!==t.prototype)return!1;var n;if(r){for(n=0;n<r.length;n++)if(r[n][0]===e&&r[n][1]===t||r[n][0]===t&&r[n][1]===e)return!0}else r=[];try{var o=c(e),s=c(t)}catch(a){return!1}if(o.sort(),s.sort(),!u(o,s))return!1;r.push([e,t]);var h;for(n=o.length-1;n>=0;n--)if(h=o[n],!i(e[h],t[h],r))return!1;return!0}var p,d=require(\"chaijs~type-detect@0.1.1\");try{p=require(\"buffer\").Buffer}catch(b){p={},p.isBuffer=function(){return!1}}t.exports=i}),require.register(\"chai\",function(e,t){t.exports=require(\"chai/lib/chai.js\")}),require.register(\"chai/lib/chai.js\",function(e,t){var i=[],e=t.exports={};e.version=\"1.10.0\",e.AssertionError=require(\"chaijs~assertion-error@1.0.0\");var r=require(\"chai/lib/chai/utils/index.js\");e.use=function(e){return~i.indexOf(e)||(e(this,r),i.push(e)),this};var n=require(\"chai/lib/chai/config.js\");e.config=n;var o=require(\"chai/lib/chai/assertion.js\");e.use(o);var s=require(\"chai/lib/chai/core/assertions.js\");e.use(s);var a=require(\"chai/lib/chai/interface/expect.js\");e.use(a);var c=require(\"chai/lib/chai/interface/should.js\");e.use(c);var u=require(\"chai/lib/chai/interface/assert.js\");e.use(u)}),require.register(\"chai/lib/chai/assertion.js\",function(e,t){var i=require(\"chai/lib/chai/config.js\"),r=function(){};t.exports=function(e,t){function n(e,t,i){s(this,\"ssfi\",i||arguments.callee),s(this,\"object\",e),s(this,\"message\",t)}var o=e.AssertionError,s=t.flag;e.Assertion=n,Object.defineProperty(n,\"includeStack\",{get:function(){return console.warn(\"Assertion.includeStack is deprecated, use chai.config.includeStack instead.\"),i.includeStack},set:function(e){console.warn(\"Assertion.includeStack is deprecated, use chai.config.includeStack instead.\"),i.includeStack=e}}),Object.defineProperty(n,\"showDiff\",{get:function(){return console.warn(\"Assertion.showDiff is deprecated, use chai.config.showDiff instead.\"),i.showDiff},set:function(e){console.warn(\"Assertion.showDiff is deprecated, use chai.config.showDiff instead.\"),i.showDiff=e}}),n.addProperty=function(e,i){t.addProperty(this.prototype,e,i)},n.addMethod=function(e,i){t.addMethod(this.prototype,e,i)},n.addChainableMethod=function(e,i,r){t.addChainableMethod(this.prototype,e,i,r)},n.addChainableNoop=function(e,i){t.addChainableMethod(this.prototype,e,r,i)},n.overwriteProperty=function(e,i){t.overwriteProperty(this.prototype,e,i)},n.overwriteMethod=function(e,i){t.overwriteMethod(this.prototype,e,i)},n.overwriteChainableMethod=function(e,i,r){t.overwriteChainableMethod(this.prototype,e,i,r)},n.prototype.assert=function(e,r,n,a,c,u){var h=t.test(this,arguments);if(!0!==u&&(u=!1),!0!==i.showDiff&&(u=!1),!h){var r=t.getMessage(this,arguments),l=t.getActual(this,arguments);throw new o(r,{actual:l,expected:a,showDiff:u},i.includeStack?this.assert:s(this,\"ssfi\"))}},Object.defineProperty(n.prototype,\"_obj\",{get:function(){return s(this,\"object\")},set:function(e){s(this,\"object\",e)}})}}),require.register(\"chai/lib/chai/config.js\",function(e,t){t.exports={includeStack:!1,showDiff:!0,truncateThreshold:40}}),require.register(\"chai/lib/chai/core/assertions.js\",function(e,t){t.exports=function(e,t){function i(e,i){i&&x(this,\"message\",i),e=e.toLowerCase();var r=x(this,\"object\"),n=~[\"a\",\"e\",\"i\",\"o\",\"u\"].indexOf(e.charAt(0))?\"an \":\"a \";this.assert(e===t.type(r),\"expected #{this} to be \"+n+e,\"expected #{this} not to be \"+n+e)}function r(){x(this,\"contains\",!0)}function n(e,i){i&&x(this,\"message\",i);var r=x(this,\"object\"),n=!1;if(\"array\"===t.type(r)&&\"object\"===t.type(e)){for(var o in r)if(t.eql(r[o],e)){n=!0;break}}else if(\"object\"===t.type(e)){if(!x(this,\"negate\")){for(var s in e)new j(r).property(s,e[s]);return}var a={};for(var s in e)a[s]=r[s];n=t.eql(a,e)}else n=r&&~r.indexOf(e);this.assert(n,\"expected #{this} to include \"+t.inspect(e),\"expected #{this} to not include \"+t.inspect(e))}function o(){var e=x(this,\"object\"),t=Object.prototype.toString.call(e);this.assert(\"[object Arguments]\"===t,\"expected #{this} to be arguments but got \"+t,\"expected #{this} to not be arguments\")}function s(e,t){t&&x(this,\"message\",t);var i=x(this,\"object\");return x(this,\"deep\")?this.eql(e):void this.assert(e===i,\"expected #{this} to equal #{exp}\",\"expected #{this} to not equal #{exp}\",e,this._obj,!0)}function a(e,i){i&&x(this,\"message\",i),this.assert(t.eql(e,x(this,\"object\")),\"expected #{this} to deeply equal #{exp}\",\"expected #{this} to not deeply equal #{exp}\",e,this._obj,!0)}function c(e,t){t&&x(this,\"message\",t);var i=x(this,\"object\");if(x(this,\"doLength\")){new j(i,t).to.have.property(\"length\");var r=i.length;this.assert(r>e,\"expected #{this} to have a length above #{exp} but got #{act}\",\"expected #{this} to not have a length above #{exp}\",e,r)}else this.assert(i>e,\"expected #{this} to be above \"+e,\"expected #{this} to be at most \"+e)}function u(e,t){t&&x(this,\"message\",t);var i=x(this,\"object\");if(x(this,\"doLength\")){new j(i,t).to.have.property(\"length\");var r=i.length;this.assert(r>=e,\"expected #{this} to have a length at least #{exp} but got #{act}\",\"expected #{this} to have a length below #{exp}\",e,r)}else this.assert(i>=e,\"expected #{this} to be at least \"+e,\"expected #{this} to be below \"+e)}function h(e,t){t&&x(this,\"message\",t);var i=x(this,\"object\");if(x(this,\"doLength\")){new j(i,t).to.have.property(\"length\");var r=i.length;this.assert(e>r,\"expected #{this} to have a length below #{exp} but got #{act}\",\"expected #{this} to not have a length below #{exp}\",e,r)}else this.assert(e>i,\"expected #{this} to be below \"+e,\"expected #{this} to be at least \"+e)}function l(e,t){t&&x(this,\"message\",t);var i=x(this,\"object\");if(x(this,\"doLength\")){new j(i,t).to.have.property(\"length\");var r=i.length;this.assert(e>=r,\"expected #{this} to have a length at most #{exp} but got #{act}\",\"expected #{this} to have a length above #{exp}\",e,r)}else this.assert(e>=i,\"expected #{this} to be at most \"+e,\"expected #{this} to be above \"+e)}function f(e,i){i&&x(this,\"message\",i);var r=t.getName(e);this.assert(x(this,\"object\")instanceof e,\"expected #{this} to be an instance of \"+r,\"expected #{this} to not be an instance of \"+r)}function p(e,i){i&&x(this,\"message\",i);var r=x(this,\"object\");this.assert(r.hasOwnProperty(e),\"expected #{this} to have own property \"+t.inspect(e),\"expected #{this} to not have own property \"+t.inspect(e))}function d(){x(this,\"doLength\",!0)}function b(e,t){t&&x(this,\"message\",t);var i=x(this,\"object\");new j(i,t).to.have.property(\"length\");var r=i.length;this.assert(r==e,\"expected #{this} to have a length of #{exp} but got #{act}\",\"expected #{this} to not have a length of #{act}\",e,r)}function g(e){var i,r=x(this,\"object\"),n=!0;if(e=e instanceof Array?e:Array.prototype.slice.call(arguments),!e.length)throw new Error(\"keys required\");var o=Object.keys(r),s=e,a=e.length;if(n=e.every(function(e){return~o.indexOf(e)}),x(this,\"negate\")||x(this,\"contains\")||(n=n&&e.length==o.length),a>1){e=e.map(function(e){return t.inspect(e)});var c=e.pop();i=e.join(\", \")+\", and \"+c}else i=t.inspect(e[0]);i=(a>1?\"keys \":\"key \")+i,i=(x(this,\"contains\")?\"contain \":\"have \")+i,this.assert(n,\"expected #{this} to \"+i,\"expected #{this} to not \"+i,s.sort(),o.sort(),!0)}function v(e,i,r){r&&x(this,\"message\",r);var n=x(this,\"object\");new j(n,r).is.a(\"function\");var o=!1,s=null,a=null,c=null;0===arguments.length?(i=null,e=null):e&&(e instanceof RegExp||\"string\"==typeof e)?(i=e,e=null):e&&e instanceof Error?(s=e,e=null,i=null):\"function\"==typeof e?(a=e.prototype.name||e.name,\"Error\"===a&&e!==Error&&(a=(new e).name)):e=null;try{n()}catch(u){if(s)return this.assert(u===s,\"expected #{this} to throw #{exp} but #{act} was thrown\",\"expected #{this} to not throw #{exp}\",s instanceof Error?s.toString():s,u instanceof Error?u.toString():u),x(this,\"object\",u),this;if(e&&(this.assert(u instanceof e,\"expected #{this} to throw #{exp} but #{act} was thrown\",\"expected #{this} to not throw #{exp} but #{act} was thrown\",a,u instanceof Error?u.toString():u),!i))return x(this,\"object\",u),this;var h=\"object\"===t.type(u)&&\"message\"in u?u.message:\"\"+u;if(null!=h&&i&&i instanceof RegExp)return this.assert(i.exec(h),\"expected #{this} to throw error matching #{exp} but got #{act}\",\"expected #{this} to throw error not matching #{exp}\",i,h),x(this,\"object\",u),this;if(null!=h&&i&&\"string\"==typeof i)return this.assert(~h.indexOf(i),\"expected #{this} to throw error including #{exp} but got #{act}\",\"expected #{this} to throw error not including #{act}\",i,h),x(this,\"object\",u),this;o=!0,c=u}var l=\"\",f=null!==a?a:s?\"#{exp}\":\"an error\";o&&(l=\" but #{act} was thrown\"),this.assert(o===!0,\"expected #{this} to throw \"+f+l,\"expected #{this} to not throw \"+f+l,s instanceof Error?s.toString():s,c instanceof Error?c.toString():c),x(this,\"object\",c)}function y(e,t,i){return e.every(function(e){return i?t.some(function(t){return i(e,t)}):-1!==t.indexOf(e)})}var j=e.Assertion,x=(Object.prototype.toString,t.flag);[\"to\",\"be\",\"been\",\"is\",\"and\",\"has\",\"have\",\"with\",\"that\",\"at\",\"of\",\"same\"].forEach(function(e){j.addProperty(e,function(){return this})}),j.addProperty(\"not\",function(){x(this,\"negate\",!0)}),j.addProperty(\"deep\",function(){x(this,\"deep\",!0)}),j.addChainableMethod(\"an\",i),j.addChainableMethod(\"a\",i),j.addChainableMethod(\"include\",n,r),j.addChainableMethod(\"contain\",n,r),j.addChainableNoop(\"ok\",function(){this.assert(x(this,\"object\"),\"expected #{this} to be truthy\",\"expected #{this} to be falsy\")}),j.addChainableNoop(\"true\",function(){this.assert(!0===x(this,\"object\"),\"expected #{this} to be true\",\"expected #{this} to be false\",!this.negate)}),j.addChainableNoop(\"false\",function(){this.assert(!1===x(this,\"object\"),\"expected #{this} to be false\",\"expected #{this} to be true\",!!this.negate)}),j.addChainableNoop(\"null\",function(){this.assert(null===x(this,\"object\"),\"expected #{this} to be null\",\"expected #{this} not to be null\")}),j.addChainableNoop(\"undefined\",function(){this.assert(void 0===x(this,\"object\"),\"expected #{this} to be undefined\",\"expected #{this} not to be undefined\")}),j.addChainableNoop(\"exist\",function(){this.assert(null!=x(this,\"object\"),\"expected #{this} to exist\",\"expected #{this} to not exist\")}),j.addChainableNoop(\"empty\",function(){var e=x(this,\"object\"),t=e;Array.isArray(e)||\"string\"==typeof object?t=e.length:\"object\"==typeof e&&(t=Object.keys(e).length),this.assert(!t,\"expected #{this} to be empty\",\"expected #{this} not to be empty\")}),j.addChainableNoop(\"arguments\",o),j.addChainableNoop(\"Arguments\",o),j.addMethod(\"equal\",s),j.addMethod(\"equals\",s),j.addMethod(\"eq\",s),j.addMethod(\"eql\",a),j.addMethod(\"eqls\",a),j.addMethod(\"above\",c),j.addMethod(\"gt\",c),j.addMethod(\"greaterThan\",c),j.addMethod(\"least\",u),j.addMethod(\"gte\",u),j.addMethod(\"below\",h),j.addMethod(\"lt\",h),j.addMethod(\"lessThan\",h),j.addMethod(\"most\",l),j.addMethod(\"lte\",l),j.addMethod(\"within\",function(e,t,i){i&&x(this,\"message\",i);var r=x(this,\"object\"),n=e+\"..\"+t;if(x(this,\"doLength\")){new j(r,i).to.have.property(\"length\");var o=r.length;this.assert(o>=e&&t>=o,\"expected #{this} to have a length within \"+n,\"expected #{this} to not have a length within \"+n)}else this.assert(r>=e&&t>=r,\"expected #{this} to be within \"+n,\"expected #{this} to not be within \"+n)}),j.addMethod(\"instanceof\",f),j.addMethod(\"instanceOf\",f),j.addMethod(\"property\",function(e,i,r){r&&x(this,\"message\",r);var n=x(this,\"deep\")?\"deep property \":\"property \",o=x(this,\"negate\"),s=x(this,\"object\"),a=x(this,\"deep\")?t.getPathValue(e,s):s[e];if(o&&void 0!==i){if(void 0===a)throw r=null!=r?r+\": \":\"\",new Error(r+t.inspect(s)+\" has no \"+n+t.inspect(e))}else this.assert(void 0!==a,\"expected #{this} to have a \"+n+t.inspect(e),\"expected #{this} to not have \"+n+t.inspect(e));void 0!==i&&this.assert(i===a,\"expected #{this} to have a \"+n+t.inspect(e)+\" of #{exp}, but got #{act}\",\"expected #{this} to not have a \"+n+t.inspect(e)+\" of #{act}\",i,a),x(this,\"object\",a)}),j.addMethod(\"ownProperty\",p),j.addMethod(\"haveOwnProperty\",p),j.addChainableMethod(\"length\",b,d),j.addMethod(\"lengthOf\",b),j.addMethod(\"match\",function(e,t){t&&x(this,\"message\",t);var i=x(this,\"object\");this.assert(e.exec(i),\"expected #{this} to match \"+e,\"expected #{this} not to match \"+e)}),j.addMethod(\"string\",function(e,i){i&&x(this,\"message\",i);var r=x(this,\"object\");new j(r,i).is.a(\"string\"),this.assert(~r.indexOf(e),\"expected #{this} to contain \"+t.inspect(e),\"expected #{this} to not contain \"+t.inspect(e))}),j.addMethod(\"keys\",g),j.addMethod(\"key\",g),j.addMethod(\"throw\",v),j.addMethod(\"throws\",v),j.addMethod(\"Throw\",v),j.addMethod(\"respondTo\",function(e,i){i&&x(this,\"message\",i);var r=x(this,\"object\"),n=x(this,\"itself\"),o=\"function\"!==t.type(r)||n?r[e]:r.prototype[e];this.assert(\"function\"==typeof o,\"expected #{this} to respond to \"+t.inspect(e),\"expected #{this} to not respond to \"+t.inspect(e))}),j.addProperty(\"itself\",function(){x(this,\"itself\",!0)}),j.addMethod(\"satisfy\",function(e,i){i&&x(this,\"message\",i);var r=x(this,\"object\"),n=e(r);this.assert(n,\"expected #{this} to satisfy \"+t.objDisplay(e),\"expected #{this} to not satisfy\"+t.objDisplay(e),!this.negate,n)}),j.addMethod(\"closeTo\",function(e,i,r){r&&x(this,\"message\",r);var n=x(this,\"object\");if(new j(n,r).is.a(\"number\"),\"number\"!==t.type(e)||\"number\"!==t.type(i))throw new Error(\"the arguments to closeTo must be numbers\");this.assert(Math.abs(n-e)<=i,\"expected #{this} to be close to \"+e+\" +/- \"+i,\"expected #{this} not to be close to \"+e+\" +/- \"+i)}),j.addMethod(\"members\",function(e,i){i&&x(this,\"message\",i);var r=x(this,\"object\");new j(r).to.be.an(\"array\"),new j(e).to.be.an(\"array\");var n=x(this,\"deep\")?t.eql:void 0;return x(this,\"contains\")?this.assert(y(e,r,n),\"expected #{this} to be a superset of #{act}\",\"expected #{this} to not be a superset of #{act}\",r,e):void this.assert(y(r,e,n)&&y(e,r,n),\"expected #{this} to have the same members as #{act}\",\"expected #{this} to not have the same members as #{act}\",r,e)})}}),require.register(\"chai/lib/chai/interface/assert.js\",function(exports,module){module.exports=function(chai,util){var Assertion=chai.Assertion,flag=util.flag,assert=chai.assert=function(e,t){var i=new Assertion(null,null,chai.assert);i.assert(e,t,\"[ negation message unavailable ]\")};assert.fail=function(e,t,i,r){throw i=i||\"assert.fail()\",new chai.AssertionError(i,{actual:e,expected:t,operator:r},assert.fail)},assert.ok=function(e,t){new Assertion(e,t).is.ok},assert.notOk=function(e,t){new Assertion(e,t).is.not.ok},assert.equal=function(e,t,i){var r=new Assertion(e,i,assert.equal);r.assert(t==flag(r,\"object\"),\"expected #{this} to equal #{exp}\",\"expected #{this} to not equal #{act}\",t,e)},assert.notEqual=function(e,t,i){var r=new Assertion(e,i,assert.notEqual);r.assert(t!=flag(r,\"object\"),\"expected #{this} to not equal #{exp}\",\"expected #{this} to equal #{act}\",t,e)},assert.strictEqual=function(e,t,i){new Assertion(e,i).to.equal(t)},assert.notStrictEqual=function(e,t,i){new Assertion(e,i).to.not.equal(t)},assert.deepEqual=function(e,t,i){new Assertion(e,i).to.eql(t)},assert.notDeepEqual=function(e,t,i){new Assertion(e,i).to.not.eql(t)},assert.isTrue=function(e,t){new Assertion(e,t).is[\"true\"]},assert.isFalse=function(e,t){new Assertion(e,t).is[\"false\"]},assert.isNull=function(e,t){new Assertion(e,t).to.equal(null)},assert.isNotNull=function(e,t){new Assertion(e,t).to.not.equal(null)},assert.isUndefined=function(e,t){new Assertion(e,t).to.equal(void 0)},assert.isDefined=function(e,t){new Assertion(e,t).to.not.equal(void 0)},assert.isFunction=function(e,t){new Assertion(e,t).to.be.a(\"function\")},assert.isNotFunction=function(e,t){new Assertion(e,t).to.not.be.a(\"function\")},assert.isObject=function(e,t){new Assertion(e,t).to.be.a(\"object\")},assert.isNotObject=function(e,t){new Assertion(e,t).to.not.be.a(\"object\")},assert.isArray=function(e,t){new Assertion(e,t).to.be.an(\"array\")},assert.isNotArray=function(e,t){new Assertion(e,t).to.not.be.an(\"array\")},assert.isString=function(e,t){new Assertion(e,t).to.be.a(\"string\")},assert.isNotString=function(e,t){new Assertion(e,t).to.not.be.a(\"string\")},assert.isNumber=function(e,t){new Assertion(e,t).to.be.a(\"number\")},assert.isNotNumber=function(e,t){new Assertion(e,t).to.not.be.a(\"number\")},assert.isBoolean=function(e,t){new Assertion(e,t).to.be.a(\"boolean\")},assert.isNotBoolean=function(e,t){new Assertion(e,t).to.not.be.a(\"boolean\")},assert.typeOf=function(e,t,i){new Assertion(e,i).to.be.a(t)},assert.notTypeOf=function(e,t,i){new Assertion(e,i).to.not.be.a(t)},assert.instanceOf=function(e,t,i){new Assertion(e,i).to.be.instanceOf(t)},assert.notInstanceOf=function(e,t,i){new Assertion(e,i).to.not.be.instanceOf(t)},assert.include=function(e,t,i){new Assertion(e,i,assert.include).include(t)},assert.notInclude=function(e,t,i){new Assertion(e,i,assert.notInclude).not.include(t)},assert.match=function(e,t,i){new Assertion(e,i).to.match(t)},assert.notMatch=function(e,t,i){new Assertion(e,i).to.not.match(t)},assert.property=function(e,t,i){new Assertion(e,i).to.have.property(t)},assert.notProperty=function(e,t,i){new Assertion(e,i).to.not.have.property(t)},assert.deepProperty=function(e,t,i){new Assertion(e,i).to.have.deep.property(t)},assert.notDeepProperty=function(e,t,i){new Assertion(e,i).to.not.have.deep.property(t)},assert.propertyVal=function(e,t,i,r){new Assertion(e,r).to.have.property(t,i)},assert.propertyNotVal=function(e,t,i,r){new Assertion(e,r).to.not.have.property(t,i)},assert.deepPropertyVal=function(e,t,i,r){new Assertion(e,r).to.have.deep.property(t,i)},assert.deepPropertyNotVal=function(e,t,i,r){new Assertion(e,r).to.not.have.deep.property(t,i)},assert.lengthOf=function(e,t,i){new Assertion(e,i).to.have.length(t)},assert.Throw=function(e,t,i,r){(\"string\"==typeof t||t instanceof RegExp)&&(i=t,t=null);var n=new Assertion(e,r).to.Throw(t,i);return flag(n,\"object\")},assert.doesNotThrow=function(e,t,i){\"string\"==typeof t&&(i=t,t=null),new Assertion(e,i).to.not.Throw(t)},assert.operator=function(val,operator,val2,msg){if(!~[\"==\",\"===\",\">\",\">=\",\"<\",\"<=\",\"!=\",\"!==\"].indexOf(operator))throw new Error('Invalid operator \"'+operator+'\"');var test=new Assertion(eval(val+operator+val2),msg);test.assert(!0===flag(test,\"object\"),\"expected \"+util.inspect(val)+\" to be \"+operator+\" \"+util.inspect(val2),\"expected \"+util.inspect(val)+\" to not be \"+operator+\" \"+util.inspect(val2))},assert.closeTo=function(e,t,i,r){new Assertion(e,r).to.be.closeTo(t,i)},assert.sameMembers=function(e,t,i){new Assertion(e,i).to.have.same.members(t)},assert.includeMembers=function(e,t,i){new Assertion(e,i).to.include.members(t)},assert.ifError=function(e,t){new Assertion(e,t).to.not.be.ok},function e(t,i){return assert[i]=assert[t],e}(\"Throw\",\"throw\")(\"Throw\",\"throws\")}}),require.register(\"chai/lib/chai/interface/expect.js\",function(e,t){t.exports=function(e,t){e.expect=function(t,i){return new e.Assertion(t,i)}}}),require.register(\"chai/lib/chai/interface/should.js\",function(e,t){t.exports=function(e,t){function i(){function e(){return this instanceof String||this instanceof Number?new r(this.constructor(this),null,e):this instanceof Boolean?new r(1==this,null,e):new r(this,null,e)}function t(e){Object.defineProperty(this,\"should\",{value:e,enumerable:!0,configurable:!0,writable:!0})}Object.defineProperty(Object.prototype,\"should\",{set:t,get:e,configurable:!0});var i={};return i.equal=function(e,t,i){new r(e,i).to.equal(t)},i.Throw=function(e,t,i,n){new r(e,n).to.Throw(t,i)},i.exist=function(e,t){new r(e,t).to.exist},i.not={},i.not.equal=function(e,t,i){new r(e,i).to.not.equal(t)},i.not.Throw=function(e,t,i,n){new r(e,n).to.not.Throw(t,i)},i.not.exist=function(e,t){new r(e,t).to.not.exist},i[\"throw\"]=i.Throw,i.not[\"throw\"]=i.not.Throw,i}var r=e.Assertion;e.should=i,e.Should=i}}),require.register(\"chai/lib/chai/utils/addChainableMethod.js\",function(e,t){var i=require(\"chai/lib/chai/utils/transferFlags.js\"),r=require(\"chai/lib/chai/utils/flag.js\"),n=require(\"chai/lib/chai/config.js\"),o=\"__proto__\"in Object,s=/^(?:length|name|arguments|caller)$/,a=Function.prototype.call,c=Function.prototype.apply;t.exports=function(e,t,u,h){\"function\"!=typeof h&&(h=function(){});var l={method:u,chainingBehavior:h};e.__methods||(e.__methods={}),e.__methods[t]=l,Object.defineProperty(e,t,{get:function(){l.chainingBehavior.call(this);var t=function f(){var e=r(this,\"ssfi\");e&&n.includeStack===!1&&r(this,\"ssfi\",f);var t=l.method.apply(this,arguments);return void 0===t?this:t};if(o){var u=t.__proto__=Object.create(this);u.call=a,u.apply=c}else{var h=Object.getOwnPropertyNames(e);h.forEach(function(i){if(!s.test(i)){var r=Object.getOwnPropertyDescriptor(e,i);Object.defineProperty(t,i,r)}})}return i(this,t),t},configurable:!0})}}),require.register(\"chai/lib/chai/utils/addMethod.js\",function(e,t){var i=require(\"chai/lib/chai/config.js\"),r=require(\"chai/lib/chai/utils/flag.js\");t.exports=function(e,t,n){e[t]=function(){var o=r(this,\"ssfi\");o&&i.includeStack===!1&&r(this,\"ssfi\",e[t]);var s=n.apply(this,arguments);return void 0===s?this:s}}}),require.register(\"chai/lib/chai/utils/addProperty.js\",function(e,t){t.exports=function(e,t,i){Object.defineProperty(e,t,{get:function(){var e=i.call(this);return void 0===e?this:e},configurable:!0})}}),require.register(\"chai/lib/chai/utils/flag.js\",function(e,t){t.exports=function(e,t,i){var r=e.__flags||(e.__flags=Object.create(null));return 3!==arguments.length?r[t]:void(r[t]=i)}}),require.register(\"chai/lib/chai/utils/getActual.js\",function(e,t){t.exports=function(e,t){return t.length>4?t[4]:e._obj}}),require.register(\"chai/lib/chai/utils/getEnumerableProperties.js\",function(e,t){t.exports=function(e){var t=[];for(var i in e)t.push(i);return t}}),require.register(\"chai/lib/chai/utils/getMessage.js\",function(e,t){var i=require(\"chai/lib/chai/utils/flag.js\"),r=require(\"chai/lib/chai/utils/getActual.js\"),n=(require(\"chai/lib/chai/utils/inspect.js\"),require(\"chai/lib/chai/utils/objDisplay.js\"));t.exports=function(e,t){var o=i(e,\"negate\"),s=i(e,\"object\"),a=t[3],c=r(e,t),u=o?t[2]:t[1],h=i(e,\"message\");return\"function\"==typeof u&&(u=u()),u=u||\"\",u=u.replace(/#{this}/g,n(s)).replace(/#{act}/g,n(c)).replace(/#{exp}/g,n(a)),h?h+\": \"+u:u}}),require.register(\"chai/lib/chai/utils/getName.js\",function(e,t){t.exports=function(e){if(e.name)return e.name;var t=/^\\s?function ([^(]*)\\(/.exec(e);return t&&t[1]?t[1]:\"\"}}),require.register(\"chai/lib/chai/utils/getPathValue.js\",function(e,t){function i(e){var t=e.replace(/\\[/g,\".[\"),i=t.match(/(\\\\\\.|[^.]+?)+/g);return i.map(function(e){var t=/\\[(\\d+)\\]$/,i=t.exec(e);return i?{i:parseFloat(i[1])}:{p:e}})}function r(e,t){for(var i,r=t,n=0,o=e.length;o>n;n++){var s=e[n];r?(\"undefined\"!=typeof s.p?r=r[s.p]:\"undefined\"!=typeof s.i&&(r=r[s.i]),n==o-1&&(i=r)):i=void 0}return i}t.exports=function(e,t){var n=i(e);return r(n,t)}}),require.register(\"chai/lib/chai/utils/getProperties.js\",function(e,t){t.exports=function(e){function t(e){-1===i.indexOf(e)&&i.push(e)}for(var i=Object.getOwnPropertyNames(subject),r=Object.getPrototypeOf(subject);null!==r;)Object.getOwnPropertyNames(r).forEach(t),r=Object.getPrototypeOf(r);return i}}),require.register(\"chai/lib/chai/utils/index.js\",function(e,t){var e=t.exports={};e.test=require(\"chai/lib/chai/utils/test.js\"),e.type=require(\"chai/lib/chai/utils/type.js\"),e.getMessage=require(\"chai/lib/chai/utils/getMessage.js\"),e.getActual=require(\"chai/lib/chai/utils/getActual.js\"),e.inspect=require(\"chai/lib/chai/utils/inspect.js\"),e.objDisplay=require(\"chai/lib/chai/utils/objDisplay.js\"),e.flag=require(\"chai/lib/chai/utils/flag.js\"),e.transferFlags=require(\"chai/lib/chai/utils/transferFlags.js\"),e.eql=require(\"chaijs~deep-eql@0.1.3\"),e.getPathValue=require(\"chai/lib/chai/utils/getPathValue.js\"),e.getName=require(\"chai/lib/chai/utils/getName.js\"),e.addProperty=require(\"chai/lib/chai/utils/addProperty.js\"),e.addMethod=require(\"chai/lib/chai/utils/addMethod.js\"),e.overwriteProperty=require(\"chai/lib/chai/utils/overwriteProperty.js\"),e.overwriteMethod=require(\"chai/lib/chai/utils/overwriteMethod.js\"),e.addChainableMethod=require(\"chai/lib/chai/utils/addChainableMethod.js\"),e.overwriteChainableMethod=require(\"chai/lib/chai/utils/overwriteChainableMethod.js\")}),require.register(\"chai/lib/chai/utils/inspect.js\",function(e,t){function i(e,t,i,n){var o={showHidden:t,seen:[],stylize:function(e){return e}};return r(o,e,\"undefined\"==typeof i?2:i)}function r(t,i,p){if(i&&\"function\"==typeof i.inspect&&i.inspect!==e.inspect&&(!i.constructor||i.constructor.prototype!==i)){var y=i.inspect(p);return\"string\"!=typeof y&&(y=r(t,y,p)),y}var j=n(t,i);if(j)return j;if(v(i)){if(\"outerHTML\"in i)return i.outerHTML;try{if(document.xmlVersion){var x=new XMLSerializer;return x.serializeToString(i)}var w=\"http://www.w3.org/1999/xhtml\",m=document.createElementNS(w,\"_\");return m.appendChild(i.cloneNode(!1)),html=m.innerHTML.replace(\"><\",\">\"+i.innerHTML+\"<\"),m.innerHTML=\"\",html}catch(q){}}var A=g(i),O=t.showHidden?b(i):A;if(0===O.length||f(i)&&(1===O.length&&\"stack\"===O[0]||2===O.length&&\"description\"===O[0]&&\"stack\"===O[1])){if(\"function\"==typeof i){var M=d(i),S=M?\": \"+M:\"\";return t.stylize(\"[Function\"+S+\"]\",\"special\")}if(h(i))return t.stylize(RegExp.prototype.toString.call(i),\"regexp\");if(l(i))return t.stylize(Date.prototype.toUTCString.call(i),\"date\");if(f(i))return o(i)}var _=\"\",E=!1,P=[\"{\",\"}\"];if(u(i)&&(E=!0,P=[\"[\",\"]\"]),\"function\"==typeof i){var M=d(i),S=M?\": \"+M:\"\";_=\" [Function\"+S+\"]\"}if(h(i)&&(_=\" \"+RegExp.prototype.toString.call(i)),l(i)&&(_=\" \"+Date.prototype.toUTCString.call(i)),f(i))return o(i);if(0===O.length&&(!E||0==i.length))return P[0]+_+P[1];if(0>p)return h(i)?t.stylize(RegExp.prototype.toString.call(i),\"regexp\"):t.stylize(\"[Object]\",\"special\");t.seen.push(i);var k;return k=E?s(t,i,p,A,O):O.map(function(e){return a(t,i,p,A,e,E)}),t.seen.pop(),c(k,_,P)}function n(e,t){switch(typeof t){case\"undefined\":return e.stylize(\"undefined\",\"undefined\");case\"string\":var i=\"'\"+JSON.stringify(t).replace(/^\"|\"$/g,\"\").replace(/'/g,\"\\\\'\").replace(/\\\\\"/g,'\"')+\"'\";return e.stylize(i,\"string\");case\"number\":return 0===t&&1/t===-(1/0)?e.stylize(\"-0\",\"number\"):e.stylize(\"\"+t,\"number\");case\"boolean\":return e.stylize(\"\"+t,\"boolean\")}return null===t?e.stylize(\"null\",\"null\"):void 0}function o(e){return\"[\"+Error.prototype.toString.call(e)+\"]\"}function s(e,t,i,r,n){for(var o=[],s=0,c=t.length;c>s;++s)Object.prototype.hasOwnProperty.call(t,String(s))?o.push(a(e,t,i,r,String(s),!0)):o.push(\"\");return n.forEach(function(n){n.match(/^\\d+$/)||o.push(a(e,t,i,r,n,!0))}),o}function a(e,t,i,n,o,s){var a,c;if(t.__lookupGetter__&&(t.__lookupGetter__(o)?c=t.__lookupSetter__(o)?e.stylize(\"[Getter/Setter]\",\"special\"):e.stylize(\"[Getter]\",\"special\"):t.__lookupSetter__(o)&&(c=e.stylize(\"[Setter]\",\"special\"))),n.indexOf(o)<0&&(a=\"[\"+o+\"]\"),c||(e.seen.indexOf(t[o])<0?(c=null===i?r(e,t[o],null):r(e,t[o],i-1),c.indexOf(\"\\n\")>-1&&(c=s?c.split(\"\\n\").map(function(e){return\"  \"+e}).join(\"\\n\").substr(2):\"\\n\"+c.split(\"\\n\").map(function(e){return\"   \"+e}).join(\"\\n\"))):c=e.stylize(\"[Circular]\",\"special\")),\"undefined\"==typeof a){if(s&&o.match(/^\\d+$/))return c;a=JSON.stringify(\"\"+o),a.match(/^\"([a-zA-Z_][a-zA-Z_0-9]*)\"$/)?(a=a.substr(1,a.length-2),a=e.stylize(a,\"name\")):(a=a.replace(/'/g,\"\\\\'\").replace(/\\\\\"/g,'\"').replace(/(^\"|\"$)/g,\"'\"),a=e.stylize(a,\"string\"))}return a+\": \"+c}function c(e,t,i){var r=0,n=e.reduce(function(e,t){return r++,t.indexOf(\"\\n\")>=0&&r++,e+t.length+1},0);return n>60?i[0]+(\"\"===t?\"\":t+\"\\n \")+\" \"+e.join(\",\\n  \")+\" \"+i[1]:i[0]+t+\" \"+e.join(\", \")+\" \"+i[1]}function u(e){return Array.isArray(e)||\"object\"==typeof e&&\"[object Array]\"===p(e)}function h(e){return\"object\"==typeof e&&\"[object RegExp]\"===p(e)}function l(e){return\"object\"==typeof e&&\"[object Date]\"===p(e)}function f(e){return\"object\"==typeof e&&\"[object Error]\"===p(e)}function p(e){return Object.prototype.toString.call(e)}var d=require(\"chai/lib/chai/utils/getName.js\"),b=require(\"chai/lib/chai/utils/getProperties.js\"),g=require(\"chai/lib/chai/utils/getEnumerableProperties.js\");t.exports=i;var v=function(e){return\"object\"==typeof HTMLElement?e instanceof HTMLElement:e&&\"object\"==typeof e&&1===e.nodeType&&\"string\"==typeof e.nodeName}}),require.register(\"chai/lib/chai/utils/objDisplay.js\",function(e,t){var i=require(\"chai/lib/chai/utils/inspect.js\"),r=require(\"chai/lib/chai/config.js\");\nt.exports=function(e){var t=i(e),n=Object.prototype.toString.call(e);if(r.truncateThreshold&&t.length>=r.truncateThreshold){if(\"[object Function]\"===n)return e.name&&\"\"!==e.name?\"[Function: \"+e.name+\"]\":\"[Function]\";if(\"[object Array]\"===n)return\"[ Array(\"+e.length+\") ]\";if(\"[object Object]\"===n){var o=Object.keys(e),s=o.length>2?o.splice(0,2).join(\", \")+\", ...\":o.join(\", \");return\"{ Object (\"+s+\") }\"}return t}return t}}),require.register(\"chai/lib/chai/utils/overwriteMethod.js\",function(e,t){t.exports=function(e,t,i){var r=e[t],n=function(){return this};r&&\"function\"==typeof r&&(n=r),e[t]=function(){var e=i(n).apply(this,arguments);return void 0===e?this:e}}}),require.register(\"chai/lib/chai/utils/overwriteProperty.js\",function(e,t){t.exports=function(e,t,i){var r=Object.getOwnPropertyDescriptor(e,t),n=function(){};r&&\"function\"==typeof r.get&&(n=r.get),Object.defineProperty(e,t,{get:function(){var e=i(n).call(this);return void 0===e?this:e},configurable:!0})}}),require.register(\"chai/lib/chai/utils/overwriteChainableMethod.js\",function(e,t){t.exports=function(e,t,i,r){var n=e.__methods[t],o=n.chainingBehavior;n.chainingBehavior=function(){var e=r(o).call(this);return void 0===e?this:e};var s=n.method;n.method=function(){var e=i(s).apply(this,arguments);return void 0===e?this:e}}}),require.register(\"chai/lib/chai/utils/test.js\",function(e,t){var i=require(\"chai/lib/chai/utils/flag.js\");t.exports=function(e,t){var r=i(e,\"negate\"),n=t[0];return r?!n:n}}),require.register(\"chai/lib/chai/utils/transferFlags.js\",function(e,t){t.exports=function(e,t,i){var r=e.__flags||(e.__flags=Object.create(null));t.__flags||(t.__flags=Object.create(null)),i=3===arguments.length?i:!0;for(var n in r)(i||\"object\"!==n&&\"ssfi\"!==n&&\"message\"!=n)&&(t.__flags[n]=r[n])}}),require.register(\"chai/lib/chai/utils/type.js\",function(e,t){var i={\"[object Arguments]\":\"arguments\",\"[object Array]\":\"array\",\"[object Date]\":\"date\",\"[object Function]\":\"function\",\"[object Number]\":\"number\",\"[object RegExp]\":\"regexp\",\"[object String]\":\"string\"};t.exports=function(e){var t=Object.prototype.toString.call(e);return i[t]?i[t]:null===e?\"null\":void 0===e?\"undefined\":e===Object(e)?\"object\":typeof e}}),\"object\"==typeof exports?module.exports=require(\"chai\"):\"function\"==typeof define&&define.amd?define(\"chai\",[],function(){return require(\"chai\")}):(this||window).chai=require(\"chai\")}(),this.chai});"
  },
  {
    "path": "test/dist/fixtures/desktop.html",
    "content": "<html>\n    <head>\n        <meta charset=\"utf-8\">\n        <link href=\"mocha.css\" rel=\"stylesheet\" />\n        <link href=\"../../../dist/desktop/bem-core.css\" rel=\"stylesheet\" />\n    </head>\n    <body>\n        <div id=\"mocha\"></div>\n        <script src=\"https://yastatic.net/es5-shims/0.0.2/es5-shims.min.js\"></script>\n        <script src=\"../../../dist/desktop/bem-core.js+bemhtml.js\"></script>\n        <script src=\"mocha.js\"></script>\n        <script src=\"sinon.js\"></script>\n        <script src=\"chai.js\"></script>\n        <script src=\"sinon-chai.js\"></script>\n        <script>\n            (function() {\n                modules.define('spec', ['mocha', 'chai', 'sinon', 'sinon-chai'],\n                    function(provide, mocha, chai, sinon, sinonChai) {\n                        mocha.ui('bdd');\n\n                        chai.use(sinonChai);\n                        chai.should();\n\n                        provide();\n                    });\n            }());\n        </script>\n        <script src=\"../../../common.blocks/cookie/cookie.spec.js\"></script>\n        <script src=\"../../../common.blocks/dom/dom.spec.js\"></script>\n        <script src=\"../../../common.blocks/functions/functions.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/i-bem-dom.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem/i-bem.spec.js\"></script>\n        <script src=\"../../../common.blocks/events/events.spec.js\"></script>\n        <script src=\"../../../common.blocks/i18n/i18n.spec.js\"></script>\n        <script src=\"../../../common.blocks/identify/identify.spec.js\"></script>\n        <script src=\"../../../common.blocks/inherit/inherit.spec.js\"></script>\n        <script src=\"../../../common.blocks/next-tick/next-tick.spec.js\"></script>\n        <script src=\"../../../common.blocks/objects/objects.spec.js\"></script>\n        <script src=\"../../../common.blocks/tick/tick.spec.js\"></script>\n        <script src=\"../../../common.blocks/uri/uri.spec.js\"></script>\n        <script src=\"../../../common.blocks/functions/__debounce/functions__debounce.spec.js\"></script>\n        <script src=\"../../../common.blocks/functions/__throttle/functions__throttle.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/__collection/i-bem-dom__collection.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/__init/i-bem-dom__init.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem/__collection/i-bem__collection.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem/__internal/i-bem__internal.spec.js\"></script>\n        <script src=\"../../../common.blocks/events/__observable/events__observable.spec.js\"></script>\n        <script src=\"../../../common.blocks/loader/_type/loader_type_js.spec.js\"></script>\n        <script src=\"../../../common.blocks/strings/__escape/strings__escape.spec.js\"></script>\n        <script src=\"../../../common.blocks/uri/__querystring/uri__querystring.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_bem.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_dom.spec.js\"></script>\n        <script src=\"../../../common.blocks/events/__observable/_type/events__observable_type_bem-dom.spec.js\"></script>\n        <script src=\"../../../common.blocks/jquery/__event/_type/jquery__event_type_pointerclick.spec.js\"></script>\n        <script src=\"../../../common.blocks/jquery/__event/_type/jquery__event_type_pointernative.spec.js\"></script>\n        <script src=\"../../../common.blocks/jquery/__event/_type/jquery__event_type_pointerpressrelease.spec.js\"></script>\n        <script>\n            (function() {\n                var global = this;\n\n                modules.require(['jquery', 'mocha', 'spec'], function($, mocha) {\n                    (global.mochaPhantomJS || mocha).run(done);\n\n                    function done() {\n                         $('#mocha').show();\n                    }\n                });\n            }());\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "test/dist/fixtures/mocha.css",
    "content": "@charset \"utf-8\";#mocha h1,#mocha h2,body{margin:0}#mocha{font:20px/1.5 \"Helvetica Neue\",Helvetica,Arial,sans-serif;margin:60px 50px}#mocha li,#mocha ul{margin:0;padding:0}#mocha ul{list-style:none}#mocha h1{margin-top:15px;font-size:1em;font-weight:200}#mocha h1 a,#mocha-stats a{text-decoration:none;color:inherit}#mocha h1 a:hover{text-decoration:underline}#mocha .suite .suite h1{margin-top:0;font-size:.8em}#mocha .hidden{display:none}#mocha h2{font-size:12px;font-weight:400;cursor:pointer}#mocha .suite,#mocha .test{margin-left:15px}#mocha .test{overflow:hidden}#mocha .test.pending:hover h2::after{content:'(pending)';font-family:arial,sans-serif}#mocha .test.pass.medium .duration{background:#c09853}#mocha .test.pass.slow .duration{background:#b94a48}#mocha .test.pass::before{content:'✓';font-size:12px;display:block;float:left;margin-right:5px;color:#00d6b2}#mocha .test.pass .duration{font-size:9px;margin-left:5px;padding:2px 5px;color:#fff;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.2);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.2);box-shadow:inset 0 1px 1px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px}#mocha .test.pass.fast .duration,#mocha-report.fail .test.pass,#mocha-report.pass .test.fail,#mocha-report.pending .test.fail,#mocha-report.pending .test.pass{display:none}#mocha .test.pending{color:#0b97c4}#mocha .test.pending::before{content:'◦';color:#0b97c4}#mocha .test.fail{color:#c00}#mocha .test.fail pre,#mocha-stats em{color:#000}#mocha .test.fail::before{content:'✖';font-size:12px;display:block;float:left;margin-right:5px;color:#c00}#mocha .test pre.error{color:#c00;max-height:300px;overflow:auto}#mocha .test pre{display:block;float:left;clear:left;font:12px/1.5 monaco,monospace;margin:5px;padding:15px;border:1px solid #eee;max-width:85%;max-width:calc(100% - 42px);word-wrap:break-word;border-bottom-color:#ddd;-webkit-border-radius:3px;-webkit-box-shadow:0 1px 3px #eee;-moz-border-radius:3px;-moz-box-shadow:0 1px 3px #eee;border-radius:3px}#mocha .test h2{position:relative}#mocha .test a.replay{position:absolute;top:3px;right:0;text-decoration:none;vertical-align:middle;display:block;width:15px;height:15px;line-height:15px;text-align:center;background:#eee;font-size:15px;-moz-border-radius:15px;border-radius:15px;-webkit-transition:opacity 200ms;-moz-transition:opacity 200ms;transition:opacity 200ms;opacity:.3;color:#888}#mocha .test:hover a.replay{opacity:1}#mocha-report.pending .test.pass.pending{display:block}#mocha-error{color:#c00;font-size:1.5em;font-weight:100;letter-spacing:1px}#mocha-stats{position:fixed;top:15px;right:10px;font-size:12px;margin:0;color:#888;z-index:1}#mocha-stats .progress{float:right;padding-top:0}#mocha-stats a:hover{border-bottom:1px solid #eee}#mocha-stats li{display:inline-block;margin:0 5px;list-style:none;padding-top:11px}#mocha-stats canvas{width:40px;height:40px}#mocha code .comment{color:#ddd}#mocha code .init{color:#2f6fad}#mocha code .string{color:#5890ad}#mocha code .keyword{color:#8a6343}#mocha code .number{color:#2f6fad}@media screen and (max-device-width:480px){#mocha{margin:60px 0}#mocha #stats{position:absolute}}#mocha{display:none}"
  },
  {
    "path": "test/dist/fixtures/mocha.js",
    "content": "!function(t,e,n){var r=n.call(e,e);\"object\"==typeof modules?modules.define(t,function(t){t(r)}):e[t]=r}(\"mocha\",this,function(t){return function(){function t(e){var n=t.resolve(e),r=t.modules[n];if(!r)throw new Error('failed to require \"'+e+'\"');return r.exports||(r.exports={},r.call(r.exports,r,r.exports,t.relative(n))),r.exports}function e(){for(var t=(new r).getTime();f.length&&(new r).getTime()-t<100;)f.shift()();c=f.length?o(e,0):null}t.modules={},t.resolve=function(e){var n=e,r=e+\".js\",o=e+\"/index.js\";return t.modules[r]&&r||t.modules[o]&&o||n},t.register=function(e,n){t.modules[e]=n},t.relative=function(e){return function(n){if(\".\"!=n.charAt(0))return t(n);var r=e.split(\"/\"),o=n.split(\"/\");r.pop();for(var i=0;i<o.length;i++){var s=o[i];\"..\"==s?r.pop():\".\"!=s&&r.push(s)}return t(r.join(\"/\"))}},t.register(\"browser/debug.js\",function(t,e,n){t.exports=function(t){return function(){}}}),t.register(\"browser/diff.js\",function(t,e,n){var r=function(){function t(t){return{newPos:t.newPos,components:t.components.slice(0)}}function e(t){for(var e=[],n=0;n<t.length;n++)t[n]&&e.push(t[n]);return e}function n(t){var e=t;return e=e.replace(/&/g,\"&amp;\"),e=e.replace(/</g,\"&lt;\"),e=e.replace(/>/g,\"&gt;\"),e=e.replace(/\"/g,\"&quot;\")}var r=function(t){this.ignoreWhitespace=t};r.prototype={diff:function(e,n){if(n===e)return[{value:n}];if(!n)return[{value:e,removed:!0}];if(!e)return[{value:n,added:!0}];n=this.tokenize(n),e=this.tokenize(e);var r=n.length,o=e.length,i=r+o,s=[{newPos:-1,components:[]}],a=this.extractCommon(s[0],n,e,0);if(s[0].newPos+1>=r&&a+1>=o)return s[0].components;for(var u=1;i>=u;u++)for(var l=-1*u;u>=l;l+=2){var c,f=s[l-1],p=s[l+1];a=(p?p.newPos:0)-l,f&&(s[l-1]=void 0);var h=f&&f.newPos+1<r,d=p&&a>=0&&o>a;if(h||d){!h||d&&f.newPos<p.newPos?(c=t(p),this.pushComponent(c.components,e[a],void 0,!0)):(c=t(f),c.newPos++,this.pushComponent(c.components,n[c.newPos],!0,void 0));var a=this.extractCommon(c,n,e,l);if(c.newPos+1>=r&&a+1>=o)return c.components;s[l]=c}else s[l]=void 0}},pushComponent:function(t,e,n,r){var o=t[t.length-1];o&&o.added===n&&o.removed===r?t[t.length-1]={value:this.join(o.value,e),added:n,removed:r}:t.push({value:e,added:n,removed:r})},extractCommon:function(t,e,n,r){for(var o=e.length,i=n.length,s=t.newPos,a=s-r;o>s+1&&i>a+1&&this.equals(e[s+1],n[a+1]);)s++,a++,this.pushComponent(t.components,e[s],void 0,void 0);return t.newPos=s,a},equals:function(t,e){var n=/\\S/;return!this.ignoreWhitespace||n.test(t)||n.test(e)?t===e:!0},join:function(t,e){return t+e},tokenize:function(t){return t}};var o=new r,i=new r(!0),s=new r;i.tokenize=s.tokenize=function(t){return e(t.split(/(\\s+|\\b)/))};var a=new r(!0);a.tokenize=function(t){return e(t.split(/([{}:;,]|\\s+)/))};var u=new r;return u.tokenize=function(t){return t.split(/^/m)},{Diff:r,diffChars:function(t,e){return o.diff(t,e)},diffWords:function(t,e){return i.diff(t,e)},diffWordsWithSpace:function(t,e){return s.diff(t,e)},diffLines:function(t,e){return u.diff(t,e)},diffCss:function(t,e){return a.diff(t,e)},createPatch:function(t,e,n,r,o){function i(t){return t.map(function(t){return\" \"+t})}function s(t,e,n){var r=l[l.length-2],o=e===l.length-2,i=e===l.length-3&&(n.added!==r.added||n.removed!==r.removed);/\\n$/.test(n.value)||!o&&!i||t.push(\"\\\\ No newline at end of file\")}var a=[];a.push(\"Index: \"+t),a.push(\"===================================================================\"),a.push(\"--- \"+t+(\"undefined\"==typeof r?\"\":\"\t\"+r)),a.push(\"+++ \"+t+(\"undefined\"==typeof o?\"\":\"\t\"+o));var l=u.diff(e,n);l[l.length-1].value||l.pop(),l.push({value:\"\",lines:[]});for(var c=0,f=0,p=[],h=1,d=1,g=0;g<l.length;g++){var m=l[g],v=m.lines||m.value.replace(/\\n$/,\"\").split(\"\\n\");if(m.lines=v,m.added||m.removed){if(!c){var y=l[g-1];c=h,f=d,y&&(p=i(y.lines.slice(-4)),c-=p.length,f-=p.length)}p.push.apply(p,v.map(function(t){return(m.added?\"+\":\"-\")+t})),s(p,g,m),m.added?d+=v.length:h+=v.length}else{if(c)if(v.length<=8&&g<l.length-2)p.push.apply(p,i(v));else{var w=Math.min(v.length,4);a.push(\"@@ -\"+c+\",\"+(h-c+w)+\" +\"+f+\",\"+(d-f+w)+\" @@\"),a.push.apply(a,p),a.push.apply(a,i(v.slice(0,w))),v.length<=4&&s(a,g,m),c=0,f=0,p=[]}h+=v.length,d+=v.length}}return a.join(\"\\n\")+\"\\n\"},applyPatch:function(t,e){for(var n=e.split(\"\\n\"),r=[],o=!1,i=!1,s=\"I\"===n[0][0]?4:0;s<n.length;s++)if(\"@\"===n[s][0]){var a=n[s].split(/@@ -(\\d+),(\\d+) \\+(\\d+),(\\d+) @@/);r.unshift({start:a[3],oldlength:a[2],oldlines:[],newlength:a[4],newlines:[]})}else\"+\"===n[s][0]?r[0].newlines.push(n[s].substr(1)):\"-\"===n[s][0]?r[0].oldlines.push(n[s].substr(1)):\" \"===n[s][0]?(r[0].newlines.push(n[s].substr(1)),r[0].oldlines.push(n[s].substr(1))):\"\\\\\"===n[s][0]&&(\"+\"===n[s-1][0]?o=!0:\"-\"===n[s-1][0]&&(i=!0));for(var u=t.split(\"\\n\"),s=r.length-1;s>=0;s--){for(var l=r[s],c=0;c<l.oldlength;c++)if(u[l.start-1+c]!==l.oldlines[c])return!1;Array.prototype.splice.apply(u,[l.start-1,+l.oldlength].concat(l.newlines))}if(o)for(;!u[u.length-1];)u.pop();else i&&u.push(\"\");return u.join(\"\\n\")},convertChangesToXML:function(t){for(var e=[],r=0;r<t.length;r++){var o=t[r];o.added?e.push(\"<ins>\"):o.removed&&e.push(\"<del>\"),e.push(n(o.value)),o.added?e.push(\"</ins>\"):o.removed&&e.push(\"</del>\")}return e.join(\"\")},convertChangesToDMP:function(t){for(var e,n=[],r=0;r<t.length;r++)e=t[r],n.push([e.added?1:e.removed?-1:0,e.value]);return n}}}();\"undefined\"!=typeof t&&(t.exports=r)}),t.register(\"browser/escape-string-regexp.js\",function(t,e,n){\"use strict\";var r=/[|\\\\{}()[\\]^$+*?.]/g;t.exports=function(t){if(\"string\"!=typeof t)throw new TypeError(\"Expected a string\");return t.replace(r,\"\\\\$&\")}}),t.register(\"browser/events.js\",function(t,e,n){function r(t){return\"[object Array]\"=={}.toString.call(t)}function o(){}e.EventEmitter=o,o.prototype.on=function(t,e){return this.$events||(this.$events={}),this.$events[t]?r(this.$events[t])?this.$events[t].push(e):this.$events[t]=[this.$events[t],e]:this.$events[t]=e,this},o.prototype.addListener=o.prototype.on,o.prototype.once=function(t,e){function n(){r.removeListener(t,n),e.apply(this,arguments)}var r=this;return n.listener=e,this.on(t,n),this},o.prototype.removeListener=function(t,e){if(this.$events&&this.$events[t]){var n=this.$events[t];if(r(n)){for(var o=-1,i=0,s=n.length;s>i;i++)if(n[i]===e||n[i].listener&&n[i].listener===e){o=i;break}if(0>o)return this;n.splice(o,1),n.length||delete this.$events[t]}else(n===e||n.listener&&n.listener===e)&&delete this.$events[t]}return this},o.prototype.removeAllListeners=function(t){return void 0===t?(this.$events={},this):(this.$events&&this.$events[t]&&(this.$events[t]=null),this)},o.prototype.listeners=function(t){return this.$events||(this.$events={}),this.$events[t]||(this.$events[t]=[]),r(this.$events[t])||(this.$events[t]=[this.$events[t]]),this.$events[t]},o.prototype.emit=function(t){if(!this.$events)return!1;var e=this.$events[t];if(!e)return!1;var n=[].slice.call(arguments,1);if(\"function\"==typeof e)e.apply(this,n);else{if(!r(e))return!1;for(var o=e.slice(),i=0,s=o.length;s>i;i++)o[i].apply(this,n)}return!0}}),t.register(\"browser/fs.js\",function(t,e,n){}),t.register(\"browser/glob.js\",function(t,e,n){}),t.register(\"browser/path.js\",function(t,e,n){}),t.register(\"browser/progress.js\",function(t,e,n){function r(){this.percent=0,this.size(0),this.fontSize(11),this.font(\"helvetica, arial, sans-serif\")}t.exports=r,r.prototype.size=function(t){return this._size=t,this},r.prototype.text=function(t){return this._text=t,this},r.prototype.fontSize=function(t){return this._fontSize=t,this},r.prototype.font=function(t){return this._font=t,this},r.prototype.update=function(t){return this.percent=t,this},r.prototype.draw=function(t){try{var e=Math.min(this.percent,100),n=this._size,r=n/2,o=r,i=r,s=r-1,a=this._fontSize;t.font=a+\"px \"+this._font;var u=2*Math.PI*(e/100);t.clearRect(0,0,n,n),t.strokeStyle=\"#9f9f9f\",t.beginPath(),t.arc(o,i,s,0,u,!1),t.stroke(),t.strokeStyle=\"#eee\",t.beginPath(),t.arc(o,i,s-1,0,u,!0),t.stroke();var l=this._text||(0|e)+\"%\",c=t.measureText(l).width;t.fillText(l,o-c/2+1,i+a/2-1)}catch(f){}return this}}),t.register(\"browser/tty.js\",function(t,e,r){e.isatty=function(){return!0},e.getWindowSize=function(){return\"innerHeight\"in n?[n.innerHeight,n.innerWidth]:[640,480]}}),t.register(\"context.js\",function(t,e,n){function r(){}t.exports=r,r.prototype.runnable=function(t){return 0==arguments.length?this._runnable:(this.test=this._runnable=t,this)},r.prototype.timeout=function(t){return 0===arguments.length?this.runnable().timeout():(this.runnable().timeout(t),this)},r.prototype.enableTimeouts=function(t){return this.runnable().enableTimeouts(t),this},r.prototype.slow=function(t){return this.runnable().slow(t),this},r.prototype.inspect=function(){return JSON.stringify(this,function(t,e){return\"_runnable\"!=t&&\"test\"!=t?e:void 0},2)}}),t.register(\"hook.js\",function(t,e,n){function r(t,e){i.call(this,t,e),this.type=\"hook\"}function o(){}var i=n(\"./runnable\");t.exports=r,o.prototype=i.prototype,r.prototype=new o,r.prototype.constructor=r,r.prototype.error=function(t){if(0==arguments.length){var t=this._error;return this._error=null,t}this._error=t}}),t.register(\"interfaces/bdd.js\",function(t,e,n){var r=n(\"../suite\"),o=n(\"../test\"),i=(n(\"../utils\"),n(\"browser/escape-string-regexp\"));t.exports=function(t){var e=[t];t.on(\"pre-require\",function(t,n,s){t.before=function(t,n){e[0].beforeAll(t,n)},t.after=function(t,n){e[0].afterAll(t,n)},t.beforeEach=function(t,n){e[0].beforeEach(t,n)},t.afterEach=function(t,n){e[0].afterEach(t,n)},t.describe=t.context=function(t,o){var i=r.create(e[0],t);return i.file=n,e.unshift(i),o.call(i),e.shift(),i},t.xdescribe=t.xcontext=t.describe.skip=function(t,n){var o=r.create(e[0],t);o.pending=!0,e.unshift(o),n.call(o),e.shift()},t.describe.only=function(e,n){var r=t.describe(e,n);return s.grep(r.fullTitle()),r},t.it=t.specify=function(t,r){var i=e[0];i.pending&&(r=null);var s=new o(t,r);return s.file=n,i.addTest(s),s},t.it.only=function(e,n){var r=t.it(e,n),o=\"^\"+i(r.fullTitle())+\"$\";return s.grep(new RegExp(o)),r},t.xit=t.xspecify=t.it.skip=function(e){t.it(e)}})}}),t.register(\"interfaces/exports.js\",function(t,e,n){var r=n(\"../suite\"),o=n(\"../test\");t.exports=function(t){function e(t,i){var s;for(var a in t)if(\"function\"==typeof t[a]){var u=t[a];switch(a){case\"before\":n[0].beforeAll(u);break;case\"after\":n[0].afterAll(u);break;case\"beforeEach\":n[0].beforeEach(u);break;case\"afterEach\":n[0].afterEach(u);break;default:var l=new o(a,u);l.file=i,n[0].addTest(l)}}else s=r.create(n[0],a),n.unshift(s),e(t[a]),n.shift()}var n=[t];t.on(\"require\",e)}}),t.register(\"interfaces/index.js\",function(t,e,n){e.bdd=n(\"./bdd\"),e.tdd=n(\"./tdd\"),e.qunit=n(\"./qunit\"),e.exports=n(\"./exports\")}),t.register(\"interfaces/qunit.js\",function(t,e,n){var r=n(\"../suite\"),o=n(\"../test\"),i=n(\"browser/escape-string-regexp\");n(\"../utils\");t.exports=function(t){var e=[t];t.on(\"pre-require\",function(t,n,s){t.before=function(t,n){e[0].beforeAll(t,n)},t.after=function(t,n){e[0].afterAll(t,n)},t.beforeEach=function(t,n){e[0].beforeEach(t,n)},t.afterEach=function(t,n){e[0].afterEach(t,n)},t.suite=function(t){e.length>1&&e.shift();var o=r.create(e[0],t);return o.file=n,e.unshift(o),o},t.suite.only=function(e,n){var r=t.suite(e,n);s.grep(r.fullTitle())},t.test=function(t,r){var i=new o(t,r);return i.file=n,e[0].addTest(i),i},t.test.only=function(e,n){var r=t.test(e,n),o=\"^\"+i(r.fullTitle())+\"$\";s.grep(new RegExp(o))},t.test.skip=function(e){t.test(e)}})}}),t.register(\"interfaces/tdd.js\",function(t,e,n){var r=n(\"../suite\"),o=n(\"../test\"),i=n(\"browser/escape-string-regexp\");n(\"../utils\");t.exports=function(t){var e=[t];t.on(\"pre-require\",function(t,n,s){t.setup=function(t,n){e[0].beforeEach(t,n)},t.teardown=function(t,n){e[0].afterEach(t,n)},t.suiteSetup=function(t,n){e[0].beforeAll(t,n)},t.suiteTeardown=function(t,n){e[0].afterAll(t,n)},t.suite=function(t,o){var i=r.create(e[0],t);return i.file=n,e.unshift(i),o.call(i),e.shift(),i},t.suite.skip=function(t,n){var o=r.create(e[0],t);o.pending=!0,e.unshift(o),n.call(o),e.shift()},t.suite.only=function(e,n){var r=t.suite(e,n);s.grep(r.fullTitle())},t.test=function(t,r){var i=e[0];i.pending&&(r=null);var s=new o(t,r);return s.file=n,i.addTest(s),s},t.test.only=function(e,n){var r=t.test(e,n),o=\"^\"+i(r.fullTitle())+\"$\";s.grep(new RegExp(o))},t.test.skip=function(e){t.test(e)}})}}),t.register(\"mocha.js\",function(t,e,r){function o(t){return __dirname+\"/../images/\"+t+\".png\"}function s(t){t=t||{},this.files=[],this.options=t,this.grep(t.grep),this.suite=new e.Suite(\"\",new e.Context),this.ui(t.ui),this.bail(t.bail),this.reporter(t.reporter),null!=t.timeout&&this.timeout(t.timeout),this.useColors(t.useColors),null!==t.enableTimeouts&&this.enableTimeouts(t.enableTimeouts),t.slow&&this.slow(t.slow),this.suite.on(\"pre-require\",function(t){e.afterEach=t.afterEach||t.teardown,e.after=t.after||t.suiteTeardown,e.beforeEach=t.beforeEach||t.setup,e.before=t.before||t.suiteSetup,e.describe=t.describe||t.suite,e.it=t.it||t.test,e.setup=t.setup||t.beforeEach,e.suiteSetup=t.suiteSetup||t.before,e.suiteTeardown=t.suiteTeardown||t.after,e.suite=t.suite||t.describe,e.teardown=t.teardown||t.afterEach,e.test=t.test||t.it})}var a=r(\"browser/path\"),u=r(\"browser/escape-string-regexp\"),l=r(\"./utils\");if(e=t.exports=s,\"undefined\"!=typeof i&&\"function\"==typeof i.cwd){var c=a.join,f=i.cwd();t.paths.push(f,c(f,\"node_modules\"))}e.utils=l,e.interfaces=r(\"./interfaces\"),e.reporters=r(\"./reporters\"),e.Runnable=r(\"./runnable\"),e.Context=r(\"./context\"),e.Runner=r(\"./runner\"),e.Suite=r(\"./suite\"),e.Hook=r(\"./hook\"),e.Test=r(\"./test\"),s.prototype.bail=function(t){return 0==arguments.length&&(t=!0),this.suite.bail(t),this},s.prototype.addFile=function(t){return this.files.push(t),this},s.prototype.reporter=function(t){if(\"function\"==typeof t)this._reporter=t;else{t=t||\"spec\";var e;try{e=r(\"./reporters/\"+t)}catch(n){}if(!e)try{e=r(t)}catch(n){}if(e||\"teamcity\"!==t||console.warn(\"The Teamcity reporter was moved to a package named mocha-teamcity-reporter (https://npmjs.org/package/mocha-teamcity-reporter).\"),!e)throw new Error('invalid reporter \"'+t+'\"');this._reporter=e}return this},s.prototype.ui=function(t){if(t=t||\"bdd\",this._ui=e.interfaces[t],!this._ui)try{this._ui=r(t)}catch(n){}if(!this._ui)throw new Error('invalid interface \"'+t+'\"');return this._ui=this._ui(this.suite),this},s.prototype.loadFiles=function(t){var e=this,o=this.suite,i=this.files.length;this.files.forEach(function(s){s=a.resolve(s),o.emit(\"pre-require\",n,s,e),o.emit(\"require\",r(s),s,e),o.emit(\"post-require\",n,s,e),--i||t&&t()})},s.prototype._growl=function(t,e){var n=r(\"growl\");t.on(\"end\",function(){var r=e.stats;if(r.failures){var i=r.failures+\" of \"+t.total+\" tests failed\";n(i,{name:\"mocha\",title:\"Failed\",image:o(\"error\")})}else n(r.passes+\" tests passed in \"+r.duration+\"ms\",{name:\"mocha\",title:\"Passed\",image:o(\"ok\")})})},s.prototype.grep=function(t){return this.options.grep=\"string\"==typeof t?new RegExp(u(t)):t,this},s.prototype.invert=function(){return this.options.invert=!0,this},s.prototype.ignoreLeaks=function(t){return this.options.ignoreLeaks=!!t,this},s.prototype.checkLeaks=function(){return this.options.ignoreLeaks=!1,this},s.prototype.growl=function(){return this.options.growl=!0,this},s.prototype.globals=function(t){return this.options.globals=(this.options.globals||[]).concat(t),this},s.prototype.useColors=function(t){return this.options.useColors=arguments.length&&void 0!=t?t:!0,this},s.prototype.useInlineDiffs=function(t){return this.options.useInlineDiffs=arguments.length&&void 0!=t?t:!1,this},s.prototype.timeout=function(t){return this.suite.timeout(t),this},s.prototype.slow=function(t){return this.suite.slow(t),this},s.prototype.enableTimeouts=function(t){return this.suite.enableTimeouts(arguments.length&&void 0!==t?t:!0),this},s.prototype.asyncOnly=function(){return this.options.asyncOnly=!0,this},s.prototype.noHighlighting=function(){return this.options.noHighlighting=!0,this},s.prototype.run=function(t){this.files.length&&this.loadFiles();var n=this.suite,r=this.options;r.files=this.files;var o=new e.Runner(n),i=new this._reporter(o,r);return o.ignoreLeaks=!1!==r.ignoreLeaks,o.asyncOnly=r.asyncOnly,r.grep&&o.grep(r.grep,r.invert),r.globals&&o.globals(r.globals),r.growl&&this._growl(o,i),e.reporters.Base.useColors=r.useColors,e.reporters.Base.inlineDiffs=r.useInlineDiffs,o.run(t)}}),t.register(\"ms.js\",function(t,e,n){function r(t){var e=/^((?:\\d+)?\\.?\\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(t);if(e){var n=parseFloat(e[1]),r=(e[2]||\"ms\").toLowerCase();switch(r){case\"years\":case\"year\":case\"y\":return n*f;case\"days\":case\"day\":case\"d\":return n*c;case\"hours\":case\"hour\":case\"h\":return n*l;case\"minutes\":case\"minute\":case\"m\":return n*u;case\"seconds\":case\"second\":case\"s\":return n*a;case\"ms\":return n}}}function o(t){return t>=c?Math.round(t/c)+\"d\":t>=l?Math.round(t/l)+\"h\":t>=u?Math.round(t/u)+\"m\":t>=a?Math.round(t/a)+\"s\":t+\"ms\"}function i(t){return s(t,c,\"day\")||s(t,l,\"hour\")||s(t,u,\"minute\")||s(t,a,\"second\")||t+\" ms\"}function s(t,e,n){return e>t?void 0:1.5*e>t?Math.floor(t/e)+\" \"+n:Math.ceil(t/e)+\" \"+n+\"s\"}var a=1e3,u=60*a,l=60*u,c=24*l,f=365.25*c;t.exports=function(t,e){return e=e||{},\"string\"==typeof t?r(t):e[\"long\"]?i(t):o(t)}}),t.register(\"reporters/base.js\",function(t,e,r){function o(t){var e=this.stats={suites:0,tests:0,passes:0,pending:0,failures:0},n=this.failures=[];t&&(this.runner=t,t.stats=e,t.on(\"start\",function(){e.start=new v}),t.on(\"suite\",function(t){e.suites=e.suites||0,t.root||e.suites++}),t.on(\"test end\",function(t){e.tests=e.tests||0,e.tests++}),t.on(\"pass\",function(t){e.passes=e.passes||0;var n=t.slow()/2;t.speed=t.duration>t.slow()?\"slow\":t.duration>n?\"medium\":\"fast\",e.passes++}),t.on(\"fail\",function(t,r){e.failures=e.failures||0,e.failures++,t.err=r,n.push(t)}),t.on(\"end\",function(){e.end=new v,e.duration=new v-e.start}),t.on(\"pending\",function(){e.pending++}))}function s(t,e){return t=String(t),Array(e-t.length+1).join(\" \")+t}function a(t,e){var n=l(t,\"WordsWithSpace\",e),r=n.split(\"\\n\");if(r.length>4){var o=String(r.length).length;n=r.map(function(t,e){return s(++e,o)+\" | \"+t}).join(\"\\n\")}return n=\"\\n\"+w(\"diff removed\",\"actual\")+\" \"+w(\"diff added\",\"expected\")+\"\\n\\n\"+n+\"\\n\",n=n.replace(/^/gm,\"      \")}function u(t,e){function n(t){return e&&(t=c(t)),\"+\"===t[0]?o+f(\"diff added\",t):\"-\"===t[0]?o+f(\"diff removed\",t):t.match(/\\@\\@/)?null:t.match(/\\\\ No newline/)?null:o+t}function r(t){return null!=t}var o=\"      \";msg=d.createPatch(\"string\",t.actual,t.expected);var i=msg.split(\"\\n\").splice(4);return\"\\n      \"+f(\"diff added\",\"+ expected\")+\" \"+f(\"diff removed\",\"- actual\")+\"\\n\\n\"+i.map(n).filter(r).join(\"\\n\")}function l(t,e,n){var r=n?c(t.actual):t.actual,o=n?c(t.expected):t.expected;return d[\"diff\"+e](r,o).map(function(t){return t.added?f(\"diff added\",t.value):t.removed?f(\"diff removed\",t.value):t.value}).join(\"\")}function c(t){return t.replace(/\\t/g,\"<tab>\").replace(/\\r/g,\"<CR>\").replace(/\\n/g,\"<LF>\\n\")}function f(t,e){return e.split(\"\\n\").map(function(e){return w(t,e)}).join(\"\\n\")}function p(t,e){return t=Object.prototype.toString.call(t),e=Object.prototype.toString.call(e),t==e}var h=r(\"browser/tty\"),d=r(\"browser/diff\"),g=r(\"../ms\"),m=r(\"../utils\"),v=n.Date,y=(n.setTimeout,n.setInterval,n.clearTimeout,n.clearInterval,h.isatty(1)&&h.isatty(2));e=t.exports=o,e.useColors=y||void 0!==i.env.MOCHA_COLORS,e.inlineDiffs=!1,e.colors={pass:90,fail:31,\"bright pass\":92,\"bright fail\":91,\"bright yellow\":93,pending:36,suite:0,\"error title\":0,\"error message\":31,\"error stack\":90,checkmark:32,fast:90,medium:33,slow:31,green:32,light:90,\"diff gutter\":90,\"diff added\":42,\"diff removed\":41},e.symbols={ok:\"✓\",err:\"✖\",dot:\"․\"},\"win32\"==i.platform&&(e.symbols.ok=\"√\",e.symbols.err=\"×\",e.symbols.dot=\".\");var w=e.color=function(t,n){return e.useColors?\"\u001b[\"+e.colors[t]+\"m\"+n+\"\u001b[0m\":n};e.window={width:y?i.stdout.getWindowSize?i.stdout.getWindowSize(1)[0]:h.getWindowSize()[1]:75},e.cursor={hide:function(){y&&i.stdout.write(\"\u001b[?25l\")},show:function(){y&&i.stdout.write(\"\u001b[?25h\")},deleteLine:function(){y&&i.stdout.write(\"\u001b[2K\")},beginningOfLine:function(){y&&i.stdout.write(\"\u001b[0G\")},CR:function(){y?(e.cursor.deleteLine(),e.cursor.beginningOfLine()):i.stdout.write(\"\\r\")}},e.list=function(t){console.error(),t.forEach(function(t,n){var r=w(\"error title\",\"  %s) %s:\\n\")+w(\"error message\",\"     %s\")+w(\"error stack\",\"\\n%s\\n\"),o=t.err,i=o.message||\"\",s=o.stack||i,l=s.indexOf(i)+i.length,c=s.slice(0,l),f=o.actual,h=o.expected,d=!0;if(o.uncaught&&(c=\"Uncaught \"+c),o.showDiff&&p(f,h)&&(d=!1,o.actual=f=m.stringify(f),o.expected=h=m.stringify(h)),o.showDiff&&\"string\"==typeof f&&\"string\"==typeof h){r=w(\"error title\",\"  %s) %s:\\n%s\")+w(\"error stack\",\"\\n%s\\n\");var g=i.match(/^([^:]+): expected/);c=\"\\n      \"+w(\"error message\",g?g[1]:c),c+=e.inlineDiffs?a(o,d):u(o,d)}s=s.slice(l?l+1:l).replace(/^/gm,\"  \"),console.error(r,n+1,t.fullTitle(),c,s)})},o.prototype.epilogue=function(){var t,e=this.stats;console.log(),t=w(\"bright pass\",\" \")+w(\"green\",\" %d passing\")+w(\"light\",\" (%s)\"),console.log(t,e.passes||0,g(e.duration)),e.pending&&(t=w(\"pending\",\" \")+w(\"pending\",\" %d pending\"),console.log(t,e.pending)),e.failures&&(t=w(\"fail\",\"  %d failing\"),console.error(t,e.failures),o.list(this.failures),console.error()),console.log()}}),t.register(\"reporters/doc.js\",function(t,e,n){function r(t){function e(){return Array(n).join(\"  \")}o.call(this,t);var n=(this.stats,t.total,2);t.on(\"suite\",function(t){t.root||(++n,console.log('%s<section class=\"suite\">',e()),++n,console.log(\"%s<h1>%s</h1>\",e(),i.escape(t.title)),console.log(\"%s<dl>\",e()))}),t.on(\"suite end\",function(t){t.root||(console.log(\"%s</dl>\",e()),--n,console.log(\"%s</section>\",e()),--n)}),t.on(\"pass\",function(t){console.log(\"%s  <dt>%s</dt>\",e(),i.escape(t.title));var n=i.escape(i.clean(t.fn.toString()));console.log(\"%s  <dd><pre><code>%s</code></pre></dd>\",e(),n)}),t.on(\"fail\",function(t,n){console.log('%s  <dt class=\"error\">%s</dt>',e(),i.escape(t.title));var r=i.escape(i.clean(t.fn.toString()));console.log('%s  <dd class=\"error\"><pre><code>%s</code></pre></dd>',e(),r),console.log('%s  <dd class=\"error\">%s</dd>',e(),i.escape(n))})}var o=n(\"./base\"),i=n(\"../utils\");e=t.exports=r}),t.register(\"reporters/dot.js\",function(t,e,n){function r(t){s.call(this,t);var e=this,n=(this.stats,.75*s.window.width|0),r=-1;t.on(\"start\",function(){i.stdout.write(\"\\n  \")}),t.on(\"pending\",function(t){++r%n==0&&i.stdout.write(\"\\n  \"),i.stdout.write(a(\"pending\",s.symbols.dot))}),t.on(\"pass\",function(t){++r%n==0&&i.stdout.write(\"\\n  \"),\"slow\"==t.speed?i.stdout.write(a(\"bright yellow\",s.symbols.dot)):i.stdout.write(a(t.speed,s.symbols.dot))}),t.on(\"fail\",function(t,e){++r%n==0&&i.stdout.write(\"\\n  \"),i.stdout.write(a(\"fail\",s.symbols.dot))}),t.on(\"end\",function(){console.log(),e.epilogue()})}function o(){}var s=n(\"./base\"),a=s.color;e=t.exports=r,o.prototype=s.prototype,r.prototype=new o,r.prototype.constructor=r}),t.register(\"reporters/html-cov.js\",function(t,e,n){function r(t){var e=n(\"jade\"),r=__dirname+\"/templates/coverage.jade\",u=a.readFileSync(r,\"utf8\"),l=e.compile(u,{filename:r}),c=this;s.call(this,t,!1),t.on(\"end\",function(){i.stdout.write(l({cov:c.cov,coverageClass:o}))})}function o(t){return t>=75?\"high\":t>=50?\"medium\":t>=25?\"low\":\"terrible\"}var s=n(\"./json-cov\"),a=n(\"browser/fs\");e=t.exports=r}),t.register(\"reporters/html.js\",function(t,e,r){function o(t){f.call(this,t);var e,n,r=this,o=this.stats,v=(t.total,s(m)),y=v.getElementsByTagName(\"li\"),w=y[1].getElementsByTagName(\"em\")[0],b=y[1].getElementsByTagName(\"a\")[0],x=y[2].getElementsByTagName(\"em\")[0],T=y[2].getElementsByTagName(\"a\")[0],k=y[3].getElementsByTagName(\"em\")[0],E=v.getElementsByTagName(\"canvas\")[0],j=s('<ul id=\"mocha-report\"></ul>'),_=[j],C=document.getElementById(\"mocha\");if(E.getContext){var S=window.devicePixelRatio||1;E.style.width=E.width,E.style.height=E.height,E.width*=S,E.height*=S,n=E.getContext(\"2d\"),n.scale(S,S),e=new h}return C?(c(b,\"click\",function(){u();var t=/pass/.test(j.className)?\"\":\" pass\";j.className=j.className.replace(/fail|pass/g,\"\")+t,j.className.trim()&&a(\"test pass\")}),c(T,\"click\",function(){u();var t=/fail/.test(j.className)?\"\":\" fail\";j.className=j.className.replace(/fail|pass/g,\"\")+t,j.className.trim()&&a(\"test fail\")}),C.appendChild(v),C.appendChild(j),e&&e.size(40),t.on(\"suite\",function(t){if(!t.root){var e=r.suiteURL(t),n=s('<li class=\"suite\"><h1><a href=\"%s\">%s</a></h1></li>',e,d(t.title));_[0].appendChild(n),_.unshift(document.createElement(\"ul\")),n.appendChild(_[0])}}),t.on(\"suite end\",function(t){t.root||_.shift()}),t.on(\"fail\",function(e,n){\"hook\"==e.type&&t.emit(\"test end\",e)}),void t.on(\"test end\",function(t){var i=o.tests/this.total*100|0;e&&e.update(i).draw(n);var a=new g-o.start;if(l(w,o.passes),l(x,o.failures),l(k,(a/1e3).toFixed(2)),\"passed\"==t.state)var u=r.testURL(t),f=s('<li class=\"test pass %e\"><h2>%e<span class=\"duration\">%ems</span> <a href=\"%s\" class=\"replay\">‣</a></h2></li>',t.speed,t.title,t.duration,u);else if(t.pending)var f=s('<li class=\"test pass pending\"><h2>%e</h2></li>',t.title);else{var f=s('<li class=\"test fail\"><h2>%e <a href=\"?grep=%e\" class=\"replay\">‣</a></h2></li>',t.title,encodeURIComponent(t.fullTitle())),h=t.err.stack||t.err.toString();~h.indexOf(t.err.message)||(h=t.err.message+\"\\n\"+h),\"[object Error]\"==h&&(h=t.err.message),!t.err.stack&&t.err.sourceURL&&void 0!==t.err.line&&(h+=\"\\n(\"+t.err.sourceURL+\":\"+t.err.line+\")\"),f.appendChild(s('<pre class=\"error\">%e</pre>',h))}if(!t.pending){var d=f.getElementsByTagName(\"h2\")[0];c(d,\"click\",function(){m.style.display=\"none\"==m.style.display?\"block\":\"none\"});var m=s(\"<pre><code>%e</code></pre>\",p.clean(t.fn.toString()));f.appendChild(m),m.style.display=\"none\"}_[0]&&_[0].appendChild(f)})):i(\"#mocha div missing, add it to your document\")}function i(t){document.body.appendChild(s('<div id=\"mocha-error\">%s</div>',t))}function s(t){var e=arguments,n=document.createElement(\"div\"),r=1;return n.innerHTML=t.replace(/%([se])/g,function(t,n){switch(n){case\"s\":return String(e[r++]);case\"e\":return d(e[r++])}}),n.firstChild}function a(t){for(var e=document.getElementsByClassName(\"suite\"),n=0;n<e.length;n++){var r=e[n].getElementsByClassName(t);0==r.length&&(e[n].className+=\" hidden\")}}function u(){for(var t=document.getElementsByClassName(\"suite hidden\"),e=0;e<t.length;++e)t[e].className=t[e].className.replace(\"suite hidden\",\"suite\")}function l(t,e){t.textContent?t.textContent=e:t.innerText=e}function c(t,e,n){t.addEventListener?t.addEventListener(e,n,!1):t.attachEvent(\"on\"+e,n)}var f=r(\"./base\"),p=r(\"../utils\"),h=r(\"../browser/progress\"),d=p.escape,g=n.Date;n.setTimeout,n.setInterval,n.clearTimeout,n.clearInterval;e=t.exports=o;var m='<ul id=\"mocha-stats\"><li class=\"progress\"><canvas width=\"40\" height=\"40\"></canvas></li><li class=\"passes\"><a href=\"#\">passes:</a> <em>0</em></li><li class=\"failures\"><a href=\"#\">failures:</a> <em>0</em></li><li class=\"duration\">duration: <em>0</em>s</li></ul>',v=function(t){var e=window.location.search;return(e?e+\"&\":\"?\")+\"grep=\"+encodeURIComponent(t)};o.prototype.suiteURL=function(t){return v(t.fullTitle())},o.prototype.testURL=function(t){return v(t.fullTitle())}}),t.register(\"reporters/index.js\",function(t,e,n){e.Base=n(\"./base\"),e.Dot=n(\"./dot\"),e.Doc=n(\"./doc\"),e.TAP=n(\"./tap\"),e.JSON=n(\"./json\"),e.HTML=n(\"./html\"),e.List=n(\"./list\"),e.Min=n(\"./min\"),e.Spec=n(\"./spec\"),e.Nyan=n(\"./nyan\"),e.XUnit=n(\"./xunit\"),e.Markdown=n(\"./markdown\"),e.Progress=n(\"./progress\"),e.Landing=n(\"./landing\"),e.JSONCov=n(\"./json-cov\"),e.HTMLCov=n(\"./html-cov\"),e.JSONStream=n(\"./json-stream\")}),t.register(\"reporters/json-cov.js\",function(t,e,r){function o(t,e){var r=this,e=1==arguments.length?!0:e;l.call(this,t);var o=[],a=[],c=[];t.on(\"test end\",function(t){o.push(t)}),t.on(\"pass\",function(t){c.push(t)}),t.on(\"fail\",function(t){a.push(t)}),t.on(\"end\",function(){var t=n._$jscoverage||{},l=r.cov=s(t);l.stats=r.stats,l.tests=o.map(u),l.failures=a.map(u),l.passes=c.map(u),e&&i.stdout.write(JSON.stringify(l,null,2))})}function s(t){var e={instrumentation:\"node-jscoverage\",sloc:0,hits:0,misses:0,coverage:0,files:[]};for(var n in t){var r=a(n,t[n]);e.files.push(r),e.hits+=r.hits,e.misses+=r.misses,e.sloc+=r.sloc}return e.files.sort(function(t,e){return t.filename.localeCompare(e.filename)}),e.sloc>0&&(e.coverage=e.hits/e.sloc*100),e}function a(t,e){var n={filename:t,coverage:0,hits:0,misses:0,sloc:0,source:{}};return e.source.forEach(function(t,r){r++,0===e[r]?(n.misses++,n.sloc++):void 0!==e[r]&&(n.hits++,n.sloc++),n.source[r]={source:t,coverage:void 0===e[r]?\"\":e[r]}}),n.coverage=n.hits/n.sloc*100,n}function u(t){return{title:t.title,fullTitle:t.fullTitle(),duration:t.duration}}var l=r(\"./base\");e=t.exports=o}),t.register(\"reporters/json-stream.js\",function(t,e,n){function r(t){s.call(this,t);var e=this,n=(this.stats,t.total);t.on(\"start\",function(){console.log(JSON.stringify([\"start\",{total:n}]))}),t.on(\"pass\",function(t){console.log(JSON.stringify([\"pass\",o(t)]))}),t.on(\"fail\",function(t,e){t=o(t),t.err=e.message,console.log(JSON.stringify([\"fail\",t]))}),t.on(\"end\",function(){i.stdout.write(JSON.stringify([\"end\",e.stats]))})}function o(t){return{title:t.title,fullTitle:t.fullTitle(),duration:t.duration}}var s=n(\"./base\");s.color;e=t.exports=r}),t.register(\"reporters/json.js\",function(t,e,n){function r(t){var e=this;a.call(this,t);var n=[],r=[],s=[],u=[];t.on(\"test end\",function(t){n.push(t)}),t.on(\"pass\",function(t){u.push(t)}),t.on(\"fail\",function(t){s.push(t)}),t.on(\"pending\",function(t){r.push(t)}),t.on(\"end\",function(){var a={stats:e.stats,tests:n.map(o),pending:r.map(o),failures:s.map(o),passes:u.map(o)};t.testResults=a,i.stdout.write(JSON.stringify(a,null,2))})}function o(t){return{title:t.title,fullTitle:t.fullTitle(),duration:t.duration,err:s(t.err||{})}}function s(t){var e={};return Object.getOwnPropertyNames(t).forEach(function(n){e[n]=t[n]},t),e}var a=n(\"./base\");a.cursor,a.color;e=t.exports=r}),t.register(\"reporters/landing.js\",function(t,e,n){function r(t){function e(){var t=Array(r).join(\"-\");return\"  \"+u(\"runway\",t)}s.call(this,t);var n=this,r=(this.stats,.75*s.window.width|0),o=t.total,l=i.stdout,c=u(\"plane\",\"✈\"),f=-1,p=0;t.on(\"start\",function(){l.write(\"\\n\\n\\n  \"),a.hide()}),t.on(\"test end\",function(t){var n=-1==f?r*++p/o|0:f;\"failed\"==t.state&&(c=u(\"plane crash\",\"✈\"),f=n),l.write(\"\u001b[\"+(r+1)+\"D\u001b[2A\"),l.write(e()),l.write(\"\\n  \"),l.write(u(\"runway\",Array(n).join(\"⋅\"))),l.write(c),l.write(u(\"runway\",Array(r-n).join(\"⋅\")+\"\\n\")),l.write(e()),l.write(\"\u001b[0m\")}),t.on(\"end\",function(){a.show(),console.log(),n.epilogue()})}function o(){}var s=n(\"./base\"),a=s.cursor,u=s.color;e=t.exports=r,s.colors.plane=0,s.colors[\"plane crash\"]=31,s.colors.runway=90,o.prototype=s.prototype,r.prototype=new o,r.prototype.constructor=r}),t.register(\"reporters/list.js\",function(t,e,n){function r(t){s.call(this,t);var e=this,n=(this.stats,0);t.on(\"start\",function(){console.log()}),t.on(\"test\",function(t){i.stdout.write(u(\"pass\",\"    \"+t.fullTitle()+\": \"))}),t.on(\"pending\",function(t){var e=u(\"checkmark\",\"  -\")+u(\"pending\",\" %s\");console.log(e,t.fullTitle())}),t.on(\"pass\",function(t){var e=u(\"checkmark\",\"  \"+s.symbols.dot)+u(\"pass\",\" %s: \")+u(t.speed,\"%dms\");a.CR(),console.log(e,t.fullTitle(),t.duration)}),t.on(\"fail\",function(t,e){a.CR(),console.log(u(\"fail\",\"  %d) %s\"),++n,t.fullTitle())}),t.on(\"end\",e.epilogue.bind(e))}function o(){}var s=n(\"./base\"),a=s.cursor,u=s.color;e=t.exports=r,o.prototype=s.prototype,r.prototype=new o,r.prototype.constructor=r}),t.register(\"reporters/markdown.js\",function(t,e,n){function r(t){function e(t){return Array(u).join(\"#\")+\" \"+t}function n(t,e){var r=e;return e=e[t.title]=e[t.title]||{suite:t},t.suites.forEach(function(t){n(t,e)}),r}function r(t,e){++e;var n,o=\"\";for(var i in t)\"suite\"!=i&&(i&&(n=\" - [\"+i+\"](#\"+s.slug(t[i].suite.fullTitle())+\")\\n\"),i&&(o+=Array(e).join(\"  \")+n),o+=r(t[i],e));return--e,o}function a(t){var e=n(t,{});return r(e,0)}o.call(this,t);var u=(this.stats,0),l=\"\";a(t.suite),t.on(\"suite\",function(t){++u;var n=s.slug(t.fullTitle());\nl+='<a name=\"'+n+'\"></a>\\n',l+=e(t.title)+\"\\n\"}),t.on(\"suite end\",function(t){--u}),t.on(\"pass\",function(t){var e=s.clean(t.fn.toString());l+=t.title+\".\\n\",l+=\"\\n```js\\n\",l+=e+\"\\n\",l+=\"```\\n\\n\"}),t.on(\"end\",function(){i.stdout.write(\"# TOC\\n\"),i.stdout.write(a(t.suite)),i.stdout.write(l)})}var o=n(\"./base\"),s=n(\"../utils\");e=t.exports=r}),t.register(\"reporters/min.js\",function(t,e,n){function r(t){s.call(this,t),t.on(\"start\",function(){i.stdout.write(\"\u001b[2J\"),i.stdout.write(\"\u001b[1;3H\")}),t.on(\"end\",this.epilogue.bind(this))}function o(){}var s=n(\"./base\");e=t.exports=r,o.prototype=s.prototype,r.prototype=new o,r.prototype.constructor=r}),t.register(\"reporters/nyan.js\",function(t,e,n){function r(t){a.call(this,t);var e=this,n=(this.stats,.75*a.window.width|0),r=(this.rainbowColors=e.generateColors(),this.colorIndex=0,this.numberOfLines=4,this.trajectories=[[],[],[],[]],this.nyanCatWidth=11);this.trajectoryWidthMax=n-r,this.scoreboardWidth=5,this.tick=0;t.on(\"start\",function(){a.cursor.hide(),e.draw()}),t.on(\"pending\",function(t){e.draw()}),t.on(\"pass\",function(t){e.draw()}),t.on(\"fail\",function(t,n){e.draw()}),t.on(\"end\",function(){a.cursor.show();for(var t=0;t<e.numberOfLines;t++)o(\"\\n\");e.epilogue()})}function o(t){i.stdout.write(t)}function s(){}var a=n(\"./base\");a.color;e=t.exports=r,r.prototype.draw=function(){this.appendRainbow(),this.drawScoreboard(),this.drawRainbow(),this.drawNyanCat(),this.tick=!this.tick},r.prototype.drawScoreboard=function(){function t(t,e){o(\" \"),o(\"\u001b[\"+t+\"m\"+e+\"\u001b[0m\"),o(\"\\n\")}var e=this.stats,n=a.colors;t(n.green,e.passes),t(n.fail,e.failures),t(n.pending,e.pending),o(\"\\n\"),this.cursorUp(this.numberOfLines)},r.prototype.appendRainbow=function(){for(var t=this.tick?\"_\":\"-\",e=this.rainbowify(t),n=0;n<this.numberOfLines;n++){var r=this.trajectories[n];r.length>=this.trajectoryWidthMax&&r.shift(),r.push(e)}},r.prototype.drawRainbow=function(){var t=this;this.trajectories.forEach(function(e,n){o(\"\u001b[\"+t.scoreboardWidth+\"C\"),o(e.join(\"\")),o(\"\\n\")}),this.cursorUp(this.numberOfLines)},r.prototype.drawNyanCat=function(){var t=this,e=this.scoreboardWidth+this.trajectories[0].length,n=\"\u001b[\"+e+\"C\",r=\"\";o(n),o(\"_,------,\"),o(\"\\n\"),o(n),r=t.tick?\"  \":\"   \",o(\"_|\"+r+\"/\\\\_/\\\\ \"),o(\"\\n\"),o(n),r=t.tick?\"_\":\"__\";var i=t.tick?\"~\":\"^\";o(i+\"|\"+r+this.face()+\" \"),o(\"\\n\"),o(n),r=t.tick?\" \":\"  \",o(r+'\"\"  \"\" '),o(\"\\n\"),this.cursorUp(this.numberOfLines)},r.prototype.face=function(){var t=this.stats;return t.failures?\"( x .x)\":t.pending?\"( o .o)\":t.passes?\"( ^ .^)\":\"( - .-)\"},r.prototype.cursorUp=function(t){o(\"\u001b[\"+t+\"A\")},r.prototype.cursorDown=function(t){o(\"\u001b[\"+t+\"B\")},r.prototype.generateColors=function(){for(var t=[],e=0;42>e;e++){var n=Math.floor(Math.PI/3),r=e*(1/6),o=Math.floor(3*Math.sin(r)+3),i=Math.floor(3*Math.sin(r+2*n)+3),s=Math.floor(3*Math.sin(r+4*n)+3);t.push(36*o+6*i+s+16)}return t},r.prototype.rainbowify=function(t){var e=this.rainbowColors[this.colorIndex%this.rainbowColors.length];return this.colorIndex+=1,\"\u001b[38;5;\"+e+\"m\"+t+\"\u001b[0m\"},s.prototype=a.prototype,r.prototype=new s,r.prototype.constructor=r}),t.register(\"reporters/progress.js\",function(t,e,n){function r(t,e){s.call(this,t);var n=this,e=e||{},r=(this.stats,.5*s.window.width|0),o=t.total,l=0,c=(Math.max,-1);e.open=e.open||\"[\",e.complete=e.complete||\"▬\",e.incomplete=e.incomplete||s.symbols.dot,e.close=e.close||\"]\",e.verbose=!1,t.on(\"start\",function(){console.log(),a.hide()}),t.on(\"test end\",function(){l++;var t=l/o,n=r*t|0,s=r-n;(c!==n||e.verbose)&&(c=n,a.CR(),i.stdout.write(\"\u001b[J\"),i.stdout.write(u(\"progress\",\"  \"+e.open)),i.stdout.write(Array(n).join(e.complete)),i.stdout.write(Array(s).join(e.incomplete)),i.stdout.write(u(\"progress\",e.close)),e.verbose&&i.stdout.write(u(\"progress\",\" \"+l+\" of \"+o)))}),t.on(\"end\",function(){a.show(),console.log(),n.epilogue()})}function o(){}var s=n(\"./base\"),a=s.cursor,u=s.color;e=t.exports=r,s.colors.progress=90,o.prototype=s.prototype,r.prototype=new o,r.prototype.constructor=r}),t.register(\"reporters/spec.js\",function(t,e,n){function r(t){function e(){return Array(r).join(\"  \")}i.call(this,t);var n=this,r=(this.stats,0),o=0;t.on(\"start\",function(){console.log()}),t.on(\"suite\",function(t){++r,console.log(a(\"suite\",\"%s%s\"),e(),t.title)}),t.on(\"suite end\",function(t){--r,1==r&&console.log()}),t.on(\"pending\",function(t){var n=e()+a(\"pending\",\"  - %s\");console.log(n,t.title)}),t.on(\"pass\",function(t){if(\"fast\"==t.speed){var n=e()+a(\"checkmark\",\"  \"+i.symbols.ok)+a(\"pass\",\" %s \");s.CR(),console.log(n,t.title)}else{var n=e()+a(\"checkmark\",\"  \"+i.symbols.ok)+a(\"pass\",\" %s \")+a(t.speed,\"(%dms)\");s.CR(),console.log(n,t.title,t.duration)}}),t.on(\"fail\",function(t,n){s.CR(),console.log(e()+a(\"fail\",\"  %d) %s\"),++o,t.title)}),t.on(\"end\",n.epilogue.bind(n))}function o(){}var i=n(\"./base\"),s=i.cursor,a=i.color;e=t.exports=r,o.prototype=i.prototype,r.prototype=new o,r.prototype.constructor=r}),t.register(\"reporters/tap.js\",function(t,e,n){function r(t){i.call(this,t);var e=(this.stats,1),n=0,r=0;t.on(\"start\",function(){var e=t.grepTotal(t.suite);console.log(\"%d..%d\",1,e)}),t.on(\"test end\",function(){++e}),t.on(\"pending\",function(t){console.log(\"ok %d %s # SKIP -\",e,o(t))}),t.on(\"pass\",function(t){n++,console.log(\"ok %d %s\",e,o(t))}),t.on(\"fail\",function(t,n){r++,console.log(\"not ok %d %s\",e,o(t)),n.stack&&console.log(n.stack.replace(/^/gm,\"  \"))}),t.on(\"end\",function(){console.log(\"# tests \"+(n+r)),console.log(\"# pass \"+n),console.log(\"# fail \"+r)})}function o(t){return t.fullTitle().replace(/#/g,\"\")}var i=n(\"./base\");i.cursor,i.color;e=t.exports=r}),t.register(\"reporters/xunit.js\",function(t,e,r){function o(t){l.call(this,t);var e=this.stats,n=[];t.on(\"pending\",function(t){n.push(t)}),t.on(\"pass\",function(t){n.push(t)}),t.on(\"fail\",function(t){n.push(t)}),t.on(\"end\",function(){console.log(a(\"testsuite\",{name:\"Mocha Tests\",tests:e.tests,failures:e.failures,errors:e.failures,skipped:e.tests-e.failures-e.passes,timestamp:(new p).toUTCString(),time:e.duration/1e3||0},!1)),n.forEach(s),console.log(\"</testsuite>\")})}function i(){}function s(t){var e={classname:t.parent.fullTitle(),name:t.title,time:t.duration/1e3||0};if(\"failed\"==t.state){var n=t.err;console.log(a(\"testcase\",e,!1,a(\"failure\",{},!1,u(f(n.message)+\"\\n\"+n.stack))))}else t.pending?console.log(a(\"testcase\",e,!1,a(\"skipped\",{},!0))):console.log(a(\"testcase\",e,!0))}function a(t,e,n,r){var o,i=n?\"/>\":\">\",s=[];for(var a in e)s.push(a+'=\"'+f(e[a])+'\"');return o=\"<\"+t+(s.length?\" \"+s.join(\" \"):\"\")+i,r&&(o+=r+\"</\"+t+i),o}function u(t){return\"<![CDATA[\"+f(t)+\"]]>\"}var l=r(\"./base\"),c=r(\"../utils\"),f=c.escape,p=n.Date;n.setTimeout,n.setInterval,n.clearTimeout,n.clearInterval;e=t.exports=o,i.prototype=l.prototype,o.prototype=new i,o.prototype.constructor=o}),t.register(\"runnable.js\",function(t,e,r){function o(t,e){this.title=t,this.fn=e,this.async=e&&e.length,this.sync=!this.async,this._timeout=2e3,this._slow=75,this._enableTimeouts=!0,this.timedOut=!1,this._trace=new Error(\"done() called multiple times\")}function i(){}var s=r(\"browser/events\").EventEmitter,a=r(\"browser/debug\")(\"mocha:runnable\"),u=r(\"./ms\"),l=n.Date,c=n.setTimeout,f=(n.setInterval,n.clearTimeout),p=(n.clearInterval,Object.prototype.toString);t.exports=o,i.prototype=s.prototype,o.prototype=new i,o.prototype.constructor=o,o.prototype.timeout=function(t){return 0==arguments.length?this._timeout:(0===t&&(this._enableTimeouts=!1),\"string\"==typeof t&&(t=u(t)),a(\"timeout %d\",t),this._timeout=t,this.timer&&this.resetTimeout(),this)},o.prototype.slow=function(t){return 0===arguments.length?this._slow:(\"string\"==typeof t&&(t=u(t)),a(\"timeout %d\",t),this._slow=t,this)},o.prototype.enableTimeouts=function(t){return 0===arguments.length?this._enableTimeouts:(a(\"enableTimeouts %s\",t),this._enableTimeouts=t,this)},o.prototype.fullTitle=function(){return this.parent.fullTitle()+\" \"+this.title},o.prototype.clearTimeout=function(){f(this.timer)},o.prototype.inspect=function(){return JSON.stringify(this,function(t,e){return\"_\"!=t[0]?\"parent\"==t?\"#<Suite>\":\"ctx\"==t?\"#<Context>\":e:void 0},2)},o.prototype.resetTimeout=function(){var t=this,e=this.timeout()||1e9;this._enableTimeouts&&(this.clearTimeout(),this.timer=c(function(){t._enableTimeouts&&(t.callback(new Error(\"timeout of \"+e+\"ms exceeded\")),t.timedOut=!0)},e))},o.prototype.globals=function(t){this._allowedGlobals=t},o.prototype.run=function(t){function e(t){i||(i=!0,s.emit(\"error\",t||new Error(\"done() called multiple times; stacktrace may be inaccurate\")))}function n(n){var r=s.timeout();if(!s.timedOut){if(o)return e(n||s._trace);s.clearTimeout(),s.duration=new l-a,o=!0,!n&&s.duration>r&&s._enableTimeouts&&(n=new Error(\"timeout of \"+r+\"ms exceeded\")),t(n)}}function r(t){var e=t.call(u);e&&\"function\"==typeof e.then?(s.resetTimeout(),e.then(function(){n()},function(t){n(t||new Error(\"Promise rejected with no or falsy reason\"))})):n()}var o,i,s=this,a=new l,u=this.ctx;if(u&&u.runnable&&u.runnable(this),this.callback=n,this.async){this.resetTimeout();try{this.fn.call(u,function(t){return t instanceof Error||\"[object Error]\"===p.call(t)?n(t):null!=t?n(\"[object Object]\"===Object.prototype.toString.call(t)?new Error(\"done() invoked with non-Error: \"+JSON.stringify(t)):new Error(\"done() invoked with non-Error: \"+t)):void n()})}catch(c){n(c)}}else{if(this.asyncOnly)return n(new Error(\"--async-only option in use without declaring `done()`\"));try{this.pending?n():r(this.fn)}catch(c){n(c)}}}}),t.register(\"runner.js\",function(t,e,r){function o(t){var e=this;this._globals=[],this._abort=!1,this.suite=t,this.total=t.total(),this.failures=0,this.on(\"test end\",function(t){e.checkGlobals(t)}),this.on(\"hook end\",function(t){e.checkGlobals(t)}),this.grep(/.*/),this.globals(this.globalProps().concat(u()))}function s(){}function a(t,e){return p(e,function(e){if(/^d+/.test(e))return!1;if(n.navigator&&/^getInterface/.test(e))return!1;if(n.navigator&&/^\\d+/.test(e))return!1;if(/^mocha-/.test(e))return!1;var r=p(t,function(t){return~t.indexOf(\"*\")?0==e.indexOf(t.split(\"*\")[0]):e==t});return 0==r.length&&(!n.navigator||\"onerror\"!==e)})}function u(){if(\"object\"==typeof i&&\"string\"==typeof i.version){var t=i.version.split(\".\").reduce(function(t,e){return t<<8|e});if(2315>t)return[\"errno\"]}return[]}var l=r(\"browser/events\").EventEmitter,c=r(\"browser/debug\")(\"mocha:runner\"),f=(r(\"./test\"),r(\"./utils\")),p=f.filter,h=(f.keys,[\"setTimeout\",\"clearTimeout\",\"setInterval\",\"clearInterval\",\"XMLHttpRequest\",\"Date\"]);t.exports=o,o.immediately=n.setImmediate||i.nextTick,s.prototype=l.prototype,o.prototype=new s,o.prototype.constructor=o,o.prototype.grep=function(t,e){return c(\"grep %s\",t),this._grep=t,this._invert=e,this.total=this.grepTotal(this.suite),this},o.prototype.grepTotal=function(t){var e=this,n=0;return t.eachTest(function(t){var r=e._grep.test(t.fullTitle());e._invert&&(r=!r),r&&n++}),n},o.prototype.globalProps=function(){for(var t=f.keys(n),e=0;e<h.length;++e)~f.indexOf(t,h[e])||t.push(h[e]);return t},o.prototype.globals=function(t){return 0==arguments.length?this._globals:(c(\"globals %j\",t),this._globals=this._globals.concat(t),this)},o.prototype.checkGlobals=function(t){if(!this.ignoreLeaks){var e,n=this._globals,r=this.globalProps();t&&(n=n.concat(t._allowedGlobals||[])),this.prevGlobalsLength!=r.length&&(this.prevGlobalsLength=r.length,e=a(n,r),this._globals=this._globals.concat(e),e.length>1?this.fail(t,new Error(\"global leaks detected: \"+e.join(\", \"))):e.length&&this.fail(t,new Error(\"global leak detected: \"+e[0])))}},o.prototype.fail=function(t,e){++this.failures,t.state=\"failed\",\"string\"==typeof e&&(e=new Error('the string \"'+e+'\" was thrown, throw an Error :)')),this.emit(\"fail\",t,e)},o.prototype.failHook=function(t,e){this.fail(t,e),this.suite.bail()&&this.emit(\"end\")},o.prototype.hook=function(t,e){function n(t){var o=i[t];return o?s.failures&&r.bail()?e():(s.currentRunnable=o,o.ctx.currentTest=s.test,s.emit(\"hook\",o),o.on(\"error\",function(t){s.failHook(o,t)}),void o.run(function(r){o.removeAllListeners(\"error\");var i=o.error();return i&&s.fail(s.test,i),r?(s.failHook(o,r),e(r)):(s.emit(\"hook end\",o),delete o.ctx.currentTest,void n(++t))})):e()}var r=this.suite,i=r[\"_\"+t],s=this;o.immediately(function(){n(0)})},o.prototype.hooks=function(t,e,n){function r(s){return o.suite=s,s?void o.hook(t,function(t){if(t){var s=o.suite;return o.suite=i,n(t,s)}r(e.pop())}):(o.suite=i,n())}var o=this,i=this.suite;r(e.pop())},o.prototype.hookUp=function(t,e){var n=[this.suite].concat(this.parents()).reverse();this.hooks(t,n,e)},o.prototype.hookDown=function(t,e){var n=[this.suite].concat(this.parents());this.hooks(t,n,e)},o.prototype.parents=function(){for(var t=this.suite,e=[];t=t.parent;)e.push(t);return e},o.prototype.runTest=function(t){var e=this.test,n=this;this.asyncOnly&&(e.asyncOnly=!0);try{e.on(\"error\",function(t){n.fail(e,t)}),e.run(t)}catch(r){t(r)}},o.prototype.runTests=function(t,e){function n(t,r,o){var s=i.suite;i.suite=o?r.parent:r,i.suite?i.hookUp(\"afterEach\",function(t,o){return i.suite=s,t?n(t,o,!0):void e(r)}):(i.suite=s,e(r))}function r(a,u){if(i.failures&&t._bail)return e();if(i._abort)return e();if(a)return n(a,u,!0);if(o=s.shift(),!o)return e();var l=i._grep.test(o.fullTitle());return i._invert&&(l=!l),l?o.pending?(i.emit(\"pending\",o),i.emit(\"test end\",o),r()):(i.emit(\"test\",i.test=o),void i.hookDown(\"beforeEach\",function(t,e){return t?n(t,e,!1):(i.currentRunnable=i.test,void i.runTest(function(t){return o=i.test,t?(i.fail(o,t),i.emit(\"test end\",o),i.hookUp(\"afterEach\",r)):(o.state=\"passed\",i.emit(\"pass\",o),i.emit(\"test end\",o),void i.hookUp(\"afterEach\",r))}))})):r()}var o,i=this,s=t.tests.slice();this.next=r,r()},o.prototype.runSuite=function(t,e){function n(e){if(e)return e==t?r():r(e);if(i._abort)return r();var o=t.suites[s++];return o?void i.runSuite(o,n):r()}function r(n){i.suite=t,i.hook(\"afterAll\",function(){i.emit(\"suite end\",t),e(n)})}var o=this.grepTotal(t),i=this,s=0;return c(\"run suite %s\",t.fullTitle()),o?(this.emit(\"suite\",this.suite=t),void this.hook(\"beforeAll\",function(e){return e?r():void i.runTests(t,n)})):e()},o.prototype.uncaught=function(t){t?c(\"uncaught exception %s\",t!==function(){return this}.call(t)?t:t.message||t):(c(\"uncaught undefined exception\"),t=new Error(\"Caught undefined error, did you throw without specifying what?\")),t.uncaught=!0;var e=this.currentRunnable;if(e){var n=e.state;if(this.fail(e,t),e.clearTimeout(),!n)return\"test\"==e.type?(this.emit(\"test end\",e),void this.hookUp(\"afterEach\",this.next)):void this.emit(\"end\")}},o.prototype.run=function(t){function e(t){n.uncaught(t)}var n=this,t=t||function(){};return c(\"start\"),this.on(\"end\",function(){c(\"end\"),i.removeListener(\"uncaughtException\",e),t(n.failures)}),this.emit(\"start\"),this.runSuite(this.suite,function(){c(\"finished running\"),n.emit(\"end\")}),i.on(\"uncaughtException\",e),this},o.prototype.abort=function(){c(\"aborting\"),this._abort=!0}}),t.register(\"suite.js\",function(t,e,n){function r(t,e){this.title=t;var n=function(){};n.prototype=e,this.ctx=new n,this.suites=[],this.tests=[],this.pending=!1,this._beforeEach=[],this._beforeAll=[],this._afterEach=[],this._afterAll=[],this.root=!t,this._timeout=2e3,this._enableTimeouts=!0,this._slow=75,this._bail=!1}function o(){}var i=n(\"browser/events\").EventEmitter,s=n(\"browser/debug\")(\"mocha:suite\"),a=n(\"./ms\"),u=n(\"./utils\"),l=n(\"./hook\");e=t.exports=r,e.create=function(t,e){var n=new r(e,t.ctx);return n.parent=t,t.pending&&(n.pending=!0),e=n.fullTitle(),t.addSuite(n),n},o.prototype=i.prototype,r.prototype=new o,r.prototype.constructor=r,r.prototype.clone=function(){var t=new r(this.title);return s(\"clone\"),t.ctx=this.ctx,t.timeout(this.timeout()),t.enableTimeouts(this.enableTimeouts()),t.slow(this.slow()),t.bail(this.bail()),t},r.prototype.timeout=function(t){return 0==arguments.length?this._timeout:(0===t&&(this._enableTimeouts=!1),\"string\"==typeof t&&(t=a(t)),s(\"timeout %d\",t),this._timeout=parseInt(t,10),this)},r.prototype.enableTimeouts=function(t){return 0===arguments.length?this._enableTimeouts:(s(\"enableTimeouts %s\",t),this._enableTimeouts=t,this)},r.prototype.slow=function(t){return 0===arguments.length?this._slow:(\"string\"==typeof t&&(t=a(t)),s(\"slow %d\",t),this._slow=t,this)},r.prototype.bail=function(t){return 0==arguments.length?this._bail:(s(\"bail %s\",t),this._bail=t,this)},r.prototype.beforeAll=function(t,e){if(this.pending)return this;\"function\"==typeof t&&(e=t,t=e.name),t='\"before all\" hook'+(t?\": \"+t:\"\");var n=new l(t,e);return n.parent=this,n.timeout(this.timeout()),n.enableTimeouts(this.enableTimeouts()),n.slow(this.slow()),n.ctx=this.ctx,this._beforeAll.push(n),this.emit(\"beforeAll\",n),this},r.prototype.afterAll=function(t,e){if(this.pending)return this;\"function\"==typeof t&&(e=t,t=e.name),t='\"after all\" hook'+(t?\": \"+t:\"\");var n=new l(t,e);return n.parent=this,n.timeout(this.timeout()),n.enableTimeouts(this.enableTimeouts()),n.slow(this.slow()),n.ctx=this.ctx,this._afterAll.push(n),this.emit(\"afterAll\",n),this},r.prototype.beforeEach=function(t,e){if(this.pending)return this;\"function\"==typeof t&&(e=t,t=e.name),t='\"before each\" hook'+(t?\": \"+t:\"\");var n=new l(t,e);return n.parent=this,n.timeout(this.timeout()),n.enableTimeouts(this.enableTimeouts()),n.slow(this.slow()),n.ctx=this.ctx,this._beforeEach.push(n),this.emit(\"beforeEach\",n),this},r.prototype.afterEach=function(t,e){if(this.pending)return this;\"function\"==typeof t&&(e=t,t=e.name),t='\"after each\" hook'+(t?\": \"+t:\"\");var n=new l(t,e);return n.parent=this,n.timeout(this.timeout()),n.enableTimeouts(this.enableTimeouts()),n.slow(this.slow()),n.ctx=this.ctx,this._afterEach.push(n),this.emit(\"afterEach\",n),this},r.prototype.addSuite=function(t){return t.parent=this,t.timeout(this.timeout()),t.enableTimeouts(this.enableTimeouts()),t.slow(this.slow()),t.bail(this.bail()),this.suites.push(t),this.emit(\"suite\",t),this},r.prototype.addTest=function(t){return t.parent=this,t.timeout(this.timeout()),t.enableTimeouts(this.enableTimeouts()),t.slow(this.slow()),t.ctx=this.ctx,this.tests.push(t),this.emit(\"test\",t),this},r.prototype.fullTitle=function(){if(this.parent){var t=this.parent.fullTitle();if(t)return t+\" \"+this.title}return this.title},r.prototype.total=function(){return u.reduce(this.suites,function(t,e){return t+e.total()},0)+this.tests.length},r.prototype.eachTest=function(t){return u.forEach(this.tests,t),u.forEach(this.suites,function(e){e.eachTest(t)}),this}}),t.register(\"test.js\",function(t,e,n){function r(t,e){i.call(this,t,e),this.pending=!e,this.type=\"test\"}function o(){}var i=n(\"./runnable\");t.exports=r,o.prototype=i.prototype,r.prototype=new o,r.prototype.constructor=r}),t.register(\"utils.js\",function(t,e,n){function r(t){return!~p.indexOf(t)}function o(t){return t.replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\\/\\/(.*)/gm,'<span class=\"comment\">//$1</span>').replace(/('.*?')/gm,'<span class=\"string\">$1</span>').replace(/(\\d+\\.\\d+)/gm,'<span class=\"number\">$1</span>').replace(/(\\d+)/gm,'<span class=\"number\">$1</span>').replace(/\\bnew[ \\t]+(\\w+)/gm,'<span class=\"keyword\">new</span> <span class=\"init\">$1</span>').replace(/\\b(function|new|throw|return|var|if|else)\\b/gm,'<span class=\"keyword\">$1</span>')}var i=n(\"browser/fs\"),s=n(\"browser/path\"),a=s.basename,u=i.existsSync||s.existsSync,l=n(\"browser/glob\"),c=s.join,f=n(\"browser/debug\")(\"mocha:watch\"),p=[\"node_modules\",\".git\"];e.escape=function(t){return String(t).replace(/&/g,\"&amp;\").replace(/\"/g,\"&quot;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\")},e.forEach=function(t,e,n){for(var r=0,o=t.length;o>r;r++)e.call(n,t[r],r)},e.map=function(t,e,n){for(var r=[],o=0,i=t.length;i>o;o++)r.push(e.call(n,t[o],o));return r},e.indexOf=function(t,e,n){for(var r=n||0,o=t.length;o>r;r++)if(t[r]===e)return r;return-1},e.reduce=function(t,e,n){for(var r=n,o=0,i=t.length;i>o;o++)r=e(r,t[o],o,t);return r},e.filter=function(t,e){for(var n=[],r=0,o=t.length;o>r;r++){var i=t[r];e(i,r,t)&&n.push(i)}return n},e.keys=Object.keys||function(t){var e=[],n=Object.prototype.hasOwnProperty;for(var r in t)n.call(t,r)&&e.push(r);return e},e.watch=function(t,e){var n={interval:100};t.forEach(function(t){f(\"file %s\",t),i.watchFile(t,n,function(n,r){r.mtime<n.mtime&&e(t)})})},e.files=function(t,n,o){o=o||[],n=n||[\"js\"];var s=new RegExp(\"\\\\.(\"+n.join(\"|\")+\")$\");return i.readdirSync(t).filter(r).forEach(function(r){r=c(t,r),i.statSync(r).isDirectory()?e.files(r,n,o):r.match(s)&&o.push(r)}),o},e.slug=function(t){return t.toLowerCase().replace(/ +/g,\"-\").replace(/[^-\\w]/g,\"\")},e.clean=function(t){t=t.replace(/\\r\\n?|[\\n\\u2028\\u2029]/g,\"\\n\").replace(/^\\uFEFF/,\"\").replace(/^function *\\(.*\\) *{|\\(.*\\) *=> *{?/,\"\").replace(/\\s+\\}$/,\"\");var n=t.match(/^\\n?( *)/)[1].length,r=t.match(/^\\n?(\\t*)/)[1].length,o=new RegExp(\"^\\n?\"+(r?\"\t\":\" \")+\"{\"+(r?r:n)+\"}\",\"gm\");return t=t.replace(o,\"\"),e.trim(t)},e.trim=function(t){return t.replace(/^\\s+|\\s+$/g,\"\")},e.parseQuery=function(t){return e.reduce(t.replace(\"?\",\"\").split(\"&\"),function(t,e){var n=e.indexOf(\"=\"),r=e.slice(0,n),o=e.slice(++n);return t[r]=decodeURIComponent(o),t},{})},e.highlightTags=function(t){for(var e=document.getElementById(\"mocha\").getElementsByTagName(t),n=0,r=e.length;r>n;++n)e[n].innerHTML=o(e[n].innerHTML)},e.stringify=function(t){return t instanceof RegExp?t.toString():JSON.stringify(e.canonicalize(t),null,2).replace(/,(\\n|$)/g,\"$1\")},e.canonicalize=function(t,n){if(n=n||[],-1!==e.indexOf(n,t))return\"[Circular]\";var r;return\"[object Array]\"==={}.toString.call(t)?(n.push(t),r=e.map(t,function(t){return e.canonicalize(t,n)}),n.pop()):\"object\"==typeof t&&null!==t?(n.push(t),r={},e.forEach(e.keys(t).sort(),function(o){r[o]=e.canonicalize(t[o],n)}),n.pop()):r=t,r},e.lookupFiles=function h(t,e,n){var r=[],o=new RegExp(\"\\\\.(\"+e.join(\"|\")+\")$\");if(!u(t)){if(!u(t+\".js\")){if(r=l.sync(t),!r.length)throw new Error(\"cannot resolve path (or pattern) '\"+t+\"'\");return r}t+=\".js\"}try{var s=i.statSync(t);if(s.isFile())return t}catch(f){return}return i.readdirSync(t).forEach(function(s){s=c(t,s);try{var u=i.statSync(s);if(u.isDirectory())return void(n&&(r=r.concat(h(s,e,n))))}catch(l){return}u.isFile()&&o.test(s)&&\".\"!==a(s)[0]&&r.push(s)}),r}});var n=function(){return this}(),r=n.Date,o=n.setTimeout,i=(n.setInterval,n.clearTimeout,n.clearInterval,{});i.exit=function(t){},i.stdout={};var s=[],a=n.onerror;i.removeListener=function(t,e){if(\"uncaughtException\"==t){a?n.onerror=a:n.onerror=function(){};var r=u.utils.indexOf(s,e);-1!=r&&s.splice(r,1)}},i.on=function(t,e){\"uncaughtException\"==t&&(n.onerror=function(t,n,r){return e(new Error(t+\" (\"+n+\":\"+r+\")\")),!0},s.push(e))};var u=n.Mocha=t(\"mocha\"),l=n.mocha=new u({reporter:\"html\"});l.suite.removeAllListeners(\"pre-require\");var c,f=[];u.Runner.immediately=function(t){f.push(t),c||(c=o(e,0))},l.throwError=function(t){throw u.utils.forEach(s,function(e){e(t)}),t},l.ui=function(t){return u.prototype.ui.call(this,t),this.suite.emit(\"pre-require\",n,null,this),this},l.setup=function(t){\"string\"==typeof t&&(t={ui:t});for(var e in t)this[e](t[e]);return this},l.run=function(t){var e=l.options;l.globals(\"location\");var r=u.utils.parseQuery(n.location.search||\"\");return r.grep&&l.grep(r.grep),r.invert&&l.invert(),u.prototype.run.call(l,function(r){var o=n.document;o&&o.getElementById(\"mocha\")&&e.noHighlighting!==!0&&u.utils.highlightTags(\"code\"),t&&t(r)})},u.process=i}(),t.mocha});"
  },
  {
    "path": "test/dist/fixtures/sinon-chai.js",
    "content": "!function(t,e,n){var a=n.call(t);\"object\"==typeof modules?modules.define(\"sinon-chai\",e,function(t){t(a)}):t.sinonChai=a}(this,[\"chai\"],function(){var t;return function(t){!function(e){\"use strict\";\"function\"==typeof require&&\"object\"==typeof exports&&\"object\"==typeof module?module.exports=e:\"function\"==typeof define&&define.amd?define(function(){return e}):t.use(e)}(function(t,e){\"use strict\";function n(t){return\"function\"==typeof t&&\"function\"==typeof t.getCall&&\"function\"==typeof t.calledWithExactly}function a(t){return 1===t?\"once\":2===t?\"twice\":3===t?\"thrice\":(t||0)+\" times\"}function i(t){return t&&n(t.proxy)}function o(t){if(!n(t._obj)&&!i(t._obj))throw new TypeError(e.inspect(t._obj)+\" is not a spy or a call to a spy!\")}function c(t,e,a,i,o){function c(e){return t.printf.apply(t,e)}var l=i?\"always have \":\"have \";return a=a||\"\",n(t.proxy)&&(t=t.proxy),{affirmative:function(){return c([\"expected %n to \"+l+e+a].concat(o))},negative:function(){return c([\"expected %n to not \"+l+e].concat(o))}}}function l(n,a,i){e.addProperty(t.Assertion.prototype,n,function(){o(this);var t=c(this._obj,a,i,!1);this.assert(this._obj[n],t.affirmative,t.negative)})}function r(n,i,l){e.addMethod(t.Assertion.prototype,n,function(t){o(this);var e=c(this._obj,i,l,!1,[a(t)]);this.assert(this._obj[n]===t,e.affirmative,e.negative)})}function s(t,n,a){return function(){o(this);var i=\"always\"+t[0].toUpperCase()+t.substring(1),l=e.flag(this,\"always\")&&\"function\"==typeof this._obj[i],r=l?i:t,s=c(this._obj,n,a,l,h.call(arguments));this.assert(this._obj[r].apply(this._obj,arguments),s.affirmative,s.negative)}}function u(n,a,i){var o=s(n,a,i);e.addProperty(t.Assertion.prototype,n,o)}function f(n,a,i,o){var c=s(a,i,o);e.addMethod(t.Assertion.prototype,n,c)}function d(t,e,n){f(t,t,e,n)}var h=Array.prototype.slice;e.addProperty(t.Assertion.prototype,\"always\",function(){e.flag(this,\"always\",!0)}),l(\"called\",\"been called\",\" at least once, but it was never called\"),r(\"callCount\",\"been called exactly %1\",\", but it was called %c%C\"),l(\"calledOnce\",\"been called exactly once\",\", but it was called %c%C\"),l(\"calledTwice\",\"been called exactly twice\",\", but it was called %c%C\"),l(\"calledThrice\",\"been called exactly thrice\",\", but it was called %c%C\"),u(\"calledWithNew\",\"been called with new\"),d(\"calledBefore\",\"been called before %1\"),d(\"calledAfter\",\"been called after %1\"),d(\"calledOn\",\"been called with %1 as this\",\", but it was called with %t instead\"),d(\"calledWith\",\"been called with arguments %*\",\"%C\"),d(\"calledWithExactly\",\"been called with exact arguments %*\",\"%C\"),d(\"calledWithMatch\",\"been called with arguments matching %*\",\"%C\"),d(\"returned\",\"returned %1\"),f(\"thrown\",\"threw\",\"thrown %1\")})}.call(this,{use:function(e){t=e}}),t});"
  },
  {
    "path": "test/dist/fixtures/sinon.js",
    "content": "!function(e,t){var n=t.call(e);\"object\"==typeof modules?modules.define(\"sinon\",function(e){e(n)}):e.sinon=n}(this,function(){return function(e,t){\"use strict\";\"function\"==typeof define&&define.amd?define(\"sinon\",[],function(){return e.sinon=t()}):\"object\"==typeof exports?module.exports=t():e.sinon=t()}(this,function(){\"use strict\";var samsam,formatio,lolex;!function(){function define(e,t,n){\"samsam\"==e?samsam=t():\"function\"==typeof t&&0===e.length?lolex=t():\"function\"==typeof n&&(formatio=n(samsam))}define.amd={},(\"function\"==typeof define&&define.amd&&function(e){define(\"samsam\",e)}||\"object\"==typeof module&&function(e){module.exports=e()}||function(e){this.samsam=e()})(function(){function e(e){var t=e;return\"number\"==typeof e&&e!==t}function t(e){return f.toString.call(e).split(/[ \\]]/)[1]}function n(e){if(\"Arguments\"===t(e))return!0;if(\"object\"!=typeof e||\"number\"!=typeof e.length||\"Array\"===t(e))return!1;if(\"function\"==typeof e.callee)return!0;try{e[e.length]=6,delete e[e.length]}catch(n){return!0}return!1}function r(e){if(!e||1!==e.nodeType||!d)return!1;try{e.appendChild(d),e.removeChild(d)}catch(t){return!1}return!0}function o(e){var t,n=[];for(t in e)f.hasOwnProperty.call(e,t)&&n.push(t);return n}function i(e){return\"function\"==typeof e.getTime&&e.getTime()==e.valueOf()}function s(e){return 0===e&&1/e===-(1/0)}function a(t,n){return t===n||e(t)&&e(n)?0!==t||s(t)===s(n):void 0}function u(s,u){function l(e){return!(\"object\"!=typeof e||null===e||e instanceof Boolean||e instanceof Date||e instanceof Number||e instanceof RegExp||e instanceof String)}function c(e,t){var n;for(n=0;n<e.length;n++)if(e[n]===t)return n;return-1}var d=[],h=[],p=[],y=[],m={};return function v(s,u,g,b){var w=typeof s,x=typeof u;if(s===u||e(s)||e(u)||null==s||null==u||\"object\"!==w||\"object\"!==x)return a(s,u);if(r(s)||r(u))return!1;var C=i(s),E=i(u);if((C||E)&&(!C||!E||s.getTime()!==u.getTime()))return!1;if(s instanceof RegExp&&u instanceof RegExp&&s.toString()!==u.toString())return!1;var T=t(s),k=t(u),A=o(s),j=o(u);if(n(s)||n(u)){if(s.length!==u.length)return!1}else if(w!==x||T!==k||A.length!==j.length)return!1;var S,O,q,R,D,I,N,P,M,L,F;for(O=0,q=A.length;q>O;O++){if(S=A[O],!f.hasOwnProperty.call(u,S))return!1;if(R=s[S],D=u[S],I=l(R),N=l(D),P=I?c(d,R):-1,M=N?c(h,D):-1,L=-1!==P?p[P]:g+\"[\"+JSON.stringify(S)+\"]\",F=-1!==M?y[M]:b+\"[\"+JSON.stringify(S)+\"]\",m[L+F])return!0;if(-1===P&&I&&(d.push(R),p.push(L)),-1===M&&N&&(h.push(D),y.push(F)),I&&N&&(m[L+F]=!0),!v(R,D,L,F))return!1}return!0}(s,u,\"$1\",\"$2\")}function l(e,t){if(0===t.length)return!0;var n,r,o,i;for(n=0,r=e.length;r>n;++n)if(c(e[n],t[0])){for(o=0,i=t.length;i>o;++o)if(!c(e[n+o],t[o]))return!1;return!0}return!1}var c,f=Object.prototype,d=\"undefined\"!=typeof document&&document.createElement(\"div\");return c=function h(e,n){if(n&&\"function\"==typeof n.test)return n.test(e);if(\"function\"==typeof n)return n(e)===!0;if(\"string\"==typeof n){n=n.toLowerCase();var r=\"string\"==typeof e||!!e;return r&&String(e).toLowerCase().indexOf(n)>=0}if(\"number\"==typeof n)return n===e;if(\"boolean\"==typeof n)return n===e;if(\"undefined\"==typeof n)return\"undefined\"==typeof e;if(null===n)return null===e;if(\"Array\"===t(e)&&\"Array\"===t(n))return l(e,n);if(n&&\"object\"==typeof n){if(n===e)return!0;var o;for(o in n){var i=e[o];if(\"undefined\"==typeof i&&\"function\"==typeof e.getAttribute&&(i=e.getAttribute(o)),null===n[o]||\"undefined\"==typeof n[o]){if(i!==n[o])return!1}else if(\"undefined\"==typeof i||!h(i,n[o]))return!1}return!0}throw new Error(\"Matcher was not a string, a number, a function, a boolean or an object\")},{isArguments:n,isElement:r,isDate:i,isNegZero:s,identical:a,deepEqual:u,match:c,keys:o}}),(\"function\"==typeof define&&define.amd&&function(e){define(\"formatio\",[\"samsam\"],e)}||\"object\"==typeof module&&function(e){module.exports=e(require(\"samsam\"))}||function(e){this.formatio=e(this.samsam)})(function(e){function t(e){if(!e)return\"\";if(e.displayName)return e.displayName;if(e.name)return e.name;var t=e.toString().match(/function\\s+([^\\(]+)/m);return t&&t[1]||\"\"}function n(e,n){var r,o,i=t(n&&n.constructor),a=e.excludeConstructors||s.excludeConstructors||[];for(r=0,o=a.length;o>r;++r){if(\"string\"==typeof a[r]&&a[r]===i)return\"\";if(a[r].test&&a[r].test(i))return\"\"}return i}function r(e,t){if(\"object\"!=typeof e)return!1;var n,r;for(n=0,r=t.length;r>n;++n)if(t[n]===e)return!0;return!1}function o(t,n,i,s){if(\"string\"==typeof n){var u=t.quoteStrings,l=\"boolean\"!=typeof u||u;return i||l?'\"'+n+'\"':n}if(\"function\"==typeof n&&!(n instanceof RegExp))return o.func(n);if(i=i||[],r(n,i))return\"[Circular]\";if(\"[object Array]\"===Object.prototype.toString.call(n))return o.array.call(t,n,i);if(!n)return String(1/n===-(1/0)?\"-0\":n);if(e.isElement(n))return o.element(n);if(\"function\"==typeof n.toString&&n.toString!==Object.prototype.toString)return n.toString();var c,f;for(c=0,f=a.length;f>c;c++)if(n===a[c].object)return a[c].value;return o.object.call(t,n,i,s)}function i(e){for(var t in e)this[t]=e[t]}var s={excludeConstructors:[\"Object\",/^.$/],quoteStrings:!0,limitChildrenCount:0},a=(Object.prototype.hasOwnProperty,[]);return\"undefined\"!=typeof global&&a.push({object:global,value:\"[object global]\"}),\"undefined\"!=typeof document&&a.push({object:document,value:\"[object HTMLDocument]\"}),\"undefined\"!=typeof window&&a.push({object:window,value:\"[object Window]\"}),o.func=function(e){return\"function \"+t(e)+\"() {}\"},o.array=function(e,t){t=t||[],t.push(e);var n,r,i=[];for(r=this.limitChildrenCount>0?Math.min(this.limitChildrenCount,e.length):e.length,n=0;r>n;++n)i.push(o(this,e[n],t));return r<e.length&&i.push(\"[... \"+(e.length-r)+\" more elements]\"),\"[\"+i.join(\", \")+\"]\"},o.object=function(t,i,s){i=i||[],i.push(t),s=s||0;var a,u,l,c,f,d,h=[],p=e.keys(t).sort(),y=3;for(d=this.limitChildrenCount>0?Math.min(this.limitChildrenCount,p.length):p.length,c=0;d>c;++c)a=p[c],l=t[a],u=r(l,i)?\"[Circular]\":o(this,l,i,s+2),u=(/\\s/.test(a)?'\"'+a+'\"':a)+\": \"+u,y+=u.length,h.push(u);var m=n(this,t),v=m?\"[\"+m+\"] \":\"\",g=\"\";for(c=0,f=s;f>c;++c)g+=\" \";return d<p.length&&h.push(\"[... \"+(p.length-d)+\" more elements]\"),y+s>80?v+\"{\\n  \"+g+h.join(\",\\n  \"+g)+\"\\n\"+g+\"}\":v+\"{ \"+h.join(\", \")+\" }\"},o.element=function(e){var t,n,r,o,i,s=e.tagName.toLowerCase(),a=e.attributes,u=[];for(r=0,o=a.length;o>r;++r)t=a.item(r),n=t.nodeName.toLowerCase().replace(\"html:\",\"\"),i=t.nodeValue,\"contenteditable\"===n&&\"inherit\"===i||i&&u.push(n+'=\"'+i+'\"');var l=\"<\"+s+(u.length>0?\" \":\"\"),c=e.innerHTML;c.length>20&&(c=c.substr(0,20)+\"[...]\");var f=l+u.join(\" \")+\">\"+c+\"</\"+s+\">\";return f.replace(/ contentEditable=\"inherit\"/,\"\")},i.prototype={functionName:t,configure:function(e){return new i(e)},constructorName:function(e){return n(this,e)},ascii:function(e,t,n){return o(this,e,t,n)}},i.prototype}),!function(e){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=e();else if(\"function\"==typeof define&&define.amd)define([],e);else{var t;\"undefined\"!=typeof window?t=window:\"undefined\"!=typeof global?t=global:\"undefined\"!=typeof self&&(t=self),t.lolex=e()}}(function(){var define,module,exports;return function e(t,n,r){function o(s,a){if(!n[s]){if(!t[s]){var u=\"function\"==typeof require&&require;if(!a&&u)return u(s,!0);if(i)return i(s,!0);var l=new Error(\"Cannot find module '\"+s+\"'\");throw l.code=\"MODULE_NOT_FOUND\",l}var c=n[s]={exports:{}};t[s][0].call(c.exports,function(e){var n=t[s][1][e];return o(n?n:e)},c,c.exports,e,t,n,r)}return n[s].exports}for(var i=\"function\"==typeof require&&require,s=0;s<r.length;s++)o(r[s]);return o}({1:[function(require,module,exports){(function(global){!function(global){function parseTime(e){if(!e)return 0;var t,n=e.split(\":\"),r=n.length,o=r,i=0;if(r>3||!/^(\\d\\d:){0,2}\\d\\d?$/.test(e))throw new Error(\"tick only understands numbers and 'h:m:s'\");for(;o--;){if(t=parseInt(n[o],10),t>=60)throw new Error(\"Invalid time \"+e);i+=t*Math.pow(60,r-o-1)}return 1e3*i}function getEpoch(e){if(!e)return 0;if(\"function\"==typeof e.getTime)return e.getTime();if(\"number\"==typeof e)return e;throw new TypeError(\"now should be milliseconds since UNIX epoch\")}function inRange(e,t,n){return n&&n.callAt>=e&&n.callAt<=t}function mirrorDateProperties(e,t){var n;for(n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return t.now?e.now=function(){return e.clock.now}:delete e.now,t.toSource?e.toSource=function(){return t.toSource()}:delete e.toSource,e.toString=function(){return t.toString()},e.prototype=t.prototype,e.parse=t.parse,e.UTC=t.UTC,e.prototype.toUTCString=t.prototype.toUTCString,e}function createDate(){function e(t,n,r,o,i,s,a){switch(arguments.length){case 0:return new NativeDate(e.clock.now);case 1:return new NativeDate(t);case 2:return new NativeDate(t,n);case 3:return new NativeDate(t,n,r);case 4:return new NativeDate(t,n,r,o);case 5:return new NativeDate(t,n,r,o,i);case 6:return new NativeDate(t,n,r,o,i,s);default:return new NativeDate(t,n,r,o,i,s,a)}}return mirrorDateProperties(e,NativeDate)}function addTimer(e,t){if(void 0===t.func)throw new Error(\"Callback must be provided to timer calls\");return e.timers||(e.timers={}),t.id=uniqueTimerId++,t.createdAt=e.now,t.callAt=e.now+(t.delay||(e.duringTick?1:0)),e.timers[t.id]=t,addTimerReturnsObject?{id:t.id,ref:NOOP,unref:NOOP}:t.id}function compareTimers(e,t){return e.callAt<t.callAt?-1:e.callAt>t.callAt?1:e.immediate&&!t.immediate?-1:!e.immediate&&t.immediate?1:e.createdAt<t.createdAt?-1:e.createdAt>t.createdAt?1:e.id<t.id?-1:e.id>t.id?1:void 0}function firstTimerInRange(e,t,n){var r,o,i=e.timers,s=null;for(r in i)i.hasOwnProperty(r)&&(o=inRange(t,n,i[r]),!o||s&&1!==compareTimers(s,i[r])||(s=i[r]));return s}function callTimer(clock,timer){var exception;\"number\"==typeof timer.interval?clock.timers[timer.id].callAt+=timer.interval:delete clock.timers[timer.id];try{\"function\"==typeof timer.func?timer.func.apply(null,timer.args):eval(timer.func)}catch(e){exception=e}if(clock.timers[timer.id]){if(exception)throw exception}else if(exception)throw exception}function uninstall(e,t){var n,r,o;for(r=0,o=e.methods.length;o>r;r++)if(n=e.methods[r],t[n].hadOwnProperty)t[n]=e[\"_\"+n];else try{delete t[n]}catch(i){}e.methods=[]}function hijackMethod(e,t,n){var r;if(n[t].hadOwnProperty=Object.prototype.hasOwnProperty.call(e,t),n[\"_\"+t]=e[t],\"Date\"===t){var o=mirrorDateProperties(n[t],e[t]);e[t]=o}else{e[t]=function(){return n[t].apply(n,arguments)};for(r in n[t])n[t].hasOwnProperty(r)&&(e[t][r]=n[t][r])}e[t].clock=n}function createClock(e){var t={now:getEpoch(e),timeouts:{},Date:createDate()};return t.Date.clock=t,t.setTimeout=function(e,n){return addTimer(t,{func:e,args:Array.prototype.slice.call(arguments,2),delay:n})},t.clearTimeout=function(e){e&&(t.timers||(t.timers=[]),\"object\"==typeof e&&(e=e.id),t.timers.hasOwnProperty(e)&&delete t.timers[e])},t.setInterval=function(e,n){return addTimer(t,{func:e,args:Array.prototype.slice.call(arguments,2),delay:n,interval:n})},t.clearInterval=function(e){t.clearTimeout(e)},t.setImmediate=function(e){return addTimer(t,{func:e,args:Array.prototype.slice.call(arguments,1),immediate:!0})},t.clearImmediate=function(e){t.clearTimeout(e)},t.tick=function(e){e=\"number\"==typeof e?e:parseTime(e);var n=t.now,r=t.now+e,o=t.now,i=firstTimerInRange(t,n,r);t.duringTick=!0;for(var s;i&&r>=n;){if(t.timers[i.id]){n=t.now=i.callAt;try{callTimer(t,i)}catch(a){s=s||a}}i=firstTimerInRange(t,o,r),o=n}if(t.duringTick=!1,t.now=r,s)throw s;return t.now},t.reset=function(){t.timers={}},t}function detectKnownFailSituation(e){if(!(e.indexOf(\"Date\")<0)){if(e.indexOf(\"setTimeout\")<0)throw new Error(\"Native setTimeout will not work when Date is faked\");if(e.indexOf(\"setImmediate\")<0)throw new Error(\"Native setImmediate will not work when Date is faked\")}}var glbl=global;global.setTimeout=glbl.setTimeout,global.clearTimeout=glbl.clearTimeout,global.setImmediate=glbl.setImmediate,global.clearImmediate=glbl.clearImmediate,global.setInterval=glbl.setInterval,global.clearInterval=glbl.clearInterval,global.Date=glbl.Date;var NOOP=function(){},timeoutResult=setTimeout(NOOP,0),addTimerReturnsObject=\"object\"==typeof timeoutResult;clearTimeout(timeoutResult);var NativeDate=Date,uniqueTimerId=1,timers={setTimeout:setTimeout,clearTimeout:clearTimeout,setImmediate:global.setImmediate,clearImmediate:global.clearImmediate,setInterval:setInterval,clearInterval:clearInterval,Date:Date},keys=Object.keys||function(e){var t,n=[];for(t in e)e.hasOwnProperty(t)&&n.push(t);return n};exports.timers=timers,exports.createClock=createClock,exports.install=function(e,t,n){var r,o;\"number\"==typeof e&&(n=t,t=e,e=null),e||(e=global);var i=createClock(t);for(i.uninstall=function(){uninstall(i,e)},i.methods=n||[],0===i.methods.length&&(i.methods=keys(timers)),detectKnownFailSituation(i.methods),r=0,o=i.methods.length;o>r;r++)hijackMethod(e,i.methods[r],i);return i}}(global||this)}).call(this,\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:{})},{}]},{},[1])(1)})}();var define,sinon=function(){function e(e,n,r){t=r.exports=e(\"./sinon/util/core\"),e(\"./sinon/extend\"),e(\"./sinon/typeOf\"),e(\"./sinon/times_in_words\"),e(\"./sinon/spy\"),e(\"./sinon/call\"),e(\"./sinon/behavior\"),e(\"./sinon/stub\"),e(\"./sinon/mock\"),e(\"./sinon/collection\"),e(\"./sinon/assert\"),e(\"./sinon/sandbox\"),e(\"./sinon/test\"),e(\"./sinon/test_case\"),e(\"./sinon/match\"),e(\"./sinon/format\"),e(\"./sinon/log_error\")}var t,n=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,r=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return r?define(e):n?(e(require,module.exports,module),t=module.exports):t={},t}();return function(e){function t(e){var t=!1;try{e.appendChild(l),t=l.parentNode===e}catch(n){return!1}finally{try{e.removeChild(l)}catch(n){}}return t}function n(e){return l&&e&&1===e.nodeType&&t(e)}function r(e){return\"function\"==typeof e||!!(e&&e.constructor&&e.call&&e.apply)}function o(e){return\"number\"==typeof e&&isNaN(e)}function i(e,t){for(var n in t)c.call(e,n)||(e[n]=t[n])}function s(e){return\"function\"==typeof e&&\"function\"==typeof e.restore&&e.restore.sinon}function a(e){return e.wrapMethod=function(t,n,o){function s(e){var t;if(r(e)){if(e.restore&&e.restore.sinon)t=new TypeError(\"Attempted to wrap \"+n+\" which is already wrapped\");else if(e.calledBefore){var o=e.returns?\"stubbed\":\"spied on\";t=new TypeError(\"Attempted to wrap \"+n+\" which is already \"+o)}}else t=new TypeError(\"Attempted to wrap \"+typeof e+\" property \"+n+\" as function\");if(t)throw e&&e.stackTrace&&(t.stack+=\"\\n--------------\\n\"+e.stackTrace),t}if(!t)throw new TypeError(\"Should wrap property of object\");if(\"function\"!=typeof o&&\"object\"!=typeof o)throw new TypeError(\"Method wrapper should be a function or a property descriptor\");var a,u,l,d=t.hasOwnProperty?t.hasOwnProperty(n):c.call(t,n);if(f){var h=\"function\"==typeof o?{value:o}:o,p=e.getPropertyDescriptor(t,n);if(p?p.restore&&p.restore.sinon&&(a=new TypeError(\"Attempted to wrap \"+n+\" which is already wrapped\")):a=new TypeError(\"Attempted to wrap \"+typeof u+\" property \"+n+\" as function\"),a)throw p&&p.stackTrace&&(a.stack+=\"\\n--------------\\n\"+p.stackTrace),a;var y=e.objectKeys(h);for(l=0;l<y.length;l++)u=p[y[l]],s(u);for(i(h,p),l=0;l<y.length;l++)i(h[y[l]],p[y[l]]);Object.defineProperty(t,n,h)}else u=t[n],s(u),t[n]=o,o.displayName=n;return o.displayName=n,o.stackTrace=new Error(\"Stack Trace for original\").stack,o.restore=function(){if(d)f&&Object.defineProperty(t,n,p);else try{delete t[n]}catch(e){}t[n]===o&&(t[n]=u)},o.restore.sinon=!0,f||i(o,u),o},e.create=function(e){var t=function(){};return t.prototype=e,new t},e.deepEqual=function t(r,i){if(e.match&&e.match.isMatcher(r))return r.test(i);if(\"object\"!=typeof r||\"object\"!=typeof i)return o(r)&&o(i)||r===i;if(n(r)||n(i))return r===i;if(r===i)return!0;if(null===r&&null!==i||null!==r&&null===i)return!1;if(r instanceof RegExp&&i instanceof RegExp)return r.source===i.source&&r.global===i.global&&r.ignoreCase===i.ignoreCase&&r.multiline===i.multiline;var s=Object.prototype.toString.call(r);if(s!==Object.prototype.toString.call(i))return!1;if(\"[object Date]\"===s)return r.valueOf()===i.valueOf();var a,u=0,l=0;if(\"[object Array]\"===s&&r.length!==i.length)return!1;for(a in r)if(r.hasOwnProperty(a)){if(u+=1,!(a in i))return!1;if(!t(r[a],i[a]))return!1}for(a in i)i.hasOwnProperty(a)&&(l+=1);return u===l},e.functionName=function(e){var t=e.displayName||e.name;if(!t){var n=e.toString().match(/function ([^\\s\\(]+)/);t=n&&n[1]}return t},e.functionToString=function(){if(this.getCall&&this.callCount)for(var e,t,n=this.callCount;n--;){e=this.getCall(n).thisValue;for(t in e)if(e[t]===this)return t}return this.displayName||\"sinon fake\"},e.objectKeys=function(e){if(e!==Object(e))throw new TypeError(\"sinon.objectKeys called on a non-object\");var t,n=[];for(t in e)c.call(e,t)&&n.push(t);return n},e.getPropertyDescriptor=function(e,t){for(var n,r=e;r&&!(n=Object.getOwnPropertyDescriptor(r,t));)r=Object.getPrototypeOf(r);return n},e.getConfig=function(t){var n={};t=t||{};var r=e.defaultConfig;for(var o in r)r.hasOwnProperty(o)&&(n[o]=t.hasOwnProperty(o)?t[o]:r[o]);return n},e.defaultConfig={injectIntoThis:!0,injectInto:null,properties:[\"spy\",\"stub\",\"mock\",\"clock\",\"server\",\"requests\"],useFakeTimers:!0,useFakeServer:!0},e.timesInWords=function(e){return 1===e&&\"once\"||2===e&&\"twice\"||3===e&&\"thrice\"||(e||0)+\" times\"},e.calledInOrder=function(e){for(var t=1,n=e.length;n>t;t++)if(!e[t-1].calledBefore(e[t])||!e[t].called)return!1;return!0},e.orderByFirstCall=function(e){return e.sort(function(e,t){var n=e.getCall(0),r=t.getCall(0),o=n&&n.callId||-1,i=r&&r.callId||-1;return i>o?-1:1})},e.createStubInstance=function(t){if(\"function\"!=typeof t)throw new TypeError(\"The constructor should be a function.\");return e.stub(e.create(t.prototype))},e.restore=function(e){if(null!==e&&\"object\"==typeof e)for(var t in e)s(e[t])&&e[t].restore();else s(e)&&e.restore()},e}function u(e,t){a(t)}var l=\"undefined\"!=typeof document&&document.createElement(\"div\"),c=Object.prototype.hasOwnProperty,f=\"keys\"in Object,d=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,h=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return h?void define(u):d?void u(require,module.exports,module):void(e&&a(e))}(\"object\"==typeof sinon&&sinon),function(e){function t(e){function t(e){var t,r,o,i=Array.prototype.slice.call(arguments,1);for(r=0;r<i.length;r++){t=i[r];for(o in t)t.hasOwnProperty(o)&&(e[o]=t[o]);n&&t.hasOwnProperty(\"toString\")&&t.toString!==e.toString&&(e.toString=t.toString)}return e}var n=function(){var e={constructor:function(){return\"0\"},toString:function(){return\"1\"},valueOf:function(){return\"2\"},toLocaleString:function(){return\"3\"},prototype:function(){return\"4\"},isPrototypeOf:function(){return\"5\"},propertyIsEnumerable:function(){return\"6\"},hasOwnProperty:function(){return\"7\"},length:function(){return\"8\"},unique:function(){return\"9\"}},t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(e[n]());return\"0123456789\"!==t.join(\"\")}();return e.extend=t,e.extend}function n(e,n,r){var o=e(\"./util/core\");r.exports=t(o)}var r=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,o=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return o?void define(n):r?void n(require,module.exports,module):void(e&&t(e))}(\"object\"==typeof sinon&&sinon),function(e){function t(e){function t(e){switch(e){case 1:return\"once\";case 2:return\"twice\";case 3:return\"thrice\";default:return(e||0)+\" times\"}}return e.timesInWords=t,e.timesInWords}function n(e,n,r){var o=e(\"./util/core\");r.exports=t(o)}var r=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,o=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return o?void define(n):r?void n(require,module.exports,module):void(e&&t(e))}(\"object\"==typeof sinon&&sinon),function(e){function t(e){function t(e){if(null===e)return\"null\";if(void 0===e)return\"undefined\";var t=Object.prototype.toString.call(e);return t.substring(8,t.length-1).toLowerCase()}return e.typeOf=t,e.typeOf}function n(e,n,r){var o=e(\"./util/core\");r.exports=t(o)}var r=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,o=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return o?void define(n):r?void n(require,module.exports,module):void(e&&t(e))}(\"object\"==typeof sinon&&sinon),function(e){function t(e){function t(t,n,r){var o=e.typeOf(t);if(o!==n)throw new TypeError(\"Expected type of \"+r+\" to be \"+n+\", but was \"+o)}function n(e){return s.isPrototypeOf(e)}function r(t,o){if(null===o||void 0===o)return!1;for(var i in t)if(t.hasOwnProperty(i)){var s=t[i],a=o[i];if(n(s)){if(!s.test(a))return!1}else if(\"object\"===e.typeOf(s)){if(!r(s,a))return!1}else if(!e.deepEqual(s,a))return!1}return!0}function o(t,n){var o=e.create(s),i=e.typeOf(t);switch(i){case\"object\":if(\"function\"==typeof t.test)return o.test=function(e){return t.test(e)===!0},o.message=\"match(\"+e.functionName(t.test)+\")\",o;var a=[];for(var u in t)t.hasOwnProperty(u)&&a.push(u+\": \"+t[u]);o.test=function(e){return r(t,e)},o.message=\"match(\"+a.join(\", \")+\")\";break;case\"number\":o.test=function(e){return t==e};break;case\"string\":o.test=function(e){return\"string\"!=typeof e?!1:-1!==e.indexOf(t)},o.message='match(\"'+t+'\")';break;case\"regexp\":o.test=function(e){return\"string\"!=typeof e?!1:t.test(e)};break;case\"function\":o.test=t,n?o.message=n:o.message=\"match(\"+e.functionName(t)+\")\";break;default:o.test=function(n){return e.deepEqual(t,n)}}return o.message||(o.message=\"match(\"+t+\")\"),o}function i(n,r){return function(i,s){t(i,\"string\",\"property\");var a=1===arguments.length,u=r+'(\"'+i+'\"';return a||(u+=\", \"+s),u+=\")\",o(function(t){return void 0!==t&&null!==t&&n(t,i)?a||e.deepEqual(s,t[i]):!1},u)}}var s={toString:function(){return this.message}};return s.or=function(t){if(!arguments.length)throw new TypeError(\"Matcher expected\");n(t)||(t=o(t));var r=this,i=e.create(s);return i.test=function(e){return r.test(e)||t.test(e)},i.message=r.message+\".or(\"+t.message+\")\",i},s.and=function(t){if(!arguments.length)throw new TypeError(\"Matcher expected\");n(t)||(t=o(t));var r=this,i=e.create(s);return i.test=function(e){return r.test(e)&&t.test(e)},i.message=r.message+\".and(\"+t.message+\")\",i},o.isMatcher=n,o.any=o(function(){return!0},\"any\"),o.defined=o(function(e){return null!==e&&void 0!==e},\"defined\"),o.truthy=o(function(e){return!!e},\"truthy\"),o.falsy=o(function(e){return!e},\"falsy\"),o.same=function(e){return o(function(t){return e===t},\"same(\"+e+\")\")},o.typeOf=function(n){return t(n,\"string\",\"type\"),o(function(t){return e.typeOf(t)===n},'typeOf(\"'+n+'\")')},o.instanceOf=function(n){return t(n,\"function\",\"type\"),o(function(e){return e instanceof n},\"instanceOf(\"+e.functionName(n)+\")\")},o.has=i(function(e,t){return\"object\"==typeof e?t in e:void 0!==e[t]},\"has\"),o.hasOwn=i(function(e,t){return e.hasOwnProperty(t)},\"hasOwn\"),o.bool=o.typeOf(\"boolean\"),o.number=o.typeOf(\"number\"),o.string=o.typeOf(\"string\"),o.object=o.typeOf(\"object\"),o.func=o.typeOf(\"function\"),o.array=o.typeOf(\"array\"),o.regexp=o.typeOf(\"regexp\"),o.date=o.typeOf(\"date\"),e.match=o,o}function n(e,n,r){var o=e(\"./util/core\");e(\"./typeOf\"),r.exports=t(o)}var r=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,o=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return o?void define(n):r?void n(require,module.exports,module):void(e&&t(e))}(\"object\"==typeof sinon&&sinon),function(e,t){function n(e){function n(e){return\"\"+e}function r(){function e(){return n.ascii.apply(n,arguments)}var n=t.configure({quoteStrings:!1,limitChildrenCount:250});return e}function o(){function e(e){var n=\"object\"==typeof e&&e.toString===Object.prototype.toString;return n?t.inspect(e):e}try{var t=require(\"util\")}catch(r){}return t?e:n}var i,s=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require;if(s)try{t=require(\"formatio\")}catch(a){}return i=t?r():s?o():n,e.format=i,e.format}function r(e,t,r){var o=e(\"./util/core\");r.exports=n(o)}var o=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,i=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return i?void define(r):o?void r(require,module.exports,module):void(e&&n(e))}(\"object\"==typeof sinon&&sinon,\"object\"==typeof formatio&&formatio),function(e){function t(e){function t(t,n,o){var i=e.functionName(t)+n;throw o.length&&(i+=\" Received [\"+r.call(o).join(\", \")+\"]\"),new Error(i)}function n(t,n,r,i,s,a,u){if(\"number\"!=typeof a)throw new TypeError(\"Call id is not a number\");var l=e.create(o);return l.proxy=t,l.thisValue=n,l.args=r,l.returnValue=i,l.exception=s,l.callId=a,l.stack=u,l}var o={calledOn:function(t){return e.match&&e.match.isMatcher(t)?t.test(this.thisValue):this.thisValue===t},calledWith:function(){var t=arguments.length;if(t>this.args.length)return!1;for(var n=0;t>n;n+=1)if(!e.deepEqual(arguments[n],this.args[n]))return!1;return!0},calledWithMatch:function(){var t=arguments.length;if(t>this.args.length)return!1;for(var n=0;t>n;n+=1){var r=this.args[n],o=arguments[n];if(!e.match||!e.match(o).test(r))return!1}return!0},calledWithExactly:function(){return arguments.length===this.args.length&&this.calledWith.apply(this,arguments)},notCalledWith:function(){return!this.calledWith.apply(this,arguments)},notCalledWithMatch:function(){return!this.calledWithMatch.apply(this,arguments)},returned:function(t){return e.deepEqual(t,this.returnValue)},threw:function(e){return\"undefined\"!=typeof e&&this.exception?this.exception===e||this.exception.name===e:!!this.exception},calledWithNew:function(){return this.proxy.prototype&&this.thisValue instanceof this.proxy},calledBefore:function(e){return this.callId<e.callId},calledAfter:function(e){return this.callId>e.callId},callArg:function(e){this.args[e]()},callArgOn:function(e,t){this.args[e].apply(t)},callArgWith:function(e){this.callArgOnWith.apply(this,[e,null].concat(r.call(arguments,1)))},callArgOnWith:function(e,t){var n=r.call(arguments,2);this.args[e].apply(t,n)},\"yield\":function(){this.yieldOn.apply(this,[null].concat(r.call(arguments,0)))},yieldOn:function(e){for(var n=this.args,o=0,i=n.length;i>o;++o)if(\"function\"==typeof n[o])return void n[o].apply(e,r.call(arguments,1));t(this.proxy,\" cannot yield since no callback was passed.\",n)},yieldTo:function(e){this.yieldToOn.apply(this,[e,null].concat(r.call(arguments,1)))},yieldToOn:function(e,n){for(var o=this.args,i=0,s=o.length;s>i;++i)if(o[i]&&\"function\"==typeof o[i][e])return void o[i][e].apply(n,r.call(arguments,2));t(this.proxy,\" cannot yield to '\"+e+\"' since no callback was passed.\",o)},getStackFrames:function(){return this.stack&&this.stack.split(\"\\n\").slice(3)},toString:function(){for(var t=this.proxy.toString()+\"(\",n=[],r=0,o=this.args.length;o>r;++r)n.push(e.format(this.args[r]));return t=t+n.join(\", \")+\")\",\"undefined\"!=typeof this.returnValue&&(t+=\" => \"+e.format(this.returnValue)),this.exception&&(t+=\" !\"+this.exception.name,this.exception.message&&(t+=\"(\"+this.exception.message+\")\")),this.stack&&(t+=this.getStackFrames()[0].replace(/^\\s*(?:at\\s+|@)?/,\" at \")),t}};return o.invokeCallback=o[\"yield\"],n.toString=o.toString,e.spyCall=n,n}function n(e,n,r){var o=e(\"./util/core\");e(\"./match\"),e(\"./format\"),r.exports=t(o)}var r=Array.prototype.slice,o=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,i=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return i?void define(n):o?void n(require,module.exports,module):void(e&&t(e))}(\"object\"==typeof sinon&&sinon),function(sinonGlobal){function makeApi(sinon){function spy(e,t,n){if(!t&&\"function\"==typeof e)return spy.create(e);if(!e&&!t)return spy.create(function(){});if(n){for(var r=sinon.getPropertyDescriptor(e,t),o=0;o<n.length;o++)r[n[o]]=spy.create(r[n[o]]);return sinon.wrapMethod(e,t,r)}return sinon.wrapMethod(e,t,spy.create(e[t]))}function matchingFake(e,t,n){if(e)for(var r=0,o=e.length;o>r;r++)if(e[r].matches(t,n))return e[r]}function incrementCallCount(){this.called=!0,this.callCount+=1,this.notCalled=!1,this.calledOnce=1===this.callCount,this.calledTwice=2===this.callCount,this.calledThrice=3===this.callCount}function createCallProperties(){this.firstCall=this.getCall(0),this.secondCall=this.getCall(1),this.thirdCall=this.getCall(2),this.lastCall=this.getCall(this.callCount-1)}function createProxy(func,proxyLength){var p;return proxyLength?eval(\"p = (function proxy(\"+vars.substring(0,2*proxyLength-1)+\") { return p.invoke(func, this, slice.call(arguments)); });\"):p=function(){return p.invoke(func,this,slice.call(arguments))},p.isSinonProxy=!0,p}function delegateToCalls(e,t,n,r){spyApi[e]=function(){if(!this.called)return r?r.apply(this,arguments):!1;for(var o,i=0,s=0,a=this.callCount;a>s;s+=1)if(o=this.getCall(s),o[n||e].apply(o,arguments)&&(i+=1,t))return!0;return i===this.callCount}}var push=Array.prototype.push,slice=Array.prototype.slice,callId=0,vars=\"a,b,c,d,e,f,g,h,i,j,k,l\",uuid=0,spyApi={reset:function(){if(this.invoking){var e=new Error(\"Cannot reset Sinon function while invoking it. Move the call to .reset outside of the callback.\");throw e.name=\"InvalidResetException\",e}if(this.called=!1,this.notCalled=!0,this.calledOnce=!1,this.calledTwice=!1,this.calledThrice=!1,this.callCount=0,this.firstCall=null,this.secondCall=null,this.thirdCall=null,this.lastCall=null,this.args=[],this.returnValues=[],this.thisValues=[],this.exceptions=[],this.callIds=[],this.stacks=[],this.fakes)for(var t=0;t<this.fakes.length;t++)this.fakes[t].reset();return this},create:function(e,t){var n;\"function\"!=typeof e?e=function(){}:n=sinon.functionName(e),t||(t=e.length);var r=createProxy(e,t);return sinon.extend(r,spy),delete r.create,sinon.extend(r,e),r.reset(),r.prototype=e.prototype,r.displayName=n||\"spy\",r.toString=sinon.functionToString,r.instantiateFake=sinon.spy.create,r.id=\"spy#\"+uuid++,r},invoke:function(e,t,n){var r,o,i=matchingFake(this.fakes,n);incrementCallCount.call(this),push.call(this.thisValues,t),push.call(this.args,n),push.call(this.callIds,callId++),createCallProperties.call(this);try{this.invoking=!0,o=i?i.invoke(e,t,n):(this.func||e).apply(t,n);var s=this.getCall(this.callCount-1);s.calledWithNew()&&\"object\"!=typeof o&&(o=t)}catch(a){r=a}finally{delete this.invoking}if(push.call(this.exceptions,r),push.call(this.returnValues,o),push.call(this.stacks,(new Error).stack),createCallProperties.call(this),void 0!==r)throw r;return o},named:function(e){return this.displayName=e,this},getCall:function(e){return 0>e||e>=this.callCount?null:sinon.spyCall(this,this.thisValues[e],this.args[e],this.returnValues[e],this.exceptions[e],this.callIds[e],this.stacks[e])},getCalls:function(){var e,t=[];for(e=0;e<this.callCount;e++)t.push(this.getCall(e));return t},calledBefore:function(e){return this.called?e.called?this.callIds[0]<e.callIds[e.callIds.length-1]:!0:!1},calledAfter:function(e){return this.called&&e.called?this.callIds[this.callCount-1]>e.callIds[e.callCount-1]:!1},withArgs:function(){var e=slice.call(arguments);if(this.fakes){var t=matchingFake(this.fakes,e,!0);if(t)return t}else this.fakes=[];var n=this,r=this.instantiateFake();r.matchingAguments=e,r.parent=this,push.call(this.fakes,r),r.withArgs=function(){return n.withArgs.apply(n,arguments)};for(var o=0;o<this.args.length;o++)r.matches(this.args[o])&&(incrementCallCount.call(r),push.call(r.thisValues,this.thisValues[o]),push.call(r.args,this.args[o]),push.call(r.returnValues,this.returnValues[o]),push.call(r.exceptions,this.exceptions[o]),push.call(r.callIds,this.callIds[o]));return createCallProperties.call(r),r},matches:function(e,t){var n=this.matchingAguments;return n.length<=e.length&&sinon.deepEqual(n,e.slice(0,n.length))?!t||n.length===e.length:void 0},printf:function(e){var t,n=this,r=slice.call(arguments,1);return(e||\"\").replace(/%(.)/g,function(e,o){return t=spyApi.formatters[o],\"function\"==typeof t?t.call(null,n,r):isNaN(parseInt(o,10))?\"%\"+o:sinon.format(r[o-1])})}};return delegateToCalls(\"calledOn\",!0),delegateToCalls(\"alwaysCalledOn\",!1,\"calledOn\"),delegateToCalls(\"calledWith\",!0),delegateToCalls(\"calledWithMatch\",!0),\ndelegateToCalls(\"alwaysCalledWith\",!1,\"calledWith\"),delegateToCalls(\"alwaysCalledWithMatch\",!1,\"calledWithMatch\"),delegateToCalls(\"calledWithExactly\",!0),delegateToCalls(\"alwaysCalledWithExactly\",!1,\"calledWithExactly\"),delegateToCalls(\"neverCalledWith\",!1,\"notCalledWith\",function(){return!0}),delegateToCalls(\"neverCalledWithMatch\",!1,\"notCalledWithMatch\",function(){return!0}),delegateToCalls(\"threw\",!0),delegateToCalls(\"alwaysThrew\",!1,\"threw\"),delegateToCalls(\"returned\",!0),delegateToCalls(\"alwaysReturned\",!1,\"returned\"),delegateToCalls(\"calledWithNew\",!0),delegateToCalls(\"alwaysCalledWithNew\",!1,\"calledWithNew\"),delegateToCalls(\"callArg\",!1,\"callArgWith\",function(){throw new Error(this.toString()+\" cannot call arg since it was not yet invoked.\")}),spyApi.callArgWith=spyApi.callArg,delegateToCalls(\"callArgOn\",!1,\"callArgOnWith\",function(){throw new Error(this.toString()+\" cannot call arg since it was not yet invoked.\")}),spyApi.callArgOnWith=spyApi.callArgOn,delegateToCalls(\"yield\",!1,\"yield\",function(){throw new Error(this.toString()+\" cannot yield since it was not yet invoked.\")}),spyApi.invokeCallback=spyApi[\"yield\"],delegateToCalls(\"yieldOn\",!1,\"yieldOn\",function(){throw new Error(this.toString()+\" cannot yield since it was not yet invoked.\")}),delegateToCalls(\"yieldTo\",!1,\"yieldTo\",function(e){throw new Error(this.toString()+\" cannot yield to '\"+e+\"' since it was not yet invoked.\")}),delegateToCalls(\"yieldToOn\",!1,\"yieldToOn\",function(e){throw new Error(this.toString()+\" cannot yield to '\"+e+\"' since it was not yet invoked.\")}),spyApi.formatters={c:function(e){return sinon.timesInWords(e.callCount)},n:function(e){return e.toString()},C:function(e){for(var t=[],n=0,r=e.callCount;r>n;++n){var o=\"    \"+e.getCall(n).toString();/\\n/.test(t[n-1])&&(o=\"\\n\"+o),push.call(t,o)}return t.length>0?\"\\n\"+t.join(\"\\n\"):\"\"},t:function(e){for(var t=[],n=0,r=e.callCount;r>n;++n)push.call(t,sinon.format(e.thisValues[n]));return t.join(\", \")},\"*\":function(e,t){for(var n=[],r=0,o=t.length;o>r;++r)push.call(n,sinon.format(t[r]));return n.join(\", \")}},sinon.extend(spy,spyApi),spy.spyCall=sinon.spyCall,sinon.spy=spy,spy}function loadDependencies(e,t,n){var r=e(\"./util/core\");e(\"./call\"),e(\"./extend\"),e(\"./times_in_words\"),e(\"./format\"),n.exports=makeApi(r)}var isNode=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,isAMD=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return isAMD?void define(loadDependencies):isNode?void loadDependencies(require,module.exports,module):void(sinonGlobal&&makeApi(sinonGlobal))}(\"object\"==typeof sinon&&sinon),function(e){function t(e,t){return\"string\"==typeof e?(this.exception=new Error(t||\"\"),this.exception.name=e):e?this.exception=e:this.exception=new Error(\"Error\"),this}function n(e,t){var n=e.callArgAt;if(n>=0)return t[n];var r;n===a&&(r=t),n===u&&(r=i.call(t).reverse());for(var o=e.callArgProp,s=0,l=r.length;l>s;++s){if(!o&&\"function\"==typeof r[s])return r[s];if(o&&r[s]&&\"function\"==typeof r[s][o])return r[s][o]}return null}function r(e){function r(t,n,r){if(t.callArgAt<0){var o;return o=t.callArgProp?e.functionName(t.stub)+\" expected to yield to '\"+t.callArgProp+\"', but no object with such a property was passed.\":e.functionName(t.stub)+\" expected to yield, but no callback was passed.\",r.length>0&&(o+=\" Received [\"+s.call(r,\", \")+\"]\"),o}return\"argument at index \"+t.callArgAt+\" is not a function: \"+n}function o(e,t){if(\"number\"==typeof e.callArgAt){var o=n(e,t);if(\"function\"!=typeof o)throw new TypeError(r(e,o,t));e.callbackAsync?l(function(){o.apply(e.callbackContext,e.callbackArguments)}):o.apply(e.callbackContext,e.callbackArguments)}}function c(e){return function(){var t=this[e].apply(this,arguments);return this.callbackAsync=!0,t}}var f={create:function(t){var n=e.extend({},e.behavior);return delete n.create,n.stub=t,n},isPresent:function(){return\"number\"==typeof this.callArgAt||this.exception||\"number\"==typeof this.returnArgAt||this.returnThis||this.returnValueDefined},invoke:function(e,t){if(o(this,t),this.exception)throw this.exception;return\"number\"==typeof this.returnArgAt?t[this.returnArgAt]:this.returnThis?e:this.returnValue},onCall:function(e){return this.stub.onCall(e)},onFirstCall:function(){return this.stub.onFirstCall()},onSecondCall:function(){return this.stub.onSecondCall()},onThirdCall:function(){return this.stub.onThirdCall()},withArgs:function(){throw new Error('Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" is not supported. Use \"stub.withArgs(...).onCall(...)\" to define sequential behavior for calls with certain arguments.')},callsArg:function(e){if(\"number\"!=typeof e)throw new TypeError(\"argument index is not number\");return this.callArgAt=e,this.callbackArguments=[],this.callbackContext=void 0,this.callArgProp=void 0,this.callbackAsync=!1,this},callsArgOn:function(e,t){if(\"number\"!=typeof e)throw new TypeError(\"argument index is not number\");if(\"object\"!=typeof t)throw new TypeError(\"argument context is not an object\");return this.callArgAt=e,this.callbackArguments=[],this.callbackContext=t,this.callArgProp=void 0,this.callbackAsync=!1,this},callsArgWith:function(e){if(\"number\"!=typeof e)throw new TypeError(\"argument index is not number\");return this.callArgAt=e,this.callbackArguments=i.call(arguments,1),this.callbackContext=void 0,this.callArgProp=void 0,this.callbackAsync=!1,this},callsArgOnWith:function(e,t){if(\"number\"!=typeof e)throw new TypeError(\"argument index is not number\");if(\"object\"!=typeof t)throw new TypeError(\"argument context is not an object\");return this.callArgAt=e,this.callbackArguments=i.call(arguments,2),this.callbackContext=t,this.callArgProp=void 0,this.callbackAsync=!1,this},yields:function(){return this.callArgAt=a,this.callbackArguments=i.call(arguments,0),this.callbackContext=void 0,this.callArgProp=void 0,this.callbackAsync=!1,this},yieldsRight:function(){return this.callArgAt=u,this.callbackArguments=i.call(arguments,0),this.callbackContext=void 0,this.callArgProp=void 0,this.callbackAsync=!1,this},yieldsOn:function(e){if(\"object\"!=typeof e)throw new TypeError(\"argument context is not an object\");return this.callArgAt=a,this.callbackArguments=i.call(arguments,1),this.callbackContext=e,this.callArgProp=void 0,this.callbackAsync=!1,this},yieldsTo:function(e){return this.callArgAt=a,this.callbackArguments=i.call(arguments,1),this.callbackContext=void 0,this.callArgProp=e,this.callbackAsync=!1,this},yieldsToOn:function(e,t){if(\"object\"!=typeof t)throw new TypeError(\"argument context is not an object\");return this.callArgAt=a,this.callbackArguments=i.call(arguments,2),this.callbackContext=t,this.callArgProp=e,this.callbackAsync=!1,this},\"throws\":t,throwsException:t,returns:function(e){return this.returnValue=e,this.returnValueDefined=!0,this.exception=void 0,this},returnsArg:function(e){if(\"number\"!=typeof e)throw new TypeError(\"argument index is not number\");return this.returnArgAt=e,this},returnsThis:function(){return this.returnThis=!0,this}};for(var d in f)f.hasOwnProperty(d)&&d.match(/^(callsArg|yields)/)&&!d.match(/Async/)&&(f[d+\"Async\"]=c(d));return e.behavior=f,f}function o(e,t,n){var o=e(\"./util/core\");e(\"./extend\"),n.exports=r(o)}var i=Array.prototype.slice,s=Array.prototype.join,a=-1,u=-2,l=function(){return\"object\"==typeof process&&\"function\"==typeof process.nextTick?process.nextTick:\"function\"==typeof setImmediate?setImmediate:function(e){setTimeout(e,0)}}(),c=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,f=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return f?void define(o):c?void o(require,module.exports,module):void(e&&r(e))}(\"object\"==typeof sinon&&sinon),function(e){function t(e){function t(n,r,o){if(o&&\"function\"!=typeof o&&\"object\"!=typeof o)throw new TypeError(\"Custom stub should be a function or a property descriptor\");var i,s;if(o){if(\"function\"==typeof o)i=e.spy&&e.spy.create?e.spy.create(o):o;else if(i=o,e.spy&&e.spy.create)for(var a=e.objectKeys(i),u=0;u<a.length;u++)i[a[u]]=e.spy.create(i[a[u]])}else{var l=0;\"object\"==typeof n&&\"function\"==typeof n[r]&&(l=n[r].length),i=t.create(l)}if(!n&&\"undefined\"==typeof r)return e.stub.create();if(\"undefined\"==typeof r&&\"object\"==typeof n){for(s in n)\"function\"==typeof e.getPropertyDescriptor(n,s).value&&t(n,s);return n}return e.wrapMethod(n,r,i)}function n(e){return e.parent&&o(e.parent)}function r(t){return t.defaultBehavior||n(t)||e.behavior.create(t)}function o(e){var t=e.behaviors[e.callCount-1];return t&&t.isPresent()?t:r(e)}function i(t){return function(){return this.defaultBehavior=this.defaultBehavior||e.behavior.create(this),this.defaultBehavior[t].apply(this.defaultBehavior,arguments),this}}var s=0,a={create:function(n){var r=function(){return o(r).invoke(this,arguments)};r.id=\"stub#\"+s++;var i=r;return r=e.spy.create(r,n),r.func=i,e.extend(r,t),r.instantiateFake=e.stub.create,r.displayName=\"stub\",r.toString=e.functionToString,r.defaultBehavior=null,r.behaviors=[],r},resetBehavior:function(){var e;if(this.defaultBehavior=null,this.behaviors=[],delete this.returnValue,delete this.returnArgAt,this.returnThis=!1,this.fakes)for(e=0;e<this.fakes.length;e++)this.fakes[e].resetBehavior()},onCall:function(t){return this.behaviors[t]||(this.behaviors[t]=e.behavior.create(this)),this.behaviors[t]},onFirstCall:function(){return this.onCall(0)},onSecondCall:function(){return this.onCall(1)},onThirdCall:function(){return this.onCall(2)}};for(var u in e.behavior)e.behavior.hasOwnProperty(u)&&!a.hasOwnProperty(u)&&\"create\"!==u&&\"withArgs\"!==u&&\"invoke\"!==u&&(a[u]=i(u));return e.extend(t,a),e.stub=t,t}function n(e,n,r){var o=e(\"./util/core\");e(\"./behavior\"),e(\"./spy\"),e(\"./extend\"),r.exports=t(o)}var r=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,o=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return o?void define(n):r?void n(require,module.exports,module):void(e&&t(e))}(\"object\"==typeof sinon&&sinon),function(e){function t(e){function t(n){return n?t.create(n):e.expectation.create(\"Anonymous mock\")}function n(e,t){if(e)for(var n=0,r=e.length;r>n;n+=1)t(e[n])}function r(t,n,r){if(r&&t.length!==n.length)return!1;for(var o=0,i=t.length;i>o;o++)if(!e.deepEqual(t[o],n[o]))return!1;return!0}function o(e){return 0===e?\"never called\":\"called \"+f(e)}function i(e){var t=e.minCalls,n=e.maxCalls;if(\"number\"==typeof t&&\"number\"==typeof n){var r=f(t);return t!==n&&(r=\"at least \"+r+\" and at most \"+f(n)),r}return\"number\"==typeof t?\"at least \"+f(t):\"at most \"+f(n)}function s(e){var t=\"number\"==typeof e.minCalls;return!t||e.callCount>=e.minCalls}function a(e){return\"number\"!=typeof e.maxCalls?!1:e.callCount===e.maxCalls}function u(e,t){var n=c&&c.isMatcher(e);return n&&e.test(t)||!0}var l=[].push,c=e.match;e.extend(t,{create:function(n){if(!n)throw new TypeError(\"object is null\");var r=e.extend({},t);return r.object=n,delete r.create,r},expects:function(t){if(!t)throw new TypeError(\"method is falsy\");if(this.expectations||(this.expectations={},this.proxies=[]),!this.expectations[t]){this.expectations[t]=[];var n=this;e.wrapMethod(this.object,t,function(){return n.invokeMethod(t,this,arguments)}),l.call(this.proxies,t)}var r=e.expectation.create(t);return l.call(this.expectations[t],r),r},restore:function(){var e=this.object;n(this.proxies,function(t){\"function\"==typeof e[t].restore&&e[t].restore()})},verify:function(){var t=this.expectations||{},r=[],o=[];return n(this.proxies,function(e){n(t[e],function(e){e.met()?l.call(o,e.toString()):l.call(r,e.toString())})}),this.restore(),r.length>0?e.expectation.fail(r.concat(o).join(\"\\n\")):o.length>0&&e.expectation.pass(r.concat(o).join(\"\\n\")),!0},invokeMethod:function(t,n,o){var i,s,a=this.expectations&&this.expectations[t]?this.expectations[t]:[],u=[],c=o||[];for(i=0;i<a.length;i+=1){var f=a[i].expectedArguments||[];r(f,c,a[i].expectsExactArgCount)&&u.push(a[i])}for(i=0;i<u.length;i+=1)if(!u[i].met()&&u[i].allowsCall(n,o))return u[i].apply(n,o);var d=[],h=0;for(i=0;i<u.length;i+=1)u[i].allowsCall(n,o)?s=s||u[i]:h+=1;if(s&&0===h)return s.apply(n,o);for(i=0;i<a.length;i+=1)l.call(d,\"    \"+a[i].toString());d.unshift(\"Unexpected call: \"+e.spyCall.toString.call({proxy:t,args:o})),e.expectation.fail(d.join(\"\\n\"))}});var f=e.timesInWords,d=Array.prototype.slice;return e.expectation={minCalls:1,maxCalls:1,create:function(t){var n=e.extend(e.stub.create(),e.expectation);return delete n.create,n.method=t,n},invoke:function(t,n,r){return this.verifyCallAllowed(n,r),e.spy.invoke.apply(this,arguments)},atLeast:function(e){if(\"number\"!=typeof e)throw new TypeError(\"'\"+e+\"' is not number\");return this.limitsSet||(this.maxCalls=null,this.limitsSet=!0),this.minCalls=e,this},atMost:function(e){if(\"number\"!=typeof e)throw new TypeError(\"'\"+e+\"' is not number\");return this.limitsSet||(this.minCalls=null,this.limitsSet=!0),this.maxCalls=e,this},never:function(){return this.exactly(0)},once:function(){return this.exactly(1)},twice:function(){return this.exactly(2)},thrice:function(){return this.exactly(3)},exactly:function(e){if(\"number\"!=typeof e)throw new TypeError(\"'\"+e+\"' is not a number\");return this.atLeast(e),this.atMost(e)},met:function(){return!this.failed&&s(this)},verifyCallAllowed:function(t,n){if(a(this)&&(this.failed=!0,e.expectation.fail(this.method+\" already called \"+f(this.maxCalls))),\"expectedThis\"in this&&this.expectedThis!==t&&e.expectation.fail(this.method+\" called with \"+t+\" as thisValue, expected \"+this.expectedThis),\"expectedArguments\"in this){n||e.expectation.fail(this.method+\" received no arguments, expected \"+e.format(this.expectedArguments)),n.length<this.expectedArguments.length&&e.expectation.fail(this.method+\" received too few arguments (\"+e.format(n)+\"), expected \"+e.format(this.expectedArguments)),this.expectsExactArgCount&&n.length!==this.expectedArguments.length&&e.expectation.fail(this.method+\" received too many arguments (\"+e.format(n)+\"), expected \"+e.format(this.expectedArguments));for(var r=0,o=this.expectedArguments.length;o>r;r+=1)u(this.expectedArguments[r],n[r])||e.expectation.fail(this.method+\" received wrong arguments \"+e.format(n)+\", didn't match \"+this.expectedArguments.toString()),e.deepEqual(this.expectedArguments[r],n[r])||e.expectation.fail(this.method+\" received wrong arguments \"+e.format(n)+\", expected \"+e.format(this.expectedArguments))}},allowsCall:function(t,n){if(this.met()&&a(this))return!1;if(\"expectedThis\"in this&&this.expectedThis!==t)return!1;if(!(\"expectedArguments\"in this))return!0;if(n=n||[],n.length<this.expectedArguments.length)return!1;if(this.expectsExactArgCount&&n.length!==this.expectedArguments.length)return!1;for(var r=0,o=this.expectedArguments.length;o>r;r+=1){if(!u(this.expectedArguments[r],n[r]))return!1;if(!e.deepEqual(this.expectedArguments[r],n[r]))return!1}return!0},withArgs:function(){return this.expectedArguments=d.call(arguments),this},withExactArgs:function(){return this.withArgs.apply(this,arguments),this.expectsExactArgCount=!0,this},on:function(e){return this.expectedThis=e,this},toString:function(){var t=(this.expectedArguments||[]).slice();this.expectsExactArgCount||l.call(t,\"[...]\");var n=e.spyCall.toString.call({proxy:this.method||\"anonymous mock expectation\",args:t}),r=n.replace(\", [...\",\"[, ...\")+\" \"+i(this);return this.met()?\"Expectation met: \"+r:\"Expected \"+r+\" (\"+o(this.callCount)+\")\"},verify:function(){return this.met()?e.expectation.pass(this.toString()):e.expectation.fail(this.toString()),!0},pass:function(t){e.assert.pass(t)},fail:function(e){var t=new Error(e);throw t.name=\"ExpectationError\",t}},e.mock=t,t}function n(e,n,r){var o=e(\"./util/core\");e(\"./times_in_words\"),e(\"./call\"),e(\"./extend\"),e(\"./match\"),e(\"./spy\"),e(\"./stub\"),e(\"./format\"),r.exports=t(o)}var r=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,o=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return o?void define(n):r?void n(require,module.exports,module):void(e&&t(e))}(\"object\"==typeof sinon&&sinon),function(e){function t(e){return e.fakes||(e.fakes=[]),e.fakes}function n(e,n){for(var r=t(e),o=0,i=r.length;i>o;o+=1)\"function\"==typeof r[o][n]&&r[o][n]()}function r(e){for(var n=t(e),r=0;r<n.length;)n.splice(r,1)}function o(e){var o={verify:function(){n(this,\"verify\")},restore:function(){n(this,\"restore\"),r(this)},reset:function(){n(this,\"reset\")},verifyAndRestore:function(){var e;try{this.verify()}catch(t){e=t}if(this.restore(),e)throw e},add:function(e){return s.call(t(this),e),e},spy:function(){return this.add(e.spy.apply(e,arguments))},stub:function(t,n,r){if(n){var o=t[n];if(\"function\"!=typeof o){if(!a.call(t,n))throw new TypeError(\"Cannot stub non-existent own property \"+n);return t[n]=r,this.add({restore:function(){t[n]=o}})}}if(!n&&t&&\"object\"==typeof t){var i=e.stub.apply(e,arguments);for(var s in i)\"function\"==typeof i[s]&&this.add(i[s]);return i}return this.add(e.stub.apply(e,arguments))},mock:function(){return this.add(e.mock.apply(e,arguments))},inject:function(e){var t=this;return e.spy=function(){return t.spy.apply(t,arguments)},e.stub=function(){return t.stub.apply(t,arguments)},e.mock=function(){return t.mock.apply(t,arguments)},e}};return e.collection=o,o}function i(e,t,n){var r=e(\"./util/core\");e(\"./mock\"),e(\"./spy\"),e(\"./stub\"),n.exports=o(r)}var s=[].push,a=Object.prototype.hasOwnProperty,u=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,l=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return l?void define(i):u?void i(require,module.exports,module):void(e&&o(e))}(\"object\"==typeof sinon&&sinon),function(){function e(e,t){var n=\"undefined\"!=typeof lolex?lolex:t;e.useFakeTimers=function(){var e,t=Array.prototype.slice.call(arguments);e=\"string\"==typeof t[0]?0:t.shift();var r=n.install(e||0,t);return r.restore=r.uninstall,r},e.clock={create:function(e){return n.createClock(e)}},e.timers={setTimeout:setTimeout,clearTimeout:clearTimeout,setImmediate:\"undefined\"!=typeof setImmediate?setImmediate:void 0,clearImmediate:\"undefined\"!=typeof clearImmediate?clearImmediate:void 0,setInterval:setInterval,clearInterval:clearInterval,Date:Date}}function t(t,n,r,o){var i=t(\"./core\");e(i,o),r.exports=i}var n=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,r=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;r?define(t):n?t(require,module.exports,module,require(\"lolex\")):e(sinon)}(),\"undefined\"==typeof sinon&&(this.sinon={}),function(){function e(e){e.Event=function(e,t,n,r){this.initEvent(e,t,n,r)},e.Event.prototype={initEvent:function(e,t,n,r){this.type=e,this.bubbles=t,this.cancelable=n,this.target=r},stopPropagation:function(){},preventDefault:function(){this.defaultPrevented=!0}},e.ProgressEvent=function(e,t,n){this.initEvent(e,!1,!1,n),this.loaded=t.loaded||null,this.total=t.total||null,this.lengthComputable=!!t.total},e.ProgressEvent.prototype=new e.Event,e.ProgressEvent.prototype.constructor=e.ProgressEvent,e.CustomEvent=function(e,t,n){this.initEvent(e,!1,!1,n),this.detail=t.detail||null},e.CustomEvent.prototype=new e.Event,e.CustomEvent.prototype.constructor=e.CustomEvent,e.EventTarget={addEventListener:function(e,t){this.eventListeners=this.eventListeners||{},this.eventListeners[e]=this.eventListeners[e]||[],n.call(this.eventListeners[e],t)},removeEventListener:function(e,t){for(var n=this.eventListeners&&this.eventListeners[e]||[],r=0,o=n.length;o>r;++r)if(n[r]===t)return n.splice(r,1)},dispatchEvent:function(e){for(var t=e.type,n=this.eventListeners&&this.eventListeners[t]||[],r=0;r<n.length;r++)\"function\"==typeof n[r]?n[r].call(this,e):n[r].handleEvent(e);return!!e.defaultPrevented}}}function t(t){var n=t(\"./core\");e(n)}var n=[].push,r=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,o=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;o?define(t):r?t(require):e(sinon)}(),function(e){function t(e){function t(){}function n(t,r){var o=t+\" threw exception: \";e.log(o+\"[\"+r.name+\"] \"+r.message),r.stack&&e.log(r.stack),n.setTimeout(function(){throw r.message=o+r.message,r},0)}n.setTimeout=function(e,t){r(e,t)};var o={};return o.log=e.log=t,o.logError=e.logError=n,o}function n(e,n,r){var o=e(\"./util/core\");r.exports=t(o)}var r=setTimeout,o=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,i=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return i?void define(n):o?void n(require,module.exports,module):void(e&&t(e))}(\"object\"==typeof sinon&&sinon),\"undefined\"==typeof sinon&&(this.sinon={}),function(e){function t(t){function n(){this.readyState=n.UNSENT,this.requestBody=null,this.requestHeaders={},this.status=0,this.timeout=null,\"function\"==typeof n.onCreate&&n.onCreate(this)}function o(e){if(e.readyState!==n.OPENED)throw new Error(\"INVALID_STATE_ERR\");if(e.sendFlag)throw new Error(\"INVALID_STATE_ERR\")}function i(e){if(e.readyState===n.UNSENT)throw new Error(\"Request not sent\");if(e.readyState===n.DONE)throw new Error(\"Request done\")}function s(e){if(\"string\"!=typeof e){var t=new Error(\"Attempted to respond to fake XDomainRequest with \"+e+\", which is not a string.\");throw t.name=\"InvalidBodyException\",t}}t.xdr=r,t.extend(n.prototype,t.EventTarget,{open:function(e,t){this.method=e,this.url=t,this.responseText=null,this.sendFlag=!1,this.readyStateChange(n.OPENED)},readyStateChange:function(e){this.readyState=e;var r=\"\";switch(this.readyState){case n.UNSENT:break;case n.OPENED:break;case n.LOADING:this.sendFlag&&(r=\"onprogress\");break;case n.DONE:r=this.isTimeout?\"ontimeout\":this.errorFlag||this.status<200||this.status>299?\"onerror\":\"onload\"}if(r&&\"function\"==typeof this[r])try{this[r]()}catch(o){t.logError(\"Fake XHR \"+r+\" handler\",o)}},send:function(e){o(this),/^(get|head)$/i.test(this.method)||(this.requestBody=e),this.requestHeaders[\"Content-Type\"]=\"text/plain;charset=utf-8\",this.errorFlag=!1,this.sendFlag=!0,this.readyStateChange(n.OPENED),\"function\"==typeof this.onSend&&this.onSend(this)},abort:function(){this.aborted=!0,this.responseText=null,this.errorFlag=!0,this.readyState>t.FakeXDomainRequest.UNSENT&&this.sendFlag&&(this.readyStateChange(t.FakeXDomainRequest.DONE),this.sendFlag=!1)},setResponseBody:function(e){i(this),s(e);var t=this.chunkSize||10,r=0;this.responseText=\"\";do this.readyStateChange(n.LOADING),this.responseText+=e.substring(r,r+t),r+=t;while(r<e.length);this.readyStateChange(n.DONE)},respond:function(e,t,n){this.status=\"number\"==typeof e?e:200,this.setResponseBody(n||\"\")},simulatetimeout:function(){this.status=0,this.isTimeout=!0,this.responseText=void 0,this.readyStateChange(n.DONE)}}),t.extend(n,{UNSENT:0,OPENED:1,LOADING:3,DONE:4}),t.useFakeXDomainRequest=function(){return t.FakeXDomainRequest.restore=function(n){r.supportsXDR&&(e.XDomainRequest=r.GlobalXDomainRequest),delete t.FakeXDomainRequest.restore,n!==!0&&delete t.FakeXDomainRequest.onCreate},r.supportsXDR&&(e.XDomainRequest=t.FakeXDomainRequest),t.FakeXDomainRequest},t.FakeXDomainRequest=n}function n(e,n,r){var o=e(\"./core\");e(\"../extend\"),e(\"./event\"),e(\"../log_error\"),t(o),r.exports=o}var r={XDomainRequest:e.XDomainRequest};r.GlobalXDomainRequest=e.XDomainRequest,r.supportsXDR=\"undefined\"!=typeof r.GlobalXDomainRequest,r.workingXDR=r.supportsXDR?r.GlobalXDomainRequest:!1;var o=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,i=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;i?define(n):o?n(require,module.exports,module):t(sinon)}(\"undefined\"!=typeof global?global:self),function(e,t){function n(e){var t=\"undefined\"!=typeof e.XMLHttpRequest;if(t)return e.XMLHttpRequest;var n=\"undefined\"!=typeof e.ActiveXObject;return n?function(){return new e.ActiveXObject(\"MSXML2.XMLHTTP.3.0\")}:!1}function r(){this.eventListeners={progress:[],load:[],abort:[],error:[]}}function o(){function e(e){t.addEventListener(e,function(n){var r=t[\"on\"+e];r&&\"function\"==typeof r&&r.call(this,n)})}this.readyState=o.UNSENT,this.requestHeaders={},this.requestBody=null,this.status=0,this.statusText=\"\",this.upload=new r,this.responseType=\"\",this.response=\"\",g.supportsCORS&&(this.withCredentials=!1);for(var t=this,n=[\"loadstart\",\"load\",\"abort\",\"loadend\"],i=n.length-1;i>=0;i--)e(n[i]);\"function\"==typeof o.onCreate&&o.onCreate(this)}function i(e){if(e.readyState!==o.OPENED)throw new Error(\"INVALID_STATE_ERR\");if(e.sendFlag)throw new Error(\"INVALID_STATE_ERR\")}function s(e,t){t=t.toLowerCase();for(var n in e)if(n.toLowerCase()===t)return n;return null}function a(e,t){if(e)for(var n=0,r=e.length;r>n;n+=1)t(e[n])}function u(e,t){for(var n=0;n<e.length;n++)if(t(e[n])===!0)return!0;return!1}function l(e){if(e.readyState!==o.OPENED)throw new Error(\"INVALID_STATE_ERR - \"+e.readyState)}function c(e){if(e.readyState===o.DONE)throw new Error(\"Request done\")}function f(e){if(e.async&&e.readyState!==o.HEADERS_RECEIVED)throw new Error(\"No headers received\")}function d(e){if(\"string\"!=typeof e){var t=new Error(\"Attempted to respond to fake XMLHttpRequest with \"+e+\", which is not a string.\");throw t.name=\"InvalidBodyException\",t}}function h(e){e.xhr=g,e.extend(o.prototype,e.EventTarget,{async:!0,open:function(e,t,n,r,i){if(this.method=e,this.url=t,this.async=\"boolean\"==typeof n?n:!0,this.username=r,this.password=i,this.responseText=null,this.response=\"json\"===this.responseType?null:\"\",this.responseXML=null,this.requestHeaders={},this.sendFlag=!1,o.useFilters===!0){var s=arguments,a=u(o.filters,function(e){return e.apply(this,s)});if(a)return o.defake(this,arguments)}this.readyStateChange(o.OPENED)},readyStateChange:function(t){this.readyState=t;var n=new e.Event(\"readystatechange\",!1,!1,this);if(\"function\"==typeof this.onreadystatechange)try{this.onreadystatechange(n)}catch(r){e.logError(\"Fake XHR onreadystatechange handler\",r)}switch(this.readyState){case o.DONE:y&&(this.upload.dispatchEvent(new e.ProgressEvent(\"progress\",{loaded:100,total:100})),this.dispatchEvent(new e.ProgressEvent(\"progress\",{loaded:100,total:100}))),this.upload.dispatchEvent(new e.Event(\"load\",!1,!1,this)),this.dispatchEvent(new e.Event(\"load\",!1,!1,this)),this.dispatchEvent(new e.Event(\"loadend\",!1,!1,this))}this.dispatchEvent(n)},setRequestHeader:function(e,t){if(i(this),b[e]||/^(Sec-|Proxy-)/.test(e))throw new Error('Refused to set unsafe header \"'+e+'\"');this.requestHeaders[e]?this.requestHeaders[e]+=\",\"+t:this.requestHeaders[e]=t},setResponseHeaders:function(e){l(this),this.responseHeaders={};for(var t in e)e.hasOwnProperty(t)&&(this.responseHeaders[t]=e[t]);this.async?this.readyStateChange(o.HEADERS_RECEIVED):this.readyState=o.HEADERS_RECEIVED},send:function(t){if(i(this),!/^(get|head)$/i.test(this.method)){var n=s(this.requestHeaders,\"Content-Type\");if(this.requestHeaders[n]){var r=this.requestHeaders[n].split(\";\");this.requestHeaders[n]=r[0]+\";charset=utf-8\"}else!v||t instanceof FormData||(this.requestHeaders[\"Content-Type\"]=\"text/plain;charset=utf-8\");this.requestBody=t}this.errorFlag=!1,this.sendFlag=this.async,this.response=\"json\"===this.responseType?null:\"\",this.readyStateChange(o.OPENED),\"function\"==typeof this.onSend&&this.onSend(this),this.dispatchEvent(new e.Event(\"loadstart\",!1,!1,this))},abort:function(){this.aborted=!0,this.responseText=null,this.response=\"json\"===this.responseType?null:\"\",this.errorFlag=!0,this.requestHeaders={},this.responseHeaders={},this.readyState>o.UNSENT&&this.sendFlag&&(this.readyStateChange(o.DONE),this.sendFlag=!1),this.readyState=o.UNSENT,this.dispatchEvent(new e.Event(\"abort\",!1,!1,this)),this.upload.dispatchEvent(new e.Event(\"abort\",!1,!1,this)),\"function\"==typeof this.onerror&&this.onerror()},getResponseHeader:function(e){return this.readyState<o.HEADERS_RECEIVED?null:/^Set-Cookie2?$/i.test(e)?null:(e=s(this.responseHeaders,e),this.responseHeaders[e]||null)},getAllResponseHeaders:function(){if(this.readyState<o.HEADERS_RECEIVED)return\"\";var e=\"\";for(var t in this.responseHeaders)this.responseHeaders.hasOwnProperty(t)&&!/^Set-Cookie2?$/i.test(t)&&(e+=t+\": \"+this.responseHeaders[t]+\"\\r\\n\");return e},setResponseBody:function(e){c(this),f(this),d(e);var t=this.chunkSize||10,n=0;this.responseText=\"\";do this.async&&this.readyStateChange(o.LOADING),this.responseText+=e.substring(n,n+t),n+=t;while(n<e.length);var r=this.getResponseHeader(\"Content-Type\");if(this.responseText&&(!r||/(text\\/xml)|(application\\/xml)|(\\+xml)/.test(r)))try{this.responseXML=o.parseXML(this.responseText)}catch(i){}this.response=\"json\"===this.responseType?JSON.parse(this.responseText):this.responseText,this.readyStateChange(o.DONE)},respond:function(e,t,n){this.status=\"number\"==typeof e?e:200,this.statusText=o.statusCodes[this.status],this.setResponseHeaders(t||{}),this.setResponseBody(n||\"\")},uploadProgress:function(t){y&&this.upload.dispatchEvent(new e.ProgressEvent(\"progress\",t))},downloadProgress:function(t){y&&this.dispatchEvent(new e.ProgressEvent(\"progress\",t))},uploadError:function(t){m&&this.upload.dispatchEvent(new e.CustomEvent(\"error\",{detail:t}))}}),e.extend(o,{UNSENT:0,OPENED:1,HEADERS_RECEIVED:2,LOADING:3,DONE:4}),e.useFakeXMLHttpRequest=function(){return o.restore=function(e){g.supportsXHR&&(t.XMLHttpRequest=g.GlobalXMLHttpRequest),g.supportsActiveX&&(t.ActiveXObject=g.GlobalActiveXObject),delete o.restore,e!==!0&&delete o.onCreate},g.supportsXHR&&(t.XMLHttpRequest=o),g.supportsActiveX&&(t.ActiveXObject=function(e){return\"Microsoft.XMLHTTP\"===e||/^Msxml2\\.XMLHTTP/i.test(e)?new o:new g.GlobalActiveXObject(e)}),o},e.FakeXMLHttpRequest=o}function p(e,t,n){var r=e(\"./core\");e(\"../extend\"),e(\"./event\"),e(\"../log_error\"),h(r),n.exports=r}var y=\"undefined\"!=typeof ProgressEvent,m=\"undefined\"!=typeof CustomEvent,v=\"undefined\"!=typeof FormData,g={XMLHttpRequest:t.XMLHttpRequest};g.GlobalXMLHttpRequest=t.XMLHttpRequest,g.GlobalActiveXObject=t.ActiveXObject,g.supportsActiveX=\"undefined\"!=typeof g.GlobalActiveXObject,g.supportsXHR=\"undefined\"!=typeof g.GlobalXMLHttpRequest,g.workingXHR=n(t),g.supportsCORS=g.supportsXHR&&\"withCredentials\"in new g.GlobalXMLHttpRequest;var b={\"Accept-Charset\":!0,\"Accept-Encoding\":!0,Connection:!0,\"Content-Length\":!0,Cookie:!0,Cookie2:!0,\"Content-Transfer-Encoding\":!0,Date:!0,Expect:!0,Host:!0,\"Keep-Alive\":!0,Referer:!0,TE:!0,Trailer:!0,\"Transfer-Encoding\":!0,Upgrade:!0,\"User-Agent\":!0,Via:!0};r.prototype.addEventListener=function(e,t){this.eventListeners[e].push(t)},r.prototype.removeEventListener=function(e,t){for(var n=this.eventListeners[e]||[],r=0,o=n.length;o>r;++r)if(n[r]===t)return n.splice(r,1)},r.prototype.dispatchEvent=function(e){for(var t,n=this.eventListeners[e.type]||[],r=0;null!=(t=n[r]);r++)t(e)};var w=function(e,t,n){switch(n.length){case 0:return e[t]();case 1:return e[t](n[0]);case 2:return e[t](n[0],n[1]);case 3:return e[t](n[0],n[1],n[2]);case 4:return e[t](n[0],n[1],n[2],n[3]);case 5:return e[t](n[0],n[1],n[2],n[3],n[4])}};o.filters=[],o.addFilter=function(e){this.filters.push(e)};var x=/MSIE 6/;o.defake=function(e,t){var n=new g.workingXHR;a([\"open\",\"setRequestHeader\",\"send\",\"abort\",\"getResponseHeader\",\"getAllResponseHeaders\",\"addEventListener\",\"overrideMimeType\",\"removeEventListener\"],function(t){e[t]=function(){return w(n,t,arguments)}});var r=function(t){a(t,function(t){try{e[t]=n[t]}catch(r){if(!x.test(navigator.userAgent))throw r}})},i=function(){e.readyState=n.readyState,n.readyState>=o.HEADERS_RECEIVED&&r([\"status\",\"statusText\"]),n.readyState>=o.LOADING&&r([\"responseText\",\"response\"]),n.readyState===o.DONE&&r([\"responseXML\"]),e.onreadystatechange&&e.onreadystatechange.call(e,{target:e})};if(n.addEventListener){for(var s in e.eventListeners)e.eventListeners.hasOwnProperty(s)&&a(e.eventListeners[s],function(e){n.addEventListener(s,e)});n.addEventListener(\"readystatechange\",i)}else n.onreadystatechange=i;w(n,\"open\",t)},o.useFilters=!1,o.parseXML=function(e){var t;if(\"undefined\"!=typeof DOMParser){var n=new DOMParser;t=n.parseFromString(e,\"text/xml\")}else t=new window.ActiveXObject(\"Microsoft.XMLDOM\"),t.async=\"false\",t.loadXML(e);return t},o.statusCodes={100:\"Continue\",101:\"Switching Protocols\",200:\"OK\",201:\"Created\",\n202:\"Accepted\",203:\"Non-Authoritative Information\",204:\"No Content\",205:\"Reset Content\",206:\"Partial Content\",207:\"Multi-Status\",300:\"Multiple Choice\",301:\"Moved Permanently\",302:\"Found\",303:\"See Other\",304:\"Not Modified\",305:\"Use Proxy\",307:\"Temporary Redirect\",400:\"Bad Request\",401:\"Unauthorized\",402:\"Payment Required\",403:\"Forbidden\",404:\"Not Found\",405:\"Method Not Allowed\",406:\"Not Acceptable\",407:\"Proxy Authentication Required\",408:\"Request Timeout\",409:\"Conflict\",410:\"Gone\",411:\"Length Required\",412:\"Precondition Failed\",413:\"Request Entity Too Large\",414:\"Request-URI Too Long\",415:\"Unsupported Media Type\",416:\"Requested Range Not Satisfiable\",417:\"Expectation Failed\",422:\"Unprocessable Entity\",500:\"Internal Server Error\",501:\"Not Implemented\",502:\"Bad Gateway\",503:\"Service Unavailable\",504:\"Gateway Timeout\",505:\"HTTP Version Not Supported\"};var C=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,E=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return E?void define(p):C?void p(require,module.exports,module):void(e&&h(e))}(\"object\"==typeof sinon&&sinon,\"undefined\"!=typeof global?global:self),function(){function e(e){var t=e;if(\"[object Array]\"!==Object.prototype.toString.call(e)&&(t=[200,{},e]),\"string\"!=typeof t[2])throw new TypeError(\"Fake server response body should be string, but was \"+typeof t[2]);return t}function t(e,t,n){var r=e.method,o=!r||r.toLowerCase()===t.toLowerCase(),i=e.url,s=!i||i===n||\"function\"==typeof i.test&&i.test(n);return o&&s}function n(e,n){var r=n.url;if(/^https?:\\/\\//.test(r)&&!a.test(r)||(r=r.replace(a,\"\")),t(e,this.getHTTPMethod(n),r)){if(\"function\"==typeof e.response){var o=e.url,i=[n].concat(o&&\"function\"==typeof o.exec?o.exec(r).slice(1):[]);return e.response.apply(e,i)}return!0}return!1}function r(t){t.fakeServer={create:function(e){var n=t.create(this);return n.configure(e),t.xhr.supportsCORS?this.xhr=t.useFakeXMLHttpRequest():this.xhr=t.useFakeXDomainRequest(),n.requests=[],this.xhr.onCreate=function(e){n.addRequest(e)},n},configure:function(e){var t,n={autoRespond:!0,autoRespondAfter:!0,respondImmediately:!0,fakeHTTPMethods:!0};e=e||{};for(t in e)n.hasOwnProperty(t)&&e.hasOwnProperty(t)&&(this[t]=e[t])},addRequest:function(e){var t=this;i.call(this.requests,e),e.onSend=function(){t.handleRequest(this),t.respondImmediately?t.respond():t.autoRespond&&!t.responding&&(setTimeout(function(){t.responding=!1,t.respond()},t.autoRespondAfter||10),t.responding=!0)}},getHTTPMethod:function(e){if(this.fakeHTTPMethods&&/post/i.test(e.method)){var t=(e.requestBody||\"\").match(/_method=([^\\b;]+)/);return t?t[1]:e.method}return e.method},handleRequest:function(e){e.async?(this.queue||(this.queue=[]),i.call(this.queue,e)):this.processRequest(e)},log:function(e,n){var r;r=\"Request:\\n\"+t.format(n)+\"\\n\\n\",r+=\"Response:\\n\"+t.format(e)+\"\\n\\n\",t.log(r)},respondWith:function(t,n,r){return 1===arguments.length&&\"function\"!=typeof t?void(this.response=e(t)):(this.responses||(this.responses=[]),1===arguments.length&&(r=t,n=t=null),2===arguments.length&&(r=n,n=t,t=null),void i.call(this.responses,{method:t,url:n,response:\"function\"==typeof r?r:e(r)}))},respond:function(){arguments.length>0&&this.respondWith.apply(this,arguments);for(var e=this.queue||[],t=e.splice(0,e.length),n=0;n<t.length;n++)this.processRequest(t[n])},processRequest:function(e){try{if(e.aborted)return;var r=this.response||[404,{},\"\"];if(this.responses)for(var o=this.responses.length,i=o-1;i>=0;i--)if(n.call(this,this.responses[i],e)){r=this.responses[i].response;break}4!==e.readyState&&(this.log(r,e),e.respond(r[0],r[1],r[2]))}catch(s){t.logError(\"Fake server request processing\",s)}},restore:function(){return this.xhr.restore&&this.xhr.restore.apply(this.xhr,arguments)}}}function o(e,t,n){var o=e(\"./core\");e(\"./fake_xdomain_request\"),e(\"./fake_xml_http_request\"),e(\"../format\"),r(o),n.exports=o}var i=[].push,s=\"undefined\"!=typeof window?window.location:{},a=new RegExp(\"^\"+s.protocol+\"//\"+s.host),u=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,l=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;l?define(o):u?o(require,module.exports,module):r(sinon)}(),function(){function e(e){function t(){}t.prototype=e.fakeServer,e.fakeServerWithClock=new t,e.fakeServerWithClock.addRequest=function(t){if(t.async&&(\"object\"==typeof setTimeout.clock?this.clock=setTimeout.clock:(this.clock=e.useFakeTimers(),this.resetClock=!0),!this.longestTimeout)){var n=this.clock.setTimeout,r=this.clock.setInterval,o=this;this.clock.setTimeout=function(e,t){return o.longestTimeout=Math.max(t,o.longestTimeout||0),n.apply(this,arguments)},this.clock.setInterval=function(e,t){return o.longestTimeout=Math.max(t,o.longestTimeout||0),r.apply(this,arguments)}}return e.fakeServer.addRequest.call(this,t)},e.fakeServerWithClock.respond=function(){var t=e.fakeServer.respond.apply(this,arguments);return this.clock&&(this.clock.tick(this.longestTimeout||0),this.longestTimeout=0,this.resetClock&&(this.clock.restore(),this.resetClock=!1)),t},e.fakeServerWithClock.restore=function(){return this.clock&&this.clock.restore(),e.fakeServer.restore.apply(this,arguments)}}function t(t){var n=t(\"./core\");t(\"./fake_server\"),t(\"./fake_timers\"),e(n)}var n=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,r=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;r?define(t):n?t(require):e(sinon)}(),function(e){function t(e){function t(e,t,n,o){o&&(!t.injectInto||n in t.injectInto?r.call(e.args,o):(t.injectInto[n]=o,e.injectedKeys.push(n)))}function n(t){var n=e.create(e.sandbox);return t.useFakeServer&&(\"object\"==typeof t.useFakeServer&&(n.serverPrototype=t.useFakeServer),n.useFakeServer()),t.useFakeTimers&&(\"object\"==typeof t.useFakeTimers?n.useFakeTimers.apply(n,t.useFakeTimers):n.useFakeTimers()),n}var r=[].push;return e.sandbox=e.extend(e.create(e.collection),{useFakeTimers:function(){return this.clock=e.useFakeTimers.apply(e,arguments),this.add(this.clock)},serverPrototype:e.fakeServer,useFakeServer:function(){var t=this.serverPrototype||e.fakeServer;return t&&t.create?(this.server=t.create(),this.add(this.server)):null},inject:function(t){return e.collection.inject.call(this,t),this.clock&&(t.clock=this.clock),this.server&&(t.server=this.server,t.requests=this.server.requests),t.match=e.match,t},restore:function(){e.collection.restore.apply(this,arguments),this.restoreContext()},restoreContext:function(){if(this.injectedKeys){for(var e=0,t=this.injectedKeys.length;t>e;e++)delete this.injectInto[this.injectedKeys[e]];this.injectedKeys=[]}},create:function(r){if(!r)return e.create(e.sandbox);var o=n(r);o.args=o.args||[],o.injectedKeys=[],o.injectInto=r.injectInto;var i,s,a=o.inject({});if(r.properties)for(var u=0,l=r.properties.length;l>u;u++)i=r.properties[u],s=a[i]||\"sandbox\"===i&&o,t(o,r,i,s);else t(o,r,\"sandbox\",s);return o},match:e.match}),e.sandbox.useFakeXMLHttpRequest=e.sandbox.useFakeServer,e.sandbox}function n(e,n,r){var o=e(\"./util/core\");e(\"./extend\"),e(\"./util/fake_server_with_clock\"),e(\"./util/fake_timers\"),e(\"./collection\"),r.exports=t(o)}var r=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,o=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return o?void define(n):r?void n(require,module.exports,module):void(e&&t(e))}(\"object\"==typeof sinon&&sinon),function(e){function t(e){function t(t){function r(){var r=e.getConfig(e.config);r.injectInto=r.injectIntoThis&&this||r.injectInto;var o,i,s=e.sandbox.create(r),a=n.call(arguments),u=a.length&&a[a.length-1];\"function\"==typeof u&&(a[a.length-1]=function(e){if(e)throw s.restore(),o;s.verifyAndRestore(),u(e)});try{i=t.apply(this,a.concat(s.args))}catch(l){o=l}if(\"function\"!=typeof u){if(\"undefined\"!=typeof o)throw s.restore(),o;s.verifyAndRestore()}return i}var o=typeof t;if(\"function\"!==o)throw new TypeError(\"sinon.test needs to wrap a test function, got \"+o);return t.length?function(e){return r.apply(this,arguments)}:r}var n=Array.prototype.slice;return t.config={injectIntoThis:!0,injectInto:null,properties:[\"spy\",\"stub\",\"mock\",\"clock\",\"server\",\"requests\"],useFakeTimers:!0,useFakeServer:!0},e.test=t,t}function n(e,n,r){var o=e(\"./util/core\");e(\"./sandbox\"),r.exports=t(o)}var r=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,o=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;o?define(n):r?n(require,module.exports,module):e&&t(e)}(\"object\"==typeof sinon&&sinon||null),function(e){function t(e,t,n){return function(){t&&t.apply(this,arguments);var r,o;try{o=e.apply(this,arguments)}catch(i){r=i}if(n&&n.apply(this,arguments),r)throw r;return o}}function n(e){function n(n,r){if(!n||\"object\"!=typeof n)throw new TypeError(\"sinon.testCase needs an object with test functions\");r=r||\"test\";var o,i,s,a=new RegExp(\"^\"+r),u={},l=n.setUp,c=n.tearDown;for(o in n)n.hasOwnProperty(o)&&!/^(setUp|tearDown)$/.test(o)&&(i=n[o],\"function\"==typeof i&&a.test(o)?(s=i,(l||c)&&(s=t(i,l,c)),u[o]=e.test(s)):u[o]=n[o]);return u}return e.testCase=n,n}function r(e,t,r){var o=e(\"./util/core\");e(\"./test\"),r.exports=n(o)}var o=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,i=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return i?void define(r):o?void r(require,module.exports,module):void(e&&n(e))}(\"object\"==typeof sinon&&sinon),function(e,t){function n(e){function n(){for(var e,t=0,r=arguments.length;r>t;++t)e=arguments[t],e||a.fail(\"fake is not a spy\"),e.proxy&&e.proxy.isSinonProxy?n(e.proxy):(\"function\"!=typeof e&&a.fail(e+\" is not a function\"),\"function\"!=typeof e.getCall&&a.fail(e+\" is not stubbed\"))}function r(e,n){e=e||t;var r=e.fail||a.fail;r.call(e,n)}function i(e,t,i){2===arguments.length&&(i=t,t=e),a[e]=function(s){n(s);var u=o.call(arguments,1),l=!1;l=\"function\"==typeof t?!t(s):\"function\"==typeof s[t]?!s[t].apply(s,u):!s[t],l?r(this,(s.printf||s.proxy.printf).apply(s,[i].concat(u))):a.pass(e)}}function s(e,t){return!e||/^fail/.test(t)?t:e+t.slice(0,1).toUpperCase()+t.slice(1)}var a;return a={failException:\"AssertError\",fail:function(e){var t=new Error(e);throw t.name=this.failException||a.failException,t},pass:function(){},callOrder:function(){n.apply(null,arguments);var t=\"\",i=\"\";if(e.calledInOrder(arguments))a.pass(\"callOrder\");else{try{t=[].join.call(arguments,\", \");for(var s=o.call(arguments),u=s.length;u;)s[--u].called||s.splice(u,1);i=e.orderByFirstCall(s).join(\", \")}catch(l){}r(this,\"expected \"+t+\" to be called in order but were called as \"+i)}},callCount:function(t,o){if(n(t),t.callCount!==o){var i=\"expected %n to be called \"+e.timesInWords(o)+\" but was called %c%C\";r(this,t.printf(i))}else a.pass(\"callCount\")},expose:function(e,t){if(!e)throw new TypeError(\"target is null or undefined\");var n=t||{},r=\"undefined\"==typeof n.prefix&&\"assert\"||n.prefix,o=\"undefined\"==typeof n.includeFail||!!n.includeFail;for(var i in this)\"expose\"===i||!o&&/^(fail)/.test(i)||(e[s(r,i)]=this[i]);return e},match:function(t,n){var o=e.match(n);if(o.test(t))a.pass(\"match\");else{var i=[\"expected value to match\",\"    expected = \"+e.format(n),\"    actual = \"+e.format(t)];r(this,i.join(\"\\n\"))}}},i(\"called\",\"expected %n to have been called at least once but was never called\"),i(\"notCalled\",function(e){return!e.called},\"expected %n to not have been called but was called %c%C\"),i(\"calledOnce\",\"expected %n to be called once but was called %c%C\"),i(\"calledTwice\",\"expected %n to be called twice but was called %c%C\"),i(\"calledThrice\",\"expected %n to be called thrice but was called %c%C\"),i(\"calledOn\",\"expected %n to be called with %1 as this but was called with %t\"),i(\"alwaysCalledOn\",\"expected %n to always be called with %1 as this but was called with %t\"),i(\"calledWithNew\",\"expected %n to be called with new\"),i(\"alwaysCalledWithNew\",\"expected %n to always be called with new\"),i(\"calledWith\",\"expected %n to be called with arguments %*%C\"),i(\"calledWithMatch\",\"expected %n to be called with match %*%C\"),i(\"alwaysCalledWith\",\"expected %n to always be called with arguments %*%C\"),i(\"alwaysCalledWithMatch\",\"expected %n to always be called with match %*%C\"),i(\"calledWithExactly\",\"expected %n to be called with exact arguments %*%C\"),i(\"alwaysCalledWithExactly\",\"expected %n to always be called with exact arguments %*%C\"),i(\"neverCalledWith\",\"expected %n to never be called with arguments %*%C\"),i(\"neverCalledWithMatch\",\"expected %n to never be called with match %*%C\"),i(\"threw\",\"%n did not throw exception%C\"),i(\"alwaysThrew\",\"%n did not always throw exception%C\"),e.assert=a,a}function r(e,t,r){var o=e(\"./util/core\");e(\"./match\"),e(\"./format\"),r.exports=n(o)}var o=Array.prototype.slice,i=\"undefined\"!=typeof module&&module.exports&&\"function\"==typeof require,s=\"function\"==typeof define&&\"object\"==typeof define.amd&&define.amd;return s?void define(r):i?void r(require,module.exports,module):void(e&&n(e))}(\"object\"==typeof sinon&&sinon,\"undefined\"!=typeof global?global:self),sinon}),this.sinon});"
  },
  {
    "path": "test/dist/fixtures/touch.html",
    "content": "<html>\n    <head>\n        <meta charset=\"utf-8\">\n        <link href=\"mocha.css\" rel=\"stylesheet\" />\n        <link href=\"../../../dist/touch/bem-core.css\" rel=\"stylesheet\" />\n    </head>\n    <body>\n        <div id=\"mocha\"></div>\n        <script src=\"https://yastatic.net/es5-shims/0.0.2/es5-shims.min.js\"></script>\n        <script src=\"../../../dist/touch/bem-core.js+bemhtml.js\"></script>\n        <script src=\"mocha.js\"></script>\n        <script src=\"sinon.js\"></script>\n        <script src=\"chai.js\"></script>\n        <script src=\"sinon-chai.js\"></script>\n        <script>\n            (function() {\n                modules.define('spec', ['mocha', 'chai', 'sinon', 'sinon-chai'],\n                    function(provide, mocha, chai, sinon, sinonChai) {\n                        mocha.ui('bdd');\n\n                        chai.use(sinonChai);\n                        chai.should();\n\n                        provide();\n                    });\n            }());\n        </script>\n        <script src=\"../../../common.blocks/cookie/cookie.spec.js\"></script>\n        <script src=\"../../../common.blocks/dom/dom.spec.js\"></script>\n        <script src=\"../../../common.blocks/events/events.spec.js\"></script>\n        <script src=\"../../../common.blocks/functions/functions.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem/i-bem.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/i-bem-dom.spec.js\"></script>\n        <script src=\"../../../common.blocks/i18n/i18n.spec.js\"></script>\n        <script src=\"../../../common.blocks/identify/identify.spec.js\"></script>\n        <script src=\"../../../common.blocks/inherit/inherit.spec.js\"></script>\n        <script src=\"../../../common.blocks/objects/objects.spec.js\"></script>\n        <script src=\"../../../common.blocks/next-tick/next-tick.spec.js\"></script>\n        <script src=\"../../../common.blocks/tick/tick.spec.js\"></script>\n        <script src=\"../../../common.blocks/uri/uri.spec.js\"></script>\n        <script src=\"../../../common.blocks/events/__observable/events__observable.spec.js\"></script>\n        <script src=\"../../../common.blocks/functions/__debounce/functions__debounce.spec.js\"></script>\n        <script src=\"../../../common.blocks/functions/__throttle/functions__throttle.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem/__collection/i-bem__collection.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem/__internal/i-bem__internal.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/__collection/i-bem-dom__collection.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/__init/i-bem-dom__init.spec.js\"></script>\n        <script src=\"../../../common.blocks/loader/_type/loader_type_js.spec.js\"></script>\n        <script src=\"../../../common.blocks/strings/__escape/strings__escape.spec.js\"></script>\n        <script src=\"../../../common.blocks/uri/__querystring/uri__querystring.spec.js\"></script>\n        <script src=\"../../../common.blocks/events/__observable/_type/events__observable_type_bem-dom.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_bem.spec.js\"></script>\n        <script src=\"../../../common.blocks/i-bem-dom/__events/_type/i-bem-dom__events_type_dom.spec.js\"></script>\n        <script src=\"../../../common.blocks/jquery/__event/_type/jquery__event_type_pointerclick.spec.js\"></script>\n        <script src=\"../../../common.blocks/jquery/__event/_type/jquery__event_type_pointernative.spec.js\"></script>\n        <script src=\"../../../common.blocks/jquery/__event/_type/jquery__event_type_pointerpressrelease.spec.js\"></script>\n        <script>\n            (function() {\n                var global = this;\n\n                modules.require(['jquery', 'mocha', 'spec'], function($, mocha) {\n                    (global.mochaPhantomJS || mocha).run(done);\n\n                    function done() {\n                         $('#mocha').show();\n                    }\n                });\n            }());\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "touch.blocks/page/__icon/page__icon.bemhtml.js",
    "content": "block('page').elem('icon').def()(function() {\n    var ctx = this.ctx;\n    return applyCtx([\n        ctx.src16 && {\n            elem : 'link',\n            attrs : { rel : 'shortcut icon', href : ctx.src16 }\n        },\n        ctx.src114 && {\n            elem : 'link',\n            attrs : {\n                rel : 'apple-touch-icon-precomposed',\n                sizes : '114x114',\n                href : ctx.src114\n            }\n        },\n        ctx.src72 && {\n            elem : 'link',\n            attrs : {\n                rel : 'apple-touch-icon-precomposed',\n                sizes : '72x72',\n                href : ctx.src72\n            }\n        },\n        ctx.src57 && {\n            elem : 'link',\n            attrs : { rel : 'apple-touch-icon-precomposed', href : ctx.src57 }\n        }\n    ]);\n});\n"
  },
  {
    "path": "touch.blocks/page/__icon/page__icon.bh.js",
    "content": "module.exports = function(bh) {\n    bh.match('page__icon', function(ctx, json) {\n        ctx.content([\n            json.src16 && {\n                elem : 'link',\n                attrs : { rel : 'shortcut icon', href : json.src16 }\n            },\n            json.src114 && {\n                elem : 'link',\n                attrs : {\n                    rel : 'apple-touch-icon-precomposed',\n                    sizes : '114x114',\n                    href : json.src114\n                }\n            },\n            json.src72 && {\n                elem : 'link',\n                attrs : {\n                    rel : 'apple-touch-icon-precomposed',\n                    sizes : '72x72',\n                    href : json.src72\n                }\n            },\n            json.src57 && {\n                elem : 'link',\n                attrs : { rel : 'apple-touch-icon-precomposed', href : json.src57 }\n            }\n        ], true);\n    });\n};\n"
  },
  {
    "path": "touch.blocks/page/page.bemhtml.js",
    "content": "block('page')(\n\n    def()(function() {\n        return applyNext({ _zoom : this.ctx.zoom });\n    }),\n\n    elem('head').content()(function() {\n        return [\n            applyNext(),\n            {\n                elem : 'meta',\n                attrs : {\n                    name : 'viewport',\n                    content : 'width=device-width,' +\n                        (this._zoom?\n                            'initial-scale=1' :\n                            'maximum-scale=1,initial-scale=1,user-scalable=no')\n                }\n            },\n            { elem : 'meta', attrs : { name : 'format-detection', content : 'telephone=no' } },\n            { elem : 'link', attrs : { name : 'apple-mobile-web-app-capable', content : 'yes' } }\n        ];\n    }),\n\n    mix()(function() {\n        var mix = applyNext(),\n            uaMix = [{ block : 'ua', attrs : { nonce : this._nonceCsp }, js : true }];\n\n        return mix? uaMix.concat(mix) : uaMix;\n    })\n);\n"
  },
  {
    "path": "touch.blocks/page/page.bh.js",
    "content": "module.exports = function(bh) {\n    bh.match('page', function(ctx, json) {\n        ctx\n            .mix({ block : 'ua', js : true })\n            .tParam('zoom', json.zoom);\n    });\n\n    bh.match('page__head', function(ctx, json) {\n        ctx\n            .applyBase()\n            .content([\n                json.content,\n                {\n                    elem : 'meta',\n                    attrs : {\n                        name : 'viewport',\n                        content : 'width=device-width,' +\n                            (ctx.tParam('zoom')?\n                                'initial-scale=1' :\n                                'maximum-scale=1,initial-scale=1,user-scalable=no')\n                    }\n                },\n                { elem : 'meta', attrs : { name : 'format-detection', content : 'telephone=no' } },\n                { elem : 'link', attrs : { name : 'apple-mobile-web-app-capable', content : 'yes' } }\n            ], true);\n    });\n\n};\n"
  },
  {
    "path": "touch.blocks/page/page.deps.js",
    "content": "({})\n"
  },
  {
    "path": "touch.blocks/page/page.ru.md",
    "content": "﻿# page\n\nНа уровне переопределения `touch.blocks` блок предоставляет шаблоны, создающие дополнительный набор HTML-элементов внутри `head`.\n\n## Обзор\n\n### Специализированные поля блока\n\n| Поле | Тип | Описание |\n| ---- | --- | -------- |\n| <a href=\"#elems-meta-declfields-zoom\">zoom</a> | `{Boolean}` | Наличие масштабирования. |\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| ------- | --------------------- | -------- |\n| <a href=\"#elems-icon\">icon</a> | `BEMJSON` | Позволяет задать ссылку на значки Web Clips, для отображения на рабочем столе iOS при добавлении ссылки на сайт. |\n\n### Специализированные поля элементов блока\n\n| Элемент | Поле | Тип | Описание |\n| ------- | ---- | --- | -------- |\n| <a href=\"#elems-icon\">icon</a> | <a href=\"#elems-icon-declfields-src\">src{X}</a> | `{String}` | Используются для указания пути к файлу значка. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `bh.js`\n* `bemhtml`\n\n## Подробности\n\nБлок создает HTML-элементы:\n\n* `<meta>` с атрибутом `name` в значении `'format-detection'`. Значением `content` служит `'telephone=no'`. Элемент отключает автоматическое распознавание телефонных номеров в html-коде и их набор по нажатию.\n* `<link>` с атрибутом `name` в значении `'apple-mobile-web-app-capable'`. Значением `content` служит `'yes'`. Элемент задает для страницы полноэкранный режим отображения на устройствах с iOS.\n* `<meta>` с атрибутом `name` в значении `'viewport'`. Элемент позволяет управлять масштабированием страницы. По умолчанию, масштабирование отключено. Для включения используйте специализированное поле `zoom` со значением `true`.\n\nКроме того, к элементу <body> с классом `page` подмешивается блок [ua](https://github.com/bem/bem-core/blob/v2/desktop.blocks/ua/ua.ru.md).\n\n<a name=\"declfields\"></a>\n### Специализированные поля блока\n\n<a name=\"declfields-zoom\"></a>\n##### Специализированное поле `zoom`\n\nТип: `{Boolean}`.\n\nУправляет масштабированием страницы. Определяет значение атрибута `content` HTML-элемент `<meta>` с атрибутом `name` в значении `'viewport'`:\n\n* со значением `true` – `'initial-scale=1'`. Масштабирование включено. Масштаб по умолчанию устанавливается равным 100%.\n* без значения или `false` – `'maximum-scale=1,initial-scale=1,user-scalable=no'`. Масштаб по умолчанию устанавливается равным 100%. Масштабирование отключено.\n\n```js\n{\n    block : 'page',\n    title : 'Hello, World!',\n    zoom : true,\n    content : 'Включение масштабирования страницы'\n}\n```\n\n\n<a name=\"elems\"></a>\n### Элементы блока\n\n<a name=\"elems-icon\"></a>\n#### Элемент `icon` \n\nПозволяет задать ссылку на значки Web Clips, для отображения на рабочем столе iOS при добавлении ссылки на сайт. Ссылка задается через специализированное поле `src{X}`.\n\n<a name=\"elems-icon-declfields-src\"></a>\n##### Специализированное поле `src{X}`\n\nТип: `String`.\n\nПоле вида `src{X}` используются для указания пути к файлу значка. В зависимости от значения `{X}` элемент `icon` преобразуется в HTML-элемент:\n\n* `src16` – `<link>` c атрибутом `rel` со значением `'shortcut icon'`. \n* `src57` – `<link>` c атрибутами: \n    * `sizes` со значением `'57x57'`;\n    * `rel` со значением `'apple-touch-icon-precomposed'`. \n* `src72` – `<link>` c атрибутами: \n    * `sizes` со значением `'72x72'`;\n    * `rel` со значением `'apple-touch-icon-precomposed'`. \n* `src114` – `<link>` c атрибутом `rel` со значением `'apple-touch-icon-precomposed'`. \n\n```js\n{\n    block : 'page',\n    title : 'Page title',\n    head : { elem : 'icon', src72 : 'example.png' },\n    content : 'Страница с подключенным значком'\n}\n```\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/00-empty.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <meta name=\"viewport\" content=\"width=device-width,maximum-scale=1,initial-scale=1,user-scalable=no\"/>\n    <meta name=\"format-detection\" content=\"telephone=no\"/>\n    <link name=\"apple-mobile-web-app-capable\" content=\"yes\"/>\n</head>\n<body class=\"page ua i-bem\" data-bem=\"{&quot;ua&quot;:{}}\"></body>\n</html>\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/10-simple.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n    <title>Простой пример html/head/body-обвязки страницы</title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <link rel=\"stylesheet\" href=\"10-simple.css\"/>\n    <script src=\"10-simple.js\"></script>\n    <meta name=\"keywords\" content=\"BEM\"/>\n    <link rel=\"shortcut icon\" href=\"//bem.info/favicon.ico\"/>\n    <meta name=\"viewport\" content=\"width=device-width,maximum-scale=1,initial-scale=1,user-scalable=no\"/>\n    <meta name=\"format-detection\" content=\"telephone=no\"/>\n    <link name=\"apple-mobile-web-app-capable\" content=\"yes\"/>\n</head>\n<body class=\"page ua i-bem\" data-bem=\"{&quot;ua&quot;:{}}\"><div class=\"bla\">bla</div></body>\n</html>\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/20-style.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <style>.b-blah { color: #f00 }</style>\n    <meta name=\"viewport\" content=\"width=device-width,maximum-scale=1,initial-scale=1,user-scalable=no\"/>\n    <meta name=\"format-detection\" content=\"telephone=no\"/>\n    <link name=\"apple-mobile-web-app-capable\" content=\"yes\"/>\n</head>\n<body class=\"page ua i-bem\" data-bem=\"{&quot;ua&quot;:{}}\"></body>\n</html>\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/25-styles.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <style>.b-blah { color: red }</style>\n    <style>.b-blah2 { color: green }</style>\n    <meta name=\"viewport\" content=\"width=device-width,maximum-scale=1,initial-scale=1,user-scalable=no\"/>\n    <meta name=\"format-detection\" content=\"telephone=no\"/>\n    <link name=\"apple-mobile-web-app-capable\" content=\"yes\"/>\n</head>\n<body class=\"page ua i-bem\" data-bem=\"{&quot;ua&quot;:{}}\"></body>\n</html>\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/30-scripts.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <meta name=\"viewport\" content=\"width=device-width,maximum-scale=1,initial-scale=1,user-scalable=no\"/>\n    <meta name=\"format-detection\" content=\"telephone=no\"/>\n    <link name=\"apple-mobile-web-app-capable\" content=\"yes\"/>\n</head>\n<body class=\"page ua i-bem\" data-bem=\"{&quot;ua&quot;:{}}\">\n    <div class=\"bla\">bla-bla</div>\n    <script src=\"https://yastatic.net/jquery/2.1.1/jquery.min.js\"></script>\n    <script src=\"https://yastatic.net/jquery/easing/1.3/jquery.easing.min.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/40-nonce.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n    <title></title>\n    <script nonce=\"123\">\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <meta name=\"viewport\" content=\"width=device-width,maximum-scale=1,initial-scale=1,user-scalable=no\"/>\n    <meta name=\"format-detection\" content=\"telephone=no\"/>\n    <link name=\"apple-mobile-web-app-capable\" content=\"yes\"/>\n</head>\n<body class=\"page ua i-bem\" data-bem=\"{&quot;ua&quot;:{}}\">\n<div class=\"bla\">bla-bla</div>\n<script src=\"https://yastatic.net/jquery/2.1.1/jquery.min.js\"></script>\n<script src=\"https://yastatic.net/jquery/easing/1.3/jquery.easing.min.js\"></script>\n<script nonce=\"123\">var a = true;</script>\n</body>\n</html>\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/60-x-ua-compatible.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <title>Remove x-ua-compatible</title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <meta name=\"viewport\" content=\"width=device-width,maximum-scale=1,initial-scale=1,user-scalable=no\"/>\n    <meta name=\"format-detection\" content=\"telephone=no\"/>\n    <link name=\"apple-mobile-web-app-capable\" content=\"yes\"/>\n</head>\n<body class=\"page ua i-bem\" data-bem=\"{&quot;ua&quot;:{}}\"></body>\n</html>\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/70-lang.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\" lang=\"en-us\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <meta name=\"viewport\" content=\"width=device-width,maximum-scale=1,initial-scale=1,user-scalable=no\"/>\n    <meta name=\"format-detection\" content=\"telephone=no\"/>\n    <link name=\"apple-mobile-web-app-capable\" content=\"yes\"/>\n</head>\n<body class=\"page ua i-bem\" data-bem=\"{&quot;ua&quot;:{}}\"></body>\n</html>\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/70-zoom.bemjson.js",
    "content": "({\n    block : 'page',\n    zoom : true\n})\n"
  },
  {
    "path": "touch.blocks/page/page.tmpl-specs/70-zoom.html",
    "content": "<!DOCTYPE html>\n<html class=\"ua_js_no\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"IE=edge\" http-equiv=\"X-UA-Compatible\"/>\n    <title></title>\n    <script>\n        (function(e,c){e[c]=e[c].replace(/(ua_js_)no/g,\"$1yes\");})(document.documentElement,\"className\");\n    </script>\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\n    <meta name=\"format-detection\" content=\"telephone=no\"/>\n    <link name=\"apple-mobile-web-app-capable\" content=\"yes\"/>\n</head>\n<body class=\"page ua i-bem\" data-bem=\"{&quot;ua&quot;:{}}\"></body>\n</html>\n"
  },
  {
    "path": "touch.blocks/ua/__dom/ua__dom.deps.js",
    "content": "({\n    mustDeps : ['ua', 'i-bem-dom']\n})\n"
  },
  {
    "path": "touch.blocks/ua/__dom/ua__dom.js",
    "content": "/**\n * @module ua__dom\n * @description Use ua module to provide user agent features by modifiers and update some on orient change\n */\n\nimport bemDom from 'bem:i-bem-dom';\nimport ua from 'bem:ua';\n\nexport default bemDom.declBlock('ua',\n    {\n        onSetMod : {\n            'js' : {\n                'inited' : function() {\n                    this\n                        .setMod('platform',\n                            ua.ios? 'ios' :\n                                ua.android? 'android' :\n                                    ua.bada? 'bada' :\n                                        ua.wp? 'wp' :\n                                            ua.opera? 'opera' :\n                                                'other')\n                        .setMod('browser',\n                            ua.opera? 'opera' :\n                                ua.chrome? 'chrome' :\n                                    '')\n                        .setMod('ios', ua.ios? ua.ios.charAt(0) : '')\n                        .setMod('android', ua.android? ua.android.charAt(0) : '')\n                        .setMod('ios-subversion', ua.ios? ua.ios.match(/(\\d\\.\\d)/)[1].replace('.', '') : '')\n                        .setMod('screen-size', ua.screenSize)\n                        .setMod('svg', ua.svg? 'yes' : 'no')\n                        .setMod('orient', ua.landscape? 'landscape' : 'portrait')\n                        ._domEvents(bemDom.win).on(\n                            'orientchange',\n                            function(e, data) {\n                                ua.width = data.width;\n                                ua.height = data.height;\n                                ua.landscape = data.landscape;\n                                this.setMod('orient', data.landscape? 'landscape' : 'portrait');\n                            });\n                }\n            }\n        }\n    },\n    ua);\n"
  },
  {
    "path": "touch.blocks/ua/__dom/ua__dom.ru.md",
    "content": "<a name=\"#elems-dom\">\n# Элемент `dom` блока `ua`\n\nЭлемент служит для дополнения базовой БЭМ-сущности блока `ua` набором модификаторов на основе данных, собранных блоком `ua` на touch-уровне.\n\nЭто позволяет учитывать особенности мобильного устройства, проверяя наличие и значение модификаторов.\n\n```js\nmodules.define('ios-test', ['i-bem-dom', 'ua'], function(provide, bemDom, Ua) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod: {\n        js: {\n            inited: function() {\n                this.findParentBlock(Ua).hasMod('platform', 'ios') &&\n                    this.setMod('ios');\n            }\n        },\n        'ios': function() {\n            console.log('You are iOS user');\n        }\n    }\n}));\n\n});\n```\n\nЭлемент автоматически подключается с блоком `page`. Не требуется подключать его вручную, если в проекте используется `page`.\n\n<a name=\"modifiers\"></a>\n## Модификаторы элемента\n\nЗначения всех модификаторов элемента, кроме `orient`, устанавливаются в момент инициализации блока и остаются неизменными.\n\n<a name=\"modifiers-platform\"></a>\n### Модификатор `platform`\n\nДопустимые значения: `'ios'`, `'android'`, `'bada'`, `'wp'`, `'other'`.\n\nСпособ использования: `JS`.\n\nМодификатор указывает мобильную платформу пользовательского устройства.\n\n* `ios` – iOS.\n* `android` – Android.\n* `bada` – Bada OS.\n* `wp` – Windows Phone.\n* `other` – все остальные мобильные платформы.\n\n<a name=\"modifiers-browser\"></a>\n### Модификатор `browser`\n\nДопустимые значения: `'opera'`, `'chrome'`.\n\nСпособ использования: `JS`.\n\nМодификатор указывает тип мобильного браузера.\n\n* `opera` – Opera.\n* `chrome` – Chrome.\n\n<a name=\"modifiers-ios\"></a>\n### Модификатор `ios`\n\nДопустимые значения: `'8'`, `'7'` ...\n\nСпособ использования: `JS`.\n\nМодификатор указывает версию операционной системы для устройств iOS.\n\n<a name=\"modifiers-ios-subversion\"></a>\n### Модификатор `ios-subversion`\n\nДопустимые значения: `'81'`, `'80'` ...\n\nСпособ использования: `JS`.\n\nМодификатор указывает подверсию операционной системы для устройств iOS. Номер подверсии состоит из номера версии и первого символа после разделителя. Номер указывается без символов-разделителей `'.'`. Например, для iOS версии 8.1.3 значением модификатора будет `'81'`.\n\n<a name=\"modifiers-android\"></a>\n### Модификатор `android`\n\nДопустимые значения: `'4'`, `'3'` ...\n\nСпособ использования: `JS`.\n\nМодификатор указывает версию операционной системы для устройств Android.\n\n<a name=\"modifiers-screen-size\"></a>\n### Модификатор `screen-size`\n\nДопустимые значения: `'large'`, `'normal'`, `'small'`.\n\nСпособ использования: `JS`.\n\nМодификатор указывает размер экрана пользовательского устройства.\n\n* `large` – размер экрана больше 320 px.\n* `normal` – размер экрана равен 320 px.\n* `small` – размер экрана меньше 320 px.\n\n<a name=\"modifiers-svg\"></a>\n### Модификатор `svg`\n\nДопустимые значения: `'yes'`, `'no'`.\n\nСпособ использования: `JS`.\n\nМодификатор указывает на наличие у пользовательского устройства поддержки формата SVG.\n\n* `yes` – поддержка SVG присутствует.\n* `no` – поддержка SVG отсутствует.\n\n<a name=\"modifiers-orient\"></a>\n### Модификатор `orient`\n\nДопустимые значения: `'landscape'`, `'portrait'`.\n\nСпособ использования: `JS`.\n\nМодификатор указывает текущую ориентацию устройства.\n\n* `landscape` – горизонтальная ориентация.\n* `portrait` – вертикальная ориентация.\n\nЗначение модификатора изменяется динамически при смене ориентации устройства. Поэтому можно подписываться на изменение значения модификатора:\n\n```js\nmodules.define('inner', ['i-bem-dom', 'ua'], function(provide, bemDom, Ua) {\n\nprovide(bemDom.declBlock(this.name, {\n    onSetMod: {\n        js: {\n            inited: function() {\n                this._ua = this.findParentBlock(Ua);\n\n                this\n                    ._events(this.ua)\n                    .on({ modName : 'orient', modVal : '*' }, this._onOrientChange, this);\n\n                this.setMod('orient', this._ua.getMod('orient'));\n            }\n        },\n\n        'orient': {\n            'portrait': function() {\n                this._reDraw('portrait');\n            },\n            'landscape': function() {\n                this._reDraw('landscape');\n            }\n        }\n    },\n\n    _onOrientChange: function(e, data) {\n        // переключаемся между значениям собственного модификатора `orient`\n        this.setMod(data.modName, data.modVal);\n    },\n\n    _reDraw: function(orient) {\n        // обновляем содержимое контейнера `inner` при смене ориентации устройства\n        console.log(orient);\n        bemDom.update(this.domElem, orient);\n    }\n}));\n\n});\n```\n\nВ примере блок-контейнер `inner`, вложенный в `page`, подменяет свое содержимое при смене ориентации устройства.\n"
  },
  {
    "path": "touch.blocks/ua/ua.bemhtml.js",
    "content": "block('ua').js()(function() {\n    var ctxJS = applyNext();\n\n    if(ctxJS === false) return false;\n    return ctxJS || true;\n});\n"
  },
  {
    "path": "touch.blocks/ua/ua.bh.js",
    "content": "module.exports = function(bh) {\n    bh.match('ua', function(ctx) {\n        ctx.js(true);\n    });\n};\n"
  },
  {
    "path": "touch.blocks/ua/ua.deps.js",
    "content": "({\n    shouldDeps : 'jquery'\n})\n"
  },
  {
    "path": "touch.blocks/ua/ua.js",
    "content": "/**\n * @module ua\n * @description Detect some user agent features\n */\n\nimport $ from 'bem:jquery';\n\nconst win = window,\n    doc = document,\n    ua = navigator.userAgent,\n    platform = {},\n    device = {};\nlet match;\n\nif(match = ua.match(/Android\\s+([\\d.]+)/)) {\n    platform.android = match[1];\n} else if(ua.match(/\\sHTC[\\s_].*AppleWebKit/)) {\n    // фэйковый десктопный UA по умолчанию у некоторых HTC (например, HTC Sensation)\n    platform.android = '2.3';\n} else if(match = ua.match(/iPhone\\sOS\\s([\\d_]+)/)) {\n    platform.ios = match[1].replace(/_/g, '.');\n    device.iphone = true;\n} else if(match = ua.match(/iPad.*OS\\s([\\d_]+)/)) {\n    platform.ios = match[1].replace(/_/g, '.');\n    device.ipad = true;\n} else if(match = ua.match(/Bada\\/([\\d.]+)/)) {\n    platform.bada = match[1];\n} else if(match = ua.match(/Windows\\sPhone.*\\s([\\d.]+)/)) {\n    platform.wp = match[1];\n} else {\n    platform.other = true;\n}\n\nconst browser = {};\nif(win.opera) {\n    browser.opera = win.opera.version();\n} else if(match = ua.match(/\\sCrMo\\/([\\d.]+)/)) {\n    browser.chrome = match[1];\n}\n\nconst support = {},\n    connection = navigator.connection;\n\nif(connection) {\n    const connections = {};\n    connections[connection.ETHERNET] = connections[connection.WIFI] = 'wifi';\n    connections[connection.CELL_3G] = '3g';\n    connections[connection.CELL_2G] = '2g';\n    support.connection = connections[connection.type];\n}\n\nconst videoElem = doc.createElement('video');\nsupport.video = !!(videoElem.canPlayType && videoElem.canPlayType('video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"').replace(/no/, ''));\n\nsupport.svg = !!(doc.createElementNS && doc.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect);\n\nconst plugins = navigator.plugins;\nlet i = plugins.length;\nif(plugins && i) {\n    let plugin;\n    while(plugin = plugins[--i])\n        if(plugin.name === 'Shockwave Flash' && (match = plugin.description.match(/Flash ([\\d.]+)/))) {\n            support.flash = match[1];\n            break;\n        }\n}\n\n// http://stackoverflow.com/a/6603537\nlet lastOrient = win.innerWidth > win.innerHeight,\n    lastWidth = win.innerWidth;\nconst $win = $(win).bind('resize', function() {\n        const width = win.innerWidth,\n            height = win.innerHeight,\n            landscape = width > height;\n\n        // http://alxgbsn.co.uk/2012/08/27/trouble-with-web-browser-orientation/\n        // check previous device width to disallow Android shrink page and change orientation on opening software keyboard\n        if(landscape !== lastOrient && width !== lastWidth) {\n            $win.trigger('orientchange', {\n                landscape : landscape,\n                width : width,\n                height : height\n            });\n\n            lastOrient = landscape;\n            lastWidth = width;\n        }\n    });\n\nexport default {\n    /**\n     * User agent\n     * @type String\n     */\n    ua : ua,\n\n    /**\n     * iOS version\n     * @type String|undefined\n     */\n    ios : platform.ios,\n\n    /**\n     * Is iPhone\n     * @type Boolean|undefined\n     */\n    iphone : device.iphone,\n\n    /**\n     * Is iPad\n     * @type Boolean|undefined\n     */\n    ipad : device.ipad,\n\n    /**\n     * Android version\n     * @type String|undefined\n     */\n    android : platform.android,\n\n    /**\n     * Bada version\n     * @type String|undefined\n     */\n    bada : platform.bada,\n\n    /**\n     * Windows Phone version\n     * @type String|undefined\n     */\n    wp : platform.wp,\n\n    /**\n     * Undetected platform\n     * @type Boolean|undefined\n     */\n    other : platform.other,\n\n    /**\n     * Opera version\n     * @type String|undefined\n     */\n    opera : browser.opera,\n\n    /**\n     * Chrome version\n     * @type String|undefined\n     */\n    chrome : browser.chrome,\n\n    /**\n     * Screen size, one of: large, normal, small\n     * @type String\n     */\n    screenSize : screen.width > 320? 'large' : screen.width < 320? 'small' : 'normal',\n\n    /**\n     * Device pixel ratio\n     * @type Number\n     */\n    dpr : win.devicePixelRatio || 1,\n\n    /**\n     * Connection type, one of: wifi, 3g, 2g\n     * @type String\n     */\n    connection : support.connection,\n\n    /**\n     * Flash version\n     * @type String|undefined\n     */\n    flash : support.flash,\n\n    /**\n     * Is video supported?\n     * @type Boolean\n     */\n    video : support.video,\n\n    /**\n     * Is SVG supported?\n     * @type Boolean\n     */\n    svg : support.svg,\n\n    /**\n     * Viewport width\n     * @type Number\n     */\n    width : win.innerWidth,\n\n    /**\n     * Viewport height\n     * @type Number\n     */\n    height : win.innerHeight,\n\n    /**\n     * Is landscape oriented?\n     * @type Boolean\n     */\n    landscape : lastOrient\n};\n"
  },
  {
    "path": "touch.blocks/ua/ua.ru.md",
    "content": "# ua\n\nНа уровне `touch`, блок предоставляет объект, содержащий набор свойств, указывающих особенности мобильного устройства.\n\n## Обзор\n\n### Свойства и методы объекта\n\n| Имя | Тип | Описание |\n| --- | --- | -------- |\n| <a href=\"#fields-ua\">ua</a> | <code>{String}</code> | Значение HTTP-заголовка юзер-агента. |\n| <a href=\"#fields-ios\">ios</a> | <code>{String}</code>&#124;<code>{undefined}</code> | Версия мобильной платформы iOS. |\n| <a href=\"#fields-android\">android</a> | <code>{String}</code>&#124;<code>{undefined}</code> | Версия мобильной платформы Android. |\n| <a href=\"#fields-bada\">bada</a> | <code>{String}</code>&#124;<code>{undefined}</code> | Версия мобильной платформы Bada OS. |\n| <a href=\"#fields-wp\">wp</a> | <code>{String}</code>&#124;<code>{undefined}</code> | Версия мобильной платформы Windows Phone. |\n| <a href=\"#fields-other\">other</a> | <code>{Boolean}</code> | Мобильная платформа неопределена. |\n| <a href=\"#fields-opera\">opera</a> | <code>{String}</code> | Версия браузера Opera. |\n| <a href=\"#fields-chrome\">chrome</a> | <code>{String}</code> | Версия браузера Chrome. |\n| <a href=\"#fields-iphone\">iphone</a> | <code>{Boolean}</code> | Устройство – iPhone. |\n| <a href=\"#fields-ipad\">ipad</a> | <code>{Boolean}</code> | Устройство – iPad. |\n| <a href=\"#fields-screenSize\">screenSize</a> | <code>{String}</code> | Размер экрана устройства. |\n| <a href=\"#fields-connection\">connection</a> | <code>{String}</code> | Тип активного соединения. |\n| <a href=\"#fields-dpr\">dpr</a> | <code>{Number}</code> | Относительная плотность пикселей. |\n| <a href=\"#fields-flash\">flash</a> | <code>{String}</code>&#124;<code>{undefined}</code> | Версия Adobe Flash. |\n| <a href=\"#fields-video\">video</a> | <code>{Boolean}</code> | Поддержка видео. |\n| <a href=\"#fields-width\">width</a> | <code>{Number}</code> | Ширина рабочей области экрана в px. |\n| <a href=\"#fields-height\">height</a> | <code>{Number}</code> | Высота рабочей области экрана в px. |\n| <a href=\"#fields-landscape\">landscape</a> | <code>{Boolean}</code> | Ориентация устройства. |\n\n### Элементы блока\n\n| Элемент | Способы использования | Описание |\n| ------- | --------------------- | -------- |\n| <a href=\"#elems-dom\">dom</a> | `JS` | Предоставляет набор модификаторов на основании свойств блока `ua` на тач-уровне. |\n\n### Модификаторы элемента блока\n\n| Элемент | Модификатор | Допустимые значения | Способы использования | Описание |\n| ------- | ----------- | ------------------- | --------------------- | -------- |\n| <a href=\"#elems-dom\">dom</a> | <a href=\"#modifiers-platform\">platform</a> | `'ios'`, `'android'`, `'bada'`, `'wp'`, `'other'` | `JS` | Мобильная платформа пользовательского устройства. |\n|  | <a href=\"#modifiers-browser\">browser</a> | `'opera'`, `'chrome'` | `JS` | Тип браузера. |\n|  | <a href=\"#modifiers-ios\">ios</a> | `'8'`, `'7'` ... | `JS` | Версия операционной системы для устройств iOS. |\n|  | <a href=\"#modifiers-ios-subversion\">ios-subversion</a> | `'81'`, `'80'` ... | `JS` | Подверсия операционной системы для устройств iOS. |\n|  | <a href=\"#modifiers-android\">android</a> | `'4'`, `'3'` ... | `JS` | Версия операционной системы для устройств Android. |\n|  | <a href=\"#modifiers-screen-size\">screen-size</a> | `'large'`, `'normal'`, `'small'` | `JS` | Размер экрана устройства. |\n|  | <a href=\"#modifiers-svg\">svg</a> | `'yes'`, `'no'` | `JS` | Поддержка формата SVG. |\n|  | <a href=\"#modifiers-orient\">orient</a> | `'landscape'`, `'portrait'` | `JS` | Ориентация устройства. |\n\n### Публичные технологии блока\n\nБлок реализован в технологиях:\n\n* `js`\n* `bh.js`\n* `bemhtml`\n\n## Подробности\n\nБлок позволяет определить:\n\n* Версию мобильной платформы.\n* Типа браузера.\n* Версию браузера.\n* Тип соединения.\n* Наличие поддержки видео и SVG.\n* Поддержку технологии Adobe Flash.\n* Ориентацию и размер экрана.\n* Соотношение сторон экрана устройства.\n\n```js\nmodules.require('ua', function(ua) {\n\nconsole.dir(ua);\n\n});\n```\n\n\n<a name=\"fields\"></a>\n### Свойства и методы объекта\n\n<a name=\"fields-ua\"></a>\n#### Свойство `ua`\n\nТип: `{String}`.\n\nТип мобильного браузера.\n\n<a name=\"fields-ios\"></a>\n#### Свойство `ios`\n\nТип: `{String|undefined}`.\n\nВерсия мобильной платформы. Строка с номером версии, если платформа распознана как iOS.\n\n<a name=\"fields-android\"></a>\n#### Свойство `android`\n\nТип: `{String|undefined}`.\n\nВерсия мобильной платформы. Строка с номером версии, если платформа распознана как Android.\n\n<a name=\"fields-bada\"></a>\n#### Свойство `bada`\n\nТип: `{String|undefined}`.\n\nВерсия мобильной платформы. Строка с номером версии, если платформа распознана как Bada OS.\n\n<a name=\"fields-wp\"></a>\n#### Свойство `wp`\n\nТип: `{String|undefined}`.\n\nВерсия мобильной платформы. Строка с номером версии, если платформа распознана как Windows Phone.\n\n<a name=\"fields-other\"></a>\n#### Свойство `other`\n\nТип: `{Boolean}`.\n\nМобильная платформа неопределена. Устанавливается в значение `true` для всех мобильных платформ, кроме вышеперечисленных.\n\n\n<a name=\"fields-opera\"></a>\n#### Свойство `opera`\n\nТип: `{String}`.\n\nВерсия браузера Opera.\n\n<a name=\"fields-chrome\"></a>\n#### Свойство `chrome`\n\nТип: `{String}`.\n\nВерсия браузера Chrome.\n\n<a name=\"fields-iphone\"></a>\n#### Свойство `iphone`\n\nТип: `{Boolean}`.\n\nЗначение `true` характеризует устройство как iPhone.\n\n<a name=\"fields-ipad\"></a>\n#### Свойство `ipad`\n\nТип: `{Boolean}`.\n\nЗначение `true` характеризует устройство как iPad.\n\n<a name=\"fields-screenSize\"></a>\n#### Свойство `screenSize`\n\nТип: `{String}`.\n\nРазмер экрана устройства.\n\nДоступны следующие значения:\n\n* `large` – размер экрана больше 320 px.\n* `normal` – размер экрана равен 320 px.\n* `small` – размер экрана меньше 320 px.\n\n<a name=\"fields-connection\"></a>\n#### Свойство `connection`\n\nТип: `{String}`.\n\nТип активного сетевого соединения.\n\nДоступны следующие значения:\n\n* `wifi` – соединение по Wi-Fi.\n* `3g` – соединение по 3G.\n* `2g` – соединение по EDGE и GSM.\n\n<a name=\"fields-dpr\"></a>\n#### Свойство `dpr`\n\nТип: `{Number}`.\n\nКоэффициент относительной плотности пикселей. Характеризует отношение физических пикселей устройства к аппаратно независимым (dppx). Позволяет определить использует ли устройство дисплей с повышенной плотностью пикселей (например, Retina). По умолчанию `1`.\n\nНапример, можно проверить, что устройство использует Retina и отдавать браузеру изображения с высоким разрешением:\n\n```js\nmodules.require('ua', function(ua) {\n\nvar imgFile = ua.dpr === 1 ? 'image.png' : 'image@2x.png';\n// ···\n\n});\n```\n\n<a name=\"fields-flash\"></a>\n#### Свойство `flash`\n\nТип: `{String|undefined}`.\n\nВерсия Adobe Flash. `undefined`, если Flash недоступен.\n\n<a name=\"fields-video\"></a>\n#### Свойство `video`\n\nТип: `{Boolean}`.\n\nЗначение `true`, если видео поддерживается.\n\n<a name=\"fields-svg\"></a>\n#### Свойство `svg`\n\nТип: `{Boolean}`.\n\nЗначение `true`, если SVG поддерживается.\n\n<a name=\"fields-width\"></a>\n#### Свойство `width`\n\nТип: `{Number}`.\n\nШирина рабочей области экрана в пикселях.\n\n<a name=\"fields-height\"></a>\n#### Свойство `height`\n\nТип: `{Number}`.\n\nВысота рабочей области экрана в пикселях.\n\n\n<a name=\"fields-landscape\"></a>\n#### Свойство `landscape`\n\nТип: `{Boolean}`.\n\nЗначение `true` при горизонтальной ориентации.\n"
  }
]