[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    [\n      \"@babel/preset-env\",\n      {\n        \"modules\": false\n      }\n    ]\n  ],\n  \"env\": {\n    \"test\": {\n      \"presets\": [\n        [\n          \"@babel/preset-env\"\n        ]\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "# editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\nindent_size = 2\nindent_style = space\ntrim_trailing_whitespace = true\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"extends\": \"standard\",\n  \"parserOptions\": {\n    \"ecmaVersion\": 6,\n    \"sourceType\": \"module\"\n  },\n  \"env\": {\n    \"jest\": true\n  }\n}\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: jedrzejchalubek\ncustom: https://paypal.me/jedrzejchalubek\n"
  },
  {
    "path": ".github/workflows/node.js.yml",
    "content": "# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node\n# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions\n\nname: Node.js\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [12.x, 14.x, 16.x]\n        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Use Node.js ${{ matrix.node-version }}\n      uses: actions/setup-node@v2\n      with:\n        node-version: ${{ matrix.node-version }}\n        cache: 'npm'\n    - run: npm ci\n    - run: npm run build --if-present\n    - run: npm test\n"
  },
  {
    "path": ".github/workflows/npm-publish.yml",
    "content": "# This workflow will run tests using node and then publish a package to NPM when a release is created\n\nname: Publish to NPM\n\non:\n  push:\n    tags: [ 'v*.*.*' ]\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: 14\n      - run: npm ci\n      - run: npm test\n      - run: npm run build\n\n  publish-npm:\n    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: 14\n          registry-url: https://registry.npmjs.org/\n      - run: npm ci\n      - run: npm run build\n      - run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{secrets.npm_token}}\n"
  },
  {
    "path": ".github/workflows/upload-assets-to-tags.yml",
    "content": "name: Upload assets to tags\n\non: push\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    if: startsWith(github.ref, 'refs/tags/')\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: 14\n      - run: npm ci\n      - run: npm test\n      - run: npm run build\n      - name: Upload\n        uses: softprops/action-gh-release@v1\n        with:\n          files: dist/**/*\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\ncoverage/\ndist/\n\n.idea\n.vscode\n.DS_Store\n"
  },
  {
    "path": ".npmignore",
    "content": "!dist/"
  },
  {
    "path": ".stylelintrc",
    "content": "{\n  \"extends\": \"stylelint-config-standard\",\n  \"plugins\": [\"stylelint-scss\"],\n  \"rules\": {\n    \"indentation\": 2,\n    \"at-rule-no-unknown\": null,\n    \"scss/at-rule-no-unknown\": true\n  }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2013-present Jędrzej Chałubek\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![glide.js](https://glidejs.com/images/glidejs-logotype-dark.png)](https://glidejs.com)\n\n### [Glide.js](https://glidejs.com) is a dependency-free JavaScript ES6 slider and carousel. It’s lightweight, flexible and fast. Designed to slide. No less, no more\n\n[![Build Status](https://api.travis-ci.org/glidejs/glide.svg?branch=master)](https://travis-ci.org/glidejs/glide)\n\nWhat can convince you:\n- **Dependency-free**. Everything included, ready for action.\n- Lightweight. Only **~23kb (~7kb gzipped)** with every functionality on board.\n- **Modular**. Remove unused modules and drop script weight even more.\n- **Extendable**. Plug-in your own modules with additional functionalities.\n- **Bundlers ready**. Using Rollup or Webpack? We have your back.\n\n## Documentation\n\nVisit [glidejs.com](https://glidejs.com/docs) for documentation.\n\n> Looking for old documentation? [Wiki](https://github.com/glidejs/glide/wiki) contains archived documentation of Glide.js in version `^2.0.0`.\n\n## Donation\n\nGlide.js is an open source project licensed under the MIT license. It's completely free to use. However, it would be great if you buy me a cup of coffee once in a while to keep me awake :)\n\n- [PayPal](https://www.paypal.me/jedrzejchalubek)\n- [Github Sponsors](https://github.com/sponsors/jedrzejchalubek)\n\n## Getting started\n\nPull-in a latest version with NPM ...\n\n```bash\nnpm install @glidejs/glide\n```\n\n... provide `<link>` to the required core stylesheet. You can also optionally add an included theme stylesheet ...\n\n```html\n<!-- Required Core stylesheet -->\n<link rel=\"stylesheet\" href=\"node_modules/@glidejs/glide/dist/css/glide.core.min.css\">\n\n<!-- Optional Theme stylesheet -->\n<link rel=\"stylesheet\" href=\"node_modules/@glidejs/glide/dist/css/glide.theme.min.css\">\n```\n\n... then, prepare a little bit of necessary markup ...\n\n```html\n<div class=\"glide\">\n  <div data-glide-el=\"track\" class=\"glide__track\">\n    <ul class=\"glide__slides\">\n      <li class=\"glide__slide\"></li>\n      <li class=\"glide__slide\"></li>\n      <li class=\"glide__slide\"></li>\n    </ul>\n  </div>\n</div>\n```\n\n... and finally, initialize and mount a Glide.\n\n```js\nimport Glide from '@glidejs/glide'\n\nnew Glide('.glide').mount()\n```\n\nNeed a few selected modules? Import and mount only what you need.\n\n```js\nimport Glide, { Controls, Breakpoints } from '@glidejs/glide/dist/glide.modular.esm'\n\nnew Glide('.glide').mount({ Controls, Breakpoints })\n```\n\n## Contributing\n\nThe issue channel is especially for improvement proposals and bug reporting. If you have implementing problems, please write on StackOverflow with [glidejs](https://stackoverflow.com/questions/tagged/glidejs) tag.\n\n## Browser Support\n\n - IE 11+\n - Edge\n - Chrome 10+\n - Firefox 10+\n - Opera 15+\n - Safari 5.1+\n - Safari iOS 9+\n\n## Building\n\nBuild using NPM scripts. The following scripts are available:\n- `build:css` - Outputs CSS files from SASS files.\n- `build:js` - Outputs all destination variants of the script.\n- `build` - Comprehensively builds the entire library.\n- `test` - Runs complete test suite.\n- `lint` - Lints library JavaScript files.\n\n## Credits\n\n- [Jędrzej Chałubek](https://github.com/jedrzejchalubek) - Creator\n- [Contributors](../../contributors)\n\n## License\n\nCopyright (c) 2014-present, [Jędrzej Chałubek](https://jedrzejchalubek.com). Licensed under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n"
  },
  {
    "path": "build/banner.js",
    "content": "const data = require('../package.json')\n\nexport default `/*!\n * Glide.js v${data.version}\n * (c) 2013-${new Date().getFullYear()} ${data.author}\n * Released under the MIT License.\n */\n`\n"
  },
  {
    "path": "build/build.js",
    "content": "import banner from './banner'\nimport babel from '@rollup/plugin-babel'\n\nexport default {\n  output: {\n    name: 'Glide',\n    banner\n  },\n  plugins: [\n    babel({\n      babelHelpers: 'bundled'\n    })\n  ]\n}\n"
  },
  {
    "path": "build/esm.js",
    "content": "import build from './build'\n\nexport default Object.assign(build, {\n  input: 'entry/entry-complete.js',\n  output: Object.assign(build.output, {\n    file: 'dist/glide.esm.js',\n    format: 'es'\n  })\n})\n"
  },
  {
    "path": "build/esm.modular.js",
    "content": "import build from './build'\n\nexport default Object.assign(build, {\n  input: 'entry/entry-modular.js',\n  output: Object.assign(build.output, {\n    file: 'dist/glide.modular.esm.js',\n    format: 'es'\n  })\n})\n"
  },
  {
    "path": "build/umd.js",
    "content": "import build from './build'\n\nexport default Object.assign(build, {\n  input: 'entry/entry-complete.js',\n  output: Object.assign(build.output, {\n    file: 'dist/glide.js',\n    format: 'umd'\n  })\n})\n"
  },
  {
    "path": "entry/entry-complete.js",
    "content": "import Core from '../src/index'\n\n// Required components\nimport Run from '../src/components/run'\nimport Gaps from '../src/components/gaps'\nimport Html from '../src/components/html'\nimport Peek from '../src/components/peek'\nimport Move from '../src/components/move'\nimport Sizes from '../src/components/sizes'\nimport Build from '../src/components/build'\nimport Clones from '../src/components/clones'\nimport Resize from '../src/components/resize'\nimport Direction from '../src/components/direction'\nimport Translate from '../src/components/translate'\nimport Transition from '../src/components/transition'\n\n// Optional components\nimport Swipe from '../src/components/swipe'\nimport Images from '../src/components/images'\nimport Anchors from '../src/components/anchors'\nimport Controls from '../src/components/controls'\nimport Keyboard from '../src/components/keyboard'\nimport Autoplay from '../src/components/autoplay'\nimport Breakpoints from '../src/components/breakpoints'\n\nconst COMPONENTS = {\n  // Required\n  Html,\n  Translate,\n  Transition,\n  Direction,\n  Peek,\n  Sizes,\n  Gaps,\n  Move,\n  Clones,\n  Resize,\n  Build,\n  Run,\n\n  // Optional\n  Swipe,\n  Images,\n  Anchors,\n  Controls,\n  Keyboard,\n  Autoplay,\n  Breakpoints\n}\n\nexport default class Glide extends Core {\n  mount (extensions = {}) {\n    return super.mount(Object.assign({}, COMPONENTS, extensions))\n  }\n}\n"
  },
  {
    "path": "entry/entry-modular.js",
    "content": "import Core from '../src/index'\n\nimport Run from '../src/components/run'\nimport Gaps from '../src/components/gaps'\nimport Html from '../src/components/html'\nimport Peek from '../src/components/peek'\nimport Move from '../src/components/move'\nimport Sizes from '../src/components/sizes'\nimport Build from '../src/components/build'\nimport Clones from '../src/components/clones'\nimport Resize from '../src/components/resize'\nimport Direction from '../src/components/direction'\nimport Translate from '../src/components/translate'\nimport Transition from '../src/components/transition'\n\nimport Swipe from '../src/components/swipe'\nimport Images from '../src/components/images'\nimport Anchors from '../src/components/anchors'\nimport Controls from '../src/components/controls'\nimport Keyboard from '../src/components/keyboard'\nimport Autoplay from '../src/components/autoplay'\nimport Breakpoints from '../src/components/breakpoints'\n\nconst COMPONENTS = {\n  Html,\n  Translate,\n  Transition,\n  Direction,\n  Peek,\n  Sizes,\n  Gaps,\n  Move,\n  Clones,\n  Resize,\n  Build,\n  Run\n}\n\nexport {\n  Swipe,\n  Images,\n  Anchors,\n  Controls,\n  Keyboard,\n  Autoplay,\n  Breakpoints\n}\n\nexport default class Glide extends Core {\n  mount (extensions = {}) {\n    return super.mount(Object.assign({}, COMPONENTS, extensions))\n  }\n}\n"
  },
  {
    "path": "jest.config.js",
    "content": "export default {\n  testEnvironment: 'jsdom'\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": false,\n  \"name\": \"@glidejs/glide\",\n  \"version\": \"3.7.1\",\n  \"description\": \"Glide.js is a dependency-free JavaScript ES6 slider and carousel. It’s lightweight, flexible and fast. Designed to slide. No less, no more\",\n  \"author\": \"Jędrzej Chałubek (https://github.com/jedrzejchalubek/)\",\n  \"homepage\": \"https://glidejs.com\",\n  \"main\": \"dist/glide.js\",\n  \"module\": \"dist/glide.esm.js\",\n  \"unpkg\": \"dist/glide.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/glidejs/glide.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/glidejs/glide/issues\"\n  },\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"simple\",\n    \"lightweight\",\n    \"fast\",\n    \"slider\",\n    \"carousel\",\n    \"touch\",\n    \"responsive\"\n  ],\n  \"scripts\": {\n    \"sass:core\": \"sass src/assets/sass/glide.core.scss:dist/css/glide.core.css && sass src/assets/sass/glide.core.scss:dist/css/glide.core.min.css --style=compressed\",\n    \"sass:theme\": \"sass src/assets/sass/glide.theme.scss:dist/css/glide.theme.css && sass src/assets/sass/glide.theme.scss:dist/css/glide.theme.min.css --style=compressed\",\n    \"build:css\": \"npm run sass:core && npm run sass:theme\",\n    \"build:esm\": \"rollup --config build/esm.js && rollup --config build/esm.modular.js\",\n    \"build:umd\": \"rollup --config build/umd.js && rollup --config build/umd.min.js\",\n    \"build:js\": \"npm run build:esm && npm run build:umd\",\n    \"build\": \"npm run build:css && npm run build:js\",\n    \"test\": \"jest tests/**/*.test.js\",\n    \"lint\": \"eslint {src,tests}/**/*.js\"\n  },\n  \"exports\": {\n    \".\": {\n      \"import\": \"./dist/glide.esm.js\",\n      \"require\": \"./dist/glide.js\",\n      \"default\": \"./dist/glide.esm.js\"\n    },\n    \"./dist/*\": \"./dist/*\"\n  },\n  \"type\": \"module\",\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.16.0\",\n    \"@babel/preset-env\": \"^7.16.4\",\n    \"@rollup/plugin-babel\": \"^5.3.0\",\n    \"babel-jest\": \"^27.3.1\",\n    \"eslint\": \"^8.3.0\",\n    \"eslint-config-standard\": \"^12.0.0\",\n    \"eslint-plugin-import\": \"^2.18.0\",\n    \"eslint-plugin-node\": \"^7.0.0\",\n    \"eslint-plugin-promise\": \"^4.0.0\",\n    \"eslint-plugin-standard\": \"^4.0.0\",\n    \"jest\": \"^27.3.1\",\n    \"jsdom\": \"^18.1.0\",\n    \"rollup\": \"^2.50.0\",\n    \"rollup-plugin-terser\": \"^7.0.2\",\n    \"sass\": \"^1.43.4\",\n    \"stylelint\": \"^14.1.0\",\n    \"stylelint-config-standard\": \"^24.0.0\",\n    \"stylelint-scss\": \"^4.0.0\"\n  }\n}\n"
  },
  {
    "path": "src/assets/sass/_variables.scss",
    "content": "$glide-class: 'glide' !default;\n$glide-element-separator: '__' !default;\n$glide-modifier-separator: '--' !default;\n"
  },
  {
    "path": "src/assets/sass/glide.core.scss",
    "content": "@import \"variables\";\r\n\r\n.#{$glide-class} {\r\n  $this: &;\r\n\r\n  $se: $glide-element-separator;\r\n  $sm: $glide-modifier-separator;\r\n\r\n  position: relative;\r\n  width: 100%;\r\n  box-sizing: border-box;\r\n\r\n  * {\r\n    box-sizing: inherit;\r\n  }\r\n\r\n  &#{$se}track {\r\n    overflow: hidden;\r\n  }\r\n\r\n  &#{$se}slides {\r\n    position: relative;\r\n    width: 100%;\r\n    list-style: none;\r\n    backface-visibility: hidden;\r\n    transform-style: preserve-3d;\r\n    touch-action: pan-Y;\r\n    overflow: hidden;\r\n    margin: 0;\r\n    padding: 0;\r\n    white-space: nowrap;\r\n    display: flex;\r\n    flex-wrap: nowrap;\r\n    will-change: transform;\r\n\r\n    &#{$glide-modifier-separator}dragging {\r\n      user-select: none;\r\n    }\r\n  }\r\n\r\n  &#{$se}slide {\r\n    width: 100%;\r\n    height: 100%;\r\n    flex-shrink: 0;\r\n    white-space: normal;\r\n    user-select: none;\r\n    -webkit-touch-callout: none;\r\n    -webkit-tap-highlight-color: transparent;\r\n\r\n    a {\r\n      user-select: none;\r\n      -webkit-user-drag: none;\r\n      -moz-user-select: none;\r\n      -ms-user-select: none;\r\n    }\r\n  }\r\n\r\n  &#{$se}arrows {\r\n    -webkit-touch-callout: none;\r\n    user-select: none;\r\n  }\r\n\r\n  &#{$se}bullets {\r\n    -webkit-touch-callout: none;\r\n    user-select: none;\r\n  }\r\n\r\n  &#{$sm}rtl {\r\n    direction: rtl;\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/assets/sass/glide.theme.scss",
    "content": "@import 'variables';\r\n\r\n.#{$glide-class} {\r\n  $this: &;\r\n\r\n  $se: $glide-element-separator;\r\n  $sm: $glide-modifier-separator;\r\n\r\n  &#{$se}arrow {\r\n    position: absolute;\r\n    display: block;\r\n    top: 50%;\r\n    z-index: 2;\r\n    color: white;\r\n    text-transform: uppercase;\r\n    padding: 9px 12px;\r\n    background-color: transparent;\r\n    border: 2px solid rgba(255, 255, 255, 0.5);\r\n    border-radius: 4px;\r\n    box-shadow: 0 0.25em 0.5em 0 rgba(0, 0, 0, 0.1);\r\n    text-shadow: 0 0.25em 0.5em rgba(0, 0, 0, 0.1);\r\n    opacity: 1;\r\n    cursor: pointer;\r\n    transition: opacity 150ms ease, border 300ms ease-in-out;\r\n    transform: translateY(-50%);\r\n    line-height: 1;\r\n\r\n    &:focus { outline: none; }\r\n    &:hover { border-color: white; }\r\n\r\n    &#{$sm}left {\r\n      left: 2em;\r\n    }\r\n\r\n    &#{$sm}right {\r\n      right: 2em;\r\n    }\r\n\r\n    &#{$sm}disabled {\r\n      opacity: 0.33;\r\n    }\r\n  }\r\n\r\n  &#{$se}bullets {\r\n    position: absolute;\r\n    z-index: 2;\r\n    bottom: 2em;\r\n    left: 50%;\r\n    display: inline-flex;\r\n    list-style: none;\r\n    transform: translateX(-50%);\r\n  }\r\n\r\n  &#{$se}bullet {\r\n    background-color: rgba(255, 255, 255, 0.5);\r\n    width: 9px;\r\n    height: 9px;\r\n    padding: 0;\r\n    border-radius: 50%;\r\n    border: 2px solid transparent;\r\n    transition: all 300ms ease-in-out;\r\n    cursor: pointer;\r\n    line-height: 0;\r\n    box-shadow: 0 0.25em 0.5em 0 rgba(0, 0, 0, 0.1);\r\n    margin: 0 0.25em;\r\n\r\n    &:focus {\r\n      outline: none;\r\n    }\r\n\r\n    &:hover,\r\n    &:focus {\r\n      border: 2px solid white;\r\n      background-color: rgba(255, 255, 255, 0.5);\r\n    }\r\n\r\n    &#{$sm}active {\r\n      background-color: white;\r\n    }\r\n  }\r\n\r\n  &#{$sm}swipeable {\r\n    cursor: grab;\r\n    cursor: -moz-grab;\r\n    cursor: -webkit-grab;\r\n  }\r\n\r\n  &#{$sm}dragging {\r\n    cursor: grabbing;\r\n    cursor: -moz-grabbing;\r\n    cursor: -webkit-grabbing;\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/components/anchors.js",
    "content": "import { define } from '../utils/object'\n\nimport EventsBinder from '../core/event/events-binder'\n\nexport default function (Glide, Components, Events) {\n  /**\n   * Instance of the binder for DOM Events.\n   *\n   * @type {EventsBinder}\n   */\n  const Binder = new EventsBinder()\n\n  /**\n   * Holds detaching status of anchors.\n   * Prevents detaching of already detached anchors.\n   *\n   * @private\n   * @type {Boolean}\n   */\n  let detached = false\n\n  /**\n   * Holds preventing status of anchors.\n   * If `true` redirection after click will be disabled.\n   *\n   * @private\n   * @type {Boolean}\n   */\n  let prevented = false\n\n  const Anchors = {\n    /**\n     * Setups a initial state of anchors component.\n     *\n     * @returns {Void}\n     */\n    mount () {\n      /**\n       * Holds collection of anchors elements.\n       *\n       * @private\n       * @type {HTMLCollection}\n       */\n      this._a = Components.Html.wrapper.querySelectorAll('a')\n\n      this.bind()\n    },\n\n    /**\n     * Binds events to anchors inside a track.\n     *\n     * @return {Void}\n     */\n    bind () {\n      Binder.on('click', Components.Html.wrapper, this.click)\n    },\n\n    /**\n     * Unbinds events attached to anchors inside a track.\n     *\n     * @return {Void}\n     */\n    unbind () {\n      Binder.off('click', Components.Html.wrapper)\n    },\n\n    /**\n     * Handler for click event. Prevents clicks when glide is in `prevent` status.\n     *\n     * @param  {Object} event\n     * @return {Void}\n     */\n    click (event) {\n      if (prevented) {\n        event.stopPropagation()\n        event.preventDefault()\n      }\n    },\n\n    /**\n     * Detaches anchors click event inside glide.\n     *\n     * @return {self}\n     */\n    detach () {\n      prevented = true\n\n      if (!detached) {\n        for (var i = 0; i < this.items.length; i++) {\n          this.items[i].draggable = false\n        }\n\n        detached = true\n      }\n\n      return this\n    },\n\n    /**\n     * Attaches anchors click events inside glide.\n     *\n     * @return {self}\n     */\n    attach () {\n      prevented = false\n\n      if (detached) {\n        for (var i = 0; i < this.items.length; i++) {\n          this.items[i].draggable = true\n        }\n\n        detached = false\n      }\n\n      return this\n    }\n  }\n\n  define(Anchors, 'items', {\n    /**\n     * Gets collection of the arrows HTML elements.\n     *\n     * @return {HTMLElement[]}\n     */\n    get () {\n      return Anchors._a\n    }\n  })\n\n  /**\n   * Detach anchors inside slides:\n   * - on swiping, so they won't redirect to its `href` attributes\n   */\n  Events.on('swipe.move', () => {\n    Anchors.detach()\n  })\n\n  /**\n   * Attach anchors inside slides:\n   * - after swiping and transitions ends, so they can redirect after click again\n   */\n  Events.on('swipe.end', () => {\n    Components.Transition.after(() => {\n      Anchors.attach()\n    })\n  })\n\n  /**\n   * Unbind anchors inside slides:\n   * - on destroying, to bring anchors to its initial state\n   */\n  Events.on('destroy', () => {\n    Anchors.attach()\n    Anchors.unbind()\n    Binder.destroy()\n  })\n\n  return Anchors\n}\n"
  },
  {
    "path": "src/components/autoplay.js",
    "content": "import { define } from '../utils/object'\nimport { toInt, isUndefined } from '../utils/unit'\n\nimport EventsBinder from '../core/event/events-binder'\n\nexport default function (Glide, Components, Events) {\n  /**\n   * Instance of the binder for DOM Events.\n   *\n   * @type {EventsBinder}\n   */\n  const Binder = new EventsBinder()\n\n  const Autoplay = {\n    /**\n     * Initializes autoplaying and events.\n     *\n     * @return {Void}\n     */\n    mount () {\n      this.enable();\n      this.start()\n\n      if (Glide.settings.hoverpause) {\n        this.bind()\n      }\n    },\n\n    /**\n     * Enables autoplaying\n     *\n     * @returns {Void}\n     */\n    enable () {\n      this._e = true\n    },\n\n    /**\n     * Disables autoplaying.\n     *\n     * @returns {Void}\n     */\n    disable () {\n      this._e = false\n    },\n\n    /**\n     * Starts autoplaying in configured interval.\n     *\n     * @param {Boolean|Number} force Run autoplaying with passed interval regardless of `autoplay` settings\n     * @return {Void}\n     */\n    start () {\n      if (!this._e) {\n        return\n      }\n\n      this.enable()\n\n      if (Glide.settings.autoplay) {\n        if (isUndefined(this._i)) {\n          this._i = setInterval(() => {\n            this.stop()\n\n            Components.Run.make('>')\n\n            this.start()\n\n            Events.emit('autoplay')\n          }, this.time)\n        }\n      }\n    },\n\n    /**\n     * Stops autorunning of the glide.\n     *\n     * @return {Void}\n     */\n    stop () {\n      this._i = clearInterval(this._i)\n    },\n\n    /**\n     * Stops autoplaying while mouse is over glide's area.\n     *\n     * @return {Void}\n     */\n    bind () {\n      Binder.on('mouseover', Components.Html.root, () => {\n        if (this._e) {\n          this.stop()\n        }\n      })\n\n      Binder.on('mouseout', Components.Html.root, () => {\n        if (this._e) {\n          this.start()\n        }\n      })\n    },\n\n    /**\n     * Unbind mouseover events.\n     *\n     * @returns {Void}\n     */\n    unbind () {\n      Binder.off(['mouseover', 'mouseout'], Components.Html.root)\n    }\n  }\n\n  define(Autoplay, 'time', {\n    /**\n     * Gets time period value for the autoplay interval. Prioritizes\n     * times in `data-glide-autoplay` attrubutes over options.\n     *\n     * @return {Number}\n     */\n    get () {\n      let autoplay = Components.Html.slides[Glide.index].getAttribute('data-glide-autoplay')\n\n      if (autoplay) {\n        return toInt(autoplay)\n      }\n\n      return toInt(Glide.settings.autoplay)\n    }\n  })\n\n  /**\n   * Stop autoplaying and unbind events:\n   * - on destroying, to clear defined interval\n   * - on updating via API to reset interval that may changed\n   */\n  Events.on(['destroy', 'update'], () => {\n    Autoplay.unbind()\n  })\n\n  /**\n   * Stop autoplaying:\n   * - before each run, to restart autoplaying\n   * - on pausing via API\n   * - on destroying, to clear defined interval\n   * - while starting a swipe\n   * - on updating via API to reset interval that may changed\n   */\n  Events.on(['run.before', 'swipe.start', 'update'], () => {\n    Autoplay.stop()\n  })\n\n  Events.on(['pause', 'destroy'], () => {\n    Autoplay.disable();\n    Autoplay.stop()\n  })\n\n  /**\n   * Start autoplaying:\n   * - after each run, to restart autoplaying\n   * - on playing via API\n   * - while ending a swipe\n   */\n  Events.on(['run.after', 'swipe.end'], () => {\n    Autoplay.start()\n  })\n\n\n  /**\n   * Start autoplaying:\n   * - after each run, to restart autoplaying\n   * - on playing via API\n   * - while ending a swipe\n   */\n  Events.on(['play'], () => {\n    Autoplay.enable();\n    Autoplay.start()\n  })\n\n  /**\n   * Remount autoplaying:\n   * - on updating via API to reset interval that may changed\n   */\n  Events.on('update', () => {\n    Autoplay.mount()\n  })\n\n  /**\n   * Destroy a binder:\n   * - on destroying glide instance to clearup listeners\n   */\n  Events.on('destroy', () => {\n    Binder.destroy()\n  })\n\n  return Autoplay\n}\n"
  },
  {
    "path": "src/components/breakpoints.js",
    "content": "import { warn } from '../utils/log'\nimport { throttle } from '../utils/wait'\nimport { isObject } from '../utils/unit'\nimport { sortKeys, mergeOptions } from '../utils/object'\n\nimport EventsBinder from '../core/event/events-binder'\n\n/**\n * Sorts keys of breakpoint object so they will be ordered from lower to bigger.\n *\n * @param {Object} points\n * @returns {Object}\n */\nfunction sortBreakpoints (points) {\n  if (isObject(points)) {\n    return sortKeys(points)\n  } else {\n    warn(`Breakpoints option must be an object`)\n  }\n\n  return {}\n}\n\nexport default function (Glide, Components, Events) {\n  /**\n   * Instance of the binder for DOM Events.\n   *\n   * @type {EventsBinder}\n   */\n  const Binder = new EventsBinder()\n\n  /**\n   * Holds reference to settings.\n   *\n   * @type {Object}\n   */\n  let settings = Glide.settings\n\n  /**\n   * Holds reference to breakpoints object in settings. Sorts breakpoints\n   * from smaller to larger. It is required in order to proper\n   * matching currently active breakpoint settings.\n   *\n   * @type {Object}\n   */\n  let points = sortBreakpoints(settings.breakpoints)\n\n  /**\n   * Cache initial settings before overwritting.\n   *\n   * @type {Object}\n   */\n  let defaults = Object.assign({}, settings)\n\n  const Breakpoints = {\n    /**\n     * Matches settings for currectly matching media breakpoint.\n     *\n     * @param {Object} points\n     * @returns {Object}\n     */\n    match (points) {\n      if (typeof window.matchMedia !== 'undefined') {\n        for (let point in points) {\n          if (points.hasOwnProperty(point)) {\n            if (window.matchMedia(`(max-width: ${point}px)`).matches) {\n              return points[point]\n            }\n          }\n        }\n      }\n\n      return defaults\n    }\n  }\n\n  /**\n   * Overwrite instance settings with currently matching breakpoint settings.\n   * This happens right after component initialization.\n   */\n  Object.assign(settings, Breakpoints.match(points))\n\n  /**\n   * Update glide with settings of matched brekpoint:\n   * - window resize to update slider\n   */\n  Binder.on('resize', window, throttle(() => {\n    Glide.settings = mergeOptions(settings, Breakpoints.match(points))\n  }, Glide.settings.throttle))\n\n  /**\n   * Resort and update default settings:\n   * - on reinit via API, so breakpoint matching will be performed with options\n   */\n  Events.on('update', () => {\n    points = sortBreakpoints(points)\n\n    defaults = Object.assign({}, settings)\n  })\n\n  /**\n   * Unbind resize listener:\n   * - on destroying, to bring markup to its initial state\n   */\n  Events.on('destroy', () => {\n    Binder.off('resize', window)\n  })\n\n  return Breakpoints\n}\n"
  },
  {
    "path": "src/components/build.js",
    "content": "import { siblings } from '../utils/dom'\n\nexport default function (Glide, Components, Events) {\n  const Build = {\n    /**\n     * Init glide building. Adds classes, sets\n     * dimensions and setups initial state.\n     *\n     * @return {Void}\n     */\n    mount () {\n      Events.emit('build.before')\n\n      this.typeClass()\n      this.activeClass()\n\n      Events.emit('build.after')\n    },\n\n    /**\n     * Adds `type` class to the glide element.\n     *\n     * @return {Void}\n     */\n    typeClass () {\n      Components.Html.root.classList.add(Glide.settings.classes.type[Glide.settings.type])\n    },\n\n    /**\n     * Sets active class to current slide.\n     *\n     * @return {Void}\n     */\n    activeClass () {\n      const classes = Glide.settings.classes\n      const slide = Components.Html.slides[Glide.index]\n\n      if (slide) {\n        slide.classList.add(classes.slide.active)\n\n        siblings(slide).forEach((sibling) => {\n          sibling.classList.remove(classes.slide.active)\n        })\n      }\n    },\n\n    /**\n     * Removes HTML classes applied at building.\n     *\n     * @return {Void}\n     */\n    removeClasses () {\n      const { type, slide } = Glide.settings.classes\n\n      Components.Html.root.classList.remove(type[Glide.settings.type])\n\n      Components.Html.slides.forEach((sibling) => {\n        sibling.classList.remove(slide.active)\n      })\n    }\n  }\n\n  /**\n   * Clear building classes:\n   * - on destroying to bring HTML to its initial state\n   * - on updating to remove classes before remounting component\n   */\n  Events.on(['destroy', 'update'], () => {\n    Build.removeClasses()\n  })\n\n  /**\n   * Remount component:\n   * - on resizing of the window to calculate new dimensions\n   * - on updating settings via API\n   */\n  Events.on(['resize', 'update'], () => {\n    Build.mount()\n  })\n\n  /**\n   * Swap active class of current slide:\n   * - after each move to the new index\n   */\n  Events.on('move.after', () => {\n    Build.activeClass()\n  })\n\n  return Build\n}\n"
  },
  {
    "path": "src/components/clones.js",
    "content": "import { define } from '../utils/object'\n\nexport default function (Glide, Components, Events) {\n  const Clones = {\n    /**\n     * Create pattern map and collect slides to be cloned.\n     */\n    mount () {\n      this.items = []\n\n      if (Glide.isType('carousel')) {\n        this.items = this.collect()\n      }\n    },\n\n    /**\n     * Collect clones with pattern.\n     *\n     * @return {[]}\n     */\n    collect (items = []) {\n      const { slides } = Components.Html\n      const { perView, classes, cloningRatio } = Glide.settings\n\n      if (slides.length > 0) {\n        const peekIncrementer = +!!Glide.settings.peek\n        const cloneCount = perView + peekIncrementer + Math.round(perView / 2)\n        const append = slides.slice(0, cloneCount).reverse()\n        const prepend = slides.slice(cloneCount * -1)\n\n        for (let r = 0; r < Math.max(cloningRatio, Math.floor(perView / slides.length)); r++) {\n          for (let i = 0; i < append.length; i++) {\n            const clone = append[i].cloneNode(true)\n\n            clone.classList.add(classes.slide.clone)\n\n            items.push(clone)\n          }\n\n          for (let i = 0; i < prepend.length; i++) {\n            const clone = prepend[i].cloneNode(true)\n\n            clone.classList.add(classes.slide.clone)\n\n            items.unshift(clone)\n          }\n        }\n      }\n\n      return items\n    },\n\n    /**\n     * Append cloned slides with generated pattern.\n     *\n     * @return {Void}\n     */\n    append () {\n      const { items } = this\n      const { wrapper, slides } = Components.Html\n\n      const half = Math.floor(items.length / 2)\n      const prepend = items.slice(0, half).reverse()\n      const append = items.slice(half * -1).reverse()\n      const width = `${Components.Sizes.slideWidth}px`\n\n      for (let i = 0; i < append.length; i++) {\n        wrapper.appendChild(append[i])\n      }\n\n      for (let i = 0; i < prepend.length; i++) {\n        wrapper.insertBefore(prepend[i], slides[0])\n      }\n\n      for (let i = 0; i < items.length; i++) {\n        items[i].style.width = width\n      }\n    },\n\n    /**\n     * Remove all cloned slides.\n     *\n     * @return {Void}\n     */\n    remove () {\n      let { items } = this\n\n      for (let i = 0; i < items.length; i++) {\n        Components.Html.wrapper.removeChild(items[i])\n      }\n    }\n  }\n\n  define(Clones, 'grow', {\n    /**\n     * Gets additional dimensions value caused by clones.\n     *\n     * @return {Number}\n     */\n    get () {\n      return (Components.Sizes.slideWidth + Components.Gaps.value) * Clones.items.length\n    }\n  })\n\n  /**\n   * Append additional slide's clones:\n   * - while glide's type is `carousel`\n   */\n  Events.on('update', () => {\n    Clones.remove()\n    Clones.mount()\n    Clones.append()\n  })\n\n  /**\n   * Append additional slide's clones:\n   * - while glide's type is `carousel`\n   */\n  Events.on('build.before', () => {\n    if (Glide.isType('carousel')) {\n      Clones.append()\n    }\n  })\n\n  /**\n   * Remove clones HTMLElements:\n   * - on destroying, to bring HTML to its initial state\n   */\n  Events.on('destroy', () => {\n    Clones.remove()\n  })\n\n  return Clones\n}\n"
  },
  {
    "path": "src/components/controls.js",
    "content": "import { siblings, toArray } from '../utils/dom'\nimport { define } from '../utils/object'\nimport supportsPassive from '../utils/detect-passive-event'\n\nimport EventsBinder from '../core/event/events-binder'\n\nconst NAV_SELECTOR = '[data-glide-el=\"controls[nav]\"]'\nconst CONTROLS_SELECTOR = '[data-glide-el^=\"controls\"]'\nconst PREVIOUS_CONTROLS_SELECTOR = `${CONTROLS_SELECTOR} [data-glide-dir*=\"<\"]`\nconst NEXT_CONTROLS_SELECTOR = `${CONTROLS_SELECTOR} [data-glide-dir*=\">\"]`\n\nexport default function (Glide, Components, Events) {\n  /**\n   * Instance of the binder for DOM Events.\n   *\n   * @type {EventsBinder}\n   */\n  const Binder = new EventsBinder()\n\n  const capture = (supportsPassive) ? { passive: true } : false\n\n  const Controls = {\n    /**\n     * Inits arrows. Binds events listeners\n     * to the arrows HTML elements.\n     *\n     * @return {Void}\n     */\n    mount () {\n      /**\n       * Collection of navigation HTML elements.\n       *\n       * @private\n       * @type {HTMLCollection}\n       */\n      this._n = Components.Html.root.querySelectorAll(NAV_SELECTOR)\n\n      /**\n       * Collection of controls HTML elements.\n       *\n       * @private\n       * @type {HTMLCollection}\n       */\n      this._c = Components.Html.root.querySelectorAll(CONTROLS_SELECTOR)\n\n      /**\n       * Collection of arrow control HTML elements.\n       *\n       * @private\n       * @type {Object}\n       */\n      this._arrowControls = {\n        previous: Components.Html.root.querySelectorAll(PREVIOUS_CONTROLS_SELECTOR),\n        next: Components.Html.root.querySelectorAll(NEXT_CONTROLS_SELECTOR)\n      }\n\n      this.addBindings()\n    },\n\n    /**\n     * Sets active class to current slide.\n     *\n     * @return {Void}\n     */\n    setActive () {\n      for (let i = 0; i < this._n.length; i++) {\n        this.addClass(this._n[i].children)\n      }\n    },\n\n    /**\n     * Removes active class to current slide.\n     *\n     * @return {Void}\n     */\n    removeActive () {\n      for (let i = 0; i < this._n.length; i++) {\n        this.removeClass(this._n[i].children)\n      }\n    },\n\n    /**\n     * Toggles active class on items inside navigation.\n     *\n     * @param  {HTMLElement} controls\n     * @return {Void}\n     */\n    addClass (controls) {\n      const settings = Glide.settings\n      const item = controls[Glide.index]\n\n      if (!item) {\n        return\n      }\n\n      item.classList.add(settings.classes.nav.active)\n\n      siblings(item).forEach(sibling => {\n        sibling.classList.remove(settings.classes.nav.active)\n      })\n    },\n\n    /**\n     * Removes active class from active control.\n     *\n     * @param  {HTMLElement} controls\n     * @return {Void}\n     */\n    removeClass (controls) {\n      const item = controls[Glide.index]\n\n      item?.classList.remove(Glide.settings.classes.nav.active)\n    },\n\n    /**\n     * Calculates, removes or adds `Glide.settings.classes.disabledArrow` class on the control arrows\n     */\n    setArrowState () {\n      if (Glide.settings.rewind) {\n        return\n      }\n\n      const next = Controls._arrowControls.next\n      const previous = Controls._arrowControls.previous\n\n      this.resetArrowState(next, previous)\n\n      if (Glide.index === 0) {\n        this.disableArrow(previous)\n      }\n\n      if (Glide.index === Components.Run.length) {\n        this.disableArrow(next)\n      }\n    },\n\n    /**\n     * Removes `Glide.settings.classes.disabledArrow` from given NodeList elements\n     *\n     * @param {NodeList[]} lists\n     */\n    resetArrowState (...lists) {\n      const settings = Glide.settings\n\n      lists.forEach(function (list) {\n        toArray(list).forEach(function (element) {\n          element.classList.remove(settings.classes.arrow.disabled)\n        })\n      })\n    },\n\n    /**\n     * Adds `Glide.settings.classes.disabledArrow` to given NodeList elements\n     *\n     * @param {NodeList[]} lists\n     */\n    disableArrow (...lists) {\n      const settings = Glide.settings\n\n      lists.forEach(function (list) {\n        toArray(list).forEach(function (element) {\n          element.classList.add(settings.classes.arrow.disabled)\n        })\n      })\n    },\n\n    /**\n     * Adds handles to the each group of controls.\n     *\n     * @return {Void}\n     */\n    addBindings () {\n      for (let i = 0; i < this._c.length; i++) {\n        this.bind(this._c[i].children)\n      }\n    },\n\n    /**\n     * Removes handles from the each group of controls.\n     *\n     * @return {Void}\n     */\n    removeBindings () {\n      for (let i = 0; i < this._c.length; i++) {\n        this.unbind(this._c[i].children)\n      }\n    },\n\n    /**\n     * Binds events to arrows HTML elements.\n     *\n     * @param {HTMLCollection} elements\n     * @return {Void}\n     */\n    bind (elements) {\n      for (let i = 0; i < elements.length; i++) {\n        Binder.on('click', elements[i], this.click)\n        Binder.on('touchstart', elements[i], this.click, capture)\n      }\n    },\n\n    /**\n     * Unbinds events binded to the arrows HTML elements.\n     *\n     * @param {HTMLCollection} elements\n     * @return {Void}\n     */\n    unbind (elements) {\n      for (let i = 0; i < elements.length; i++) {\n        Binder.off(['click', 'touchstart'], elements[i])\n      }\n    },\n\n    /**\n     * Handles `click` event on the arrows HTML elements.\n     * Moves slider in direction given via the\n     * `data-glide-dir` attribute.\n     *\n     * @param {Object} event\n     * @return {void}\n     */\n    click (event) {\n      if (!supportsPassive && event.type === 'touchstart') {\n        event.preventDefault()\n      }\n\n      const direction = event.currentTarget.getAttribute('data-glide-dir')\n\n      Components.Run.make(Components.Direction.resolve(direction))\n    }\n  }\n\n  define(Controls, 'items', {\n    /**\n     * Gets collection of the controls HTML elements.\n     *\n     * @return {HTMLElement[]}\n     */\n    get () {\n      return Controls._c\n    }\n  })\n\n  /**\n   * Swap active class of current navigation item:\n   * - after mounting to set it to initial index\n   * - after each move to the new index\n   */\n  Events.on(['mount.after', 'move.after'], () => {\n    Controls.setActive()\n  })\n\n  /**\n   * Add or remove disabled class of arrow elements\n   */\n  Events.on(['mount.after', 'run'], () => {\n    Controls.setArrowState()\n  })\n\n  /**\n   * Remove bindings and HTML Classes:\n   * - on destroying, to bring markup to its initial state\n   */\n  Events.on('destroy', () => {\n    Controls.removeBindings()\n    Controls.removeActive()\n    Binder.destroy()\n  })\n\n  return Controls\n}\n"
  },
  {
    "path": "src/components/direction.js",
    "content": "import { warn } from '../utils/log'\nimport { define } from '../utils/object'\n\nconst VALID_DIRECTIONS = ['ltr', 'rtl']\nconst FLIPED_MOVEMENTS = {\n  '>': '<',\n  '<': '>',\n  '=': '='\n}\n\nexport default function (Glide, Components, Events) {\n  const Direction = {\n    /**\n     * Setups gap value based on settings.\n     *\n     * @return {Void}\n     */\n    mount () {\n      this.value = Glide.settings.direction\n    },\n\n    /**\n     * Resolves pattern based on direction value\n     *\n     * @param {String} pattern\n     * @returns {String}\n     */\n    resolve (pattern) {\n      const token = pattern.slice(0, 1)\n\n      if (this.is('rtl')) {\n        return pattern.split(token).join(FLIPED_MOVEMENTS[token])\n      }\n\n      return pattern\n    },\n\n    /**\n     * Checks value of direction mode.\n     *\n     * @param {String} direction\n     * @returns {Boolean}\n     */\n    is (direction) {\n      return this.value === direction\n    },\n\n    /**\n     * Applies direction class to the root HTML element.\n     *\n     * @return {Void}\n     */\n    addClass () {\n      Components.Html.root.classList.add(Glide.settings.classes.direction[this.value])\n    },\n\n    /**\n     * Removes direction class from the root HTML element.\n     *\n     * @return {Void}\n     */\n    removeClass () {\n      Components.Html.root.classList.remove(Glide.settings.classes.direction[this.value])\n    }\n  }\n\n  define(Direction, 'value', {\n    /**\n     * Gets value of the direction.\n     *\n     * @returns {Number}\n     */\n    get () {\n      return Direction._v\n    },\n\n    /**\n     * Sets value of the direction.\n     *\n     * @param {String} value\n     * @return {Void}\n     */\n    set (value) {\n      if (VALID_DIRECTIONS.indexOf(value) > -1) {\n        Direction._v = value\n      } else {\n        warn('Direction value must be `ltr` or `rtl`')\n      }\n    }\n  })\n\n  /**\n   * Clear direction class:\n   * - on destroy to bring HTML to its initial state\n   * - on update to remove class before reappling bellow\n   */\n  Events.on(['destroy', 'update'], () => {\n    Direction.removeClass()\n  })\n\n  /**\n   * Remount component:\n   * - on update to reflect changes in direction value\n   */\n  Events.on('update', () => {\n    Direction.mount()\n  })\n\n  /**\n   * Apply direction class:\n   * - before building to apply class for the first time\n   * - on updating to reapply direction class that may changed\n   */\n  Events.on(['build.before', 'update'], () => {\n    Direction.addClass()\n  })\n\n  return Direction\n}\n"
  },
  {
    "path": "src/components/gaps.js",
    "content": "import { toInt } from '../utils/unit'\nimport { define } from '../utils/object'\nimport { throttle } from '../utils/wait'\n\nconst MARGIN_TYPE = {\n  ltr: ['marginLeft', 'marginRight'],\n  rtl: ['marginRight', 'marginLeft']\n}\n\nexport default function (Glide, Components, Events) {\n  const Gaps = {\n    /**\n     * Applies gaps between slides. First and last\n     * slides do not receive it's edge margins.\n     *\n     * @param {HTMLCollection} slides\n     * @return {Void}\n     */\n    apply (slides) {\n      for (let i = 0, len = slides.length; i < len; i++) {\n        const style = slides[i].style\n        const direction = Components.Direction.value\n\n        if (i !== 0) {\n          style[MARGIN_TYPE[direction][0]] = `${this.value / 2}px`\n        } else {\n          style[MARGIN_TYPE[direction][0]] = ''\n        }\n\n        if (i !== slides.length - 1) {\n          style[MARGIN_TYPE[direction][1]] = `${this.value / 2}px`\n        } else {\n          style[MARGIN_TYPE[direction][1]] = ''\n        }\n      }\n    },\n\n    /**\n     * Removes gaps from the slides.\n     *\n     * @param {HTMLCollection} slides\n     * @returns {Void}\n    */\n    remove (slides) {\n      for (let i = 0, len = slides.length; i < len; i++) {\n        const style = slides[i].style\n\n        style.marginLeft = ''\n        style.marginRight = ''\n      }\n    }\n  }\n\n  define(Gaps, 'value', {\n    /**\n     * Gets value of the gap.\n     *\n     * @returns {Number}\n     */\n    get () {\n      return toInt(Glide.settings.gap)\n    }\n  })\n\n  define(Gaps, 'grow', {\n    /**\n     * Gets additional dimensions value caused by gaps.\n     * Used to increase width of the slides wrapper.\n     *\n     * @returns {Number}\n     */\n    get () {\n      return Gaps.value * (Components.Sizes.length)\n    }\n  })\n\n  define(Gaps, 'reductor', {\n    /**\n     * Gets reduction value caused by gaps.\n     * Used to subtract width of the slides.\n     *\n     * @returns {Number}\n     */\n    get () {\n      const perView = Glide.settings.perView\n\n      return (Gaps.value * (perView - 1)) / perView\n    }\n  })\n\n  /**\n   * Apply calculated gaps:\n   * - after building, so slides (including clones) will receive proper margins\n   * - on updating via API, to recalculate gaps with new options\n   */\n  Events.on(['build.after', 'update'], throttle(() => {\n    Gaps.apply(Components.Html.wrapper.children)\n  }, 30))\n\n  /**\n   * Remove gaps:\n   * - on destroying to bring markup to its inital state\n   */\n  Events.on('destroy', () => {\n    Gaps.remove(Components.Html.wrapper.children)\n  })\n\n  return Gaps\n}\n"
  },
  {
    "path": "src/components/html.js",
    "content": "import { warn } from '../utils/log'\nimport { exist, toArray } from '../utils/dom'\nimport { define } from '../utils/object'\nimport { isString } from '../utils/unit'\n\nconst TRACK_SELECTOR = '[data-glide-el=\"track\"]'\n\nexport default function (Glide, Components, Events) {\n  const Html = {\n    /**\n     * Setup slider HTML nodes.\n     *\n     * @param {Glide} glide\n     */\n    mount () {\n      this.root = Glide.selector\n      this.track = this.root.querySelector(TRACK_SELECTOR)\n      this.collectSlides()\n    },\n\n    /**\n     * Collect slides\n     */\n    collectSlides () {\n      this.slides = toArray(this.wrapper.children).filter((slide) => {\n        return !slide.classList.contains(Glide.settings.classes.slide.clone)\n      })\n    }\n  }\n\n  define(Html, 'root', {\n    /**\n     * Gets node of the glide main element.\n     *\n     * @return {Object}\n     */\n    get () {\n      return Html._r\n    },\n\n    /**\n     * Sets node of the glide main element.\n     *\n     * @return {Object}\n     */\n    set (r) {\n      if (isString(r)) {\n        r = document.querySelector(r)\n      }\n\n      if (r !== null) {\n        Html._r = r\n      } else {\n        warn('Root element must be a existing Html node')\n      }\n    }\n  })\n\n  define(Html, 'track', {\n    /**\n     * Gets node of the glide track with slides.\n     *\n     * @return {Object}\n     */\n    get () {\n      return Html._t\n    },\n\n    /**\n     * Sets node of the glide track with slides.\n     *\n     * @return {Object}\n     */\n    set (t) {\n        Html._t = t\n    }\n  })\n\n  define(Html, 'wrapper', {\n    /**\n     * Gets node of the slides wrapper.\n     *\n     * @return {Object}\n     */\n    get () {\n      return Html.track.children[0]\n    }\n  })\n\n  /**\n   * Add/remove/reorder dynamic slides\n   */\n  Events.on('update', () => {\n    Html.collectSlides()\n  })\n\n  return Html\n}\n"
  },
  {
    "path": "src/components/images.js",
    "content": "import EventsBinder from '../core/event/events-binder'\n\nexport default function (Glide, Components, Events) {\n  /**\n   * Instance of the binder for DOM Events.\n   *\n   * @type {EventsBinder}\n   */\n  const Binder = new EventsBinder()\n\n  const Images = {\n    /**\n     * Binds listener to glide wrapper.\n     *\n     * @return {Void}\n     */\n    mount () {\n      this.bind()\n    },\n\n    /**\n     * Binds `dragstart` event on wrapper to prevent dragging images.\n     *\n     * @return {Void}\n     */\n    bind () {\n      Binder.on('dragstart', Components.Html.wrapper, this.dragstart)\n    },\n\n    /**\n     * Unbinds `dragstart` event on wrapper.\n     *\n     * @return {Void}\n     */\n    unbind () {\n      Binder.off('dragstart', Components.Html.wrapper)\n    },\n\n    /**\n     * Event handler. Prevents dragging.\n     *\n     * @return {Void}\n     */\n    dragstart (event) {\n      event.preventDefault()\n    }\n  }\n\n  /**\n   * Remove bindings from images:\n   * - on destroying, to remove added EventListeners\n   */\n  Events.on('destroy', () => {\n    Images.unbind()\n    Binder.destroy()\n  })\n\n  return Images\n}\n"
  },
  {
    "path": "src/components/keyboard.js",
    "content": "import EventsBinder from '../core/event/events-binder'\n\nexport default function (Glide, Components, Events) {\n  /**\n   * Instance of the binder for DOM Events.\n   *\n   * @type {EventsBinder}\n   */\n  const Binder = new EventsBinder()\n\n  const Keyboard = {\n    /**\n     * Binds keyboard events on component mount.\n     *\n     * @return {Void}\n     */\n    mount () {\n      if (Glide.settings.keyboard) {\n        this.bind()\n      }\n    },\n\n    /**\n     * Adds keyboard press events.\n     *\n     * @return {Void}\n     */\n    bind () {\n      Binder.on('keyup', document, this.press)\n    },\n\n    /**\n     * Removes keyboard press events.\n     *\n     * @return {Void}\n     */\n    unbind () {\n      Binder.off('keyup', document)\n    },\n\n    /**\n     * Handles keyboard's arrows press and moving glide foward and backward.\n     *\n     * @param  {Object} event\n     * @return {Void}\n     */\n    press (event) {\n      const { perSwipe } = Glide.settings\n\n      const arrowSymbols = { ArrowRight: '>', ArrowLeft: '<' }\n      if (['ArrowRight', 'ArrowLeft'].includes(event.code)) {\n        Components.Run.make(Components.Direction.resolve(`${perSwipe}${arrowSymbols[event.code]}`))\n      }\n    }\n  }\n\n  /**\n   * Remove bindings from keyboard:\n   * - on destroying to remove added events\n   * - on updating to remove events before remounting\n   */\n  Events.on(['destroy', 'update'], () => {\n    Keyboard.unbind()\n  })\n\n  /**\n   * Remount component\n   * - on updating to reflect potential changes in settings\n   */\n  Events.on('update', () => {\n    Keyboard.mount()\n  })\n\n  /**\n   * Destroy binder:\n   * - on destroying to remove listeners\n   */\n  Events.on('destroy', () => {\n    Binder.destroy()\n  })\n\n  return Keyboard\n}\n"
  },
  {
    "path": "src/components/move.js",
    "content": "import { define } from '../utils/object'\nimport { toInt, isUndefined } from '../utils/unit'\n\nexport default function (Glide, Components, Events) {\n  const Move = {\n    /**\n     * Constructs move component.\n     *\n     * @returns {Void}\n     */\n    mount () {\n      this._o = 0\n    },\n\n    /**\n     * Calculates a movement value based on passed offset and currently active index.\n     *\n     * @param  {Number} offset\n     * @return {Void}\n     */\n    make (offset = 0) {\n      this.offset = offset\n\n      Events.emit('move', {\n        movement: this.value\n      })\n\n      Components.Transition.after(() => {\n        Events.emit('move.after', {\n          movement: this.value\n        })\n      })\n    }\n  }\n\n  define(Move, 'offset', {\n    /**\n     * Gets an offset value used to modify current translate.\n     *\n     * @return {Object}\n     */\n    get () {\n      return Move._o\n    },\n\n    /**\n     * Sets an offset value used to modify current translate.\n     *\n     * @return {Object}\n     */\n    set (value) {\n      Move._o = !isUndefined(value) ? toInt(value) : 0\n    }\n  })\n\n  define(Move, 'translate', {\n    /**\n     * Gets a raw movement value.\n     *\n     * @return {Number}\n     */\n    get () {\n      return Components.Sizes.slideWidth * Glide.index\n    }\n  })\n\n  define(Move, 'value', {\n    /**\n     * Gets an actual movement value corrected by offset.\n     *\n     * @return {Number}\n     */\n    get () {\n      const offset = this.offset\n      const translate = this.translate\n\n      if (Components.Direction.is('rtl')) {\n        return translate + offset\n      }\n\n      return translate - offset\n    }\n  })\n\n  /**\n   * Make movement to proper slide on:\n   * - before build, so glide will start at `startAt` index\n   * - on each standard run to move to newly calculated index\n   */\n  Events.on(['build.before', 'run'], () => {\n    Move.make()\n  })\n\n  return Move\n}\n"
  },
  {
    "path": "src/components/peek.js",
    "content": "import { define } from '../utils/object'\nimport { toInt, isObject } from '../utils/unit'\n\nexport default function (Glide, Components, Events) {\n  const Peek = {\n    /**\n     * Setups how much to peek based on settings.\n     *\n     * @return {Void}\n     */\n    mount () {\n      this.value = Glide.settings.peek\n    }\n  }\n\n  define(Peek, 'value', {\n    /**\n     * Gets value of the peek.\n     *\n     * @returns {Number|Object}\n     */\n    get () {\n      return Peek._v\n    },\n\n    /**\n     * Sets value of the peek.\n     *\n     * @param {Number|Object} value\n     * @return {Void}\n     */\n    set (value) {\n      if (isObject(value)) {\n        value.before = toInt(value.before)\n        value.after = toInt(value.after)\n      } else {\n        value = toInt(value)\n      }\n\n      Peek._v = value\n    }\n  })\n\n  define(Peek, 'reductor', {\n    /**\n     * Gets reduction value caused by peek.\n     *\n     * @returns {Number}\n     */\n    get () {\n      const value = Peek.value\n      const perView = Glide.settings.perView\n\n      if (isObject(value)) {\n        return (value.before / perView) + (value.after / perView)\n      }\n\n      return value * 2 / perView\n    }\n  })\n\n  /**\n   * Recalculate peeking sizes on:\n   * - when resizing window to update to proper percents\n   */\n  Events.on(['resize', 'update'], () => {\n    Peek.mount()\n  })\n\n  return Peek\n}\n"
  },
  {
    "path": "src/components/resize.js",
    "content": "import { throttle } from '../utils/wait'\n\nimport EventsBinder from '../core/event/events-binder'\n\nexport default function (Glide, Components, Events) {\n  /**\n   * Instance of the binder for DOM Events.\n   *\n   * @type {EventsBinder}\n   */\n  const Binder = new EventsBinder()\n\n  const Resize = {\n    /**\n     * Initializes window bindings.\n     */\n    mount () {\n      this.bind()\n    },\n\n    /**\n     * Binds `rezsize` listener to the window.\n     * It's a costly event, so we are debouncing it.\n     *\n     * @return {Void}\n     */\n    bind () {\n      Binder.on('resize', window, throttle(() => {\n        Events.emit('resize')\n      }, Glide.settings.throttle))\n    },\n\n    /**\n     * Unbinds listeners from the window.\n     *\n     * @return {Void}\n     */\n    unbind () {\n      Binder.off('resize', window)\n    }\n  }\n\n  /**\n   * Remove bindings from window:\n   * - on destroying, to remove added EventListener\n   */\n  Events.on('destroy', () => {\n    Resize.unbind()\n    Binder.destroy()\n  })\n\n  return Resize\n}\n"
  },
  {
    "path": "src/components/run.js",
    "content": "import { warn } from '../utils/log'\nimport { toInt } from '../utils/unit'\nimport { define } from '../utils/object'\n\nexport default function (Glide, Components, Events) {\n  const Run = {\n    /**\n     * Initializes autorunning of the glide.\n     *\n     * @return {Void}\n     */\n    mount () {\n      this._o = false\n    },\n\n    /**\n     * Makes glides running based on the passed moving schema.\n     *\n     * @param {String} move\n     */\n    make (move) {\n      if (!Glide.disabled) {\n        !Glide.settings.waitForTransition || Glide.disable()\n\n        this.move = move\n\n        Events.emit('run.before', this.move)\n\n        this.calculate()\n\n        Events.emit('run', this.move)\n\n        Components.Transition.after(() => {\n          if (this.isStart()) {\n            Events.emit('run.start', this.move)\n          }\n\n          if (this.isEnd()) {\n            Events.emit('run.end', this.move)\n          }\n\n          if (this.isOffset()) {\n            this._o = false\n\n            Events.emit('run.offset', this.move)\n          }\n\n          Events.emit('run.after', this.move)\n\n          Glide.enable()\n        })\n      }\n    },\n\n    /**\n     * Calculates current index based on defined move.\n     *\n     * @return {Number|Undefined}\n     */\n    calculate () {\n      const { move, length } = this\n      const { steps, direction } = move\n\n      // By default assume that size of view is equal to one slide\n      let viewSize = 1\n\n      // While direction is `=` we want jump to\n      // a specified index described in steps.\n      if (direction === '=') {\n        // Check if bound is true, \n        // as we want to avoid whitespaces.\n        if( Glide.settings.bound && toInt(steps) > length ) {\n          Glide.index = length\n          return\n        }\n\n        Glide.index = steps\n\n        return\n      }\n\n      // When pattern is equal to `>>` we want\n      // fast forward to the last slide.\n      if (direction === '>' && steps === '>') {\n        Glide.index = length\n\n        return\n      }\n\n      // When pattern is equal to `<<` we want\n      // fast forward to the first slide.\n      if (direction === '<' && steps === '<') {\n        Glide.index = 0\n\n        return\n      }\n\n      // pagination movement\n      if (direction === '|') {\n        viewSize = Glide.settings.perView || 1\n      }\n\n      // we are moving forward\n      if (direction === '>' || (direction === '|' && steps === '>')) {\n        const index = calculateForwardIndex(viewSize)\n\n        if (index > length) {\n          this._o = true\n        }\n\n        Glide.index = normalizeForwardIndex(index, viewSize)\n\n        return\n      }\n\n      // we are moving backward\n      if (direction === '<' || (direction === '|' && steps === '<')) {\n        const index = calculateBackwardIndex(viewSize)\n\n        if (index < 0) {\n          this._o = true\n        }\n\n        Glide.index = normalizeBackwardIndex(index, viewSize)\n\n        return\n      }\n\n      warn(`Invalid direction pattern [${direction}${steps}] has been used`)\n    },\n\n    /**\n     * Checks if we are on the first slide.\n     *\n     * @return {Boolean}\n     */\n    isStart () {\n      return Glide.index <= 0\n    },\n\n    /**\n     * Checks if we are on the last slide.\n     *\n     * @return {Boolean}\n     */\n    isEnd () {\n      return Glide.index >= this.length\n    },\n\n    /**\n     * Checks if we are making a offset run.\n     *\n     * @param {String} direction\n     * @return {Boolean}\n     */\n    isOffset (direction = undefined) {\n      if (!direction) {\n        return this._o\n      }\n\n      if (!this._o) {\n        return false\n      }\n\n      // did we view to the right?\n      if (direction === '|>') {\n        return this.move.direction === '|' && this.move.steps === '>'\n      }\n\n      // did we view to the left?\n      if (direction === '|<') {\n        return this.move.direction === '|' && this.move.steps === '<'\n      }\n\n      return this.move.direction === direction\n    },\n\n    /**\n     * Checks if bound mode is active\n     *\n     * @return {Boolean}\n     */\n    isBound () {\n      return Glide.isType('slider') && Glide.settings.focusAt !== 'center' && Glide.settings.bound\n    }\n  }\n\n  /**\n   * Returns index value to move forward/to the right\n   *\n   * @param viewSize\n   * @returns {Number}\n   */\n  function calculateForwardIndex (viewSize) {\n    const { index } = Glide\n\n    if (Glide.isType('carousel')) {\n      return index + viewSize\n    }\n\n    return index + (viewSize - (index % viewSize))\n  }\n\n  /**\n   * Normalizes the given forward index based on glide settings, preventing it to exceed certain boundaries\n   *\n   * @param index\n   * @param length\n   * @param viewSize\n   * @returns {Number}\n   */\n  function normalizeForwardIndex (index, viewSize) {\n    const { length } = Run\n\n    if (index <= length) {\n      return index\n    }\n\n    if (Glide.isType('carousel')) {\n      return index - (length + 1)\n    }\n\n    if (Glide.settings.rewind) {\n      // bound does funny things with the length, therefor we have to be certain\n      // that we are on the last possible index value given by bound\n      if (Run.isBound() && !Run.isEnd()) {\n        return length\n      }\n\n      return 0\n    }\n\n    if (Run.isBound()) {\n      return length\n    }\n\n    return Math.floor(length / viewSize) * viewSize\n  }\n\n  /**\n   * Calculates index value to move backward/to the left\n   *\n   * @param viewSize\n   * @returns {Number}\n   */\n  function calculateBackwardIndex (viewSize) {\n    const { index } = Glide\n\n    if (Glide.isType('carousel')) {\n      return index - viewSize\n    }\n\n    // ensure our back navigation results in the same index as a forward navigation\n    // to experience a homogeneous paging\n    const view = Math.ceil(index / viewSize)\n\n    return (view - 1) * viewSize\n  }\n\n  /**\n   * Normalizes the given backward index based on glide settings, preventing it to exceed certain boundaries\n   *\n   * @param index\n   * @param length\n   * @param viewSize\n   * @returns {*}\n   */\n  function normalizeBackwardIndex (index, viewSize) {\n    const { length } = Run\n\n    if (index >= 0) {\n      return index\n    }\n\n    if (Glide.isType('carousel')) {\n      return index + (length + 1)\n    }\n\n    if (Glide.settings.rewind) {\n      // bound does funny things with the length, therefor we have to be certain\n      // that we are on first possible index value before we to rewind to the length given by bound\n      if (Run.isBound() && Run.isStart()) {\n        return length\n      }\n\n      return Math.floor(length / viewSize) * viewSize\n    }\n\n    return 0\n  }\n\n  define(Run, 'move', {\n    /**\n     * Gets value of the move schema.\n     *\n     * @returns {Object}\n     */\n    get () {\n      return this._m\n    },\n\n    /**\n     * Sets value of the move schema.\n     *\n     * @returns {Object}\n     */\n    set (value) {\n      let step = value.substr(1)\n\n      this._m = {\n        direction: value.substr(0, 1),\n        steps: step ? (toInt(step) ? toInt(step) : step) : 0\n      }\n    }\n  })\n\n  define(Run, 'length', {\n    /**\n     * Gets value of the running distance based\n     * on zero-indexing number of slides.\n     *\n     * @return {Number}\n     */\n    get () {\n      let { settings } = Glide\n      let { length } = Components.Html.slides\n\n      // If the `bound` option is active, a maximum running distance should be\n      // reduced by `perView` and `focusAt` settings. Running distance\n      // should end before creating an empty space after instance.\n      if (this.isBound()) {\n        return (length - 1) - (toInt(settings.perView) - 1) + toInt(settings.focusAt)\n      }\n\n      return length - 1\n    }\n  })\n\n  define(Run, 'offset', {\n    /**\n     * Gets status of the offsetting flag.\n     *\n     * @return {Boolean}\n     */\n    get () {\n      return this._o\n    }\n  })\n\n  return Run\n}\n"
  },
  {
    "path": "src/components/sizes.js",
    "content": "import { define } from '../utils/object'\n\nexport default function (Glide, Components, Events) {\n  const Sizes = {\n    /**\n     * Setups dimensions of slides.\n     *\n     * @return {Void}\n     */\n    setupSlides () {\n      const width = `${this.slideWidth}px`\n      const slides = Components.Html.slides\n\n      for (let i = 0; i < slides.length; i++) {\n        slides[i].style.width = width\n      }\n    },\n\n    /**\n     * Setups dimensions of slides wrapper.\n     *\n     * @return {Void}\n     */\n    setupWrapper () {\n      Components.Html.wrapper.style.width = `${this.wrapperSize}px`\n    },\n\n    /**\n     * Removes applied styles from HTML elements.\n     *\n     * @returns {Void}\n     */\n    remove () {\n      const slides = Components.Html.slides\n\n      for (let i = 0; i < slides.length; i++) {\n        slides[i].style.width = ''\n      }\n\n      Components.Html.wrapper.style.width = ''\n    }\n  }\n\n  define(Sizes, 'length', {\n    /**\n     * Gets count number of the slides.\n     *\n     * @return {Number}\n     */\n    get () {\n      return Components.Html.slides.length\n    }\n  })\n\n  define(Sizes, 'width', {\n    /**\n     * Gets width value of the slider (visible area).\n     *\n     * @return {Number}\n     */\n    get () {\n      return Components.Html.track.offsetWidth\n    }\n  })\n\n  define(Sizes, 'wrapperSize', {\n    /**\n     * Gets size of the slides wrapper.\n     *\n     * @return {Number}\n     */\n    get () {\n      return Sizes.slideWidth * Sizes.length + Components.Gaps.grow + Components.Clones.grow\n    }\n  })\n\n  define(Sizes, 'slideWidth', {\n    /**\n     * Gets width value of a single slide.\n     *\n     * @return {Number}\n     */\n    get () {\n      return (Sizes.width / Glide.settings.perView) - Components.Peek.reductor - Components.Gaps.reductor\n    }\n  })\n\n  /**\n   * Apply calculated glide's dimensions:\n   * - before building, so other dimensions (e.g. translate) will be calculated propertly\n   * - when resizing window to recalculate sildes dimensions\n   * - on updating via API, to calculate dimensions based on new options\n   */\n  Events.on(['build.before', 'resize', 'update'], () => {\n    Sizes.setupSlides()\n    Sizes.setupWrapper()\n  })\n\n  /**\n   * Remove calculated glide's dimensions:\n   * - on destoting to bring markup to its inital state\n   */\n  Events.on('destroy', () => {\n    Sizes.remove()\n  })\n\n  return Sizes\n}\n"
  },
  {
    "path": "src/components/swipe.js",
    "content": "import { throttle } from '../utils/wait'\nimport { toInt, toFloat } from '../utils/unit'\nimport supportsPassive from '../utils/detect-passive-event'\n\nimport EventsBinder from '../core/event/events-binder'\n\nconst START_EVENTS = ['touchstart', 'mousedown']\nconst MOVE_EVENTS = ['touchmove', 'mousemove']\nconst END_EVENTS = ['touchend', 'touchcancel', 'mouseup', 'mouseleave']\nconst MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'mouseleave']\n\nexport default function (Glide, Components, Events) {\n  /**\n   * Instance of the binder for DOM Events.\n   *\n   * @type {EventsBinder}\n   */\n  const Binder = new EventsBinder()\n\n  let swipeSin = 0\n  let swipeStartX = 0\n  let swipeStartY = 0\n  let disabled = false\n  const capture = (supportsPassive) ? { passive: true } : false\n\n  const Swipe = {\n    /**\n     * Initializes swipe bindings.\n     *\n     * @return {Void}\n     */\n    mount () {\n      this.bindSwipeStart()\n    },\n\n    /**\n     * Handler for `swipestart` event. Calculates entry points of the user's tap.\n     *\n     * @param {Object} event\n     * @return {Void}\n     */\n    start (event) {\n      if (!disabled && !Glide.disabled) {\n        this.disable()\n\n        const swipe = this.touches(event)\n\n        swipeSin = null\n        swipeStartX = toInt(swipe.pageX)\n        swipeStartY = toInt(swipe.pageY)\n\n        this.bindSwipeMove()\n        this.bindSwipeEnd()\n\n        Events.emit('swipe.start')\n      }\n    },\n\n    /**\n     * Handler for `swipemove` event. Calculates user's tap angle and distance.\n     *\n     * @param {Object} event\n     */\n    move (event) {\n      if (!Glide.disabled) {\n        const { touchAngle, touchRatio, classes } = Glide.settings\n\n        const swipe = this.touches(event)\n\n        const subExSx = toInt(swipe.pageX) - swipeStartX\n        const subEySy = toInt(swipe.pageY) - swipeStartY\n        const powEX = Math.abs(subExSx << 2)\n        const powEY = Math.abs(subEySy << 2)\n        const swipeHypotenuse = Math.sqrt(powEX + powEY)\n        const swipeCathetus = Math.sqrt(powEY)\n\n        swipeSin = Math.asin(swipeCathetus / swipeHypotenuse)\n\n        if (swipeSin * 180 / Math.PI < touchAngle) {\n          event.stopPropagation()\n\n          Components.Move.make(subExSx * toFloat(touchRatio))\n\n          Components.Html.root.classList.add(classes.dragging)\n\n          Events.emit('swipe.move')\n        } else {\n          return false\n        }\n      }\n    },\n\n    /**\n     * Handler for `swipeend` event. Finitializes user's tap and decides about glide move.\n     *\n     * @param {Object} event\n     * @return {Void}\n     */\n    end (event) {\n      if (!Glide.disabled) {\n        const { perSwipe, touchAngle, classes } = Glide.settings\n\n        const swipe = this.touches(event)\n        const threshold = this.threshold(event)\n\n        const swipeDistance = swipe.pageX - swipeStartX\n        const swipeDeg = swipeSin * 180 / Math.PI\n\n        this.enable()\n\n        if (swipeDistance > threshold && swipeDeg < touchAngle) {\n          Components.Run.make(Components.Direction.resolve(`${perSwipe}<`))\n        } else if (swipeDistance < -threshold && swipeDeg < touchAngle) {\n          Components.Run.make(Components.Direction.resolve(`${perSwipe}>`))\n        } else {\n          // While swipe don't reach distance apply previous transform.\n          Components.Move.make()\n        }\n\n        Components.Html.root.classList.remove(classes.dragging)\n\n        this.unbindSwipeMove()\n        this.unbindSwipeEnd()\n\n        Events.emit('swipe.end')\n      }\n    },\n\n    /**\n     * Binds swipe's starting event.\n     *\n     * @return {Void}\n     */\n    bindSwipeStart () {\n      const { swipeThreshold, dragThreshold } = Glide.settings\n\n      if (swipeThreshold) {\n        Binder.on(START_EVENTS[0], Components.Html.wrapper, (event) => {\n          this.start(event)\n        }, capture)\n      }\n\n      if (dragThreshold) {\n        Binder.on(START_EVENTS[1], Components.Html.wrapper, (event) => {\n          this.start(event)\n        }, capture)\n      }\n    },\n\n    /**\n     * Unbinds swipe's starting event.\n     *\n     * @return {Void}\n     */\n    unbindSwipeStart () {\n      Binder.off(START_EVENTS[0], Components.Html.wrapper, capture)\n      Binder.off(START_EVENTS[1], Components.Html.wrapper, capture)\n    },\n\n    /**\n     * Binds swipe's moving event.\n     *\n     * @return {Void}\n     */\n    bindSwipeMove () {\n      Binder.on(MOVE_EVENTS, Components.Html.wrapper, throttle((event) => {\n        this.move(event)\n      }, Glide.settings.throttle), capture)\n    },\n\n    /**\n     * Unbinds swipe's moving event.\n     *\n     * @return {Void}\n     */\n    unbindSwipeMove () {\n      Binder.off(MOVE_EVENTS, Components.Html.wrapper, capture)\n    },\n\n    /**\n     * Binds swipe's ending event.\n     *\n     * @return {Void}\n     */\n    bindSwipeEnd () {\n      Binder.on(END_EVENTS, Components.Html.wrapper, (event) => {\n        this.end(event)\n      })\n    },\n\n    /**\n     * Unbinds swipe's ending event.\n     *\n     * @return {Void}\n     */\n    unbindSwipeEnd () {\n      Binder.off(END_EVENTS, Components.Html.wrapper)\n    },\n\n    /**\n     * Normalizes event touches points accorting to different types.\n     *\n     * @param {Object} event\n     */\n    touches (event) {\n      if (MOUSE_EVENTS.indexOf(event.type) > -1) {\n        return event\n      }\n\n      return event.touches[0] || event.changedTouches[0]\n    },\n\n    /**\n     * Gets value of minimum swipe distance settings based on event type.\n     *\n     * @return {Number}\n     */\n    threshold (event) {\n      const settings = Glide.settings\n\n      if (MOUSE_EVENTS.indexOf(event.type) > -1) {\n        return settings.dragThreshold\n      }\n\n      return settings.swipeThreshold\n    },\n\n    /**\n     * Enables swipe event.\n     *\n     * @return {self}\n     */\n    enable () {\n      disabled = false\n\n      Components.Transition.enable()\n\n      return this\n    },\n\n    /**\n     * Disables swipe event.\n     *\n     * @return {self}\n     */\n    disable () {\n      disabled = true\n\n      Components.Transition.disable()\n\n      return this\n    }\n  }\n\n  /**\n   * Add component class:\n   * - after initial building\n   */\n  Events.on('build.after', () => {\n    Components.Html.root.classList.add(Glide.settings.classes.swipeable)\n  })\n\n  /**\n   * Remove swiping bindings:\n   * - on destroying, to remove added EventListeners\n   */\n  Events.on('destroy', () => {\n    Swipe.unbindSwipeStart()\n    Swipe.unbindSwipeMove()\n    Swipe.unbindSwipeEnd()\n    Binder.destroy()\n  })\n\n  return Swipe\n}\n"
  },
  {
    "path": "src/components/transition.js",
    "content": "import { define } from '../utils/object'\n\nexport default function (Glide, Components, Events) {\n  /**\n   * Holds inactivity status of transition.\n   * When true transition is not applied.\n   *\n   * @type {Boolean}\n   */\n  let disabled = false\n\n  const Transition = {\n    /**\n     * Composes string of the CSS transition.\n     *\n     * @param {String} property\n     * @return {String}\n     */\n    compose (property) {\n      const settings = Glide.settings\n\n      if (disabled) {\n        return `${property} 0ms ${settings.animationTimingFunc}`\n      }\n\n      return `${property} ${this.duration}ms ${settings.animationTimingFunc}`\n    },\n\n    /**\n     * Sets value of transition on HTML element.\n     *\n     * @param {String=} property\n     * @return {Void}\n     */\n    set (property = 'transform') {\n      Components.Html.wrapper.style.transition = this.compose(property)\n    },\n\n    /**\n     * Removes value of transition from HTML element.\n     *\n     * @return {Void}\n     */\n    remove () {\n      Components.Html.wrapper.style.transition = ''\n    },\n\n    /**\n     * Runs callback after animation.\n     *\n     * @param  {Function} callback\n     * @return {Void}\n     */\n    after (callback) {\n      setTimeout(() => {\n        callback()\n      }, this.duration)\n    },\n\n    /**\n     * Enable transition.\n     *\n     * @return {Void}\n     */\n    enable () {\n      disabled = false\n\n      this.set()\n    },\n\n    /**\n     * Disable transition.\n     *\n     * @return {Void}\n     */\n    disable () {\n      disabled = true\n\n      this.set()\n    }\n  }\n\n  define(Transition, 'duration', {\n    /**\n     * Gets duration of the transition based\n     * on currently running animation type.\n     *\n     * @return {Number}\n     */\n    get () {\n      const settings = Glide.settings\n\n      if (Glide.isType('slider') && Components.Run.offset) {\n        return settings.rewindDuration\n      }\n\n      return settings.animationDuration\n    }\n  })\n\n  /**\n   * Set transition `style` value:\n   * - on each moving, because it may be cleared by offset move\n   */\n  Events.on('move', () => {\n    Transition.set()\n  })\n\n  /**\n   * Disable transition:\n   * - before initial build to avoid transitioning from `0` to `startAt` index\n   * - while resizing window and recalculating dimensions\n   * - on jumping from offset transition at start and end edges in `carousel` type\n   */\n  Events.on(['build.before', 'resize', 'translate.jump'], () => {\n    Transition.disable()\n  })\n\n  /**\n   * Enable transition:\n   * - on each running, because it may be disabled by offset move\n   */\n  Events.on('run', () => {\n    Transition.enable()\n  })\n\n  /**\n   * Remove transition:\n   * - on destroying to bring markup to its inital state\n   */\n  Events.on('destroy', () => {\n    Transition.remove()\n  })\n\n  return Transition\n}\n"
  },
  {
    "path": "src/components/translate.js",
    "content": "import mutator from '../mutator/index'\n\nexport default function (Glide, Components, Events) {\n  const Translate = {\n    /**\n     * Sets value of translate on HTML element.\n     *\n     * @param {Number} value\n     * @return {Void}\n     */\n    set (value) {\n      const transform = mutator(Glide, Components).mutate(value)\n      const translate3d = `translate3d(${-1 * transform}px, 0px, 0px)`\n\n      Components.Html.wrapper.style.mozTransform = translate3d // needed for supported Firefox 10-15\n      Components.Html.wrapper.style.webkitTransform = translate3d // needed for supported Chrome 10-35, Safari 5.1-8, and Opera 15-22\n      Components.Html.wrapper.style.transform = translate3d\n    },\n\n    /**\n     * Removes value of translate from HTML element.\n     *\n     * @return {Void}\n     */\n    remove () {\n      Components.Html.wrapper.style.transform = ''\n    },\n\n    /**\n     * @return {number}\n     */\n    getStartIndex () {\n      const length = Components.Sizes.length\n      const index = Glide.index\n      const perView = Glide.settings.perView\n\n      if (Components.Run.isOffset('>') || Components.Run.isOffset('|>')) {\n        return length + (index - perView)\n      }\n\n      // \"modulo length\" converts an index that equals length to zero\n      return (index + perView) % length\n    },\n\n    /**\n     * @return {number}\n     */\n    getTravelDistance () {\n      const travelDistance = Components.Sizes.slideWidth * Glide.settings.perView\n\n      if (Components.Run.isOffset('>') || Components.Run.isOffset('|>')) {\n        // reverse travel distance so that we don't have to change subtract operations\n        return travelDistance * -1\n      }\n\n      return travelDistance\n    }\n  }\n\n  /**\n   * Set new translate value:\n   * - on move to reflect index change\n   * - on updating via API to reflect possible changes in options\n   */\n  Events.on('move', (context) => {\n    if (!Glide.isType('carousel') || !Components.Run.isOffset()) {\n      return Translate.set(context.movement)\n    }\n\n    Components.Transition.after(() => {\n      Events.emit('translate.jump')\n\n      Translate.set(Components.Sizes.slideWidth * Glide.index)\n    })\n\n    const startWidth = Components.Sizes.slideWidth * Components.Translate.getStartIndex()\n    return Translate.set(startWidth - Components.Translate.getTravelDistance())\n  })\n\n  /**\n   * Remove translate:\n   * - on destroying to bring markup to its inital state\n   */\n  Events.on('destroy', () => {\n    Translate.remove()\n  })\n\n  return Translate\n}\n"
  },
  {
    "path": "src/core/event/events-binder.js",
    "content": "import { isString } from '../../utils/unit'\n\nexport default class EventsBinder {\n  /**\n   * Construct a EventsBinder instance.\n   */\n  constructor (listeners = {}) {\n    this.listeners = listeners\n  }\n\n  /**\n   * Adds events listeners to arrows HTML elements.\n   *\n   * @param  {String|Array} events\n   * @param  {Element|Window|Document} el\n   * @param  {Function} closure\n   * @param  {Boolean|Object} capture\n   * @return {Void}\n   */\n  on (events, el, closure, capture = false) {\n    if (isString(events)) {\n      events = [events]\n    }\n\n    for (let i = 0; i < events.length; i++) {\n      this.listeners[events[i]] = closure\n\n      el.addEventListener(events[i], this.listeners[events[i]], capture)\n    }\n  }\n\n  /**\n   * Removes event listeners from arrows HTML elements.\n   *\n   * @param  {String|Array} events\n   * @param  {Element|Window|Document} el\n   * @param  {Boolean|Object} capture\n   * @return {Void}\n   */\n  off (events, el, capture = false) {\n    if (isString(events)) {\n      events = [events]\n    }\n\n    for (let i = 0; i < events.length; i++) {\n      el.removeEventListener(events[i], this.listeners[events[i]], capture)\n    }\n  }\n\n  /**\n   * Destroy collected listeners.\n   *\n   * @returns {Void}\n   */\n  destroy () {\n    delete this.listeners\n  }\n}\n"
  },
  {
    "path": "src/core/event/events-bus.js",
    "content": "import { isArray } from '../../utils/unit'\n\nexport default class EventsBus {\n  /**\n   * Construct a EventBus instance.\n   *\n   * @param {Object} events\n   */\n  constructor (events = {}) {\n    this.events = events\n    this.hop = events.hasOwnProperty\n  }\n\n  /**\n   * Adds listener to the specifed event.\n   *\n   * @param {String|Array} event\n   * @param {Function} handler\n   */\n  on (event, handler) {\n    if (isArray(event)) {\n      for (let i = 0; i < event.length; i++) {\n        this.on(event[i], handler)\n      }\n\n      return\n    }\n\n    // Create the event's object if not yet created\n    if (!this.hop.call(this.events, event)) {\n      this.events[event] = []\n    }\n\n    // Add the handler to queue\n    const index = this.events[event].push(handler) - 1\n\n    // Provide handle back for removal of event\n    return {\n      remove () {\n        delete this.events[event][index]\n      }\n    }\n  }\n\n  /**\n   * Runs registered handlers for specified event.\n   *\n   * @param {String|Array} event\n   * @param {Object=} context\n   */\n  emit (event, context) {\n    if (isArray(event)) {\n      for (let i = 0; i < event.length; i++) {\n        this.emit(event[i], context)\n      }\n\n      return\n    }\n\n    // If the event doesn't exist, or there's no handlers in queue, just leave\n    if (!this.hop.call(this.events, event)) {\n      return\n    }\n\n    // Cycle through events queue, fire!\n    this.events[event].forEach((item) => {\n      item(context || {})\n    })\n  }\n}\n"
  },
  {
    "path": "src/core/index.js",
    "content": "import { warn } from '../utils/log'\nimport { isFunction } from '../utils/unit'\n\n/**\n * Creates and initializes specified collection of extensions.\n * Each extension receives access to instance of glide and rest of components.\n *\n * @param {Object} glide\n * @param {Object} extensions\n *\n * @returns {Object}\n */\nexport function mount (glide, extensions, events) {\n  let components = {}\n\n  for (let name in extensions) {\n    if (isFunction(extensions[name])) {\n      components[name] = extensions[name](glide, components, events)\n    } else {\n      warn('Extension must be a function')\n    }\n  }\n\n  for (let name in components) {\n    if (isFunction(components[name].mount)) {\n      components[name].mount()\n    }\n  }\n\n  return components\n}\n"
  },
  {
    "path": "src/defaults.js",
    "content": "export default {\n  /**\n   * Type of the movement.\n   *\n   * Available types:\n   * `slider` - Rewinds slider to the start/end when it reaches the first or last slide.\n   * `carousel` - Changes slides without starting over when it reaches the first or last slide.\n   *\n   * @type {String}\n   */\n  type: 'slider',\n\n  /**\n   * Start at specific slide number defined with zero-based index.\n   *\n   * @type {Number}\n   */\n  startAt: 0,\n\n  /**\n   * A number of slides visible on the single viewport.\n   *\n   * @type {Number}\n   */\n  perView: 1,\n\n  /**\n   * Focus currently active slide at a specified position in the track.\n   *\n   * Available inputs:\n   * `center` - Current slide will be always focused at the center of a track.\n   * `0,1,2,3...` - Current slide will be focused on the specified zero-based index.\n   *\n   * @type {String|Number}\n   */\n  focusAt: 0,\n\n  /**\n   * A size of the gap added between slides.\n   *\n   * @type {Number}\n   */\n  gap: 10,\n\n  /**\n   * Change slides after a specified interval. Use `false` for turning off autoplay.\n   *\n   * @type {Number|Boolean}\n   */\n  autoplay: false,\n\n  /**\n   * Stop autoplay on mouseover event.\n   *\n   * @type {Boolean}\n   */\n  hoverpause: true,\n\n  /**\n   * Allow for changing slides with left and right keyboard arrows.\n   *\n   * @type {Boolean}\n   */\n  keyboard: true,\n\n  /**\n   * Stop running `perView` number of slides from the end. Use this\n   * option if you don't want to have an empty space after\n   * a slider. Works only with `slider` type and a\n   * non-centered `focusAt` setting.\n   *\n   * @type {Boolean}\n   */\n  bound: false,\n\n  /**\n   * Minimal swipe distance needed to change the slide. Use `false` for turning off a swiping.\n   *\n   * @type {Number|Boolean}\n   */\n  swipeThreshold: 80,\n\n  /**\n   * Minimal mouse drag distance needed to change the slide. Use `false` for turning off a dragging.\n   *\n   * @type {Number|Boolean}\n   */\n  dragThreshold: 120,\n\n  /**\n   * A number of slides moved on single swipe.\n   *\n   * Available types:\n   * `` - Moves slider by one slide per swipe\n   * `|` - Moves slider between views per swipe (number of slides defined in `perView` options)\n   *\n   * @type {String}\n   */\n  perSwipe: '',\n\n  /**\n   * Moving distance ratio of the slides on a swiping and dragging.\n   *\n   * @type {Number}\n   */\n  touchRatio: 0.5,\n\n  /**\n   * Angle required to activate slides moving on swiping or dragging.\n   *\n   * @type {Number}\n   */\n  touchAngle: 45,\n\n  /**\n   * Duration of the animation in milliseconds.\n   *\n   * @type {Number}\n   */\n  animationDuration: 400,\n\n  /**\n   * Allows looping the `slider` type. Slider will rewind to the first/last slide when it's at the start/end.\n   *\n   * @type {Boolean}\n   */\n  rewind: true,\n\n  /**\n   * Duration of the rewinding animation of the `slider` type in milliseconds.\n   *\n   * @type {Number}\n   */\n  rewindDuration: 800,\n\n  /**\n   * Easing function for the animation.\n   *\n   * @type {String}\n   */\n  animationTimingFunc: 'cubic-bezier(.165, .840, .440, 1)',\n\n  /**\n   * Wait for the animation to finish until the next user input can be processed\n   *\n   * @type {boolean}\n   */\n  waitForTransition: true,\n\n  /**\n   * Throttle costly events at most once per every wait milliseconds.\n   *\n   * @type {Number}\n   */\n  throttle: 10,\n\n  /**\n   * Moving direction mode.\n   *\n   * Available inputs:\n   * - 'ltr' - left to right movement,\n   * - 'rtl' - right to left movement.\n   *\n   * @type {String}\n   */\n  direction: 'ltr',\n\n  /**\n   * The distance value of the next and previous viewports which\n   * have to peek in the current view. Accepts number and\n   * pixels as a string. Left and right peeking can be\n   * set up separately with a directions object.\n   *\n   * For example:\n   * `100` - Peek 100px on the both sides.\n   * { before: 100, after: 50 }` - Peek 100px on the left side and 50px on the right side.\n   *\n   * @type {Number|String|Object}\n   */\n  peek: 0,\n\n  /**\n   * Defines how many clones of current viewport will be generated.\n   *\n   * @type {Number}\n   */\n  cloningRatio: 1,\n\n  /**\n   * Collection of options applied at specified media breakpoints.\n   * For example: display two slides per view under 800px.\n   * `{\n   *   '800px': {\n   *     perView: 2\n   *   }\n   * }`\n   */\n  breakpoints: {},\n\n  /**\n   * Collection of internally used HTML classes.\n   *\n   * @todo Refactor `slider` and `carousel` properties to single `type: { slider: '', carousel: '' }` object\n   * @type {Object}\n   */\n  classes: {\n    swipeable: 'glide--swipeable',\n    dragging: 'glide--dragging',\n    direction: {\n      ltr: 'glide--ltr',\n      rtl: 'glide--rtl'\n    },\n    type: {\n      slider: 'glide--slider',\n      carousel: 'glide--carousel'\n    },\n    slide: {\n      clone: 'glide__slide--clone',\n      active: 'glide__slide--active'\n    },\n    arrow: {\n      disabled: 'glide__arrow--disabled'\n    },\n    nav: {\n      active: 'glide__bullet--active'\n    }\n  }\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "import defaults from './defaults'\r\nimport { warn } from './utils/log'\r\nimport { mount } from './core/index'\r\nimport { mergeOptions } from './utils/object'\r\nimport { toInt, isObject, isArray } from './utils/unit'\r\n\r\nimport EventsBus from './core/event/events-bus'\r\n\r\nexport default class Glide {\r\n  /**\r\n   * Construct glide.\r\n   *\r\n   * @param  {String} selector\r\n   * @param  {Object} options\r\n   */\r\n  constructor (selector, options = {}) {\r\n    this._c = {}\r\n    this._t = []\r\n    this._e = new EventsBus()\r\n\r\n    this.disabled = false\r\n    this.selector = selector\r\n    this.settings = mergeOptions(defaults, options)\r\n    this.index = this.settings.startAt\r\n  }\r\n\r\n  /**\r\n   * Initializes glide.\r\n   *\r\n   * @param {Object} extensions Collection of extensions to initialize.\r\n   * @return {Glide}\r\n   */\r\n  mount (extensions = {}) {\r\n    this._e.emit('mount.before')\r\n\r\n    if (isObject(extensions)) {\r\n      this._c = mount(this, extensions, this._e)\r\n    } else {\r\n      warn('You need to provide a object on `mount()`')\r\n    }\r\n\r\n    this._e.emit('mount.after')\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Collects an instance `translate` transformers.\r\n   *\r\n   * @param  {Array} transformers Collection of transformers.\r\n   * @return {Void}\r\n   */\r\n  mutate (transformers = []) {\r\n    if (isArray(transformers)) {\r\n      this._t = transformers\r\n    } else {\r\n      warn('You need to provide a array on `mutate()`')\r\n    }\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Updates glide with specified settings.\r\n   *\r\n   * @param {Object} settings\r\n   * @return {Glide}\r\n   */\r\n  update (settings = {}) {\r\n    this.settings = mergeOptions(this.settings, settings)\r\n\r\n    if (settings.hasOwnProperty('startAt')) {\r\n      this.index = settings.startAt\r\n    }\r\n\r\n    this._e.emit('update')\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Change slide with specified pattern. A pattern must be in the special format:\r\n   * `>` - Move one forward\r\n   * `<` - Move one backward\r\n   * `={i}` - Go to {i} zero-based slide (eq. '=1', will go to second slide)\r\n   * `>>` - Rewinds to end (last slide)\r\n   * `<<` - Rewinds to start (first slide)\r\n   * `|>` - Move one viewport forward\r\n   * `|<` - Move one viewport backward\r\n   *\r\n   * @param {String} pattern\r\n   * @return {Glide}\r\n   */\r\n  go (pattern) {\r\n    this._c.Run.make(pattern)\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Move track by specified distance.\r\n   *\r\n   * @param {String} distance\r\n   * @return {Glide}\r\n   */\r\n  move (distance) {\r\n    this._c.Transition.disable()\r\n    this._c.Move.make(distance)\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Destroy instance and revert all changes done by this._c.\r\n   *\r\n   * @return {Glide}\r\n   */\r\n  destroy () {\r\n    this._e.emit('destroy')\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Start instance autoplaying.\r\n   *\r\n   * @param {Boolean|Number} interval Run autoplaying with passed interval regardless of `autoplay` settings\r\n   * @return {Glide}\r\n   */\r\n  play (interval = false) {\r\n    if (interval) {\r\n      this.settings.autoplay = interval\r\n    }\r\n\r\n    this._e.emit('play')\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Stop instance autoplaying.\r\n   *\r\n   * @return {Glide}\r\n   */\r\n  pause () {\r\n    this._e.emit('pause')\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Sets glide into a idle status.\r\n   *\r\n   * @return {Glide}\r\n   */\r\n  disable () {\r\n    this.disabled = true\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Sets glide into a active status.\r\n   *\r\n   * @return {Glide}\r\n   */\r\n  enable () {\r\n    this.disabled = false\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Adds cuutom event listener with handler.\r\n   *\r\n   * @param  {String|Array} event\r\n   * @param  {Function} handler\r\n   * @return {Glide}\r\n   */\r\n  on (event, handler) {\r\n    this._e.on(event, handler)\r\n\r\n    return this\r\n  }\r\n\r\n  /**\r\n   * Checks if glide is a precised type.\r\n   *\r\n   * @param  {String} name\r\n   * @return {Boolean}\r\n   */\r\n  isType (name) {\r\n    return this.settings.type === name\r\n  }\r\n\r\n  /**\r\n   * Gets value of the core options.\r\n   *\r\n   * @return {Object}\r\n   */\r\n  get settings () {\r\n    return this._o\r\n  }\r\n\r\n  /**\r\n   * Sets value of the core options.\r\n   *\r\n   * @param  {Object} o\r\n   * @return {Void}\r\n   */\r\n  set settings (o) {\r\n    if (isObject(o)) {\r\n      this._o = o\r\n    } else {\r\n      warn('Options must be an `object` instance.')\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Gets current index of the slider.\r\n   *\r\n   * @return {Object}\r\n   */\r\n  get index () {\r\n    return this._i\r\n  }\r\n\r\n  /**\r\n   * Sets current index a slider.\r\n   *\r\n   * @return {Object}\r\n   */\r\n  set index (i) {\r\n    this._i = toInt(i)\r\n  }\r\n\r\n  /**\r\n   * Gets type name of the slider.\r\n   *\r\n   * @return {String}\r\n   */\r\n  get type () {\r\n    return this.settings.type\r\n  }\r\n\r\n  /**\r\n   * Gets value of the idle status.\r\n   *\r\n   * @return {Boolean}\r\n   */\r\n  get disabled () {\r\n    return this._d\r\n  }\r\n\r\n  /**\r\n   * Sets value of the idle status.\r\n   *\r\n   * @return {Boolean}\r\n   */\r\n  set disabled (status) {\r\n    this._d = !!status\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/mutator/index.js",
    "content": "import { warn } from '../utils/log'\nimport { isFunction } from '../utils/unit'\n\nimport Rtl from './transformers/rtl'\nimport Gap from './transformers/gap'\nimport Grow from './transformers/grow'\nimport Peeking from './transformers/peeking'\nimport Focusing from './transformers/focusing'\n\n/**\n * Applies diffrent transformers on translate value.\n *\n * @param  {Object} Glide\n * @param  {Object} Components\n * @return {Object}\n */\nexport default function (Glide, Components, Events) {\n  /**\n   * Merge instance transformers with collection of default transformers.\n   * It's important that the Rtl component be last on the list,\n   * so it reflects all previous transformations.\n   *\n   * @type {Array}\n   */\n  let TRANSFORMERS = [\n    Gap,\n    Grow,\n    Peeking,\n    Focusing\n  ].concat(Glide._t, [Rtl])\n\n  return {\n    /**\n     * Piplines translate value with registered transformers.\n     *\n     * @param  {Number} translate\n     * @return {Number}\n     */\n    mutate (translate) {\n      for (var i = 0; i < TRANSFORMERS.length; i++) {\n        let transformer = TRANSFORMERS[i]\n\n        if (isFunction(transformer) && isFunction(transformer().modify)) {\n          translate = transformer(Glide, Components, Events).modify(translate)\n        } else {\n          warn('Transformer should be a function that returns an object with `modify()` method')\n        }\n      }\n\n      return translate\n    }\n  }\n}\n"
  },
  {
    "path": "src/mutator/transformers/focusing.js",
    "content": "/**\n * Updates glide movement with a `focusAt` settings.\n *\n * @param  {Object} Glide\n * @param  {Object} Components\n * @return {Object}\n */\nexport default function (Glide, Components) {\n  return {\n    /**\n     * Modifies passed translate value with index in the `focusAt` setting.\n     *\n     * @param  {Number} translate\n     * @return {Number}\n     */\n    modify (translate) {\n      const gap = Components.Gaps.value\n      const width = Components.Sizes.width\n      const focusAt = Glide.settings.focusAt\n      const slideWidth = Components.Sizes.slideWidth\n\n      if (focusAt === 'center') {\n        return translate - (width / 2 - slideWidth / 2)\n      }\n\n      return translate - (slideWidth * focusAt) - (gap * focusAt)\n    }\n  }\n}\n"
  },
  {
    "path": "src/mutator/transformers/gap.js",
    "content": "/**\n * Updates glide movement with a `gap` settings.\n *\n * @param  {Object} Glide\n * @param  {Object} Components\n * @return {Object}\n */\nexport default function (Glide, Components) {\n  return {\n    /**\n     * Modifies passed translate value with number in the `gap` settings.\n     *\n     * @param  {Number} translate\n     * @return {Number}\n     */\n    modify (translate) {\n      const multiplier = Math.floor(translate / Components.Sizes.slideWidth)\n      return translate + (Components.Gaps.value * multiplier)\n    }\n  }\n}\n"
  },
  {
    "path": "src/mutator/transformers/grow.js",
    "content": "/**\n * Updates glide movement with width of additional clones width.\n *\n * @param  {Object} Glide\n * @param  {Object} Components\n * @return {Object}\n */\nexport default function (Glide, Components) {\n  return {\n    /**\n     * Adds to the passed translate width of the half of clones.\n     *\n     * @param  {Number} translate\n     * @return {Number}\n     */\n    modify (translate) {\n      return translate + (Components.Clones.grow / 2)\n    }\n  }\n}\n"
  },
  {
    "path": "src/mutator/transformers/peeking.js",
    "content": "import { isObject } from '../../utils/unit'\n\n/**\n * Updates glide movement with a `peek` settings.\n *\n * @param  {Object} Glide\n * @param  {Object} Components\n * @return {Object}\n */\nexport default function (Glide, Components) {\n  return {\n    /**\n     * Modifies passed translate value with a `peek` setting.\n     *\n     * @param  {Number} translate\n     * @return {Number}\n     */\n    modify (translate) {\n      if (Glide.settings.focusAt >= 0) {\n        const peek = Components.Peek.value\n\n        if (isObject(peek)) {\n          return translate - peek.before\n        }\n\n        return translate - peek\n      }\n\n      return translate\n    }\n  }\n}\n"
  },
  {
    "path": "src/mutator/transformers/rtl.js",
    "content": "/**\n * Reflects value of glide movement.\n *\n * @param  {Object} Glide\n * @param  {Object} Components\n * @return {Object}\n */\nexport default function (Glide, Components) {\n  return {\n    /**\n     * Negates the passed translate if glide is in RTL option.\n     *\n     * @param  {Number} translate\n     * @return {Number}\n     */\n    modify (translate) {\n      if (Components.Direction.is('rtl')) {\n        return -translate\n      }\n\n      return translate\n    }\n  }\n}\n"
  },
  {
    "path": "src/utils/detect-passive-event.js",
    "content": "/**\n * Test via a getter in the options object to see\n * if the passive property is accessed.\n *\n * @see https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection\n */\n\nlet supportsPassive = false\n\ntry {\n  let opts = Object.defineProperty({}, 'passive', {\n    get () {\n      supportsPassive = true\n    }\n  })\n\n  window.addEventListener('testPassive', null, opts)\n  window.removeEventListener('testPassive', null, opts)\n} catch (e) {}\n\nexport default supportsPassive\n"
  },
  {
    "path": "src/utils/dom.js",
    "content": "/**\n * Finds siblings nodes of the passed node.\n *\n * @param  {Element} node\n * @return {Array}\n */\nexport function siblings (node) {\n  if (node && node.parentNode) {\n    let n = node.parentNode.firstChild\n    const matched = []\n\n    for (; n; n = n.nextSibling) {\n      if (n.nodeType === 1 && n !== node) {\n        matched.push(n)\n      }\n    }\n\n    return matched\n  }\n\n  return []\n}\n\n/**\n * Coerces a NodeList to an Array.\n *\n * @param  {NodeList} nodeList\n * @return {Array}\n */\n\nexport function toArray (nodeList) {\n  return Array.prototype.slice.call(nodeList)\n}\n"
  },
  {
    "path": "src/utils/log.js",
    "content": "/**\n * Outputs warning message to the bowser console.\n *\n * @param  {String} msg\n * @return {Void}\n */\nexport function warn (msg) {\n  console.error(`[Glide warn]: ${msg}`)\n}\n"
  },
  {
    "path": "src/utils/object.js",
    "content": "/**\n * Defines getter and setter property on the specified object.\n *\n * @param  {Object} obj         Object where property has to be defined.\n * @param  {String} prop        Name of the defined property.\n * @param  {Object} definition  Get and set definitions for the property.\n * @return {Void}\n */\nexport function define (obj, prop, definition) {\n  Object.defineProperty(obj, prop, definition)\n}\n\n/**\n * Sorts aphabetically object keys.\n *\n * @param  {Object} obj\n * @return {Object}\n */\nexport function sortKeys (obj) {\n  return Object.keys(obj).sort().reduce((r, k) => {\n    r[k] = obj[k]\n\n    return (r[k], r)\n  }, {})\n}\n\n/**\n * Merges passed settings object with default options.\n *\n * @param  {Object} defaults\n * @param  {Object} settings\n * @return {Object}\n */\nexport function mergeOptions (defaults, settings) {\n  let options = Object.assign({}, defaults, settings)\n\n  // `Object.assign` do not deeply merge objects, so we\n  // have to do it manually for every nested object\n  // in options. Although it does not look smart,\n  // it's smaller and faster than some fancy\n  // merging deep-merge algorithm script.\n  if (settings.hasOwnProperty('classes')) {\n    options.classes = Object.assign({}, defaults.classes, settings.classes)\n\n    const properties = ['direction', 'type', 'slide', 'arrow', 'nav']\n    properties.forEach(property => {\n      if (settings.classes.hasOwnProperty(property)) {\n        options.classes[property] = { ...defaults.classes[property], ...settings.classes[property] }\n      }\n    })\n  }\n\n  if (settings.hasOwnProperty('breakpoints')) {\n    options.breakpoints = Object.assign({}, defaults.breakpoints, settings.breakpoints)\n  }\n\n  return options\n}\n"
  },
  {
    "path": "src/utils/string.js",
    "content": "/**\n * Makes a string's first character uppercase.\n *\n * @param  {String} string\n * @return {String}\n */\nexport function ucfirst (string) {\n  return string.charAt(0).toUpperCase() + string.slice(1)\n}\n"
  },
  {
    "path": "src/utils/time.js",
    "content": "/**\n * Returns a current time.\n *\n * @return {Number}\n */\nexport function now () {\n  return new Date().getTime()\n}\n"
  },
  {
    "path": "src/utils/unit.js",
    "content": "/**\n * Converts value entered as number\n * or string to integer value.\n *\n * @param {String} value\n * @returns {Number}\n */\nexport function toInt (value) {\n  return parseInt(value)\n}\n\n/**\n * Converts value entered as number\n * or string to flat value.\n *\n * @param {String} value\n * @returns {Number}\n */\nexport function toFloat (value) {\n  return parseFloat(value)\n}\n\n/**\n * Indicates whether the specified value is a string.\n *\n * @param  {*}   value\n * @return {Boolean}\n */\nexport function isString (value) {\n  return typeof value === 'string'\n}\n\n/**\n * Indicates whether the specified value is an object.\n *\n * @param  {*} value\n * @return {Boolean}\n *\n * @see https://github.com/jashkenas/underscore\n */\nexport function isObject (value) {\n  let type = typeof value\n\n  return type === 'function' || type === 'object' && !!value // eslint-disable-line no-mixed-operators\n}\n\n/**\n * Indicates whether the specified value is a number.\n *\n * @param  {*} value\n * @return {Boolean}\n */\nexport function isNumber (value) {\n  return typeof value === 'number'\n}\n\n/**\n * Indicates whether the specified value is a function.\n *\n * @param  {*} value\n * @return {Boolean}\n */\nexport function isFunction (value) {\n  return typeof value === 'function'\n}\n\n/**\n * Indicates whether the specified value is undefined.\n *\n * @param  {*} value\n * @return {Boolean}\n */\nexport function isUndefined (value) {\n  return typeof value === 'undefined'\n}\n\n/**\n * Indicates whether the specified value is an array.\n *\n * @param  {*} value\n * @return {Boolean}\n */\nexport function isArray (value) {\n  return value.constructor === Array\n}\n"
  },
  {
    "path": "src/utils/wait.js",
    "content": "import { now } from './time'\n\n/**\n * Returns a function, that, when invoked, will only be triggered\n * at most once during a given window of time.\n *\n * @param {Function} func\n * @param {Number} wait\n * @param {Object=} options\n * @return {Function}\n *\n * @see https://github.com/jashkenas/underscore\n */\nexport function throttle (func, wait, options = {}) {\n  let timeout, context, args, result\n  let previous = 0\n\n  const later = function () {\n    previous = options.leading === false ? 0 : now()\n    timeout = null\n    result = func.apply(context, args)\n    if (!timeout) context = args = null\n  }\n\n  const throttled = function () {\n    let at = now()\n    if (!previous && options.leading === false) previous = at\n    let remaining = wait - (at - previous)\n    context = this\n    args = arguments\n    if (remaining <= 0 || remaining > wait) {\n      if (timeout) {\n        clearTimeout(timeout)\n        timeout = null\n      }\n      previous = at\n      result = func.apply(context, args)\n      if (!timeout) context = args = null\n    } else if (!timeout && options.trailing !== false) {\n      timeout = setTimeout(later, remaining)\n    }\n    return result\n  }\n\n  throttled.cancel = function () {\n    clearTimeout(timeout)\n    previous = 0\n    timeout = context = args = null\n  }\n\n  return throttled\n}\n"
  },
  {
    "path": "tests/fixtures/html.js",
    "content": "export default `\n<div id=\"glide\" class=\"glide\">\n  <div data-glide-el=\"controls\" class=\"glide__arrows\">\n    <button class=\"glide__arrow glide__arrow--start\" data-glide-dir=\"<<\">start</button>\n    <button class=\"glide__arrow glide__arrow--start\" data-glide-dir=\"|<\">prev viewport</button>\n    <button class=\"glide__arrow glide__arrow--prev\" data-glide-dir=\"<\">prev</button>\n    <button class=\"glide__arrow glide__arrow--next\" data-glide-dir=\">\">next</button>\n    <button class=\"glide__arrow glide__arrow--start\" data-glide-dir=\"|>\">next viewport</button>\n    <button class=\"glide__arrow glide__arrow--end\" data-glide-dir=\">>\">end</button>\n  </div>\n\n  <div data-glide-el=\"controls\" class=\"glide__bullets\">\n    <button class=\"glide__bullet\" data-glide-dir=\"=0\">0</button>\n    <button class=\"glide__bullet\" data-glide-dir=\"=1\">1</button>\n    <button class=\"glide__bullet\" data-glide-dir=\"=2\">2</button>\n    <button class=\"glide__bullet\" data-glide-dir=\"=3\">3</button>\n    <button class=\"glide__bullet\" data-glide-dir=\"=4\">4</button>\n    <button class=\"glide__bullet\" data-glide-dir=\"=5\">5</button>\n    <button class=\"glide__bullet\" data-glide-dir=\"=6\">6</button>\n  </div>\n\n  <div data-glide-el=\"track\" class=\"glide__track\">\n    <ul class=\"glide__slides\">\n      <li class=\"glide__slide\">0</li>\n      <li class=\"glide__slide\">1</li>\n      <li class=\"glide__slide\">2</li>\n      <li class=\"glide__slide\">3</li>\n      <li class=\"glide__slide\">4</li>\n      <li class=\"glide__slide\">5</li>\n      <li class=\"glide__slide\">6</li>\n    </ul>\n  </div>\n</div>\n`\n"
  },
  {
    "path": "tests/fixtures/query.js",
    "content": "const ROOT_SELECTOR = '.glide'\nconst CLONE_CLASS = 'glide__slide--clone'\nconst TRACK_SELECTOR = '[data-glide-el=\"track\"]'\nconst BULLETS_SELECTOR = `[data-glide-dir*=\"=\"]`\nconst PREVIOUS_CONTROLS_SELECTOR = `[data-glide-dir*=\"<\"]`\nconst NEXT_CONTROLS_SELECTOR = `[data-glide-dir*=\">\"]`\n\nexport function query (document) {\n  const root = document.querySelector(ROOT_SELECTOR)\n  const track = root.querySelector(TRACK_SELECTOR)\n  const wrapper = track.children[0]\n\n  const bullets = root.querySelectorAll(BULLETS_SELECTOR)\n  const previousControls = root.querySelectorAll(PREVIOUS_CONTROLS_SELECTOR)\n  const nextControls = root.querySelectorAll(NEXT_CONTROLS_SELECTOR)\n  const slides = Array.from(wrapper.children).filter((slide) => {\n    return !slide.classList.contains(CLONE_CLASS)\n  })\n  const clones = Array.from(wrapper.children).filter((slide) => {\n    return slide.classList.contains(CLONE_CLASS)\n  })\n\n  return {\n    root,\n    track,\n    wrapper,\n    slides,\n    clones,\n    bullets,\n    previousControls,\n    nextControls\n  }\n}\n"
  },
  {
    "path": "tests/fixtures/transition.js",
    "content": "import defaults from '../../src/defaults'\n\nexport function afterTransition (callback) {\n  setTimeout(callback, defaults.animationDuration + 10)\n}\n\nexport function afterRewindTransition (callback) {\n  setTimeout(callback, defaults.rewindDuration + 10)\n}\n"
  },
  {
    "path": "tests/functional/autoplay.test.js",
    "content": "import html from '../fixtures/html'\nimport { query } from '../fixtures/query'\nimport { afterTransition } from '../fixtures/transition'\n\nimport defaults from '../../src/defaults'\nimport Glide from '../../entry/entry-complete'\n\ndescribe('Glide initialized with `autoplay`', () => {\n  beforeEach(() => {\n    document.body.innerHTML = html\n  })\n\n  test('as `Number` should move to the next slide after defined interval', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { autoplay: 500 }).mount()\n\n    setTimeout(() => {\n      afterTransition(() => {\n        expect(glide.index).toBe(1)\n        expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(false)\n        expect(slides[1].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n        done()\n      })\n    }, 500)\n  })\n\n  test('as `false` should not move to the next slide', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { autoplay: false }).mount()\n\n    setTimeout(() => {\n      afterTransition(() => {\n        expect(glide.index).toBe(0)\n        expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n        expect(slides[1].classList.contains(defaults.classes.slide.active)).toBe(false)\n\n        done()\n      })\n    }, 500)\n  })\n})\n"
  },
  {
    "path": "tests/functional/carousel.test.js",
    "content": "import html from '../fixtures/html'\nimport { query } from '../fixtures/query'\nimport { afterTransition } from '../fixtures/transition'\n\nimport defaults from '../../src/defaults'\nimport Glide from '../../entry/entry-complete'\n\ndescribe('Glide initialized as `carousel`', () => {\n  beforeEach(() => {\n    document.body.innerHTML = html\n  })\n\n  test('should have a correct type', () => {\n    let glide = new Glide('#glide', { type: 'carousel' }).mount()\n\n    expect(glide.isType('carousel')).toBe(true)\n  })\n\n  test('should go to the last slide when we are on the first slide and moving backward', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', {\n      type: 'carousel',\n      startAt: 0\n    }).mount()\n\n    glide.go('<')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(false)\n      expect(slides[slides.length - 1].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('should go to the first slide when we are on the last slide and moving forward', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', {\n      type: 'carousel',\n      startAt: slides.length - 1\n    }).mount()\n\n    glide.go('>')\n\n    afterTransition(() => {\n      expect(slides[slides.length - 1].classList.contains(defaults.classes.slide.active)).toBe(false)\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('with odd number of `perView` slides should create sufficient cloning buffer', (done) => {\n    let glide = new Glide('#glide', {\n      type: 'carousel',\n      perView: 3\n    })\n\n    glide.on('build.after', () => {\n      let { clones } = query(document)\n\n      expect(clones.length).toBe(10)\n\n      done()\n    })\n\n    glide.mount()\n  })\n\n  test('with even number of `perView` slides should create sufficient cloning buffer', (done) => {\n    let glide = new Glide('#glide', {\n      type: 'carousel',\n      perView: 2\n    })\n\n    glide.on('build.after', () => {\n      let { clones } = query(document)\n\n      expect(clones.length).toBe(6)\n\n      done()\n    })\n\n    glide.mount()\n  })\n\n  test('with even number of `perView` slides should create cloning buffer advanced by `cloningRatio`', (done) => {\n    let glide = new Glide('#glide', {\n      type: 'carousel',\n      perView: 2,\n      cloningRatio: 2,\n    })\n\n    glide.on('build.after', () => {\n      let { clones } = query(document)\n\n      expect(clones.length).toBe(12)\n\n      done()\n    })\n\n    glide.mount()\n  })\n})\n"
  },
  {
    "path": "tests/functional/classes.test.js",
    "content": "import html from '../fixtures/html'\nimport { query } from '../fixtures/query'\nimport { afterTransition } from '../fixtures/transition'\n\nimport defaults from '../../src/defaults'\nimport Glide from '../../entry/entry-complete'\n\ndescribe('Class', () => {\n  beforeEach(() => {\n    document.body.innerHTML = html\n  })\n\n  test('`classes.type.slider` should be applied on the root element', () => {\n    let { root } = query(document)\n\n    new Glide('#glide').mount()\n\n    expect(root.classList.contains(defaults.classes.type.slider)).toBe(true)\n  })\n\n  test('`classes.type.carousel` should be applied on the root element', () => {\n    let { root } = query(document)\n\n    new Glide('#glide', { type: 'carousel' }).mount()\n\n    expect(root.classList.contains(defaults.classes.type.carousel)).toBe(true)\n  })\n\n  test('`classes.slide.active` should be applied on the 0 indexed slide element by default', () => {\n    let { slides } = query(document)\n\n    new Glide('#glide').mount()\n\n    expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n  })\n\n  test('`classes.slide.active` should be applied on the `startAt` indexed slide element', () => {\n    let { slides } = query(document)\n\n    new Glide('#glide', { startAt: 2 }).mount()\n\n    expect(slides[2].classList.contains(defaults.classes.slide.active)).toBe(true)\n  })\n\n  test('`classes.slide.active` should be applied to the new slide element after moving', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0 }).mount()\n\n    glide.go('>')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(false)\n      expect(slides[1].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`classes.swipeable` should be applied on the root element', () => {\n    let { root } = query(document)\n\n    new Glide('#glide').mount()\n\n    expect(root.classList.contains(defaults.classes.swipeable)).toBe(true)\n  })\n\n  test('`classes.direction.ltr` should be applied on the root element in `ltr` mode', () => {\n    let { root } = query(document)\n\n    new Glide('#glide').mount()\n\n    expect(root.classList.contains(defaults.classes.direction.ltr)).toBe(true)\n  })\n\n  test('`classes.direction.rtl` should be applied on the root element in `rtl` mode', () => {\n    let { root } = query(document)\n\n    new Glide('#glide', { direction: 'rtl' }).mount()\n\n    expect(root.classList.contains(defaults.classes.direction.rtl)).toBe(true)\n  })\n\n  test('`classes.disabledArrow` should be applied to first element from start', () => {\n    const { previousControls } = query(document)\n\n    new Glide('#glide', { rewind: false }).mount()\n\n    previousControls.forEach(function (control) {\n      expect(control.classList).toContain(defaults.classes.arrow.disabled)\n    })\n  })\n\n  test('`classes.disabledArrow` should be removed from first element', () => {\n    const { previousControls } = query(document)\n\n    const glide = new Glide('#glide', { rewind: false }).mount()\n\n    glide.go('>')\n\n    previousControls.forEach(function (control) {\n      expect(control.classList).not.toContain(defaults.classes.arrow.disabled)\n    })\n  })\n\n  test('`classes.disabledArrow` should be applied to first element after navigating', () => {\n    const { previousControls } = query(document)\n\n    const glide = new Glide('#glide', { rewind: false, startAt: 1 }).mount()\n\n    glide.go('<')\n\n    previousControls.forEach(function (control) {\n      expect(control.classList).toContain(defaults.classes.arrow.disabled)\n    })\n  })\n\n  test('`classes.disabledArrow` should be applied to last element from start', () => {\n    const { nextControls, slides } = query(document)\n    const length = slides.length - 1\n\n    new Glide('#glide', { rewind: false, startAt: length }).mount()\n\n    nextControls.forEach(function (control) {\n      expect(control.classList).toContain(defaults.classes.arrow.disabled)\n    })\n  })\n\n  test('`classes.disabledArrow` should be removed from last element', () => {\n    const { nextControls, slides } = query(document)\n    const length = slides.length - 1\n\n    const glide = new Glide('#glide', { rewind: false, startAt: length }).mount()\n\n    glide.go('<')\n\n    nextControls.forEach(function (control) {\n      expect(control.classList).not.toContain(defaults.classes.arrow.disabled)\n    })\n  })\n\n  test('`classes.disabledArrow` should be applied to last element after navigating', () => {\n    const { nextControls, slides } = query(document)\n    const length = slides.length - 1\n\n    const glide = new Glide('#glide', { rewind: false, startAt: length - 1 }).mount()\n\n    glide.go('>')\n\n    nextControls.forEach(function (control) {\n      expect(control.classList).toContain(defaults.classes.arrow.disabled)\n    })\n  })\n})\n"
  },
  {
    "path": "tests/functional/destroy.test.js",
    "content": "import html from '../fixtures/html'\nimport { query } from '../fixtures/query'\n\nimport defaults from '../../src/defaults'\nimport Glide from '../../entry/entry-complete'\n\ndescribe('After destroying an instance', () => {\n  beforeEach(() => {\n    document.body.innerHTML = html\n  })\n\n  test('`classes.type.slider` should not be applied on the root element', () => {\n    let { root } = query(document)\n\n    new Glide('#glide').mount().destroy()\n\n    expect(root.classList.contains(defaults.classes.type.slider)).toBe(false)\n  })\n\n  test('`classes.type.carousel` should not be applied on the root element', () => {\n    let { root } = query(document)\n\n    new Glide('#glide', { type: 'carousel' }).mount().destroy()\n\n    expect(root.classList.contains(defaults.classes.type.carousel)).toBe(false)\n  })\n\n  test('`classes.direction.ltr` should not be applied on the root element', () => {\n    let { root } = query(document)\n\n    new Glide('#glide').mount().destroy()\n\n    expect(root.classList.contains(defaults.classes.direction.ltr)).toBe(false)\n  })\n\n  test('`classes.direction.rtl` should not be applied on the root element', () => {\n    let { root } = query(document)\n\n    new Glide('#glide', { rtl: true }).mount().destroy()\n\n    expect(root.classList.contains(defaults.classes.direction.rtl)).toBe(false)\n  })\n\n  test('`classes.slide.active` should not be applied on any slide elements', () => {\n    let { slides } = query(document)\n\n    new Glide('#glide').mount().destroy()\n\n    for (let i = 0; i < slides.length; i++) {\n      expect(slides[i].classList.contains(defaults.classes.slide.active)).toBe(false)\n    }\n  })\n})\n"
  },
  {
    "path": "tests/functional/go.test.js",
    "content": "import html from '../fixtures/html'\nimport { query } from '../fixtures/query'\nimport { afterTransition } from '../fixtures/transition'\n\nimport defaults from '../../src/defaults'\nimport Glide from '../../entry/entry-complete'\n\ndescribe('Calling `go()` method with', () => {\n  beforeEach(() => {\n    document.body.innerHTML = html\n  })\n\n  test('invalid direction pattern should result in `warn()` message', (done) => {\n    let glide = new Glide('#glide')\n    let fn = jest.fn()\n\n    global.console = { error: fn }\n\n    glide.mount().go('??')\n\n    afterTransition(() => {\n      expect(fn).toHaveBeenCalled()\n\n      done()\n    })\n  })\n\n  test('go() should wait for transition end', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0 }).mount()\n\n    glide.go('>')\n    glide.go('>')\n    glide.go('>')\n\n    afterTransition(() => {\n      expect(slides[1].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('go() should not wait for transition when configured', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0, waitForTransition: false }).mount()\n\n    glide.go('>')\n    glide.go('>')\n    glide.go('>')\n\n    afterTransition(() => {\n      expect(slides[3].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`>` should move one slide forward', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0 }).mount()\n\n    glide.go('>')\n\n    afterTransition(() => {\n      expect(slides[1].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`<` should move one slide backward', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 1 }).mount()\n\n    glide.go('<')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`>>` should rewind to the last slide', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0 }).mount()\n\n    glide.go('>>')\n\n    afterTransition(() => {\n      expect(slides[slides.length - 1].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`<<` should rewind to the first slide', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: slides.length - 1 }).mount()\n\n    glide.go('<<')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`={i}` should move to specified slide', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0 }).mount()\n\n    glide.go('=2')\n\n    afterTransition(() => {\n      expect(slides[2].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`|>` should go to the next viewport', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0, perView: 3 }).mount()\n\n    glide.go('|>')\n\n    afterTransition(() => {\n      expect(slides[3].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`|>` should go to the next viewport when active slide is not first on current viewport', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 1, perView: 3 }).mount()\n\n    glide.go('|>')\n\n    afterTransition(() => {\n      expect(slides[3].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`|<` should go to the previous viewport', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 3, perView: 3 }).mount()\n\n    glide.go('|<')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`|<` should go to the previous viewport when active slide is not first on current viewport', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 3, perView: 3 }).mount()\n\n    glide.go('|<')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`|>` should not change viewport when rewind is disabled, moving forward and we on the last viewport', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', {\n      startAt: slides.length - 1,\n      perView: 3,\n      rewind: false\n    }).mount()\n\n    glide.go('|>')\n\n    afterTransition(() => {\n      expect(slides[slides.length - 1].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`|<` should not change viewport when rewind is disabled, moving backward and we on the first viewport', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', {\n      startAt: 0,\n      perView: 3,\n      rewind: false\n    }).mount()\n\n    glide.go('|<')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`|>` should change to the first viewport when moving forward and we are on the last vievport', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', {\n      startAt: slides.length - 1,\n      perView: 3\n    }).mount()\n\n    glide.go('|>')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('`|<` should change to the last viewport when moving backward and we are on the first vievport', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', {\n      startAt: 0,\n      perView: 3\n    }).mount()\n\n    glide.go('|<')\n\n    afterTransition(() => {\n      expect(slides[slides.length - 1].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "tests/functional/slider.test.js",
    "content": "import html from '../fixtures/html'\nimport { query } from '../fixtures/query'\nimport { afterTransition } from '../fixtures/transition'\n\nimport defaults from '../../src/defaults'\nimport Glide from '../../entry/entry-complete'\n\ndescribe('Glide initialized as `slider`', () => {\n  beforeEach(() => {\n    document.body.innerHTML = html\n  })\n\n  test('should have a correct type', () => {\n    let glide = new Glide('#glide').mount()\n\n    expect(glide.isType('slider')).toBe(true)\n  })\n\n  test('should move to the last slide when we are on the first slide and moving backward', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0 }).mount()\n\n    glide.go('<')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(false)\n      expect(slides[slides.length - 1].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('should move to the first slide when we are on the last slide and moving forward', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: slides.length - 1 }).mount()\n\n    glide.go('>')\n\n    afterTransition(() => {\n      expect(slides[slides.length - 1].classList.contains(defaults.classes.slide.active)).toBe(false)\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n\n  test('should NOT move to the last slide when we are on the first slide and rewind set to false', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0, rewind: false }).mount()\n\n    glide.go('<')\n\n    afterTransition(() => {\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(true)\n      expect(slides[slides.length - 1].classList.contains(defaults.classes.slide.active)).toBe(false)\n\n      done()\n    })\n  })\n\n  test('should NOT move to the first slide when we are on the last slide and rewind set to false', (done) => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: slides.length - 1, rewind: false }).mount()\n\n    glide.go('>')\n\n    afterTransition(() => {\n      expect(slides[slides.length - 1].classList.contains(defaults.classes.slide.active)).toBe(true)\n      expect(slides[0].classList.contains(defaults.classes.slide.active)).toBe(false)\n\n      done()\n    })\n  })\n\n  test('should STOP move at `perView` number of slides from the end when `bound` option is `true`', (done) => {\n    let { slides } = query(document)\n\n    let perView = 3\n\n    let glide = new Glide('#glide', { perView: perView, bound: true }).mount()\n\n    glide.go('>>')\n\n    afterTransition(() => {\n      expect(slides[slides.length - perView].classList.contains(defaults.classes.slide.active)).toBe(true)\n\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "tests/functional/update.test.js",
    "content": "import html from '../fixtures/html'\nimport { query } from '../fixtures/query'\n\nimport defaults from '../../src/defaults'\nimport Glide from '../../entry/entry-complete'\n\ndescribe('Calling `update()` method with', () => {\n  beforeEach(() => {\n    document.body.innerHTML = html\n  })\n\n  test('new options should extend previous settings', () => {\n    let options = {\n      startAt: 1,\n      focusAt: 1,\n      perView: 2\n    }\n\n    let glide = new Glide('#glide', { startAt: 0 }).mount()\n\n    glide.update(options)\n\n    expect(glide.settings).toEqual(Object.assign(defaults, options))\n  })\n\n  test('new `startAt` option should change active slide', () => {\n    let { slides } = query(document)\n\n    let glide = new Glide('#glide', { startAt: 0 }).mount()\n\n    glide.update({ startAt: 1 })\n\n    expect(glide.index).toBe(1)\n    expect(slides[1].classList.contains(defaults.classes.slide.active)).toBe(true)\n  })\n\n  test('additional slide should be updated', () => {\n    let { wrapper } = query(document)\n    let newSlide = document.createElement('li')\n    newSlide.className = 'glide__slide'\n    newSlide.innerHTML = 0\n\n    let glide = new Glide('#glide', { startAt: 0 }).mount()\n\n    wrapper.append(newSlide)\n    glide.update()\n\n    expect(newSlide.style.width).toBeTruthy()\n  })\n})\n"
  },
  {
    "path": "tests/integration/events.test.js",
    "content": "import html from '../fixtures/html'\nimport { query } from '../fixtures/query'\nimport { afterTransition, afterRewindTransition } from '../fixtures/transition'\n\nimport Glide from '../../entry/entry-complete'\n\ndescribe(\"Event's callbacks on\", () => {\n  beforeEach(() => {\n    document.body.innerHTML = html\n  })\n\n  test('passed as array should be called only once', () => {\n    const glide = new Glide('#glide')\n\n    let callback = jest.fn()\n\n    glide.on(['mount.before'], callback)\n\n    glide.mount()\n\n    expect(callback).toHaveBeenCalledTimes(1)\n  })\n\n  test('`mount.*` should be called', () => {\n    const glide = new Glide('#glide')\n\n    let beforeCallback = jest.fn()\n    let afterCallback = jest.fn()\n\n    glide.on('mount.before', beforeCallback)\n    glide.on('mount.after', afterCallback)\n\n    glide.mount()\n\n    expect(beforeCallback).toHaveBeenCalled()\n    expect(afterCallback).toHaveBeenCalled()\n  })\n\n  test('`update` should be called', () => {\n    const glide = new Glide('#glide')\n\n    let updateCallback = jest.fn()\n\n    glide.on('update', updateCallback)\n\n    glide.mount().update()\n\n    expect(updateCallback).toHaveBeenCalled()\n  })\n\n  test('`play` should be called', () => {\n    const glide = new Glide('#glide')\n\n    let playCallback = jest.fn()\n\n    glide.on('play', playCallback)\n\n    glide.mount().play()\n\n    expect(playCallback).toHaveBeenCalled()\n  })\n\n  test('`pause` should be called', () => {\n    const glide = new Glide('#glide')\n\n    let pauseCallback = jest.fn()\n\n    glide.on('pause', pauseCallback)\n\n    glide.mount().pause()\n\n    expect(pauseCallback).toHaveBeenCalled()\n  })\n\n  test('`build.*` should be called', () => {\n    const glide = new Glide('#glide')\n\n    let beforeCallback = jest.fn()\n    let afterCallback = jest.fn()\n\n    glide.on('build.before', beforeCallback)\n    glide.on('build.after', afterCallback)\n\n    glide.mount()\n\n    expect(beforeCallback).toHaveBeenCalled()\n    expect(afterCallback).toHaveBeenCalled()\n  })\n\n  test('`run` should be called with `move` parameter', () => {\n    const glide = new Glide('#glide')\n\n    let move = { direction: '=', steps: 1 }\n    let runCallback = jest.fn()\n\n    glide.on('run', runCallback)\n\n    glide.mount().go('=1')\n\n    expect(runCallback).toBeCalledWith(move)\n  })\n\n  test('`run.before` should be called with `move` parameter', () => {\n    const glide = new Glide('#glide')\n\n    let move = { direction: '=', steps: 1 }\n    let beforeCallback = jest.fn()\n\n    glide.on('run.before', beforeCallback)\n\n    glide.mount().go('=1')\n\n    expect(beforeCallback).toBeCalledWith(move)\n  })\n\n  test('`run.after` should be called with `move` parameter', (done) => {\n    const glide = new Glide('#glide')\n\n    let move = { direction: '=', steps: 1 }\n    let afterCallback = jest.fn()\n\n    glide.on('run.after', afterCallback)\n\n    glide.mount().go('=1')\n\n    expect(afterCallback).not.toBeCalled()\n    afterTransition(() => {\n      expect(afterCallback).toBeCalledWith(move)\n\n      done()\n    })\n  })\n\n  test('`run.offset` should be called with `move` parameter when changing slide from first to the last one', (done) => {\n    const glide = new Glide('#glide', { startAt: 0 })\n\n    let move = { direction: '<', steps: 0 }\n    let offsetCallback = jest.fn()\n\n    glide.on('run.offset', offsetCallback)\n\n    glide.mount().go('<')\n\n    expect(offsetCallback).not.toBeCalled()\n    afterRewindTransition(() => {\n      expect(offsetCallback).toBeCalledWith(move)\n\n      done()\n    })\n  })\n\n  test('`run.offset` should be called with `move` parameter when changing slide from last to the first one', (done) => {\n    const { slides } = query(document)\n    const glide = new Glide('#glide', { startAt: slides.length - 1 })\n\n    let move = { direction: '>', steps: 0 }\n    let offsetCallback = jest.fn()\n\n    glide.on('run.offset', offsetCallback)\n\n    glide.mount().go('>')\n\n    expect(offsetCallback).not.toBeCalled()\n    afterRewindTransition(() => {\n      expect(offsetCallback).toBeCalledWith(move)\n\n      done()\n    })\n  })\n\n  test('`run.start` should be called with `move` parameter when changing to the first slide', (done) => {\n    const glide = new Glide('#glide', { startAt: 1 })\n\n    let move = { direction: '<', steps: '<' }\n    let startCallback = jest.fn()\n\n    glide.on('run.start', startCallback)\n\n    glide.mount().go('<<')\n\n    expect(startCallback).not.toBeCalled()\n    afterTransition(() => {\n      expect(startCallback).toBeCalledWith(move)\n\n      done()\n    })\n  })\n\n  test('`run.end` should be called with `move` parameter when changing to the first slide', (done) => {\n    const glide = new Glide('#glide', { startAt: 1 })\n\n    let move = { direction: '>', steps: '>' }\n    let endCallback = jest.fn()\n\n    glide.on('run.end', endCallback)\n\n    glide.mount().go('>>')\n\n    expect(endCallback).not.toBeCalled()\n    afterTransition(() => {\n      expect(endCallback).toBeCalledWith(move)\n\n      done()\n    })\n  })\n\n  test('`autoplay` should be called on each autoplay slide change', (done) => {\n    const glide = new Glide('#glide', { autoplay: 50 })\n\n    let autoplayCallback = jest.fn()\n\n    glide.on('autoplay', autoplayCallback)\n\n    glide.mount()\n\n    expect(autoplayCallback).not.toBeCalled()\n    setTimeout(() => {\n      expect(autoplayCallback).toHaveBeenCalledTimes(2)\n\n      done()\n    }, 110)\n  })\n\n  test('`translate.jump` should be called when making a loop change', (done) => {\n    const glide = new Glide('#glide', { type: 'carousel' })\n\n    let jumpCallback = jest.fn()\n\n    glide.on('translate.jump', jumpCallback)\n\n    glide.mount().go('<')\n\n    expect(jumpCallback).not.toBeCalled()\n    afterTransition(() => {\n      expect(jumpCallback).toBeCalled()\n\n      done()\n    })\n  })\n\n  test('`translate.jump` should be called when making a loop change', (done) => {\n    const { slides } = query(document)\n    const glide = new Glide('#glide', { type: 'carousel', startAt: slides.length - 1 })\n\n    let jumpCallback = jest.fn()\n\n    glide.on('translate.jump', jumpCallback)\n\n    glide.mount().go('>')\n\n    expect(jumpCallback).not.toBeCalled()\n    afterTransition(() => {\n      expect(jumpCallback).toBeCalled()\n\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "tests/integration/instance.test.js",
    "content": "import html from '../fixtures/html'\nimport defaults from '../../src/defaults'\nimport { mergeOptions } from '../../src/utils/object'\n\nimport Glide from '../../entry/entry-complete'\n\ndescribe('Glide', () => {\n  beforeEach(() => {\n    document.body.innerHTML = html\n  })\n\n  test('should start at 0 by default', () => {\n    let glide = new Glide('#glide').mount()\n\n    expect(glide.index).toBe(0)\n  })\n\n  test('should start at index setup in `startAt`', () => {\n    let glide = new Glide('#glide', { startAt: 2 }).mount()\n\n    expect(glide.index).toBe(2)\n  })\n\n  test('unmodified settings should return defaults', () => {\n    let glide = new Glide('#glide').mount()\n\n    expect(glide.settings).toEqual(defaults)\n  })\n\n  test('modified settings should return defaults extended by options', () => {\n    let glide = new Glide('#glide', {\n      startAt: 2,\n      classes: {\n        direction: {\n          ltr: 'one',\n          rtl: 'two'\n        }\n      }\n    }).mount()\n\n    expect(glide.settings).toEqual(mergeOptions(defaults, {\n      startAt: 2,\n      classes: {\n        direction: {\n          ltr: 'one',\n          rtl: 'two'\n        }\n      }\n    }))\n  })\n\n  test('should not be disabled at start', () => {\n    let glide = new Glide('#glide').mount()\n\n    expect(glide.disabled).toBe(false)\n  })\n\n  test('should use specified selector', () => {\n    let glide = new Glide('#glide').mount()\n\n    expect(glide.selector).toBe('#glide')\n  })\n\n  test('should initialize extensions specified at mounting', () => {\n    let fn = jest.fn()\n    let stub = () => {\n      return { mount: fn }\n    }\n\n    new Glide('#glide').mount({ stub })\n\n    expect(fn).toHaveBeenCalled()\n  })\n\n  test('should warn about invalid extensions', () => {\n    let fn = jest.fn()\n    let stub = { mount () {} }\n\n    console.error = fn\n\n    new Glide('#glide').mount({ stub })\n\n    expect(fn).toHaveBeenCalled()\n  })\n})\n"
  },
  {
    "path": "tests/unit/dom.test.js",
    "content": "import { exist, toArray, siblings } from '../../src/utils/dom'\n\ndescribe('Function', () => {\n  beforeEach(() => {\n    document.body.innerHTML = `\n      <div class=\"parent\">\n        <div class=\"child\"></div>\n        <div class=\"child\"></div>\n        <div class=\"child\"></div>\n      </div>\n    `\n  })\n\n  test('`siblings` should return siblings of the passed HTMLElements', () => {\n    let children = document.querySelectorAll('.child')\n\n    expect(siblings(children[1])).toHaveLength(2)\n  })\n\n  test('`toArray` should return an array with the same elements when passed a NodeList', () => {\n    let children = document.querySelectorAll('.child')\n\n    // eslint-disable-next-line no-undef\n    expect(NodeList.prototype.isPrototypeOf(children)).toBe(true)\n    expect(Array.isArray(toArray(children))).toBe(true)\n    toArray(children).forEach((child, i) => {\n      expect(child).toEqual(children[i])\n    })\n  })\n})\n"
  },
  {
    "path": "tests/unit/events-binder.test.js",
    "content": "import EventsBinder from '../../src/core/event/events-binder'\n\nlet events = null\nlet element = null\nlet callback = jest.fn()\n\ndescribe('EventsBinder should', () => {\n  beforeEach(() => {\n    events = new EventsBinder()\n    element = document.createElement('div')\n  })\n\n  afterEach(() => {\n    element.remove()\n  })\n\n  test('create and remove event listener from element', () => {\n    let addFn = jest.fn()\n    let rmFn = jest.fn()\n\n    element.addEventListener = addFn\n    element.removeEventListener = rmFn\n\n    events.on('click', element, callback)\n    expect(addFn).toHaveBeenCalledWith('click', callback, expect.any(Boolean))\n\n    events.off('click', element)\n    expect(rmFn).toHaveBeenCalledWith('click', callback, expect.any(Boolean))\n  })\n\n  test('store created listeners when binding with `on`', () => {\n    events.on('click', element, callback)\n\n    expect(events.listeners).toHaveProperty('click')\n    expect(events.listeners['click']).toBe(callback)\n  })\n\n  test('hold previously stored listeners when unbinding with `off`', () => {\n    events.on('click', element, callback)\n\n    events.off('click', element)\n\n    expect(events.listeners).toHaveProperty('click')\n  })\n\n  test('remove stored listeners when destroying', () => {\n    events.on('click', element, callback)\n\n    events.destroy()\n\n    expect(events).not.toHaveProperty('listeners')\n  })\n})\n"
  },
  {
    "path": "tests/unit/log.test.js",
    "content": "import { warn } from '../../src/utils/log'\n\ndescribe('Warn should', () => {\n  test('log an error to the console with suffix', () => {\n    let fn = jest.fn()\n    let msg = 'Message content'\n\n    global.console = { error: fn }\n\n    warn(msg)\n\n    expect(fn).toHaveBeenCalledWith(`[Glide warn]: ${msg}`)\n  })\n})\n"
  },
  {
    "path": "tests/unit/mount.test.js",
    "content": "import { mount } from '../../src/core/index'\n\ndescribe('`mount()` function should', () => {\n  test('initialize all registered components', () => {\n    // Here we creating a mock of glide internal component.\n    // Every component have to implement a `mount()` method and be a function\n    let fn = jest.fn()\n    let stub = { mount: fn }\n    let mock = jest.fn(() => {\n      return stub\n    })\n\n    mount('glide', { mock }, 'events')\n\n    // `mount()` method of the component should be called\n    expect(fn).toHaveBeenCalled()\n\n    // component should be initialized with glide and other components as arguments\n    expect(mock).toHaveBeenCalledWith('glide', { mock: stub }, 'events')\n  })\n})\n"
  },
  {
    "path": "tests/unit/object.test.js",
    "content": "import { define, mergeOptions } from '../../src/utils/object'\n\ndescribe('Function', () => {\n  test('`define` should create object getters and setters', () => {\n    let obj = {}\n\n    expect(typeof obj.property).toBe('undefined')\n\n    define(obj, 'property', {\n      get () {\n        return this._prop\n      },\n\n      set (value) {\n        this._prop = value\n      }\n    })\n\n    obj.property = 'value'\n\n    expect(obj.property).toBe('value')\n  })\n\n  test('`mergeOptions` should merge defaults and options object', () => {\n    let obj = mergeOptions(\n      {\n        a: 1,\n        b: 2\n      },\n      {\n        a: 5\n      }\n    )\n\n    expect(obj).toEqual({\n      a: 5,\n      b: 2\n    })\n  })\n\n  test('`mergeOptions` should deep merge classes options object', () => {\n    let obj = mergeOptions(\n      {\n        classes: {\n          direction: {\n            ltr: 'a',\n            rtl: 'a'\n          },\n          type: {\n            slider: 'a',\n            carousel: 'a'\n          },\n          slide: {\n            clone: 'a',\n            active: 'a'\n          },\n          arrow: {\n            disabled: 'a'\n          },\n          nav: {\n            active: 'a'\n          }\n        }\n      },\n      {\n        classes: {\n          type: {\n            slider: 'b',\n            carousel: 'b'\n          },\n          slide: {\n            clone: 'b',\n            active: 'b'\n          },\n          arrow: {\n            disabled: 'b'\n          },\n          nav: {\n            active: 'b'\n          }\n        }\n      }\n    )\n\n    expect(obj).toEqual({\n      classes: {\n        direction: {\n          ltr: 'a',\n          rtl: 'a'\n        },\n        type: {\n          slider: 'b',\n          carousel: 'b'\n        },\n        slide: {\n          clone: 'b',\n          active: 'b'\n        },\n        arrow: {\n          disabled: 'b'\n        },\n        nav: {\n          active: 'b'\n        }\n      }\n    })\n  })\n})\n"
  },
  {
    "path": "tests/unit/string.test.js",
    "content": "import { ucfirst } from '../../src/utils/string'\n\ndescribe('Function', () => {\n  test('`ucfirst` should capitalize passed string', () => {\n    expect(ucfirst('string')).toBe('String')\n  })\n})\n"
  },
  {
    "path": "tests/unit/unit.test.js",
    "content": "import {\n  toInt,\n  isArray,\n  isString,\n  isObject,\n  isNumber,\n  isFunction,\n  isUndefined\n} from '../../src/utils/unit'\n\ndescribe('Function', () => {\n  test('`toInt` should covert entered value in various formats to actual width number', () => {\n    expect(toInt(1, 100)).toBe(1)\n    expect(toInt('1', 100)).toBe(1)\n    expect(toInt('1px', 100)).toBe(1)\n  })\n\n  test('`isString` return `true` on valid string', () => {\n    expect(isString('undefined')).toBe(true)\n\n    expect(isString(1)).toBe(false)\n    expect(isString({})).toBe(false)\n    expect(isString([])).toBe(false)\n    expect(isString(true)).toBe(false)\n    expect(isString(() => {})).toBe(false)\n  })\n\n  test('`isNumber` return `true` on valid number', () => {\n    expect(isNumber(1)).toBe(true)\n\n    expect(isNumber([])).toBe(false)\n    expect(isNumber({})).toBe(false)\n    expect(isNumber(true)).toBe(false)\n    expect(isNumber(() => {})).toBe(false)\n    expect(isNumber('undefined')).toBe(false)\n  })\n\n  test('`isObject` return `true` on valid object', () => {\n    expect(isObject({})).toBe(true)\n\n    expect(isObject(1)).toBe(false)\n    expect(isObject(true)).toBe(false)\n    expect(isObject('undefined')).toBe(false)\n  })\n\n  test('`isArray` return `true` on valid array', () => {\n    expect(isArray([])).toBe(true)\n\n    expect(isArray(1)).toBe(false)\n    expect(isArray({})).toBe(false)\n    expect(isArray(true)).toBe(false)\n    expect(isArray(() => {})).toBe(false)\n    expect(isArray('undefined')).toBe(false)\n  })\n\n  test('`isFunction` return `true` on valid function', () => {\n    expect(isFunction(() => {})).toBe(true)\n\n    expect(isFunction(1)).toBe(false)\n    expect(isFunction({})).toBe(false)\n    expect(isFunction([])).toBe(false)\n    expect(isFunction(true)).toBe(false)\n    expect(isFunction('undefined')).toBe(false)\n  })\n\n  test('`isUndefined` return `true` on undefined', () => {\n    let value = {}\n\n    expect(isUndefined(value.prop)).toBe(true)\n\n    expect(isUndefined('undefined')).toBe(false)\n    expect(isUndefined(1)).toBe(false)\n    expect(isUndefined({})).toBe(false)\n    expect(isUndefined([])).toBe(false)\n    expect(isUndefined(true)).toBe(false)\n    expect(isUndefined(() => {})).toBe(false)\n  })\n})\n"
  }
]