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