├── .eslintrc.js
├── .github
├── contributing.md
├── issue_template.md
└── workflows
│ └── nodejs.yml
├── .gitignore
├── .nvmrc
├── README.md
├── bin
├── .eslintrc.js
├── bundle-css.js
├── bundle-js.js
├── lint-json.js
└── version.js
├── bower.json
├── css
└── flickity.css
├── dist
├── flickity.css
├── flickity.min.css
├── flickity.pkgd.js
└── flickity.pkgd.min.js
├── js
├── add-remove-cell.js
├── animate.js
├── cell.js
├── core.js
├── drag.js
├── imagesloaded.js
├── index.js
├── lazyload.js
├── page-dots.js
├── player.js
├── prev-next-button.js
└── slide.js
├── package-lock.json
├── package.json
├── sandbox
├── adaptive-height.html
├── add-remove.html
├── ajax.html
├── basic.html
├── freescroll.html
├── group-cells.html
├── jquery.html
├── js
│ ├── add-remove.js
│ ├── basic.js
│ ├── jquery.js
│ ├── scroll-event.js
│ ├── tricky-drag.js
│ ├── v2-sizzle.js
│ └── wrap-around.js
├── lazyload.html
├── media.html
├── right-to-left.html
├── sandbox.css
├── scroll-event.html
├── single.html
├── styles.html
├── tricky-drag.html
├── v2-sizzle.html
└── wrap-around.html
├── stylelint.config.js
└── test
├── drag.html
├── index.html
├── test.css
└── unit
├── adaptive-height.js
├── add-remove-cells.js
├── auto-play.js
├── cell-selector.js
├── change.js
├── contain.js
├── destroy.js
├── drag.js
├── empty.js
├── get-parent-cell.js
├── get-wrap-cells.js
├── group-cells.js
├── imagesloaded.js
├── init.js
├── initial-index.js
├── lazyload-srcset.js
├── lazyload.js
├── page-dots.js
├── position-cells.js
├── prev-next-buttons.js
├── resize.js
├── select-cell.js
├── watch.js
└── wrap-around-fill.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | module.exports = {
4 | plugins: [ 'metafizzy' ],
5 | extends: 'plugin:metafizzy/browser',
6 | env: {
7 | browser: true,
8 | commonjs: true,
9 | },
10 | parserOptions: {
11 | ecmaVersion: 2018,
12 | },
13 | globals: {
14 | Flickity: 'readonly',
15 | QUnit: 'readonly',
16 | },
17 | rules: {
18 | 'prefer-object-spread': 'error',
19 | },
20 | ignorePatterns: [ 'bower_components' ],
21 | };
22 |
--------------------------------------------------------------------------------
/.github/contributing.md:
--------------------------------------------------------------------------------
1 | ## Submitting issues
2 |
3 | ### Reduced test case required
4 |
5 | All bug reports and problem issues require a [**reduced test case**](https://css-tricks.com/reduced-test-cases/). Create one by forking any one of the [CodePen demos](https://codepen.io/desandro/pens/tags/?grid_type=list&selected_tag=flickity-docs&sort_order=asc) from [the docs](https://flickity.metafizzy.co).
6 |
7 | **CodePens**
8 |
9 | + [Basic](https://codepen.io/desandro/pen/azqbop)
10 | + [imagesLoaded](https://codepen.io/desandro/pen/MYQWEe)
11 | + [lazyLoad](https://codepen.io/desandro/pen/vOeGzL)
12 | + [autoPlay](https://codepen.io/desandro/pen/RNQwaB)
13 |
14 | **Test cases**
15 |
16 | + A reduced test case clearly demonstrates the bug or issue.
17 | + It contains the bare minimum HTML, CSS, and JavaScript required to demonstrate the bug.
18 | + A link to your production site is **not** a reduced test case.
19 |
20 | Providing a reduced test case is the best way to get your issue addressed. They help you point out the problem. They help me verify and debug the problem. They help others understand the problem. Without a reduced test case, your issue may be closed.
21 |
22 | ## Pull requests
23 |
24 | Contributions are welcome!
25 |
26 | + **For typos and one-line fixes,** send those right in.
27 | + **For larger features,** open an issue before starting any significant work. Let's discuss to see how your feature fits within Flickity's vision.
28 | + **Follow the code style.** Spaces in brackets, semicolons, trailing commas.
29 | + **Do not edit `dist/` files.** Make your edits to source files in `js/` and `css/`.
30 | + **Do not run `gulp` to update `dist/` files.** I'll take care of this when I create a new release.
31 |
32 | Your code will be used as part of a commercial product if merged. By submitting a Pull Request, you are giving your consent for your code to be integrated into Flickity as part of a commercial product.
33 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | **Test case:** https://codepen.io/desandro/pen/azqbop
4 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 | strategy:
16 | matrix:
17 | node-version: [16.x]
18 | steps:
19 | - uses: actions/checkout@v2
20 | - name: Use Node.js ${{ matrix.node-version }}
21 | uses: actions/setup-node@v1
22 | with:
23 | node-version: ${{ matrix.node-version }}
24 | - run: npm ci
25 | - run: npm run lint
26 | env:
27 | CI: true
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | node_modules/
3 | /**/browserify/bundle.js
4 | notes.md
5 | sandbox/yoshi-parallax
6 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flickity
2 |
3 | _Touch, responsive, flickable carousels_
4 |
5 | See [flickity.metafizzy.co](https://flickity.metafizzy.co) for complete docs and demos.
6 |
7 | ## Install
8 |
9 | ### Download
10 |
11 | + CSS:
12 | - [flickity.min.css](https://unpkg.com/flickity@2/dist/flickity.min.css) minified, or
13 | - [flickity.css](https://unpkg.com/flickity@2/dist/flickity.css) un-minified
14 | + JavaScript:
15 | - [flickity.pkgd.min.js](https://unpkg.com/flickity@2/dist/flickity.pkgd.min.js) minified, or
16 | - [flickity.pkgd.js](https://unpkg.com/flickity@2/dist/flickity.pkgd.js) un-minified
17 |
18 | ### CDN
19 |
20 | Link directly to Flickity files on [unpkg](https://unpkg.com).
21 |
22 | ``` html
23 |
24 | ```
25 |
26 | ``` html
27 |
28 | ```
29 |
30 | ### Package managers
31 |
32 | Bower: `bower install flickity --save`
33 |
34 | npm: `npm install flickity --save`
35 |
36 | ## License
37 |
38 | ### Commercial license
39 |
40 | If you want to use Flickity to develop commercial sites, themes, projects, and applications, the Commercial license is the appropriate license. With this option, your source code is kept proprietary. Purchase a Flickity Commercial License at [flickity.metafizzy.co](https://flickity.metafizzy.co/#commercial-license)
41 |
42 | ### Open source license
43 |
44 | If you are creating an open source application under a license compatible with the [GNU GPL license v3](https://www.gnu.org/licenses/gpl-3.0.html), you may use Flickity under the terms of the GPLv3.
45 |
46 | [Read more about Flickity's license](https://flickity.metafizzy.co/license.html).
47 |
48 | ## Usage
49 |
50 | Flickity works with a container element and a set of child cell elements
51 |
52 | ``` html
53 |
54 |
...
55 |
...
56 |
...
57 | ...
58 |
59 | ```
60 |
61 | ### Options
62 |
63 | ``` js
64 | var flky = new Flickity( '.gallery', {
65 | // options, defaults listed
66 |
67 | accessibility: true,
68 | // enable keyboard navigation, pressing left & right keys
69 |
70 | adaptiveHeight: false,
71 | // set carousel height to the selected slide
72 |
73 | autoPlay: false,
74 | // advances to the next cell
75 | // if true, default is 3 seconds
76 | // or set time between advances in milliseconds
77 | // i.e. `autoPlay: 1000` will advance every 1 second
78 |
79 | cellAlign: 'center',
80 | // alignment of cells, 'center', 'left', or 'right'
81 | // or a decimal 0-1, 0 is beginning (left) of container, 1 is end (right)
82 |
83 | cellSelector: undefined,
84 | // specify selector for cell elements
85 |
86 | contain: false,
87 | // will contain cells to container
88 | // so no excess scroll at beginning or end
89 | // has no effect if wrapAround is enabled
90 |
91 | draggable: '>1',
92 | // enables dragging & flicking
93 | // if at least 2 cells
94 |
95 | dragThreshold: 3,
96 | // number of pixels a user must scroll horizontally to start dragging
97 | // increase to allow more room for vertical scroll for touch devices
98 |
99 | freeScroll: false,
100 | // enables content to be freely scrolled and flicked
101 | // without aligning cells
102 |
103 | friction: 0.2,
104 | // smaller number = easier to flick farther
105 |
106 | groupCells: false,
107 | // group cells together in slides
108 |
109 | initialIndex: 0,
110 | // zero-based index of the initial selected cell
111 |
112 | lazyLoad: true,
113 | // enable lazy-loading images
114 | // set img data-flickity-lazyload="src.jpg"
115 | // set to number to load images adjacent cells
116 |
117 | percentPosition: true,
118 | // sets positioning in percent values, rather than pixels
119 | // Enable if items have percent widths
120 | // Disable if items have pixel widths, like images
121 |
122 | prevNextButtons: true,
123 | // creates and enables buttons to click to previous & next cells
124 |
125 | pageDots: true,
126 | // create and enable page dots
127 |
128 | resize: true,
129 | // listens to window resize events to adjust size & positions
130 |
131 | rightToLeft: false,
132 | // enables right-to-left layout
133 |
134 | setGallerySize: true,
135 | // sets the height of gallery
136 | // disable if gallery already has height set with CSS
137 |
138 | watchCSS: false,
139 | // watches the content of :after of the element
140 | // activates if #element:after { content: 'flickity' }
141 |
142 | wrapAround: false
143 | // at end of cells, wraps-around to first for infinite scrolling
144 |
145 | });
146 | ```
147 |
148 | ---
149 |
150 | By [Metafizzy 🌈🐻](https://metafizzy.co)
151 |
--------------------------------------------------------------------------------
/bin/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [ 'metafizzy' ],
3 | extends: 'plugin:metafizzy/node',
4 | };
5 |
--------------------------------------------------------------------------------
/bin/bundle-css.js:
--------------------------------------------------------------------------------
1 | const CleanCss = require('clean-css');
2 | const fs = require('fs');
3 |
4 | let srcCss = fs.readFileSync( 'css/flickity.css', 'utf8' );
5 | let minifiedCss = new CleanCss().minify( srcCss ).styles.replace( '*/', '*/\n' );
6 | fs.writeFileSync( 'dist/flickity.min.css', minifiedCss );
7 |
--------------------------------------------------------------------------------
/bin/bundle-js.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const { execSync } = require('child_process');
3 | const { minify } = require('terser');
4 |
5 | const indexPath = 'js/index.js';
6 | const distPath = 'dist/flickity.pkgd.js';
7 | const distMinPath = 'dist/flickity.pkgd.min.js';
8 |
9 | let indexContent = fs.readFileSync( `./${indexPath}`, 'utf8' );
10 | // get file paths from index.js
11 | let jsPaths = indexContent.match( /require\('([.\-/\w]+)'\)/gi )
12 | .map( ( path ) => path.replace( "require('.", 'js' ).replace( "')", '.js' ) );
13 |
14 | let paths = [
15 | 'node_modules/jquery-bridget/jquery-bridget.js',
16 | 'node_modules/ev-emitter/ev-emitter.js',
17 | 'node_modules/get-size/get-size.js',
18 | 'node_modules/fizzy-ui-utils/utils.js',
19 | 'node_modules/unidragger/unidragger.js',
20 | 'node_modules/imagesloaded/imagesloaded.js',
21 | 'js/cell.js',
22 | 'js/slide.js',
23 | 'js/animate.js',
24 | ...jsPaths,
25 | ];
26 |
27 | // concatenate files
28 | execSync(`cat ${paths.join(' ')} > ${distPath}`);
29 |
30 | // add banner
31 | let banner = indexContent.split(' */')[0] + ' */\n\n';
32 | banner = banner.replace( 'Flickity', 'Flickity PACKAGED' );
33 | let distJsContent = fs.readFileSync( distPath, 'utf8' );
34 | distJsContent = banner + distJsContent;
35 | fs.writeFileSync( distPath, distJsContent );
36 |
37 | // minify
38 | ( async function() {
39 | let { code } = await minify( distJsContent, { mangle: true } );
40 | fs.writeFileSync( distMinPath, code );
41 | } )();
42 |
--------------------------------------------------------------------------------
/bin/lint-json.js:
--------------------------------------------------------------------------------
1 | require('../package.json');
2 | require('../bower.json');
3 |
--------------------------------------------------------------------------------
/bin/version.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const version = require('../package.json').version;
3 |
4 | [ 'css/flickity.css', 'js/index.js' ].forEach( ( file ) => {
5 | let src = fs.readFileSync( file, 'utf8' );
6 | src = src.replace( /Flickity v\d+\.\d+\.\d+/, `Flickity v${version}` );
7 | fs.writeFileSync( file, src, 'utf8' );
8 | } );
9 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flickity",
3 | "description": "Touch, responsive, flickable carousels",
4 | "main": [
5 | "js/index.js",
6 | "css/flickity.css"
7 | ],
8 | "dependencies": {
9 | "ev-emitter": "^2.1.2",
10 | "fizzy-ui-utils": "^3.0.0",
11 | "get-size": "^3.0.0",
12 | "unidragger": "^3.0.1"
13 | },
14 | "devDependencies": {
15 | },
16 | "keywords": [
17 | "gallery",
18 | "carousel",
19 | "touch"
20 | ],
21 | "homepage": "https://flickity.metafizzy.co",
22 | "authors": [
23 | "Metafizzy"
24 | ],
25 | "license": "GPL-3.0",
26 | "ignore": [
27 | "**/.*",
28 | "node_modules",
29 | "bower_components",
30 | "test",
31 | "tests",
32 | "sandbox",
33 | "package.json",
34 | "gulpfile.js",
35 | "notes.md"
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/css/flickity.css:
--------------------------------------------------------------------------------
1 | /*! Flickity v3.0.0
2 | https://flickity.metafizzy.co
3 | ---------------------------------------------- */
4 |
5 | .flickity-enabled {
6 | position: relative;
7 | }
8 |
9 | .flickity-enabled:focus { outline: none; }
10 |
11 | .flickity-viewport {
12 | overflow: hidden;
13 | position: relative;
14 | height: 100%;
15 | touch-action: pan-y;
16 | }
17 |
18 | .flickity-slider {
19 | position: absolute;
20 | width: 100%;
21 | height: 100%;
22 | left: 0;
23 | }
24 |
25 | .flickity-rtl .flickity-slider {
26 | left: unset;
27 | right: 0;
28 | }
29 |
30 | /* draggable */
31 |
32 | .flickity-enabled.is-draggable {
33 | -webkit-tap-highlight-color: transparent;
34 | user-select: none;
35 | }
36 |
37 | .flickity-enabled.is-draggable .flickity-viewport {
38 | cursor: move;
39 | cursor: grab;
40 | }
41 |
42 | .flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
43 | cursor: grabbing;
44 | }
45 |
46 | /* ---- flickity-cell ---- */
47 |
48 | .flickity-cell {
49 | position: absolute;
50 | left: 0;
51 | }
52 |
53 | .flickity-rtl .flickity-cell {
54 | left: unset;
55 | right: 0;
56 | }
57 |
58 | /* ---- flickity-button ---- */
59 |
60 | .flickity-button {
61 | position: absolute;
62 | background: hsla(0, 0%, 100%, 75%);
63 | border: none;
64 | color: hsl(0, 0%, 20%);
65 | }
66 |
67 | .flickity-button:hover {
68 | background: white;
69 | cursor: pointer;
70 | }
71 |
72 | .flickity-button:focus {
73 | outline: none;
74 | box-shadow: 0 0 0 5px #19F;
75 | }
76 |
77 | .flickity-button:active {
78 | color: #19F;
79 | }
80 |
81 | .flickity-button:disabled {
82 | opacity: 0.3;
83 | cursor: auto;
84 | /* prevent disabled button from capturing pointer up event. #716 */
85 | pointer-events: none;
86 | }
87 |
88 | .flickity-button-icon {
89 | fill: currentcolor;
90 | }
91 |
92 | /* ---- previous/next buttons ---- */
93 |
94 | .flickity-prev-next-button {
95 | top: 50%;
96 | width: 44px;
97 | height: 44px;
98 | z-index: 1; /* above viewport */
99 | border-radius: 50%;
100 | /* vertically center */
101 | transform: translateY(-50%);
102 | }
103 |
104 | .flickity-prev-next-button.previous { left: 10px; }
105 | .flickity-prev-next-button.next { right: 10px; }
106 | /* right to left */
107 | .flickity-rtl .flickity-prev-next-button.previous {
108 | left: auto;
109 | right: 10px;
110 | }
111 |
112 | .flickity-rtl .flickity-prev-next-button.next {
113 | right: auto;
114 | left: 10px;
115 | }
116 |
117 | .flickity-prev-next-button .flickity-button-icon {
118 | position: absolute;
119 | left: 20%;
120 | top: 20%;
121 | width: 60%;
122 | height: 60%;
123 | }
124 |
125 | /* ---- page dots ---- */
126 |
127 | .flickity-page-dots {
128 | position: absolute;
129 | width: 100%;
130 | bottom: -25px;
131 | z-index: 1; /* above viewport */
132 | text-align: center;
133 | display: flex;
134 | justify-content: center;
135 | flex-wrap: wrap;
136 | }
137 |
138 | .flickity-rtl .flickity-page-dots { direction: rtl; }
139 |
140 | .flickity-page-dot {
141 | position: relative;
142 | display: block;
143 | width: 10px;
144 | height: 10px;
145 | padding: 0;
146 | margin: 0 8px;
147 | background: hsl(0, 0%, 20%, 25%);
148 | border-radius: 50%;
149 | cursor: pointer;
150 | appearance: none;
151 | border: none;
152 | text-indent: -9999px;
153 | overflow: hidden;
154 | }
155 |
156 | .flickity-rtl .flickity-page-dot {
157 | text-indent: 9999px;
158 | }
159 |
160 | .flickity-page-dot:hover {
161 | background: hsla(0, 0%, 20%, 75%);
162 | }
163 |
164 | .flickity-page-dot:active {
165 | background: #19F;
166 | }
167 |
168 | .flickity-page-dot.is-selected {
169 | background: hsl(0, 0%, 20%);
170 | }
171 |
--------------------------------------------------------------------------------
/dist/flickity.css:
--------------------------------------------------------------------------------
1 | /*! Flickity v3.0.0
2 | https://flickity.metafizzy.co
3 | ---------------------------------------------- */
4 |
5 | .flickity-enabled {
6 | position: relative;
7 | }
8 |
9 | .flickity-enabled:focus { outline: none; }
10 |
11 | .flickity-viewport {
12 | overflow: hidden;
13 | position: relative;
14 | height: 100%;
15 | touch-action: pan-y;
16 | }
17 |
18 | .flickity-slider {
19 | position: absolute;
20 | width: 100%;
21 | height: 100%;
22 | left: 0;
23 | }
24 |
25 | .flickity-rtl .flickity-slider {
26 | left: unset;
27 | right: 0;
28 | }
29 |
30 | /* draggable */
31 |
32 | .flickity-enabled.is-draggable {
33 | -webkit-tap-highlight-color: transparent;
34 | user-select: none;
35 | }
36 |
37 | .flickity-enabled.is-draggable .flickity-viewport {
38 | cursor: move;
39 | cursor: grab;
40 | }
41 |
42 | .flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
43 | cursor: grabbing;
44 | }
45 |
46 | /* ---- flickity-cell ---- */
47 |
48 | .flickity-cell {
49 | position: absolute;
50 | left: 0;
51 | }
52 |
53 | .flickity-rtl .flickity-cell {
54 | left: unset;
55 | right: 0;
56 | }
57 |
58 | /* ---- flickity-button ---- */
59 |
60 | .flickity-button {
61 | position: absolute;
62 | background: hsl(0 0% 100% / 75%);
63 | border: none;
64 | color: #333;
65 | }
66 |
67 | .flickity-button:hover {
68 | background: white;
69 | cursor: pointer;
70 | }
71 |
72 | .flickity-button:focus {
73 | outline: none;
74 | box-shadow: 0 0 0 5px #19F;
75 | }
76 |
77 | .flickity-button:active {
78 | opacity: 0.6;
79 | }
80 |
81 | .flickity-button:disabled {
82 | opacity: 0.3;
83 | cursor: auto;
84 | /* prevent disabled button from capturing pointer up event. #716 */
85 | pointer-events: none;
86 | }
87 |
88 | .flickity-button-icon {
89 | fill: currentColor;
90 | }
91 |
92 | /* ---- previous/next buttons ---- */
93 |
94 | .flickity-prev-next-button {
95 | top: 50%;
96 | width: 44px;
97 | height: 44px;
98 | border-radius: 50%;
99 | /* vertically center */
100 | transform: translateY(-50%);
101 | }
102 |
103 | .flickity-prev-next-button.previous { left: 10px; }
104 | .flickity-prev-next-button.next { right: 10px; }
105 | /* right to left */
106 | .flickity-rtl .flickity-prev-next-button.previous {
107 | left: auto;
108 | right: 10px;
109 | }
110 |
111 | .flickity-rtl .flickity-prev-next-button.next {
112 | right: auto;
113 | left: 10px;
114 | }
115 |
116 | .flickity-prev-next-button .flickity-button-icon {
117 | position: absolute;
118 | left: 20%;
119 | top: 20%;
120 | width: 60%;
121 | height: 60%;
122 | }
123 |
124 | /* ---- page dots ---- */
125 |
126 | .flickity-page-dots {
127 | position: absolute;
128 | width: 100%;
129 | bottom: -25px;
130 | text-align: center;
131 | display: flex;
132 | justify-content: center;
133 | flex-wrap: wrap;
134 | }
135 |
136 | .flickity-rtl .flickity-page-dots { direction: rtl; }
137 |
138 | .flickity-page-dot {
139 | display: block;
140 | width: 10px;
141 | height: 10px;
142 | padding: 0;
143 | margin: 0 8px;
144 | background: hsl(0 0% 20% / 25%);
145 | border-radius: 50%;
146 | cursor: pointer;
147 | appearance: none;
148 | border: none;
149 | text-indent: -9999px;
150 | overflow: hidden;
151 | }
152 |
153 | .flickity-rtl .flickity-page-dot {
154 | text-indent: 9999px;
155 | }
156 |
157 | .flickity-page-dot:focus {
158 | outline: none;
159 | box-shadow: 0 0 0 5px #19F;
160 | }
161 |
162 | .flickity-page-dot.is-selected {
163 | background: hsl(0 0% 20% / 100%);
164 | }
165 |
--------------------------------------------------------------------------------
/dist/flickity.min.css:
--------------------------------------------------------------------------------
1 | /*! Flickity v3.0.0
2 | https://flickity.metafizzy.co
3 | ---------------------------------------------- */
4 | .flickity-enabled{position:relative}.flickity-enabled:focus{outline:0}.flickity-viewport{overflow:hidden;position:relative;height:100%;touch-action:pan-y}.flickity-slider{position:absolute;width:100%;height:100%;left:0}.flickity-rtl .flickity-slider{left:unset;right:0}.flickity-enabled.is-draggable{-webkit-tap-highlight-color:transparent;user-select:none}.flickity-enabled.is-draggable .flickity-viewport{cursor:move;cursor:grab}.flickity-enabled.is-draggable .flickity-viewport.is-pointer-down{cursor:grabbing}.flickity-cell{position:absolute;left:0}.flickity-rtl .flickity-cell{left:unset;right:0}.flickity-button{position:absolute;background:hsl(0 0% 100% / 75%);border:none;color:#333}.flickity-button:hover{background:#fff;cursor:pointer}.flickity-button:focus{outline:0;box-shadow:0 0 0 5px #19f}.flickity-button:active{opacity:.6}.flickity-button:disabled{opacity:.3;cursor:auto;pointer-events:none}.flickity-button-icon{fill:currentColor}.flickity-prev-next-button{top:50%;width:44px;height:44px;border-radius:50%;transform:translateY(-50%)}.flickity-prev-next-button.previous{left:10px}.flickity-prev-next-button.next{right:10px}.flickity-rtl .flickity-prev-next-button.previous{left:auto;right:10px}.flickity-rtl .flickity-prev-next-button.next{right:auto;left:10px}.flickity-prev-next-button .flickity-button-icon{position:absolute;left:20%;top:20%;width:60%;height:60%}.flickity-page-dots{position:absolute;width:100%;bottom:-25px;text-align:center;display:flex;justify-content:center;flex-wrap:wrap}.flickity-rtl .flickity-page-dots{direction:rtl}.flickity-page-dot{display:block;width:10px;height:10px;padding:0;margin:0 8px;background:hsl(0 0% 20% / 25%);border-radius:50%;cursor:pointer;appearance:none;border:none;text-indent:-9999px;overflow:hidden}.flickity-rtl .flickity-page-dot{text-indent:9999px}.flickity-page-dot:focus{outline:0;box-shadow:0 0 0 5px #19f}.flickity-page-dot.is-selected{background:hsl(0 0% 20% / 100%)}
--------------------------------------------------------------------------------
/js/add-remove-cell.js:
--------------------------------------------------------------------------------
1 | // add, remove cell
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory(
7 | require('./core'),
8 | require('fizzy-ui-utils'),
9 | );
10 | } else {
11 | // browser global
12 | factory(
13 | window.Flickity,
14 | window.fizzyUIUtils,
15 | );
16 | }
17 |
18 | }( typeof window != 'undefined' ? window : this, function factory( Flickity, utils ) {
19 |
20 | // append cells to a document fragment
21 | function getCellsFragment( cells ) {
22 | let fragment = document.createDocumentFragment();
23 | cells.forEach( ( cell ) => fragment.appendChild( cell.element ) );
24 | return fragment;
25 | }
26 |
27 | // -------------------------- add/remove cell prototype -------------------------- //
28 |
29 | let proto = Flickity.prototype;
30 |
31 | /**
32 | * Insert, prepend, or append cells
33 | * @param {[Element, Array, NodeList]} elems - Elements to insert
34 | * @param {Integer} index - Zero-based number to insert
35 | */
36 | proto.insert = function( elems, index ) {
37 | let cells = this._makeCells( elems );
38 | if ( !cells || !cells.length ) return;
39 |
40 | let len = this.cells.length;
41 | // default to append
42 | index = index === undefined ? len : index;
43 | // add cells with document fragment
44 | let fragment = getCellsFragment( cells );
45 | // append to slider
46 | let isAppend = index === len;
47 | if ( isAppend ) {
48 | this.slider.appendChild( fragment );
49 | } else {
50 | let insertCellElement = this.cells[ index ].element;
51 | this.slider.insertBefore( fragment, insertCellElement );
52 | }
53 | // add to this.cells
54 | if ( index === 0 ) {
55 | // prepend, add to start
56 | this.cells = cells.concat( this.cells );
57 | } else if ( isAppend ) {
58 | // append, add to end
59 | this.cells = this.cells.concat( cells );
60 | } else {
61 | // insert in this.cells
62 | let endCells = this.cells.splice( index, len - index );
63 | this.cells = this.cells.concat( cells ).concat( endCells );
64 | }
65 |
66 | this._sizeCells( cells );
67 | this.cellChange( index );
68 | this.positionSliderAtSelected();
69 | };
70 |
71 | proto.append = function( elems ) {
72 | this.insert( elems, this.cells.length );
73 | };
74 |
75 | proto.prepend = function( elems ) {
76 | this.insert( elems, 0 );
77 | };
78 |
79 | /**
80 | * Remove cells
81 | * @param {[Element, Array, NodeList]} elems - ELements to remove
82 | */
83 | proto.remove = function( elems ) {
84 | let cells = this.getCells( elems );
85 | if ( !cells || !cells.length ) return;
86 |
87 | let minCellIndex = this.cells.length - 1;
88 | // remove cells from collection & DOM
89 | cells.forEach( ( cell ) => {
90 | cell.remove();
91 | let index = this.cells.indexOf( cell );
92 | minCellIndex = Math.min( index, minCellIndex );
93 | utils.removeFrom( this.cells, cell );
94 | } );
95 |
96 | this.cellChange( minCellIndex );
97 | this.positionSliderAtSelected();
98 | };
99 |
100 | /**
101 | * logic to be run after a cell's size changes
102 | * @param {Element} elem - cell's element
103 | */
104 | proto.cellSizeChange = function( elem ) {
105 | let cell = this.getCell( elem );
106 | if ( !cell ) return;
107 |
108 | cell.getSize();
109 |
110 | let index = this.cells.indexOf( cell );
111 | this.cellChange( index );
112 | // do not position slider after lazy load
113 | };
114 |
115 | /**
116 | * logic any time a cell is changed: added, removed, or size changed
117 | * @param {Integer} changedCellIndex - index of the changed cell, optional
118 | */
119 | proto.cellChange = function( changedCellIndex ) {
120 | let prevSelectedElem = this.selectedElement;
121 | this._positionCells( changedCellIndex );
122 | this._updateWrapShiftCells();
123 | this.setGallerySize();
124 | // update selectedIndex, try to maintain position & select previous selected element
125 | let cell = this.getCell( prevSelectedElem );
126 | if ( cell ) this.selectedIndex = this.getCellSlideIndex( cell );
127 | this.selectedIndex = Math.min( this.slides.length - 1, this.selectedIndex );
128 |
129 | this.emitEvent( 'cellChange', [ changedCellIndex ] );
130 | // position slider
131 | this.select( this.selectedIndex );
132 | };
133 |
134 | // ----- ----- //
135 |
136 | return Flickity;
137 |
138 | } ) );
139 |
--------------------------------------------------------------------------------
/js/animate.js:
--------------------------------------------------------------------------------
1 | // animate
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory( require('fizzy-ui-utils') );
7 | } else {
8 | // browser global
9 | window.Flickity = window.Flickity || {};
10 | window.Flickity.animatePrototype = factory( window.fizzyUIUtils );
11 | }
12 |
13 | }( typeof window != 'undefined' ? window : this, function factory( utils ) {
14 |
15 | // -------------------------- animate -------------------------- //
16 |
17 | let proto = {};
18 |
19 | proto.startAnimation = function() {
20 | if ( this.isAnimating ) return;
21 |
22 | this.isAnimating = true;
23 | this.restingFrames = 0;
24 | this.animate();
25 | };
26 |
27 | proto.animate = function() {
28 | this.applyDragForce();
29 | this.applySelectedAttraction();
30 |
31 | let previousX = this.x;
32 |
33 | this.integratePhysics();
34 | this.positionSlider();
35 | this.settle( previousX );
36 | // animate next frame
37 | if ( this.isAnimating ) requestAnimationFrame( () => this.animate() );
38 | };
39 |
40 | proto.positionSlider = function() {
41 | let x = this.x;
42 | // wrap position around
43 | if ( this.isWrapping ) {
44 | x = utils.modulo( x, this.slideableWidth ) - this.slideableWidth;
45 | this.shiftWrapCells( x );
46 | }
47 |
48 | this.setTranslateX( x, this.isAnimating );
49 | this.dispatchScrollEvent();
50 | };
51 |
52 | proto.setTranslateX = function( x, is3d ) {
53 | x += this.cursorPosition;
54 | // reverse if right-to-left and using transform
55 | if ( this.options.rightToLeft ) x = -x;
56 | let translateX = this.getPositionValue( x );
57 | // use 3D transforms for hardware acceleration on iOS
58 | // but use 2D when settled, for better font-rendering
59 | this.slider.style.transform = is3d ?
60 | `translate3d(${translateX},0,0)` : `translateX(${translateX})`;
61 | };
62 |
63 | proto.dispatchScrollEvent = function() {
64 | let firstSlide = this.slides[0];
65 | if ( !firstSlide ) return;
66 |
67 | let positionX = -this.x - firstSlide.target;
68 | let progress = positionX / this.slidesWidth;
69 | this.dispatchEvent( 'scroll', null, [ progress, positionX ] );
70 | };
71 |
72 | proto.positionSliderAtSelected = function() {
73 | if ( !this.cells.length ) return;
74 |
75 | this.x = -this.selectedSlide.target;
76 | this.velocity = 0; // stop wobble
77 | this.positionSlider();
78 | };
79 |
80 | proto.getPositionValue = function( position ) {
81 | if ( this.options.percentPosition ) {
82 | // percent position, round to 2 digits, like 12.34%
83 | return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 ) + '%';
84 | } else {
85 | // pixel positioning
86 | return Math.round( position ) + 'px';
87 | }
88 | };
89 |
90 | proto.settle = function( previousX ) {
91 | // keep track of frames where x hasn't moved
92 | let isResting = !this.isPointerDown &&
93 | Math.round( this.x * 100 ) === Math.round( previousX * 100 );
94 | if ( isResting ) this.restingFrames++;
95 | // stop animating if resting for 3 or more frames
96 | if ( this.restingFrames > 2 ) {
97 | this.isAnimating = false;
98 | delete this.isFreeScrolling;
99 | // render position with translateX when settled
100 | this.positionSlider();
101 | this.dispatchEvent( 'settle', null, [ this.selectedIndex ] );
102 | }
103 | };
104 |
105 | proto.shiftWrapCells = function( x ) {
106 | // shift before cells
107 | let beforeGap = this.cursorPosition + x;
108 | this._shiftCells( this.beforeShiftCells, beforeGap, -1 );
109 | // shift after cells
110 | let afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition );
111 | this._shiftCells( this.afterShiftCells, afterGap, 1 );
112 | };
113 |
114 | proto._shiftCells = function( cells, gap, shift ) {
115 | cells.forEach( ( cell ) => {
116 | let cellShift = gap > 0 ? shift : 0;
117 | this._wrapShiftCell( cell, cellShift );
118 | gap -= cell.size.outerWidth;
119 | } );
120 | };
121 |
122 | proto._unshiftCells = function( cells ) {
123 | if ( !cells || !cells.length ) return;
124 |
125 | cells.forEach( ( cell ) => this._wrapShiftCell( cell, 0 ) );
126 | };
127 |
128 | // @param {Integer} shift - 0, 1, or -1
129 | proto._wrapShiftCell = function( cell, shift ) {
130 | this._renderCellPosition( cell, cell.x + this.slideableWidth * shift );
131 | };
132 |
133 | // -------------------------- physics -------------------------- //
134 |
135 | proto.integratePhysics = function() {
136 | this.x += this.velocity;
137 | this.velocity *= this.getFrictionFactor();
138 | };
139 |
140 | proto.applyForce = function( force ) {
141 | this.velocity += force;
142 | };
143 |
144 | proto.getFrictionFactor = function() {
145 | return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ];
146 | };
147 |
148 | proto.getRestingPosition = function() {
149 | // my thanks to Steven Wittens, who simplified this math greatly
150 | return this.x + this.velocity / ( 1 - this.getFrictionFactor() );
151 | };
152 |
153 | proto.applyDragForce = function() {
154 | if ( !this.isDraggable || !this.isPointerDown ) return;
155 |
156 | // change the position to drag position by applying force
157 | let dragVelocity = this.dragX - this.x;
158 | let dragForce = dragVelocity - this.velocity;
159 | this.applyForce( dragForce );
160 | };
161 |
162 | proto.applySelectedAttraction = function() {
163 | // do not attract if pointer down or no slides
164 | let dragDown = this.isDraggable && this.isPointerDown;
165 | if ( dragDown || this.isFreeScrolling || !this.slides.length ) return;
166 |
167 | let distance = this.selectedSlide.target * -1 - this.x;
168 | let force = distance * this.options.selectedAttraction;
169 | this.applyForce( force );
170 | };
171 |
172 | return proto;
173 |
174 | } ) );
175 |
--------------------------------------------------------------------------------
/js/cell.js:
--------------------------------------------------------------------------------
1 | // Flickity.Cell
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory( require('get-size') );
7 | } else {
8 | // browser global
9 | window.Flickity = window.Flickity || {};
10 | window.Flickity.Cell = factory( window.getSize );
11 | }
12 |
13 | }( typeof window != 'undefined' ? window : this, function factory( getSize ) {
14 |
15 | const cellClassName = 'flickity-cell';
16 |
17 | function Cell( elem ) {
18 | this.element = elem;
19 | this.element.classList.add( cellClassName );
20 |
21 | this.x = 0;
22 | this.unselect();
23 | }
24 |
25 | let proto = Cell.prototype;
26 |
27 | proto.destroy = function() {
28 | // reset style
29 | this.unselect();
30 | this.element.classList.remove( cellClassName );
31 | this.element.style.transform = '';
32 | this.element.removeAttribute('aria-hidden');
33 | };
34 |
35 | proto.getSize = function() {
36 | this.size = getSize( this.element );
37 | };
38 |
39 | proto.select = function() {
40 | this.element.classList.add('is-selected');
41 | this.element.removeAttribute('aria-hidden');
42 | };
43 |
44 | proto.unselect = function() {
45 | this.element.classList.remove('is-selected');
46 | this.element.setAttribute( 'aria-hidden', 'true' );
47 | };
48 |
49 | proto.remove = function() {
50 | this.element.remove();
51 | };
52 |
53 | return Cell;
54 |
55 | } ) );
56 |
--------------------------------------------------------------------------------
/js/drag.js:
--------------------------------------------------------------------------------
1 | // drag
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory(
7 | window,
8 | require('./core'),
9 | require('unidragger'),
10 | require('fizzy-ui-utils'),
11 | );
12 | } else {
13 | // browser global
14 | window.Flickity = factory(
15 | window,
16 | window.Flickity,
17 | window.Unidragger,
18 | window.fizzyUIUtils,
19 | );
20 | }
21 |
22 | }( typeof window != 'undefined' ? window : this,
23 | function factory( window, Flickity, Unidragger, utils ) {
24 |
25 | // ----- defaults ----- //
26 |
27 | Object.assign( Flickity.defaults, {
28 | draggable: '>1',
29 | dragThreshold: 3,
30 | } );
31 |
32 | // -------------------------- drag prototype -------------------------- //
33 |
34 | let proto = Flickity.prototype;
35 | Object.assign( proto, Unidragger.prototype ); // inherit Unidragger
36 | proto.touchActionValue = '';
37 |
38 | // -------------------------- -------------------------- //
39 |
40 | Flickity.create.drag = function() {
41 | this.on( 'activate', this.onActivateDrag );
42 | this.on( 'uiChange', this._uiChangeDrag );
43 | this.on( 'deactivate', this.onDeactivateDrag );
44 | this.on( 'cellChange', this.updateDraggable );
45 | this.on( 'pointerDown', this.handlePointerDown );
46 | this.on( 'pointerUp', this.handlePointerUp );
47 | this.on( 'pointerDown', this.handlePointerDone );
48 | this.on( 'dragStart', this.handleDragStart );
49 | this.on( 'dragMove', this.handleDragMove );
50 | this.on( 'dragEnd', this.handleDragEnd );
51 | this.on( 'staticClick', this.handleStaticClick );
52 | // TODO updateDraggable on resize? if groupCells & slides change
53 | };
54 |
55 | proto.onActivateDrag = function() {
56 | this.handles = [ this.viewport ];
57 | this.bindHandles();
58 | this.updateDraggable();
59 | };
60 |
61 | proto.onDeactivateDrag = function() {
62 | this.unbindHandles();
63 | this.element.classList.remove('is-draggable');
64 | };
65 |
66 | proto.updateDraggable = function() {
67 | // disable dragging if less than 2 slides. #278
68 | if ( this.options.draggable === '>1' ) {
69 | this.isDraggable = this.slides.length > 1;
70 | } else {
71 | this.isDraggable = this.options.draggable;
72 | }
73 | this.element.classList.toggle( 'is-draggable', this.isDraggable );
74 | };
75 |
76 | proto._uiChangeDrag = function() {
77 | delete this.isFreeScrolling;
78 | };
79 |
80 | // -------------------------- pointer events -------------------------- //
81 |
82 | proto.handlePointerDown = function( event ) {
83 | if ( !this.isDraggable ) {
84 | // proceed for staticClick
85 | this.bindActivePointerEvents( event );
86 | return;
87 | }
88 |
89 | let isTouchStart = event.type === 'touchstart';
90 | let isTouchPointer = event.pointerType === 'touch';
91 | let isFocusNode = event.target.matches('input, textarea, select');
92 | if ( !isTouchStart && !isTouchPointer && !isFocusNode ) event.preventDefault();
93 | if ( !isFocusNode ) this.focus();
94 | // blur
95 | if ( document.activeElement !== this.element ) document.activeElement.blur();
96 | // stop if it was moving
97 | this.dragX = this.x;
98 | this.viewport.classList.add('is-pointer-down');
99 | // track scrolling
100 | this.pointerDownScroll = getScrollPosition();
101 | window.addEventListener( 'scroll', this );
102 | this.bindActivePointerEvents( event );
103 | };
104 |
105 | // ----- move ----- //
106 |
107 | proto.hasDragStarted = function( moveVector ) {
108 | return Math.abs( moveVector.x ) > this.options.dragThreshold;
109 | };
110 |
111 | // ----- up ----- //
112 |
113 | proto.handlePointerUp = function() {
114 | delete this.isTouchScrolling;
115 | this.viewport.classList.remove('is-pointer-down');
116 | };
117 |
118 | proto.handlePointerDone = function() {
119 | window.removeEventListener( 'scroll', this );
120 | delete this.pointerDownScroll;
121 | };
122 |
123 | // -------------------------- dragging -------------------------- //
124 |
125 | proto.handleDragStart = function() {
126 | if ( !this.isDraggable ) return;
127 |
128 | this.dragStartPosition = this.x;
129 | this.startAnimation();
130 | window.removeEventListener( 'scroll', this );
131 | };
132 |
133 | proto.handleDragMove = function( event, pointer, moveVector ) {
134 | if ( !this.isDraggable ) return;
135 |
136 | event.preventDefault();
137 |
138 | this.previousDragX = this.dragX;
139 | // reverse if right-to-left
140 | let direction = this.options.rightToLeft ? -1 : 1;
141 | // wrap around move. #589
142 | if ( this.isWrapping ) moveVector.x %= this.slideableWidth;
143 | let dragX = this.dragStartPosition + moveVector.x * direction;
144 |
145 | if ( !this.isWrapping ) {
146 | // slow drag
147 | let originBound = Math.max( -this.slides[0].target, this.dragStartPosition );
148 | dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX;
149 | let endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition );
150 | dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX;
151 | }
152 |
153 | this.dragX = dragX;
154 | this.dragMoveTime = new Date();
155 | };
156 |
157 | proto.handleDragEnd = function() {
158 | if ( !this.isDraggable ) return;
159 |
160 | let { freeScroll } = this.options;
161 | if ( freeScroll ) this.isFreeScrolling = true;
162 | // set selectedIndex based on where flick will end up
163 | let index = this.dragEndRestingSelect();
164 |
165 | if ( freeScroll && !this.isWrapping ) {
166 | // if free-scroll & not wrap around
167 | // do not free-scroll if going outside of bounding slides
168 | // so bounding slides can attract slider, and keep it in bounds
169 | let restingX = this.getRestingPosition();
170 | this.isFreeScrolling = -restingX > this.slides[0].target &&
171 | -restingX < this.getLastSlide().target;
172 | } else if ( !freeScroll && index === this.selectedIndex ) {
173 | // boost selection if selected index has not changed
174 | index += this.dragEndBoostSelect();
175 | }
176 | delete this.previousDragX;
177 | // apply selection
178 | // HACK, set flag so dragging stays in correct direction
179 | this.isDragSelect = this.isWrapping;
180 | this.select( index );
181 | delete this.isDragSelect;
182 | };
183 |
184 | proto.dragEndRestingSelect = function() {
185 | let restingX = this.getRestingPosition();
186 | // how far away from selected slide
187 | let distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) );
188 | // get closet resting going up and going down
189 | let positiveResting = this._getClosestResting( restingX, distance, 1 );
190 | let negativeResting = this._getClosestResting( restingX, distance, -1 );
191 | // use closer resting for wrap-around
192 | return positiveResting.distance < negativeResting.distance ?
193 | positiveResting.index : negativeResting.index;
194 | };
195 |
196 | /**
197 | * given resting X and distance to selected cell
198 | * get the distance and index of the closest cell
199 | * @param {Number} restingX - estimated post-flick resting position
200 | * @param {Number} distance - distance to selected cell
201 | * @param {Integer} increment - +1 or -1, going up or down
202 | * @returns {Object} - { distance: {Number}, index: {Integer} }
203 | */
204 | proto._getClosestResting = function( restingX, distance, increment ) {
205 | let index = this.selectedIndex;
206 | let minDistance = Infinity;
207 | let condition = this.options.contain && !this.isWrapping ?
208 | // if containing, keep going if distance is equal to minDistance
209 | ( dist, minDist ) => dist <= minDist :
210 | ( dist, minDist ) => dist < minDist;
211 |
212 | while ( condition( distance, minDistance ) ) {
213 | // measure distance to next cell
214 | index += increment;
215 | minDistance = distance;
216 | distance = this.getSlideDistance( -restingX, index );
217 | if ( distance === null ) break;
218 |
219 | distance = Math.abs( distance );
220 | }
221 | return {
222 | distance: minDistance,
223 | // selected was previous index
224 | index: index - increment,
225 | };
226 | };
227 |
228 | /**
229 | * measure distance between x and a slide target
230 | * @param {Number} x - horizontal position
231 | * @param {Integer} index - slide index
232 | * @returns {Number} - slide distance
233 | */
234 | proto.getSlideDistance = function( x, index ) {
235 | let len = this.slides.length;
236 | // wrap around if at least 2 slides
237 | let isWrapAround = this.options.wrapAround && len > 1;
238 | let slideIndex = isWrapAround ? utils.modulo( index, len ) : index;
239 | let slide = this.slides[ slideIndex ];
240 | if ( !slide ) return null;
241 |
242 | // add distance for wrap-around slides
243 | let wrap = isWrapAround ? this.slideableWidth * Math.floor( index/len ) : 0;
244 | return x - ( slide.target + wrap );
245 | };
246 |
247 | proto.dragEndBoostSelect = function() {
248 | // do not boost if no previousDragX or dragMoveTime
249 | if ( this.previousDragX === undefined || !this.dragMoveTime ||
250 | // or if drag was held for 100 ms
251 | new Date() - this.dragMoveTime > 100 ) {
252 | return 0;
253 | }
254 |
255 | let distance = this.getSlideDistance( -this.dragX, this.selectedIndex );
256 | let delta = this.previousDragX - this.dragX;
257 | if ( distance > 0 && delta > 0 ) {
258 | // boost to next if moving towards the right, and positive velocity
259 | return 1;
260 | } else if ( distance < 0 && delta < 0 ) {
261 | // boost to previous if moving towards the left, and negative velocity
262 | return -1;
263 | }
264 | return 0;
265 | };
266 |
267 | // ----- scroll ----- //
268 |
269 | proto.onscroll = function() {
270 | let scroll = getScrollPosition();
271 | let scrollMoveX = this.pointerDownScroll.x - scroll.x;
272 | let scrollMoveY = this.pointerDownScroll.y - scroll.y;
273 | // cancel click/tap if scroll is too much
274 | if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) {
275 | this.pointerDone();
276 | }
277 | };
278 |
279 | // ----- utils ----- //
280 |
281 | function getScrollPosition() {
282 | return {
283 | x: window.pageXOffset,
284 | y: window.pageYOffset,
285 | };
286 | }
287 |
288 | // ----- ----- //
289 |
290 | return Flickity;
291 |
292 | } ) );
293 |
--------------------------------------------------------------------------------
/js/imagesloaded.js:
--------------------------------------------------------------------------------
1 | // imagesloaded
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory(
7 | require('./core'),
8 | require('imagesloaded'),
9 | );
10 | } else {
11 | // browser global
12 | factory(
13 | window.Flickity,
14 | window.imagesLoaded,
15 | );
16 | }
17 |
18 | }( typeof window != 'undefined' ? window : this,
19 | function factory( Flickity, imagesLoaded ) {
20 |
21 | Flickity.create.imagesLoaded = function() {
22 | this.on( 'activate', this.imagesLoaded );
23 | };
24 |
25 | Flickity.prototype.imagesLoaded = function() {
26 | if ( !this.options.imagesLoaded ) return;
27 |
28 | let onImagesLoadedProgress = ( instance, image ) => {
29 | let cell = this.getParentCell( image.img );
30 | this.cellSizeChange( cell && cell.element );
31 | if ( !this.options.freeScroll ) this.positionSliderAtSelected();
32 | };
33 | imagesLoaded( this.slider ).on( 'progress', onImagesLoadedProgress );
34 | };
35 |
36 | return Flickity;
37 |
38 | } ) );
39 |
--------------------------------------------------------------------------------
/js/index.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Flickity v3.0.0
3 | * Touch, responsive, flickable carousels
4 | *
5 | * Licensed GPLv3 for open source use
6 | * or Flickity Commercial License for commercial use
7 | *
8 | * https://flickity.metafizzy.co
9 | * Copyright 2015-2022 Metafizzy
10 | */
11 |
12 | if ( typeof module == 'object' && module.exports ) {
13 | const Flickity = require('./core');
14 | require('./drag');
15 | require('./prev-next-button');
16 | require('./page-dots');
17 | require('./player');
18 | require('./add-remove-cell');
19 | require('./lazyload');
20 | require('./imagesloaded');
21 |
22 | module.exports = Flickity;
23 | }
24 |
--------------------------------------------------------------------------------
/js/lazyload.js:
--------------------------------------------------------------------------------
1 | // lazyload
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory(
7 | require('./core'),
8 | require('fizzy-ui-utils'),
9 | );
10 | } else {
11 | // browser global
12 | factory(
13 | window.Flickity,
14 | window.fizzyUIUtils,
15 | );
16 | }
17 |
18 | }( typeof window != 'undefined' ? window : this, function factory( Flickity, utils ) {
19 |
20 | const lazyAttr = 'data-flickity-lazyload';
21 | const lazySrcAttr = `${lazyAttr}-src`;
22 | const lazySrcsetAttr = `${lazyAttr}-srcset`;
23 | const imgSelector = `img[${lazyAttr}], img[${lazySrcAttr}], ` +
24 | `img[${lazySrcsetAttr}], source[${lazySrcsetAttr}]`;
25 |
26 | Flickity.create.lazyLoad = function() {
27 | this.on( 'select', this.lazyLoad );
28 |
29 | this.handleLazyLoadComplete = this.onLazyLoadComplete.bind( this );
30 | };
31 |
32 | let proto = Flickity.prototype;
33 |
34 | proto.lazyLoad = function() {
35 | let { lazyLoad } = this.options;
36 | if ( !lazyLoad ) return;
37 |
38 | // get adjacent cells, use lazyLoad option for adjacent count
39 | let adjCount = typeof lazyLoad == 'number' ? lazyLoad : 0;
40 | // lazy load images
41 | this.getAdjacentCellElements( adjCount )
42 | .map( getCellLazyImages )
43 | .flat()
44 | .forEach( ( img ) => new LazyLoader( img, this.handleLazyLoadComplete ) );
45 | };
46 |
47 | function getCellLazyImages( cellElem ) {
48 | // check if cell element is lazy image
49 | if ( cellElem.matches('img') ) {
50 | let cellAttr = cellElem.getAttribute( lazyAttr );
51 | let cellSrcAttr = cellElem.getAttribute( lazySrcAttr );
52 | let cellSrcsetAttr = cellElem.getAttribute( lazySrcsetAttr );
53 | if ( cellAttr || cellSrcAttr || cellSrcsetAttr ) {
54 | return cellElem;
55 | }
56 | }
57 | // select lazy images in cell
58 | return [ ...cellElem.querySelectorAll( imgSelector ) ];
59 | }
60 |
61 | proto.onLazyLoadComplete = function( img, event ) {
62 | let cell = this.getParentCell( img );
63 | let cellElem = cell && cell.element;
64 | this.cellSizeChange( cellElem );
65 |
66 | this.dispatchEvent( 'lazyLoad', event, cellElem );
67 | };
68 |
69 | // -------------------------- LazyLoader -------------------------- //
70 |
71 | /**
72 | * class to handle loading images
73 | * @param {Image} img - Image element
74 | * @param {Function} onComplete - callback function
75 | */
76 | function LazyLoader( img, onComplete ) {
77 | this.img = img;
78 | this.onComplete = onComplete;
79 | this.load();
80 | }
81 |
82 | LazyLoader.prototype.handleEvent = utils.handleEvent;
83 |
84 | LazyLoader.prototype.load = function() {
85 | this.img.addEventListener( 'load', this );
86 | this.img.addEventListener( 'error', this );
87 | // get src & srcset
88 | let src = this.img.getAttribute( lazyAttr ) ||
89 | this.img.getAttribute( lazySrcAttr );
90 | let srcset = this.img.getAttribute( lazySrcsetAttr );
91 | // set src & serset
92 | this.img.src = src;
93 | if ( srcset ) this.img.setAttribute( 'srcset', srcset );
94 | // remove attr
95 | this.img.removeAttribute( lazyAttr );
96 | this.img.removeAttribute( lazySrcAttr );
97 | this.img.removeAttribute( lazySrcsetAttr );
98 | };
99 |
100 | LazyLoader.prototype.onload = function( event ) {
101 | this.complete( event, 'flickity-lazyloaded' );
102 | };
103 |
104 | LazyLoader.prototype.onerror = function( event ) {
105 | this.complete( event, 'flickity-lazyerror' );
106 | };
107 |
108 | LazyLoader.prototype.complete = function( event, className ) {
109 | // unbind events
110 | this.img.removeEventListener( 'load', this );
111 | this.img.removeEventListener( 'error', this );
112 | let mediaElem = this.img.parentNode.matches('picture') ? this.img.parentNode : this.img;
113 | mediaElem.classList.add( className );
114 |
115 | this.onComplete( this.img, event );
116 | };
117 |
118 | // ----- ----- //
119 |
120 | Flickity.LazyLoader = LazyLoader;
121 |
122 | return Flickity;
123 |
124 | } ) );
125 |
--------------------------------------------------------------------------------
/js/page-dots.js:
--------------------------------------------------------------------------------
1 | // page dots
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory(
7 | require('./core'),
8 | require('fizzy-ui-utils'),
9 | );
10 | } else {
11 | // browser global
12 | factory(
13 | window.Flickity,
14 | window.fizzyUIUtils,
15 | );
16 | }
17 |
18 | }( typeof window != 'undefined' ? window : this, function factory( Flickity, utils ) {
19 |
20 | // -------------------------- PageDots -------------------------- //
21 |
22 | function PageDots() {
23 | // create holder element
24 | this.holder = document.createElement('div');
25 | this.holder.className = 'flickity-page-dots';
26 | // create dots, array of elements
27 | this.dots = [];
28 | }
29 |
30 | PageDots.prototype.setDots = function( slidesLength ) {
31 | // get difference between number of slides and number of dots
32 | let delta = slidesLength - this.dots.length;
33 | if ( delta > 0 ) {
34 | this.addDots( delta );
35 | } else if ( delta < 0 ) {
36 | this.removeDots( -delta );
37 | }
38 | };
39 |
40 | PageDots.prototype.addDots = function( count ) {
41 | let newDots = new Array( count ).fill()
42 | .map( ( item, i ) => {
43 | let dot = document.createElement('button');
44 | dot.setAttribute( 'type', 'button' );
45 | let num = i + 1 + this.dots.length;
46 | dot.className = 'flickity-button flickity-page-dot';
47 | dot.textContent = `View slide ${num}`;
48 | return dot;
49 | } );
50 |
51 | this.holder.append( ...newDots );
52 | this.dots = this.dots.concat( newDots );
53 | };
54 |
55 | PageDots.prototype.removeDots = function( count ) {
56 | // remove from this.dots collection
57 | let removeDots = this.dots.splice( this.dots.length - count, count );
58 | // remove from DOM
59 | removeDots.forEach( ( dot ) => dot.remove() );
60 | };
61 |
62 | PageDots.prototype.updateSelected = function( index ) {
63 | // remove selected class on previous
64 | if ( this.selectedDot ) {
65 | this.selectedDot.classList.remove('is-selected');
66 | this.selectedDot.removeAttribute('aria-current');
67 | }
68 | // don't proceed if no dots
69 | if ( !this.dots.length ) return;
70 |
71 | this.selectedDot = this.dots[ index ];
72 | this.selectedDot.classList.add('is-selected');
73 | this.selectedDot.setAttribute( 'aria-current', 'step' );
74 | };
75 |
76 | Flickity.PageDots = PageDots;
77 |
78 | // -------------------------- Flickity -------------------------- //
79 |
80 | Object.assign( Flickity.defaults, {
81 | pageDots: true,
82 | } );
83 |
84 | Flickity.create.pageDots = function() {
85 | if ( !this.options.pageDots ) return;
86 |
87 | this.pageDots = new PageDots();
88 | this.handlePageDotsClick = this.onPageDotsClick.bind( this );
89 | // events
90 | this.on( 'activate', this.activatePageDots );
91 | this.on( 'select', this.updateSelectedPageDots );
92 | this.on( 'cellChange', this.updatePageDots );
93 | this.on( 'resize', this.updatePageDots );
94 | this.on( 'deactivate', this.deactivatePageDots );
95 | };
96 |
97 | let proto = Flickity.prototype;
98 |
99 | proto.activatePageDots = function() {
100 | this.pageDots.setDots( this.slides.length );
101 | this.focusableElems.push( ...this.pageDots.dots );
102 | this.pageDots.holder.addEventListener( 'click', this.handlePageDotsClick );
103 | this.element.insertBefore( this.pageDots.holder, this.viewport );
104 | };
105 |
106 | proto.onPageDotsClick = function( event ) {
107 | let index = this.pageDots.dots.indexOf( event.target );
108 | if ( index === -1 ) return; // only dot clicks
109 |
110 | this.uiChange();
111 | this.select( index );
112 | };
113 |
114 | proto.updateSelectedPageDots = function() {
115 | this.pageDots.updateSelected( this.selectedIndex );
116 | };
117 |
118 | proto.updatePageDots = function() {
119 | this.pageDots.dots.forEach( ( dot ) => {
120 | utils.removeFrom( this.focusableElems, dot );
121 | } );
122 | this.pageDots.setDots( this.slides.length );
123 | this.focusableElems.push( ...this.pageDots.dots );
124 | };
125 |
126 | proto.deactivatePageDots = function() {
127 | this.pageDots.holder.remove();
128 | this.pageDots.holder.removeEventListener( 'click', this.handlePageDotsClick );
129 | };
130 |
131 | // ----- ----- //
132 |
133 | Flickity.PageDots = PageDots;
134 |
135 | return Flickity;
136 |
137 | } ) );
138 |
--------------------------------------------------------------------------------
/js/player.js:
--------------------------------------------------------------------------------
1 | // player & autoPlay
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory( require('./core') );
7 | } else {
8 | // browser global
9 | factory( window.Flickity );
10 | }
11 |
12 | }( typeof window != 'undefined' ? window : this, function factory( Flickity ) {
13 |
14 | // -------------------------- Player -------------------------- //
15 |
16 | function Player( autoPlay, onTick ) {
17 | this.autoPlay = autoPlay;
18 | this.onTick = onTick;
19 | this.state = 'stopped';
20 | // visibility change event handler
21 | this.onVisibilityChange = this.visibilityChange.bind( this );
22 | this.onVisibilityPlay = this.visibilityPlay.bind( this );
23 | }
24 |
25 | // start play
26 | Player.prototype.play = function() {
27 | if ( this.state === 'playing' ) return;
28 |
29 | // do not play if page is hidden, start playing when page is visible
30 | let isPageHidden = document.hidden;
31 | if ( isPageHidden ) {
32 | document.addEventListener( 'visibilitychange', this.onVisibilityPlay );
33 | return;
34 | }
35 |
36 | this.state = 'playing';
37 | // listen to visibility change
38 | document.addEventListener( 'visibilitychange', this.onVisibilityChange );
39 | // start ticking
40 | this.tick();
41 | };
42 |
43 | Player.prototype.tick = function() {
44 | // do not tick if not playing
45 | if ( this.state !== 'playing' ) return;
46 |
47 | // default to 3 seconds
48 | let time = typeof this.autoPlay == 'number' ? this.autoPlay : 3000;
49 | // HACK: reset ticks if stopped and started within interval
50 | this.clear();
51 | this.timeout = setTimeout( () => {
52 | this.onTick();
53 | this.tick();
54 | }, time );
55 | };
56 |
57 | Player.prototype.stop = function() {
58 | this.state = 'stopped';
59 | this.clear();
60 | // remove visibility change event
61 | document.removeEventListener( 'visibilitychange', this.onVisibilityChange );
62 | };
63 |
64 | Player.prototype.clear = function() {
65 | clearTimeout( this.timeout );
66 | };
67 |
68 | Player.prototype.pause = function() {
69 | if ( this.state === 'playing' ) {
70 | this.state = 'paused';
71 | this.clear();
72 | }
73 | };
74 |
75 | Player.prototype.unpause = function() {
76 | // re-start play if paused
77 | if ( this.state === 'paused' ) this.play();
78 | };
79 |
80 | // pause if page visibility is hidden, unpause if visible
81 | Player.prototype.visibilityChange = function() {
82 | let isPageHidden = document.hidden;
83 | this[ isPageHidden ? 'pause' : 'unpause' ]();
84 | };
85 |
86 | Player.prototype.visibilityPlay = function() {
87 | this.play();
88 | document.removeEventListener( 'visibilitychange', this.onVisibilityPlay );
89 | };
90 |
91 | // -------------------------- Flickity -------------------------- //
92 |
93 | Object.assign( Flickity.defaults, {
94 | pauseAutoPlayOnHover: true,
95 | } );
96 |
97 | Flickity.create.player = function() {
98 | this.player = new Player( this.options.autoPlay, () => {
99 | this.next( true );
100 | } );
101 |
102 | this.on( 'activate', this.activatePlayer );
103 | this.on( 'uiChange', this.stopPlayer );
104 | this.on( 'pointerDown', this.stopPlayer );
105 | this.on( 'deactivate', this.deactivatePlayer );
106 | };
107 |
108 | let proto = Flickity.prototype;
109 |
110 | proto.activatePlayer = function() {
111 | if ( !this.options.autoPlay ) return;
112 |
113 | this.player.play();
114 | this.element.addEventListener( 'mouseenter', this );
115 | };
116 |
117 | // Player API, don't hate the ... thanks I know where the door is
118 |
119 | proto.playPlayer = function() {
120 | this.player.play();
121 | };
122 |
123 | proto.stopPlayer = function() {
124 | this.player.stop();
125 | };
126 |
127 | proto.pausePlayer = function() {
128 | this.player.pause();
129 | };
130 |
131 | proto.unpausePlayer = function() {
132 | this.player.unpause();
133 | };
134 |
135 | proto.deactivatePlayer = function() {
136 | this.player.stop();
137 | this.element.removeEventListener( 'mouseenter', this );
138 | };
139 |
140 | // ----- mouseenter/leave ----- //
141 |
142 | // pause auto-play on hover
143 | proto.onmouseenter = function() {
144 | if ( !this.options.pauseAutoPlayOnHover ) return;
145 |
146 | this.player.pause();
147 | this.element.addEventListener( 'mouseleave', this );
148 | };
149 |
150 | // resume auto-play on hover off
151 | proto.onmouseleave = function() {
152 | this.player.unpause();
153 | this.element.removeEventListener( 'mouseleave', this );
154 | };
155 |
156 | // ----- ----- //
157 |
158 | Flickity.Player = Player;
159 |
160 | return Flickity;
161 |
162 | } ) );
163 |
--------------------------------------------------------------------------------
/js/prev-next-button.js:
--------------------------------------------------------------------------------
1 | // prev/next buttons
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory( require('./core') );
7 | } else {
8 | // browser global
9 | factory( window.Flickity );
10 | }
11 |
12 | }( typeof window != 'undefined' ? window : this, function factory( Flickity ) {
13 |
14 | const svgURI = 'http://www.w3.org/2000/svg';
15 |
16 | // -------------------------- PrevNextButton -------------------------- //
17 |
18 | function PrevNextButton( increment, direction, arrowShape ) {
19 | this.increment = increment;
20 | this.direction = direction;
21 | this.isPrevious = increment === 'previous';
22 | this.isLeft = direction === 'left';
23 | this._create( arrowShape );
24 | }
25 |
26 | PrevNextButton.prototype._create = function( arrowShape ) {
27 | // properties
28 | let element = this.element = document.createElement('button');
29 | element.className = `flickity-button flickity-prev-next-button ${this.increment}`;
30 | let label = this.isPrevious ? 'Previous' : 'Next';
31 | // prevent button from submitting form https://stackoverflow.com/a/10836076/182183
32 | element.setAttribute( 'type', 'button' );
33 | element.setAttribute( 'aria-label', label );
34 | // init as disabled
35 | this.disable();
36 | // create arrow
37 | let svg = this.createSVG( label, arrowShape );
38 | element.append( svg );
39 | };
40 |
41 | PrevNextButton.prototype.createSVG = function( label, arrowShape ) {
42 | let svg = document.createElementNS( svgURI, 'svg' );
43 | svg.setAttribute( 'class', 'flickity-button-icon' );
44 | svg.setAttribute( 'viewBox', '0 0 100 100' );
45 | // add title #1189
46 | let title = document.createElementNS( svgURI, 'title' );
47 | title.append( label );
48 | // add path
49 | let path = document.createElementNS( svgURI, 'path' );
50 | let pathMovements = getArrowMovements( arrowShape );
51 | path.setAttribute( 'd', pathMovements );
52 | path.setAttribute( 'class', 'arrow' );
53 | // rotate arrow
54 | if ( !this.isLeft ) {
55 | path.setAttribute( 'transform', 'translate(100, 100) rotate(180)' );
56 | }
57 | svg.append( title, path );
58 | return svg;
59 | };
60 |
61 | // get SVG path movmement
62 | function getArrowMovements( shape ) {
63 | // use shape as movement if string
64 | if ( typeof shape == 'string' ) return shape;
65 |
66 | let { x0, x1, x2, x3, y1, y2 } = shape;
67 |
68 | // create movement string
69 | return `M ${x0}, 50
70 | L ${x1}, ${y1 + 50}
71 | L ${x2}, ${y2 + 50}
72 | L ${x3}, 50
73 | L ${x2}, ${50 - y2}
74 | L ${x1}, ${50 - y1}
75 | Z`;
76 | }
77 |
78 | // ----- ----- //
79 |
80 | PrevNextButton.prototype.enable = function() {
81 | this.element.removeAttribute('disabled');
82 | };
83 |
84 | PrevNextButton.prototype.disable = function() {
85 | this.element.setAttribute( 'disabled', true );
86 | };
87 |
88 | // -------------------------- Flickity prototype -------------------------- //
89 |
90 | Object.assign( Flickity.defaults, {
91 | prevNextButtons: true,
92 | arrowShape: {
93 | x0: 10,
94 | x1: 60, y1: 50,
95 | x2: 70, y2: 40,
96 | x3: 30,
97 | },
98 | } );
99 |
100 | Flickity.create.prevNextButtons = function() {
101 | if ( !this.options.prevNextButtons ) return;
102 |
103 | let { rightToLeft, arrowShape } = this.options;
104 | let prevDirection = rightToLeft ? 'right' : 'left';
105 | let nextDirection = rightToLeft ? 'left' : 'right';
106 | this.prevButton = new PrevNextButton( 'previous', prevDirection, arrowShape );
107 | this.nextButton = new PrevNextButton( 'next', nextDirection, arrowShape );
108 | this.focusableElems.push( this.prevButton.element );
109 | this.focusableElems.push( this.nextButton.element );
110 |
111 | this.handlePrevButtonClick = () => {
112 | this.uiChange();
113 | this.previous();
114 | };
115 |
116 | this.handleNextButtonClick = () => {
117 | this.uiChange();
118 | this.next();
119 | };
120 |
121 | this.on( 'activate', this.activatePrevNextButtons );
122 | this.on( 'select', this.updatePrevNextButtons );
123 | };
124 |
125 | let proto = Flickity.prototype;
126 |
127 | proto.updatePrevNextButtons = function() {
128 | let lastIndex = this.slides.length ? this.slides.length - 1 : 0;
129 | this.updatePrevNextButton( this.prevButton, 0 );
130 | this.updatePrevNextButton( this.nextButton, lastIndex );
131 | };
132 |
133 | proto.updatePrevNextButton = function( button, disabledIndex ) {
134 | // enable is wrapAround and at least 2 slides
135 | if ( this.isWrapping && this.slides.length > 1 ) {
136 | button.enable();
137 | return;
138 | }
139 |
140 | let isEnabled = this.selectedIndex !== disabledIndex;
141 | button[ isEnabled ? 'enable' : 'disable' ]();
142 | // if disabling button that is focused,
143 | // shift focus to element to maintain keyboard accessibility
144 | let isDisabledFocused = !isEnabled && document.activeElement === button.element;
145 | if ( isDisabledFocused ) this.focus();
146 | };
147 |
148 | proto.activatePrevNextButtons = function() {
149 | this.prevButton.element.addEventListener( 'click', this.handlePrevButtonClick );
150 | this.nextButton.element.addEventListener( 'click', this.handleNextButtonClick );
151 | this.element.prepend( this.prevButton.element, this.nextButton.element );
152 | this.on( 'deactivate', this.deactivatePrevNextButtons );
153 | };
154 |
155 | proto.deactivatePrevNextButtons = function() {
156 | this.prevButton.element.remove();
157 | this.nextButton.element.remove();
158 | this.prevButton.element.removeEventListener( 'click', this.handlePrevButtonClick );
159 | this.nextButton.element.removeEventListener( 'click', this.handleNextButtonClick );
160 | this.off( 'deactivate', this.deactivatePrevNextButtons );
161 | };
162 |
163 | // -------------------------- -------------------------- //
164 |
165 | Flickity.PrevNextButton = PrevNextButton;
166 |
167 | return Flickity;
168 |
169 | } ) );
170 |
--------------------------------------------------------------------------------
/js/slide.js:
--------------------------------------------------------------------------------
1 | // slide
2 | ( function( window, factory ) {
3 | // universal module definition
4 | if ( typeof module == 'object' && module.exports ) {
5 | // CommonJS
6 | module.exports = factory();
7 | } else {
8 | // browser global
9 | window.Flickity = window.Flickity || {};
10 | window.Flickity.Slide = factory();
11 | }
12 |
13 | }( typeof window != 'undefined' ? window : this, function factory() {
14 |
15 | function Slide( beginMargin, endMargin, cellAlign ) {
16 | this.beginMargin = beginMargin;
17 | this.endMargin = endMargin;
18 | this.cellAlign = cellAlign;
19 | this.cells = [];
20 | this.outerWidth = 0;
21 | this.height = 0;
22 | }
23 |
24 | let proto = Slide.prototype;
25 |
26 | proto.addCell = function( cell ) {
27 | this.cells.push( cell );
28 | this.outerWidth += cell.size.outerWidth;
29 | this.height = Math.max( cell.size.outerHeight, this.height );
30 | // first cell stuff
31 | if ( this.cells.length === 1 ) {
32 | this.x = cell.x; // x comes from first cell
33 | this.firstMargin = cell.size[ this.beginMargin ];
34 | }
35 | };
36 |
37 | proto.updateTarget = function() {
38 | let lastCell = this.getLastCell();
39 | let lastMargin = lastCell ? lastCell.size[ this.endMargin ] : 0;
40 | let slideWidth = this.outerWidth - ( this.firstMargin + lastMargin );
41 | this.target = this.x + this.firstMargin + slideWidth * this.cellAlign;
42 | };
43 |
44 | proto.getLastCell = function() {
45 | return this.cells[ this.cells.length - 1 ];
46 | };
47 |
48 | proto.select = function() {
49 | this.cells.forEach( ( cell ) => cell.select() );
50 | };
51 |
52 | proto.unselect = function() {
53 | this.cells.forEach( ( cell ) => cell.unselect() );
54 | };
55 |
56 | proto.getCellElements = function() {
57 | return this.cells.map( ( cell ) => cell.element );
58 | };
59 |
60 | return Slide;
61 |
62 | } ) );
63 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flickity",
3 | "version": "3.0.0",
4 | "description": "Touch, responsive, flickable carousels",
5 | "main": "js/index.js",
6 | "style": "css/flickity.css",
7 | "scripts": {
8 | "test": "npm run lint && echo \"View test/ in browser\" && exit 1",
9 | "lintJs": "npx eslint .",
10 | "lintJson": "node bin/lint-json.js",
11 | "lintCss": "npx stylelint '**/*.css'",
12 | "lint": "npm run lintJson && npm run lintJs && npm run lintCss",
13 | "dist": "npm run bundleCss && npm run bundleJs",
14 | "bundleCss": "cp css/flickity.css dist/flickity.css && node bin/bundle-css.js",
15 | "bundleJs": "node bin/bundle-js.js",
16 | "version": "node bin/version.js && npm run dist && git add -A css js dist"
17 | },
18 | "dependencies": {
19 | "ev-emitter": "^2.1.2",
20 | "fizzy-ui-utils": "^3.0.0",
21 | "get-size": "^3.0.0",
22 | "imagesloaded": "^5.0.0",
23 | "unidragger": "^3.0.1"
24 | },
25 | "devDependencies": {
26 | "clean-css": "^5.2.2",
27 | "eslint": "^8.10.0",
28 | "eslint-plugin-metafizzy": "^2.0.1",
29 | "jquery-bridget": "^3.0.1",
30 | "qunit": "^2.17.2",
31 | "stylelint": "^14.2.0",
32 | "stylelint-config-standard": "^24.0.0",
33 | "terser": "^5.10.0"
34 | },
35 | "repository": {
36 | "type": "git",
37 | "url": "git://github.com/metafizzy/flickity.git"
38 | },
39 | "keywords": [
40 | "touch",
41 | "responsive",
42 | "flick",
43 | "slider",
44 | "carousel",
45 | "gallery",
46 | "DOM",
47 | "browser"
48 | ],
49 | "author": "Metafizzy",
50 | "license": "GPL-3.0",
51 | "bugs": {
52 | "url": "https://github.com/metafizzy/flickity/issues"
53 | },
54 | "homepage": "https://flickity.metafizzy.co",
55 | "directories": {
56 | "test": "test"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/sandbox/adaptive-height.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | slides
8 |
9 |
10 |
11 |
51 |
52 |
53 |
54 |
55 | slides
56 |
57 |
58 |
0
59 |
1
60 |
2
61 |
3
62 |
4
63 |
5
64 |
6
65 |
7
66 |
8
67 |
9
68 |
10
69 |
70 |
71 |
72 |

73 |

74 |

75 |

76 |

77 |

78 |

79 |

80 |

81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/sandbox/add-remove.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | add/remove cells
8 |
9 |
10 |
11 |
12 |
20 |
21 |
22 |
23 |
24 | add/remove cells
25 |
26 |
27 |
28 |
1
29 |
2
30 |
3
31 |
4
32 |
5
33 |
6
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | freeScroll
43 |
44 |
45 |
47 |
1
48 |
2
49 |
3
50 |
4
51 |
5
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | wrapAround
61 |
62 |
63 |
65 |
1
66 |
2
67 |
3
68 |
4
69 |
5
70 |
6
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | wrapAround, freeScroll
80 |
81 |
82 |
84 |
1
85 |
2
86 |
3
87 |
4
88 |
5
89 |
6
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | contain
99 |
100 |
101 |
103 |
1
104 |
2
105 |
3
106 |
4
107 |
5
108 |
6
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | reposition
119 |
120 |
121 |
122 |
1
123 |
2
124 |
3
125 |
4
126 |
5
127 |
6
128 |
7
129 |
8
130 |
9
131 |
132 |
133 |
134 | prepend with single #492
135 |
136 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/sandbox/ajax.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ajax
8 |
9 |
10 |
19 |
20 |
21 |
22 |
23 | ajax
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/sandbox/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | basic1
8 |
9 |
10 |
11 |
12 |
39 |
40 |
41 |
42 |
43 | basic1
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
76 |
77 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | contain
109 |
110 |
112 |
1
113 |
2
114 |
3
115 |
4
116 |
5
117 |
6
118 |
119 |
120 | contain, freeScroll
121 |
122 |
124 |
1
125 |
2
126 |
3
127 |
4
128 |
5
129 |
6
130 |
7
131 |
8
132 |
9
133 |
134 |
135 | contain, few
136 |
137 |
139 |
1
140 |
2
141 |
142 |
143 |
144 | watch, activate >900px
145 |
146 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/sandbox/freescroll.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | freescroll
8 |
9 |
10 |
44 |
45 |
46 |
47 |
48 | freescroll
49 |
50 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/sandbox/group-cells.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | group cells
8 |
9 |
10 |
11 |
51 |
52 |
53 |
54 |
55 | group cells
56 |
57 |
58 |
0
59 |
1
60 |
2
61 |
3
62 |
4
63 |
5
64 |
6
65 |
7
66 |
8
67 |
9
68 |
10
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/sandbox/jquery.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | jquery
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | jquery
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
1
28 |
2
29 |
3
30 |
4
31 |
5
32 |
6
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/sandbox/js/add-remove.js:
--------------------------------------------------------------------------------
1 | let cellCount = 6;
2 |
3 | function getRandom( ary ) {
4 | let index = Math.floor( Math.random() * ary.length );
5 | return ary[ index ];
6 | }
7 |
8 | let widthClasses = [ '', 'w2', 'w3' ];
9 | let nClasses = 'n1 n2 n3 n4 n5 n6'.split(' ');
10 |
11 | function makeCell() {
12 | let cell = document.createElement('div');
13 | cell.className = `cell ${getRandom( widthClasses )} ${getRandom( nClasses )}`;
14 | let b = document.createElement('b');
15 | b.textContent = ++cellCount;
16 | cell.appendChild( b );
17 | let removeButton = document.createElement('button');
18 | removeButton.className = 'remove-button';
19 | removeButton.textContent = '×';
20 | cell.appendChild( removeButton );
21 | return cell;
22 | }
23 |
24 | function makeCells() {
25 | return [ makeCell(), makeCell(), makeCell() ];
26 | }
27 |
28 | // init
29 | [ ...document.querySelectorAll('.demo') ].forEach( ( demo ) => {
30 | let container = demo.querySelector('.container');
31 | let flkty = Flickity.data( container );
32 |
33 | demo.querySelector('.container').addEventListener( 'click', function( event ) {
34 | if ( event.target.matches('.remove-button') ) return;
35 |
36 | let cellElement = event.target.closest('.cell');
37 | flkty.remove( cellElement );
38 | } );
39 |
40 | demo.querySelector('.prepend-button').addEventListener( 'click', function() {
41 | flkty.prepend( makeCells() );
42 | } );
43 |
44 | demo.querySelector('.insert-button').addEventListener( 'click', function() {
45 | flkty.insert( makeCells(), 3 );
46 | } );
47 |
48 | demo.querySelector('.append-button').addEventListener( 'click', function() {
49 | flkty.append( makeCells() );
50 | } );
51 |
52 | } );
53 |
54 | // ----- reposition ----- //
55 |
56 | ( function() {
57 | let flkty = new Flickity('#reposition .container');
58 | flkty.on( 'staticClick', function( event, pointer, cellElem ) {
59 | if ( !cellElem ) return;
60 |
61 | cellElem.classList.toggle('w3');
62 | flkty.reposition();
63 | } );
64 | } )();
65 |
66 | // ----- prepend single, #492 ----- //
67 |
68 | ( function() {
69 | let demo = document.querySelector('#prepend-single');
70 | let flkty = new Flickity( demo.querySelector('.container') );
71 | demo.querySelector('.prepend-button').addEventListener( 'click', function() {
72 | flkty.prepend( makeCell() );
73 | } );
74 | } )();
75 |
--------------------------------------------------------------------------------
/sandbox/js/basic.js:
--------------------------------------------------------------------------------
1 | let flky = window.flky = new Flickity('#full-width');
2 |
3 | // flky.on( 'dragMove', function( event, pointer ) {
4 | // console.log( event.type, pointer.pageX, pointer.pageY );
5 | // });
6 | flky.on( 'select', function() {
7 | console.log( 'selected', flky.selectedIndex );
8 | } );
9 |
10 | flky.on( 'settle', function() {
11 | console.log( 'settled', flky.x );
12 | } );
13 |
14 | let halfWidthflky = new Flickity( '#half-width', {
15 | cellAlign: 'left',
16 | } );
17 |
18 | halfWidthflky.on( 'staticClick', function( event, pointer, cellIndex, cellElement ) {
19 | console.log( cellIndex, cellElement );
20 | } );
21 |
22 | new Flickity( '#gallery3', {
23 | } );
24 |
25 | document.querySelector('#gallery3 button').onclick = function() {
26 | console.log('button click');
27 | };
28 |
--------------------------------------------------------------------------------
/sandbox/js/jquery.js:
--------------------------------------------------------------------------------
1 | /* globals $ */
2 |
3 | let $gallery1 = $('#gallery1').flickity();
4 | let flkty = $gallery1.data('flickity');
5 |
6 | // $gallery1.on( 'dragMove', function( event, pointer ) {
7 | // console.log( event.type, pointer.pageX, pointer.pageY );
8 | // });
9 |
10 | $gallery1.on( 'cellSelect.flickity', function( event ) {
11 | console.log( 'selected', event.type, `ns:${event.namespace}`, flkty.selectedIndex );
12 | } );
13 |
14 | $gallery1.on( 'settle.flickity', function( event ) {
15 | console.log( 'settled', flkty.x, event.type );
16 | } );
17 |
18 | $gallery1.on( 'staticClick.flickity', function( event, pointer, cellElem, cellIndex ) {
19 | console.log( 'staticClick', event.type, cellIndex );
20 | } );
21 |
22 | $('#gallery2').flickity({
23 | wrapAround: true,
24 | });
25 |
--------------------------------------------------------------------------------
/sandbox/js/scroll-event.js:
--------------------------------------------------------------------------------
1 | let flkty = new Flickity( '.carousel1', {
2 | initialIndex: 2,
3 | // groupCells: true,
4 | // wrapAround: true,
5 | // cellAlign: 'right'
6 | } );
7 |
8 | let progressBar = document.querySelector('.progress-bar');
9 |
10 | flkty.on( 'scroll', function( progress ) {
11 | console.log( progress );
12 | let width = Math.max( 0, Math.min( 1, progress ) );
13 | progressBar.style.width = width * 100 + '%';
14 | } );
15 |
16 | flkty.reposition();
17 |
18 | // ----- ----- //
19 |
20 | let paraBG = document.querySelector('.parallax__layer--bg');
21 | let paraFG = document.querySelector('.parallax__layer--fg');
22 |
23 | let paraFlkty = new Flickity( '.parallax__carousel', {
24 |
25 | } );
26 |
27 | let cellRatio = 0.6;
28 | let bgRatio = 0.8;
29 | let fgRatio = 1.25;
30 |
31 | paraFlkty.on( 'scroll', function( progress ) {
32 | // console.log( progress );
33 | paraBG.style.left = ( 0.5 - ( 0.5 + progress * 4 ) * cellRatio * bgRatio ) * 100 + '%';
34 | paraFG.style.left = ( 0.5 - ( 0.5 + progress * 4 ) * cellRatio * fgRatio ) * 100 + '%';
35 | } );
36 |
37 | paraFlkty.reposition();
38 |
39 | // ----- ----- //
40 |
41 | let imgFlkty = new Flickity( '.image-carousel', {
42 | } );
43 |
44 | window.onload = function() {
45 | imgFlkty.reposition();
46 | };
47 |
48 | let imgs = document.querySelectorAll('.image-carousel img');
49 |
50 | imgFlkty.on( 'scroll', function() {
51 | imgFlkty.slides.forEach( ( slide, i ) => {
52 | let img = imgs[i];
53 | let x = ( slide.target + imgFlkty.x ) * -0.333;
54 | img.style.transform = `translateX(${x}px)`;
55 | } );
56 | } );
57 |
--------------------------------------------------------------------------------
/sandbox/js/tricky-drag.js:
--------------------------------------------------------------------------------
1 | let nonDragFlkty = new Flickity( '.carousel--non-drag', {
2 | draggable: false,
3 | } );
4 |
5 | function onStaticClick( event, pointer, cellElem, cellIndex ) {
6 | console.log( 'staticClick', this.element.className, cellIndex );
7 | }
8 |
9 | nonDragFlkty.on( 'staticClick', onStaticClick );
10 |
11 | let singleCellFlkty = new Flickity('.carousel--single-cell');
12 | singleCellFlkty.on( 'staticClick', onStaticClick );
13 |
14 | let groupFlkty = new Flickity( '.carousel--group', {
15 | groupCells: true,
16 | } );
17 |
18 | groupFlkty.on( 'staticClick', function( event ) {
19 | let cellElem = event.target.closest('.carousel-cell');
20 | if ( cellElem ) groupFlkty.remove( cellElem );
21 | } );
22 |
23 | function makeGroupCell() {
24 | let cell = document.createElement('div');
25 | cell.className = 'carousel-cell';
26 | let b = document.createElement('b');
27 | b.textContent = groupFlkty.cells.length + 1;
28 | cell.appendChild( b );
29 | return cell;
30 | }
31 |
32 | document.querySelector('.add-group-cell-button').onclick = function() {
33 | groupFlkty.append( makeGroupCell() );
34 | };
35 |
--------------------------------------------------------------------------------
/sandbox/js/v2-sizzle.js:
--------------------------------------------------------------------------------
1 | let flkty = new Flickity( '.carousel', {
2 | groupCells: true,
3 | adaptiveHeight: true,
4 | wrapAround: true,
5 | } );
6 |
7 | let paraBg = document.querySelector('.parallax-layer--bg');
8 | let paraFg = document.querySelector('.parallax-layer--fg');
9 | let count = flkty.slides.length - 1;
10 |
11 | flkty.on( 'scroll', function( progress ) {
12 | paraBg.style.left = ( 0.5 - ( 0.5 + progress * count ) * 0.7 ) * ( 37/36 ) * 100 + '%';
13 | paraFg.style.left = ( 0.5 - ( 0.5 + progress * count ) * 1.5 ) * ( 37/36 ) * 100 + '%';
14 | } );
15 |
16 | flkty.reposition();
17 |
--------------------------------------------------------------------------------
/sandbox/js/wrap-around.js:
--------------------------------------------------------------------------------
1 | window.flkty = new Flickity( '#gallery1', {
2 | wrapAround: true,
3 | } );
4 |
5 | window.flkty2 = new Flickity( '#gallery2', {
6 | } );
7 |
8 | window.flkty6 = new Flickity( '#gallery6', {
9 | wrapAround: true,
10 | cellAlign: 'left',
11 | } );
12 |
13 | window.flkty4 = new Flickity( '#gallery4', {
14 | wrapAround: true,
15 | freeScroll: true,
16 | } );
17 |
18 | window.flky5 = new Flickity( '#gallery5', {
19 | freeScroll: true,
20 | } );
21 |
--------------------------------------------------------------------------------
/sandbox/lazyload.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | lazyload
8 |
9 |
10 |
11 |
49 |
50 |
51 |
52 |
53 | lazyload
54 |
55 |
56 |
![]()
57 |
![]()
58 |
![]()
59 |
![]()
60 |
![]()
61 |
![]()
62 |
![]()
63 |
![]()
64 |
![]()
65 |
66 |
67 |
69 |
![]()
70 |
![]()
71 |
![]()
72 |
![]()
73 |
![]()
74 |
![]()
75 |
![]()
76 |
![]()
77 |
![]()
78 |
79 |
80 |
82 |
83 |
![]()
84 |
85 |
![]()
86 |
87 |
![]()
88 |
89 |
![]()
90 |
91 |
![]()
92 |
93 |
![]()
94 |
95 |
![]()
96 |
97 |
![]()
98 |
99 |
![]()
100 |
101 |
102 |
103 |
![]()
104 |
![]()
105 |
![]()
106 |
![]()
107 |
![]()
108 |
![]()
109 |
![]()
110 |
![]()
111 |
![]()
112 |
113 |
114 | srcset
115 |
116 |
117 |
![]()
124 |
![]()
131 |
![]()
138 |
![]()
145 |
![]()
152 |
![]()
159 |
160 |
161 |
162 |
163 |
![]()
168 |
169 |
170 |
![]()
175 |
176 |
177 |
![]()
182 |
183 |
184 |
![]()
189 |
190 |
191 |
![]()
196 |
197 |
198 |
![]()
203 |
204 |
205 |
206 | <picture>
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
262 |
263 |
264 |
265 |
--------------------------------------------------------------------------------
/sandbox/media.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | media
8 |
9 |
10 |
11 |
12 |
40 |
41 |
42 |
43 |
44 | media
45 |
46 |
47 |
49 |
50 |

51 |

52 |

53 |

54 |

55 |

56 |

57 |

58 |

59 |
60 |
61 |
63 |
64 |
65 |
66 |

67 |
68 |
69 |

70 |
71 |
72 |

73 |
74 |
75 |

76 |
77 |
78 |

79 |
80 |
81 |

82 |
83 |
84 |

85 |
86 |
87 |

88 |
89 |
90 |

91 |
92 |
93 |
94 |
113 |
114 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/sandbox/right-to-left.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | right to left
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 | right to left
19 |
20 |
22 |
1
23 |
2
24 |
3
25 |
4
26 |
5
27 |
6
28 |
29 |
30 |
32 |
1
33 |
2
34 |
3
35 |
4
36 |
5
37 |
6
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/sandbox/sandbox.css:
--------------------------------------------------------------------------------
1 | * { box-sizing: border-box; }
2 |
3 | body {
4 | font-family: sans-serif;
5 | color: #333;
6 | }
7 |
8 | .container {
9 | border: 1px solid;
10 | margin-bottom: 50px;
11 | }
12 |
13 | .container::after {
14 | content: '';
15 | display: block;
16 | clear: both;
17 | }
18 |
19 | .container:focus { border: 1px blue dotted; }
20 |
21 | .cell {
22 | width: 100%;
23 | height: 200px;
24 | border: 0 solid white;
25 | background: #CCC;
26 | }
27 |
28 | .cell.is-selected {
29 | outline: 4px solid hsla(0, 0%, 0%, 25%);
30 | outline-offset: -4px;
31 | }
32 |
33 | .cell:nth-child(6n) { background: hsl(0, 80%, 70%); }
34 | .cell:nth-child(6n+1) { background: hsl(60, 80%, 70%); }
35 | .cell:nth-child(6n+2) { background: hsl(120, 80%, 70%); }
36 | .cell:nth-child(6n+3) { background: hsl(180, 80%, 70%); }
37 | .cell:nth-child(6n+4) { background: hsl(240, 80%, 70%); }
38 | .cell:nth-child(6n+5) { background: hsl(300, 80%, 70%); }
39 |
40 | .cell.n1 { background: hsl(0, 80%, 70%); }
41 | .cell.n2 { background: hsl(60, 80%, 70%); }
42 | .cell.n3 { background: hsl(120, 80%, 70%); }
43 | .cell.n4 { background: hsl(180, 80%, 70%); }
44 | .cell.n5 { background: hsl(240, 80%, 70%); }
45 | .cell.n6 { background: hsl(300, 80%, 70%); }
46 |
47 | .variable-width .cell {
48 | width: 20%;
49 | margin-right: 3%;
50 | }
51 |
52 | .variable-width .cell.w2 { width: 30%; }
53 | .variable-width .cell.w3 { width: 40%; }
54 |
55 | .fixed-width .cell {
56 | width: 200px;
57 | margin-right: 20px;
58 | }
59 |
60 | #half-width .cell {
61 | width: 48%;
62 | margin-right: 4%;
63 | }
64 |
65 | /* big number */
66 | .cell b {
67 | display: block;
68 | font-size: 100px;
69 | color: white;
70 | font-weight: bold;
71 | position: absolute;
72 | left: 10px;
73 | top: 10px;
74 | }
75 |
76 | /* ---- couning ---- */
77 | .counting {
78 | counter-reset: cell;
79 | }
80 |
81 | .counting .cell::before {
82 | counter-increment: cell;
83 | content: counter(cell);
84 | display: block;
85 | font-size: 100px;
86 | color: white;
87 | font-weight: bold;
88 | position: absolute;
89 | left: 10px;
90 | top: 10px;
91 | }
92 |
--------------------------------------------------------------------------------
/sandbox/scroll-event.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | scroll event
8 |
9 |
10 |
11 |
125 |
126 |
127 |
128 |
129 | scroll event
130 |
131 |
132 |
0
133 |
1
134 |
2
135 |
3
136 |
4
137 |
5
138 |
6
139 |
7
140 |
8
141 |
9
142 |
10
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/sandbox/single.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | single
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 | single
21 |
22 |
23 |
1
24 |
2
25 |
3
26 |
4
27 |
5
28 |
6
29 |
7
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/sandbox/styles.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | styles
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | styles
16 |
17 |
18 |
1
19 |
2
20 |
3
21 |
4
22 |
5
23 |
24 |
25 |
26 |
1
27 |
2
28 |
3
29 |
4
30 |
5
31 |
32 |
33 |
34 |
1
35 |
2
36 |
3
37 |
4
38 |
5
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/sandbox/tricky-drag.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | tricky drag
8 |
9 |
10 |
11 |
34 |
35 |
36 |
37 |
38 | tricky drag
39 |
40 |
41 |
1
42 |
2
43 |
3
44 |
4
45 |
5
46 |
6
47 |
7
48 |
49 |
50 |
53 |
54 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/sandbox/v2-sizzle.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | v2 sizzle
8 |
9 |
10 |
11 |
133 |
134 |
135 |
136 |
137 |
138 |
146 |
147 |
148 |
149 | Group
150 |
151 |
152 | cells
153 |
154 |
155 |
156 |
157 |
158 | Adaptive height
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
Parallax
169 |
whoa
170 |
Flickity v2
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
--------------------------------------------------------------------------------
/sandbox/wrap-around.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | wrap around
8 |
9 |
10 |
11 |
12 |
28 |
29 |
30 |
31 |
32 | wrap around
33 |
34 |
35 |
1
36 |
2
37 |
3
38 |
4
39 |
5
40 |
6
41 |
42 |
43 |
44 |
1
45 |
2
46 |
3
47 |
4
48 |
5
49 |
6
50 |
51 |
52 | Left aligned
53 |
54 |
55 |
1
56 |
2
57 |
3
58 |
4
59 |
5
60 |
6
61 |
62 |
63 | Fixed-width cells, pixel-positioning HTML init
64 |
65 |
67 |
1
68 |
2
69 |
3
70 |
4
71 |
5
72 |
6
73 |
7
74 |
8
75 |
76 |
77 | HTML init
78 |
79 |
81 |
1
82 |
2
83 |
3
84 |
4
85 |
5
86 |
6
87 |
88 |
89 | freeScroll
90 |
91 |
92 |
1
93 |
2
94 |
3
95 |
4
96 |
5
97 |
6
98 |
99 |
100 |
101 |
1
102 |
2
103 |
3
104 |
4
105 |
5
106 |
6
107 |
108 |
109 | no margin for error
110 |
112 |
1
113 |
2
114 |
3
115 |
4
116 |
5
117 |
118 |
119 | tight wrap #589
120 |
121 |
1
122 |
2
123 |
3
124 |
4
125 |
126 |
127 | wrapAround: 'fill', should disabled
128 |
129 |
1
130 |
2
131 |
3
132 |
4
133 |
134 |
135 | wrapAround: 'fill', should enabled
136 |
137 |
1
138 |
2
139 |
3
140 |
4
141 |
5
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/stylelint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'stylelint-config-standard',
3 | ignoreFiles: [ 'dist/*' ],
4 | rules: {
5 | 'color-function-notation': 'legacy',
6 | 'color-hex-case': 'upper',
7 | 'comment-empty-line-before': null,
8 | 'declaration-block-no-duplicate-properties': [ true, {
9 | ignore: [ 'consecutive-duplicates-with-different-values' ],
10 | } ],
11 | 'hue-degree-notation': 'number',
12 | 'property-no-vendor-prefix': null,
13 | 'selector-class-pattern': null,
14 | 'string-quotes': 'single',
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/test/drag.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Drag tests
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
drag and drag with wrapAround tests can be buggy. Try running them again.
46 |
47 |
drag
48 |
49 |
1
50 |
2
51 |
3
52 |
4
53 |
5
54 |
6
55 |
56 |
57 |
drag wrapAround
58 |
59 |
1
60 |
2
61 |
3
62 |
4
63 |
5
64 |
6
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Flickity tests
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Init
64 |
65 |
66 |
1
67 |
2
68 |
3
69 |
4
70 |
5
71 |
6
72 |
73 |
74 | cellSelector
75 |
76 |
77 |
not cell 1
78 |
1
79 |
2
80 |
3
81 |
4
82 |
not cell 2
83 |
5
84 |
6
85 |
86 |
87 | empty
88 |
89 |
90 |
91 | getParentCell
92 |
93 |
94 |
1
95 |
96 |
3
97 |
4
98 |
5
99 |
6
100 |
101 |
102 |
103 | position cells
104 |
105 |
106 |
1
107 |
2
108 |
3
109 |
4
110 |
5
111 |
6
112 |
113 |
114 | contain
115 |
116 |
117 |
1
118 |
2
119 |
3
120 |
4
121 |
5
122 |
6
123 |
124 |
125 | auto-play
126 |
127 |
1
128 |
2
129 |
3
130 |
131 |
132 | prev/next buttons
133 |
141 |
142 | page dots
143 |
144 |
1
145 |
2
146 |
3
147 |
4
148 |
5
149 |
6
150 |
151 |
152 | getWrapCells
153 |
154 |
1
155 |
2
156 |
3
157 |
4
158 |
5
159 |
6
160 |
161 |
162 | watch
163 |
164 |
1
165 |
2
166 |
3
167 |
4
168 |
5
169 |
6
170 |
171 |
172 | resize
173 |
174 |
1
175 |
2
176 |
3
177 |
4
178 |
5
179 |
6
180 |
181 |
182 | add/remove-cells
183 |
184 |
1
185 |
2
186 |
3
187 |
4
188 |
5
189 |
6
190 |
191 |
192 | destroy
193 |
194 |
1
195 |
2
196 |
3
197 |
4
198 |
5
199 |
6
200 |
201 |
202 | lazyload
203 |
204 |
205 |
![]()
206 |
207 |
208 |
209 |
210 |
211 |
![]()
212 |
213 |
214 |
215 | lazyload srcset
216 |
217 |
218 |
![]()
223 |
224 |
![]()
230 |
231 |
232 |
233 |
234 |
235 |
236 | groupCells
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 | adaptiveHeight
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 | selectCell
275 |
276 |
0
277 |
1
278 |
2
279 |
3
280 |
4
281 |
5
282 |
6
283 |
7
284 |
285 |
286 | change
287 |
288 |
0
289 |
1
290 |
2
291 |
3
292 |
4
293 |
294 |
295 | initialIndex
296 |
297 |
0
298 |
1
299 |
2
300 |
3
301 |
4
302 |
5
303 |
6
304 |
7
305 |
8
306 |
9
307 |
308 |
309 | wrapAround: fill
310 |
311 |
0
312 |
1
313 |
2
314 |
3
315 |
4
316 |
317 |
318 | imagesLoaded
319 |
320 |

321 |

322 |

323 |

324 |

325 |

326 |

327 |

328 |

329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
--------------------------------------------------------------------------------
/test/test.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable no-descending-specificity */
2 |
3 | body {
4 | font-family: sans-serif;
5 | color: #333;
6 | }
7 |
8 | /* move over qunit window to reduce overlaps */
9 | @media (min-height: 500px) {
10 | #qunit {
11 | left: 420px;
12 | }
13 | }
14 |
15 | .gallery {
16 | border: 1px solid;
17 | width: 400px;
18 | margin-bottom: 40px;
19 | }
20 |
21 | .gallery--imagesloaded img {
22 | display: block;
23 | height: 140px;
24 | }
25 |
26 | .gallery .cell {
27 | width: 100%;
28 | height: 100px;
29 | background: #F09;
30 | font-size: 40px;
31 | color: white;
32 | }
33 |
34 | .variable-width .cell { width: 25%; } /* 100px */
35 | .variable-width .cell.width2 {
36 | width: 40%; /* 160px */
37 | background: #F90;
38 | }
39 |
40 | .variable-width .cell.width3 {
41 | width: 60%; /* 240px */
42 | background: #09F;
43 | }
44 |
45 | #position-cells.percent-margin .cell { margin: 0 2%; }
46 | #position-cells.pixel-margin .cell { margin: 0 10px; }
47 |
48 | .drag .cell { margin-right: 5%; }
49 |
50 | #watch.has-after::after {
51 | content: 'flickity';
52 | display: none;
53 | }
54 |
55 | #lazyload img {
56 | display: block;
57 | max-height: 100px;
58 | }
59 |
60 | /* ---- group-cells ---- */
61 |
62 | #group-cells .cell {
63 | width: 100px;
64 | }
65 |
66 | #group-cells .cell--width2 { width: 200px; }
67 | #group-cells .cell--width3 { width: 300px; }
68 | #group-cells .cell--width4 { width: 400px; }
69 |
70 | #group-cells.is-expanded { width: 600px; }
71 |
72 | #group-cells .cell:nth-child(2n) { background: #09F; }
73 |
74 | /* ---- adaptive-height ---- */
75 |
76 | #adaptive-height .cell { width: 33.33%; }
77 |
78 | #adaptive-height .cell--height2 { height: 200px; }
79 | #adaptive-height .cell--height3 { height: 300px; }
80 | #adaptive-height .cell--height4 { height: 400px; }
81 |
82 | #adaptive-height .cell:nth-child(2n) { background: #09F; }
83 |
84 | /* ---- select-cell ---- */
85 |
86 | #select-cell .cell { width: 33.33%; }
87 |
88 | /* ---- wrap-around-fill ---- */
89 |
90 | .variable-width .cell.cell--wrap-around-short {
91 | width: 20%;
92 | }
93 |
--------------------------------------------------------------------------------
/test/unit/adaptive-height.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'adaptiveHeight', function( assert ) {
2 |
3 | let flkty = new Flickity( '#adaptive-height', {
4 | adaptiveHeight: true,
5 | } );
6 |
7 | // 2,1,3, 1,4,2, 1,2,1
8 |
9 | function checkSelectHeight( index, height ) {
10 | flkty.select( index, false, true );
11 | assert.equal( flkty.viewport.style.height, `${height}px`, `slide ${index}` );
12 | }
13 |
14 | checkSelectHeight( 0, 200 );
15 | checkSelectHeight( 1, 100 );
16 | checkSelectHeight( 2, 300 );
17 | checkSelectHeight( 3, 100 );
18 | checkSelectHeight( 4, 400 );
19 | checkSelectHeight( 5, 200 );
20 |
21 | flkty.options.groupCells = true;
22 | flkty.resize();
23 |
24 | checkSelectHeight( 0, 300 );
25 | checkSelectHeight( 1, 400 );
26 | checkSelectHeight( 2, 200 );
27 |
28 | } );
29 |
--------------------------------------------------------------------------------
/test/unit/add-remove-cells.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'add/remove cells', function( assert ) {
2 |
3 | function makeCellElem() {
4 | let cellElem = document.createElement('div');
5 | cellElem.className = 'cell';
6 | return cellElem;
7 | }
8 |
9 | // position values can be off by 0.1% or 1px
10 | function isPositionApprox( value, expected ) {
11 | value = value.replace( 'translateX(', '' ).replace( ')', '' );
12 | let isPercent = value.indexOf('%') !== -1;
13 | value = parseFloat( value );
14 | let diff = Math.abs( expected - value );
15 | return isPercent ? diff < 0.1 : diff <= 1;
16 | }
17 |
18 | let elem = document.querySelector('#add-remove-cells');
19 | let flkty = new Flickity( elem );
20 | let sliderElem = elem.querySelector('.flickity-slider');
21 |
22 | function checkCellElem( cellElem, index, message ) {
23 | assert.equal( sliderElem.children[ index ], cellElem,
24 | message + ' cell element in DOM correct' );
25 | assert.equal( flkty.cells[ index ].element, cellElem,
26 | message + ' element added as cell' );
27 | assert.ok( isPositionApprox( cellElem.style.transform, index * 100 ),
28 | ` element positioned ${index * 100}` );
29 | }
30 |
31 | // prepend cell element
32 | let cellElem = makeCellElem();
33 | flkty.prepend( cellElem );
34 | checkCellElem( cellElem, 0, 'prepended' );
35 | assert.equal( flkty.selectedIndex, 1, 'selectedIndex +1 after prepend' );
36 | // append cell element
37 | cellElem = makeCellElem();
38 | flkty.append( cellElem );
39 | let lastIndex = flkty.cells.length - 1;
40 | checkCellElem( cellElem, lastIndex, 'appended' );
41 | assert.equal( flkty.selectedIndex, 1, 'selectedIndex same after prepend' );
42 | // insert single cell element
43 | cellElem = makeCellElem(); // this one gets removed first
44 | flkty.select( 2 );
45 | flkty.insert( cellElem, 2 );
46 | checkCellElem( cellElem, 2, 'single inserted' );
47 | assert.equal( flkty.selectedIndex, 3, 'selectedIndex +1 after insert before' );
48 | flkty.insert( makeCellElem(), 4 );
49 | assert.equal( flkty.selectedIndex, 3, 'selectedIndex same after insert before' );
50 | // insert multiple cell elements
51 | let cellElems = [ makeCellElem(), makeCellElem(), makeCellElem() ];
52 | flkty.insert( cellElems, 3 );
53 | checkCellElem( cellElems[0], 3, 'first multiple inserted' );
54 | checkCellElem( cellElems[1], 4, 'second multiple inserted' );
55 | checkCellElem( cellElems[2], 5, 'third multiple inserted' );
56 | assert.equal( flkty.selectedIndex, 6, 'selectedIndex +6 after 3 insert before' );
57 |
58 | function checkCellPositions() {
59 | let isGap = false;
60 | flkty.cells.forEach( ( cell, i ) => {
61 | if ( !isPositionApprox( cell.element.style.transform, i * 100 ) ) {
62 | assert.ok( false, `gap in cell position ${i} after removal` );
63 | isGap = true;
64 | }
65 | } );
66 | assert.ok( !isGap, 'no gaps in cell positions' );
67 | }
68 |
69 | // remove single cell element that was inserted
70 | let len = flkty.cells.length;
71 | flkty.remove( cellElem );
72 | assert.equal( len - sliderElem.children.length, 1, 'element removed from DOM' );
73 | assert.equal( len - flkty.cells.length, 1, 'cell removed' );
74 | assert.equal( flkty.selectedIndex, 5, 'selectedIndex -1 after remove before' );
75 | checkCellPositions();
76 | // remove multiple
77 | len = flkty.cells.length;
78 | flkty.select( 4 );
79 | flkty.remove([ cellElems[2], cellElems[0], cellElems[1] ]);
80 | assert.equal( len - sliderElem.children.length, 3, 'elements removed from DOM' );
81 | assert.equal( len - flkty.cells.length, 3, 'cells removed' );
82 | checkCellPositions();
83 |
84 | // remove all cells
85 | flkty.remove( flkty.getCellElements() );
86 | assert.equal( flkty.cells.length, 0, 'all cells removed' );
87 | flkty.resize();
88 | assert.ok( true, 'resize with zero items didnt freak out' );
89 |
90 | } );
91 |
--------------------------------------------------------------------------------
/test/unit/auto-play.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'auto play', function( assert ) {
2 |
3 | let done = assert.async();
4 |
5 | let flkty = new Flickity( '#auto-play', {
6 | autoPlay: 200,
7 | } );
8 |
9 | let selectCount = 0;
10 | let testDelay = flkty.options.autoPlay + 100;
11 |
12 | let tests;
13 |
14 | function nextTest() {
15 | if ( tests.length ) {
16 | let next = tests.shift();
17 | return next();
18 | } else {
19 | flkty.stopPlayer();
20 | done();
21 | }
22 | }
23 |
24 | tests = [
25 | // check that player runs
26 | function() {
27 | flkty.on( 'select', function onSelect() {
28 | selectCount++;
29 | if ( selectCount < 5 ) {
30 | assert.equal( flkty.selectedIndex, selectCount % flkty.cells.length,
31 | `auto-played to ${flkty.selectedIndex}` );
32 | } else if ( selectCount === 5 ) {
33 | // HACK do async, should be able to stop after a tick
34 | flkty.off( 'select', onSelect );
35 | nextTest();
36 | }
37 | } );
38 | },
39 | // pause & unpause
40 | function() {
41 | function onPauseSelect() {
42 | assert.ok( false, 'player ticked during pause' );
43 | }
44 | flkty.on( 'select', onPauseSelect );
45 | flkty.pausePlayer();
46 | setTimeout( function() {
47 | assert.ok( true, 'player did not tick during pause' );
48 | flkty.off( 'select', onPauseSelect );
49 | flkty.once( 'select', function() {
50 | assert.ok( true, 'player resumed after unpausing' );
51 | nextTest();
52 | } );
53 | flkty.unpausePlayer();
54 | }, testDelay );
55 | },
56 | // stopPlayer
57 | function() {
58 | let ticks = 0;
59 | function onSelect() {
60 | ticks++;
61 | }
62 | flkty.stopPlayer();
63 | setTimeout( function() {
64 | flkty.off( 'select', onSelect );
65 | assert.equal( ticks, 0, 'no ticks after stopped' );
66 | nextTest();
67 | }, testDelay * 2 );
68 | },
69 | // double playPlayer()
70 | function() {
71 | let ticks = 0;
72 | function onSelect() {
73 | ticks++;
74 | }
75 | flkty.stopPlayer();
76 | flkty.on( 'select', onSelect );
77 | flkty.playPlayer();
78 | flkty.playPlayer();
79 | setTimeout( function() {
80 | flkty.off( 'select', onSelect );
81 | assert.equal( ticks, 1, 'only one tick after double playPlayer' );
82 | nextTest();
83 | }, testDelay );
84 | },
85 | ];
86 |
87 | nextTest();
88 |
89 | } );
90 |
--------------------------------------------------------------------------------
/test/unit/cell-selector.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'cellSelector', function( assert ) {
2 |
3 | let elem = document.querySelector('#cell-selector');
4 | let notCell1 = elem.querySelector('.not-cell1');
5 | let notCell2 = elem.querySelector('.not-cell2');
6 |
7 | let flkty = new Flickity( elem, {
8 | cellSelector: '.cell',
9 | } );
10 |
11 | let cellsMatchSelector = true;
12 | for ( let cell of flkty.cells ) {
13 | let isMatch = cell.element.matches( flkty.options.cellSelector );
14 | cellsMatchSelector = cellsMatchSelector && isMatch;
15 | }
16 |
17 | // getCellElements()
18 | let cellElems = flkty.getCellElements();
19 | let queriedCellElems = elem.querySelectorAll( flkty.options.cellSelector );
20 | cellElems.forEach( ( cellElem, i ) => {
21 | assert.equal( cellElem, queriedCellElems[i],
22 | 'cell element same as queried cell element' );
23 | } );
24 |
25 | assert.ok( cellsMatchSelector, 'all cell elements match cellSelector' );
26 |
27 | assert.equal( notCell1.parentNode, elem, 'notCell1 parent node is still gallery' );
28 | assert.equal( notCell2.parentNode, elem, 'notCell2 parent node is still gallery' );
29 |
30 | } );
31 |
--------------------------------------------------------------------------------
/test/unit/change.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-invalid-this */
2 |
3 | QUnit.test( 'change', function( assert ) {
4 |
5 | let done = assert.async();
6 |
7 | function onInitChange() {
8 | assert.ok( false, 'change should not trigger on init' );
9 | }
10 |
11 | new Flickity( '#change', {
12 | on: {
13 | change: onInitChange,
14 | ready: function() {
15 | // define events last to first for strict
16 | function onChangeC( index ) {
17 | assert.equal( index, 0, 'change triggered on select back to 0' );
18 | done();
19 | }
20 |
21 | function onChangeB() {
22 | assert.ok( false, 'change should not trigger on same select' );
23 | }
24 |
25 | function onSelectB( index ) {
26 | assert.equal( index, 1, 'select triggered on same select 1' );
27 | this.off( 'change', onChangeB );
28 | this.once( 'change', onChangeC );
29 | this.select( 0, false, true );
30 | }
31 |
32 | function onChangeA( index ) {
33 | assert.equal( index, 1, 'change triggered, selected 1' );
34 | this.once( 'change', onChangeB );
35 | this.once( 'select', onSelectB );
36 | // select 1 again
37 | this.select( 1, false, true );
38 | }
39 |
40 | // kick off
41 | this.off( 'change', onInitChange );
42 | this.once( 'change', onChangeA );
43 | this.select( 1, false, true );
44 | },
45 | },
46 | } );
47 |
48 | } );
49 |
--------------------------------------------------------------------------------
/test/unit/contain.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'contain', function( assert ) {
2 |
3 | let flkty = new Flickity( '#contain', {
4 | contain: true,
5 | } );
6 |
7 | assert.equal( Math.round( flkty.x + flkty.cursorPosition ), 0,
8 | 'selected at 0, position left edge' );
9 | flkty.select( 1 );
10 | flkty.positionSliderAtSelected();
11 | assert.equal( Math.round( flkty.x + flkty.cursorPosition ), 0,
12 | 'selected at 1, position left edge' );
13 | flkty.select( 4 );
14 | flkty.positionSliderAtSelected();
15 | let endLimit = flkty.slideableWidth - flkty.size.innerWidth * ( 1 - flkty.cellAlign );
16 | assert.equal( Math.round( -endLimit ), Math.round( flkty.x ),
17 | 'selected at 4, position right edge' );
18 | flkty.select( 5 );
19 | flkty.positionSliderAtSelected();
20 | assert.equal( Math.round( -endLimit ), Math.round( flkty.x ),
21 | 'selected at 5, position right edge' );
22 |
23 | } );
24 |
--------------------------------------------------------------------------------
/test/unit/destroy.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'destroy', function( assert ) {
2 |
3 | let elem = document.querySelector('#destroy');
4 | let flkty = new Flickity( elem );
5 |
6 | let done = assert.async();
7 | // do it async
8 | setTimeout( function() {
9 | flkty.destroy();
10 | assert.strictEqual( elem.flickityGUID, undefined, 'flickityGUID removed' );
11 | assert.ok( !flkty.isActive, 'not active' );
12 | assert.ok( !Flickity.data( elem ), '.data() returns falsey' );
13 | assert.ok( elem.children[0], '.cell', 'cell is back as first child' );
14 | assert.ok( !elem.matches('.flickity-enabled'), 'flickity-enabled class removed' );
15 | assert.ok( !elem.querySelector('.flickity-prev-next-button'), 'no buttons' );
16 | assert.ok( !elem.querySelector('.flickity-page-dots'), 'no page dots' );
17 | assert.ok( !elem.style.height, 'no height set' );
18 | assert.ok( !elem.children[0].style.left, 'first cell has no left position' );
19 |
20 | done();
21 | }, 20 );
22 |
23 | } );
24 |
--------------------------------------------------------------------------------
/test/unit/drag.js:
--------------------------------------------------------------------------------
1 | ( function() {
2 |
3 | function noop() {}
4 |
5 | let fakeDrag = window.fakeDrag = function( flkty, positions ) {
6 |
7 | function fakeEvent( type, pageX ) {
8 | return {
9 | type: type,
10 | pageX: pageX,
11 | pageY: 0,
12 | preventDefault: noop,
13 | target: flkty.viewport,
14 | };
15 | }
16 |
17 | let hasBeenDown = false;
18 |
19 | function triggerEvent() {
20 | let position = positions.shift();
21 | // down or move event
22 | if ( !hasBeenDown ) {
23 | let downEvent = fakeEvent( 'mousedown', position );
24 | flkty.pointerDown( downEvent, downEvent );
25 | hasBeenDown = true;
26 | } else {
27 | let moveEvent = fakeEvent( 'mousemove', position );
28 | flkty.pointerMove( moveEvent, moveEvent );
29 | }
30 |
31 | if ( positions.length ) {
32 | // loop next
33 | setTimeout( triggerEvent, 40 );
34 | } else {
35 | // up event
36 | let upEvent = fakeEvent( 'mouseup', position );
37 | flkty.pointerUp( upEvent, upEvent );
38 | }
39 | }
40 |
41 | triggerEvent();
42 | };
43 |
44 | let dragTests;
45 | // do each drag test one after another
46 | function getDoNextDragTest( done ) {
47 | return function doNextDragTest() {
48 | if ( dragTests.length ) {
49 | let dragTest = dragTests.shift();
50 | dragTest();
51 | } else {
52 | done();
53 | }
54 | };
55 | }
56 |
57 | // flickity, dragPositions, index, onSettle, message
58 | function getFakeDragTest( args ) {
59 | let assert = args.assert;
60 | let flkty = args.flickity;
61 | let msgCell = `'slide[${args.index}]'`;
62 |
63 | return function fakeDragTest() {
64 | let selectMsg = `${args.message ? args.message + '. ' : ''} selected ${msgCell}`;
65 | flkty.once( 'select', function() {
66 | assert.equal( flkty.selectedIndex, args.index, selectMsg );
67 | } );
68 |
69 | let settleMsg = `${args.message ? args.message + '. ' : ''} settled ${msgCell}`;
70 | let target = flkty.slides[ args.index ].target;
71 | flkty.once( 'settle', function() {
72 | assert.equal( Math.round( -flkty.x ), Math.round( target ), settleMsg );
73 | setTimeout( args.onSettle );
74 | } );
75 |
76 | fakeDrag( args.flickity, args.dragPositions );
77 | };
78 | }
79 |
80 | QUnit.test( 'drag', function( assert ) {
81 | // async test
82 | let done = assert.async();
83 |
84 | let flkty = new Flickity('#drag');
85 |
86 | let doNextDragTest = getDoNextDragTest( done );
87 |
88 | function getDragTest( args ) {
89 | args = Object.assign( args, {
90 | assert: assert,
91 | flickity: flkty,
92 | onSettle: doNextDragTest,
93 | } );
94 | return getFakeDragTest( args );
95 | }
96 |
97 | dragTests = [
98 | getDragTest({
99 | message: 'drag to 2nd cell',
100 | index: 1,
101 | dragPositions: [ 0, -10, -20 ],
102 | }),
103 | getDragTest({
104 | message: 'drag back to 1st cell',
105 | index: 0,
106 | dragPositions: [ 0, 10, 20 ],
107 | }),
108 | getDragTest({
109 | message: 'big flick to 3rd cell',
110 | index: 2,
111 | dragPositions: [ 0, -10, -80 ],
112 | }),
113 | // minimal movement to trigger static click
114 | function() {
115 | flkty.once( 'staticClick', function() {
116 | assert.ok( true, 'staticClick fired on non-drag' );
117 | assert.equal( flkty.selectedIndex, 2, 'selected index still at 2 after click' );
118 | setTimeout( doNextDragTest );
119 | } );
120 | fakeDrag( flkty, [ 0, 1, 0, -2, -1 ] );
121 | },
122 | // move out then back to where it started
123 | function() {
124 | flkty.once( 'settle', function() {
125 | assert.equal( flkty.selectedIndex, 2, 'move out then back. same cell' );
126 | setTimeout( doNextDragTest );
127 | } );
128 | fakeDrag( flkty, [ 0, 10, 20, 30, 20 ] );
129 | },
130 | getDragTest({
131 | message: 'drag and try to flick past 6th cell',
132 | index: 5,
133 | dragPositions: [ 0, -10, -50, -77, -100, -125, -150, -175, -250, -350 ],
134 | }),
135 | ];
136 |
137 | doNextDragTest();
138 |
139 | } );
140 |
141 | QUnit.test( 'drag with wrapAround', function( assert ) {
142 | // async test
143 | let done = assert.async();
144 |
145 | let flkty = new Flickity( '#drag-wrap-around', {
146 | wrapAround: true,
147 | } );
148 |
149 | let doNextDragTest = getDoNextDragTest( done );
150 |
151 | function getDragTest( args ) {
152 | args = Object.assign( args, {
153 | assert: assert,
154 | flickity: flkty,
155 | onSettle: doNextDragTest,
156 | } );
157 | return getFakeDragTest( args );
158 | }
159 |
160 | dragTests = [
161 | getDragTest({
162 | message: 'drag to last cell via wrap-around',
163 | index: 5,
164 | dragPositions: [ 0, 10, 20 ],
165 | }),
166 | getDragTest({
167 | message: 'drag to first cell via wrap-around',
168 | index: 0,
169 | dragPositions: [ 0, -10, -20 ],
170 | }),
171 | getDragTest({
172 | message: 'big flick to 5th cell via wrap-around',
173 | index: 4,
174 | dragPositions: [ 0, 10, 80 ],
175 | }),
176 | ];
177 |
178 | doNextDragTest();
179 |
180 | } );
181 |
182 | } )();
183 |
--------------------------------------------------------------------------------
/test/unit/empty.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'empty', function( assert ) {
2 |
3 | let gallery = document.querySelector('#empty');
4 |
5 | let flkty = new Flickity( gallery );
6 |
7 | assert.ok( true, 'empty gallery ok' );
8 | assert.ok( flkty.prevButton.element.disabled, 'previous button disabled' );
9 | assert.ok( flkty.nextButton.element.disabled, 'next button disabled' );
10 | assert.equal( flkty.pageDots.dots.length, 0, '0 page dots' );
11 |
12 | flkty.resize();
13 | assert.ok( true, 'resize with empty gallery ok' );
14 |
15 | function makeCellElem() {
16 | let cellElem = document.createElement('div');
17 | cellElem.className = 'cell';
18 | return cellElem;
19 | }
20 |
21 | flkty.append( makeCellElem() );
22 | assert.equal( flkty.cells.length, 1, 'added cell to empty gallery' );
23 |
24 | assert.ok( flkty.prevButton.element.disabled, 'previous button disabled' );
25 | assert.ok( flkty.nextButton.element.disabled, 'next button disabled' );
26 | assert.equal( flkty.pageDots.dots.length, 1, '1 page dots' );
27 |
28 | // destroy and re-init with higher initialIndex
29 | flkty.destroy();
30 | flkty = new Flickity( gallery, {
31 | initialIndex: 2,
32 | } );
33 |
34 | // #291
35 | assert.ok( true, 'initializing with initialIndex > cells doesnt throw error' );
36 |
37 | } );
38 |
--------------------------------------------------------------------------------
/test/unit/get-parent-cell.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'getParentCell', function( assert ) {
2 |
3 | let gallery = document.querySelector('#get-parent-cell');
4 | let flkty = new Flickity( gallery );
5 |
6 | // cell1
7 | let cell = flkty.getParentCell( gallery.querySelector('.cell1') );
8 | assert.ok( cell, 'getParentCell( cell ) ok' );
9 | assert.ok( cell instanceof Flickity.Cell, 'cell is Flickity.Cell' );
10 | let index = flkty.cells.indexOf( cell );
11 | assert.equal( index, 0, 'cell is index 0' );
12 | // cell3
13 | cell = flkty.getParentCell( gallery.querySelector('.cell3') );
14 | assert.ok( cell, 'getParentCell( cell ) ok' );
15 | assert.ok( cell instanceof Flickity.Cell, 'cell is Flickity.Cell' );
16 | index = flkty.cells.indexOf( cell );
17 | assert.equal( index, 2, 'cell is index 2' );
18 | // child1
19 | cell = flkty.getParentCell( gallery.querySelector('.child1') );
20 | assert.ok( cell, 'getParentCell( cell ) ok' );
21 | assert.ok( cell instanceof Flickity.Cell, 'cell is Flickity.Cell' );
22 | index = flkty.cells.indexOf( cell );
23 | assert.equal( index, 0, 'cell is index 0' );
24 | // child2
25 | cell = flkty.getParentCell( gallery.querySelector('.child2') );
26 | assert.ok( cell, 'getParentCell( cell ) ok' );
27 | assert.ok( cell instanceof Flickity.Cell, 'cell is Flickity.Cell' );
28 | index = flkty.cells.indexOf( cell );
29 | assert.equal( index, 1, 'cell is index 1' );
30 | // outside
31 | cell = flkty.getParentCell( document.querySelector('.outside') );
32 | assert.ok( !cell, 'getParentCell( notCell ) not ok' );
33 | index = flkty.cells.indexOf( cell );
34 | assert.equal( index, -1, 'not cell is index -1' );
35 |
36 | } );
37 |
--------------------------------------------------------------------------------
/test/unit/get-wrap-cells.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'getWrapCells', function( assert ) {
2 |
3 | let flkty = new Flickity( '#get-wrap-cells', {
4 | wrapAround: true,
5 | } );
6 | // cells are 25% width
7 | // center align, 2 cells on each side
8 | assert.equal( flkty.beforeShiftCells.length, 2, 'center align, 2 before shift cells' );
9 | assert.equal( flkty.afterShiftCells.length, 2, 'center align, 2 after shift cells' );
10 |
11 | flkty.options.cellAlign = 'left';
12 | flkty.resize();
13 | // left align, 0, 4
14 | assert.equal( flkty.beforeShiftCells.length, 0, 'left align, 1 before shift cells' );
15 | assert.equal( flkty.afterShiftCells.length, 4, 'left align, 4 after shift cells' );
16 |
17 | flkty.options.cellAlign = 'right';
18 | flkty.resize();
19 | // right align, 4, 0
20 | assert.equal( flkty.beforeShiftCells.length, 4, 'right align, 4 before shift cells' );
21 | assert.equal( flkty.afterShiftCells.length, 0, 'right align, 0 after shift cells' );
22 |
23 | } );
24 |
--------------------------------------------------------------------------------
/test/unit/group-cells.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'groupCells', function( assert ) {
2 |
3 | let done = assert.async();
4 |
5 | let flkty = new Flickity( '#group-cells', {
6 | groupCells: true,
7 | } );
8 |
9 | function getSlideCellsCount() {
10 | let counts = flkty.slides.map( function( slide ) {
11 | return slide.cells.length;
12 | } );
13 | return counts.join(',');
14 | }
15 |
16 | assert.equal( getSlideCellsCount(), '3,2,2,1,1,3,2', 'groupCells: true' );
17 | let targets = flkty.slides.map( function( slide ) {
18 | return slide.target;
19 | } );
20 | assert.deepEqual( targets, [ 200, 600, 1000, 1300, 1600, 2000, 2300 ], 'targets' );
21 |
22 | flkty.selectCell( 6 );
23 | assert.equal( flkty.selectedIndex, 2, 'selectCell(6) selects 3rd slide' );
24 | flkty.selectCell( flkty.cells[2].element );
25 | assert.equal( flkty.selectedIndex, 0, 'selectCell(3rd elem) selects 1st slide' );
26 |
27 | flkty.options.groupCells = 2;
28 | flkty.reposition();
29 | assert.equal( getSlideCellsCount(), '2,2,2,2,2,2,2', 'groupCells: 2' );
30 |
31 | flkty.options.groupCells = '75%';
32 | flkty.reposition();
33 | assert.equal( getSlideCellsCount(), '2,1,1,2,1,1,1,2,2,1', 'groupCells: 75%' );
34 |
35 | flkty.once( 'settle', function() {
36 | flkty.element.classList.add('is-expanded'); // 600px wide
37 | flkty.options.groupCells = true;
38 | flkty.resize();
39 | assert.equal( getSlideCellsCount(), '3,3,2,3,3',
40 | 'groupCells: true, container @ 600px' );
41 | done();
42 | } );
43 |
44 | } );
45 |
--------------------------------------------------------------------------------
/test/unit/imagesloaded.js:
--------------------------------------------------------------------------------
1 | /* globals imagesLoaded */
2 |
3 | ( function() {
4 |
5 | // position values can be off by 0.1% or 1px
6 | function isPositionApprox( value, expected ) {
7 | let isPercent = value.indexOf('%') !== -1;
8 | value = parseFloat( value );
9 | let diff = Math.abs( expected - value );
10 | return isPercent ? diff < 0.1 : diff <= 1;
11 | }
12 |
13 | QUnit.test( 'imagesloaded', function( assert ) {
14 | let done = assert.async();
15 | let gallery = document.querySelector('#imagesloaded');
16 |
17 | let flkty = new Flickity( gallery, {
18 | imagesLoaded: true,
19 | percentPosition: false,
20 | } );
21 |
22 | imagesLoaded( gallery, function() {
23 | flkty.cells.forEach( ( cell, i ) => {
24 | assert.ok( cell.size.width > 10, `cell ${i} has width` );
25 | let transform = cell.element.style.transform;
26 | let position = transform.replace( 'translateX(', '' ).replace( ')', '' );
27 | let isApprox = isPositionApprox( position, cell.x );
28 | assert.ok( isApprox, `cell ${i} at proper position` );
29 | } );
30 |
31 | assert.equal( flkty.viewport.style.height, '140px', 'gallery height set' );
32 |
33 | done();
34 | } );
35 |
36 | } );
37 |
38 | QUnit.test( 'imagesloaded-in-divs', function( assert ) {
39 |
40 | let done = assert.async();
41 | let gallery = document.querySelector('#imagesloaded-in-divs');
42 |
43 | let flkty = new Flickity( gallery, {
44 | imagesLoaded: true,
45 | percentPosition: false,
46 | } );
47 |
48 | imagesLoaded( gallery, function() {
49 | flkty.cells.forEach( ( cell, i ) => {
50 | assert.ok( cell.size.width > 10, `cell ${i} has width` );
51 | let transform = cell.element.style.transform;
52 | let position = transform.replace( 'translateX(', '' ).replace( ')', '' );
53 | let isApprox = isPositionApprox( position, cell.x );
54 | assert.ok( isApprox, `cell ${i} at proper position` );
55 | } );
56 |
57 | assert.equal( flkty.viewport.style.height, '140px', 'gallery height set' );
58 |
59 | done();
60 | } );
61 |
62 | } );
63 |
64 | } )();
65 |
66 |
--------------------------------------------------------------------------------
/test/unit/init.js:
--------------------------------------------------------------------------------
1 | ( function() {
2 |
3 | QUnit.module('Flickity');
4 |
5 | let utils = window.fizzyUIUtils;
6 |
7 | QUnit.test( 'init', function( assert ) {
8 |
9 | let elem = document.querySelector('#init');
10 | let flkty = new Flickity( elem );
11 |
12 | for ( let prop in Flickity.defaults ) {
13 | assert.equal( flkty.options[ prop ], Flickity.defaults[ prop ],
14 | `${prop} option matches default` );
15 | }
16 |
17 | assert.equal( flkty.element, elem, '.element is proper element' );
18 | let children = utils.makeArray( flkty.element.children );
19 | assert.notEqual( children.indexOf( flkty.viewport ), -1,
20 | 'viewport element is a child element' );
21 | assert.equal( flkty.viewport.children[0], flkty.slider, 'slider is in viewport' );
22 | assert.equal( flkty.viewport.style.height, '100px', 'viewport height set' );
23 |
24 | assert.ok( flkty.isActive, 'isActive' );
25 | assert.ok( elem.matches('.flickity-enabled'), 'flickity-enabled class added' );
26 |
27 | assert.equal( flkty.cells.length, 6, 'has 6 cells' );
28 | assert.equal( getComputedStyle( flkty.cells[0].element ).left, '0px',
29 | 'first cell left: 0px' );
30 | assert.equal( flkty.cells[0].element.style.transform,
31 | 'translateX(0%)', 'first cell translateX: 0%' );
32 | assert.equal( flkty.cells[5].element.style.transform,
33 | 'translateX(500%)', '6th cell translateX: 500%' );
34 |
35 | assert.equal( flkty.selectedIndex, 0, 'selectedIndex = 0' );
36 | assert.equal( flkty.cursorPosition, 200, 'cursorPosition = 200' );
37 | assert.equal( flkty.x + flkty.cursorPosition, 0, 'x + cursorPosition = 0' );
38 |
39 | } );
40 |
41 | } )();
42 |
--------------------------------------------------------------------------------
/test/unit/initial-index.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'initialIndex', function( assert ) {
2 | // initialIndex number
3 | let flkty = new Flickity( '#initial-index', {
4 | initialIndex: 3,
5 | } );
6 | assert.equal( flkty.selectedIndex, 3, 'initialIndex number' );
7 | // selectedIndex remains same after reactivation
8 | flkty.deactivate();
9 | flkty.activate();
10 | assert.equal( flkty.selectedIndex, 3, 'reactivated selectedIndex stays the same' );
11 | flkty.destroy();
12 | // initialIndex selector string
13 | flkty = new Flickity( '#initial-index', {
14 | initialIndex: '.cell--initial',
15 | } );
16 | assert.equal( flkty.selectedIndex, 4, 'initialIndex selector string' );
17 | flkty.destroy();
18 | // initialIndex selector string with groupCells #881
19 | flkty = new Flickity( '#initial-index', {
20 | groupCells: 3,
21 | initialIndex: '.cell--initial',
22 | } );
23 | assert.equal( flkty.selectedIndex, 1, 'initialIndex selector string with groupCells' );
24 |
25 | } );
26 |
--------------------------------------------------------------------------------
/test/unit/lazyload-srcset.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'lazyload srcset', function( assert ) {
2 |
3 | let done = assert.async();
4 |
5 | let gallery = document.querySelector('#lazyload-srcset');
6 | let flkty = new Flickity( gallery, {
7 | lazyLoad: 1,
8 | } );
9 |
10 | let loadCount = 0;
11 | flkty.on( 'lazyLoad', function( event, cellElem ) {
12 | loadCount++;
13 |
14 | assert.equal( event.type, 'load', 'event.type == load' );
15 | assert.ok( event.target.complete, `img ${loadCount} is complete` );
16 | assert.ok( cellElem, 'cellElement argument there' );
17 | let srcset = event.target.getAttribute('srcset');
18 | assert.ok( srcset, 'srcset attribute set' );
19 | let lazyAttr = event.target.getAttribute('data-flickity-lazyload-srcset');
20 | assert.ok( !lazyAttr, 'data-flickity-lazyload attribute removed' );
21 |
22 | // after first 2 have loaded, select 7th cell
23 | if ( loadCount === 2 ) {
24 | done();
25 | }
26 | } );
27 |
28 | } );
29 |
--------------------------------------------------------------------------------
/test/unit/lazyload.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'lazyload', function( assert ) {
2 |
3 | let done = assert.async();
4 |
5 | let gallery = document.querySelector('#lazyload');
6 | let flkty = new Flickity( gallery, {
7 | lazyLoad: 1,
8 | } );
9 |
10 | let loadCount = 0;
11 | flkty.on( 'lazyLoad', function( event, cellElem ) {
12 | loadCount++;
13 |
14 | assert.equal( event.type, 'load', 'event.type == load' );
15 | assert.ok( event.target.complete, `img ${loadCount} is complete` );
16 | assert.ok( cellElem, 'cellElement argument there' );
17 | let lazyAttr = event.target.getAttribute('data-flickity-lazyload');
18 | assert.ok( !lazyAttr, 'data-flickity-lazyload attribute removed' );
19 |
20 | // after first 2 have loaded, select 7th cell
21 | if ( loadCount === 2 ) {
22 | flkty.select( 6 );
23 | }
24 | if ( loadCount === 5 ) {
25 | let loadedImgs = gallery.querySelectorAll('.flickity-lazyloaded');
26 | assert.equal( loadedImgs.length, '5', 'only 5 images loaded' );
27 | done();
28 | }
29 | } );
30 |
31 | } );
32 |
--------------------------------------------------------------------------------
/test/unit/page-dots.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'pageDots', function( assert ) {
2 |
3 | let elem = document.querySelector('#page-dots');
4 | let flkty = new Flickity( elem );
5 |
6 | let dotsHolder = elem.querySelector('.flickity-page-dots');
7 | let dotsElems = [ ...dotsHolder.querySelectorAll('.flickity-page-dot') ];
8 |
9 | assert.ok( dotsHolder, 'dots holder in DOM' );
10 | assert.equal( flkty.pageDots.holder, dotsHolder,
11 | 'dots holder element matches flkty.pageDots.holder' );
12 | assert.equal( dotsElems.length, flkty.cells.length,
13 | 'number of dots matches number of cells' );
14 |
15 | function getSelectedDotIndex() {
16 | return dotsElems.indexOf( dotsHolder.querySelector('.is-selected') );
17 | }
18 |
19 | assert.equal( getSelectedDotIndex(), 0, 'first dot is selected' );
20 | flkty.select( 2 );
21 | assert.equal( getSelectedDotIndex(), 2, '3rd dot is selected' );
22 |
23 | // fake click
24 | flkty.onPageDotsClick({ target: dotsElems[4] });
25 | assert.equal( flkty.selectedIndex, 4, 'tap dot selects cell' );
26 |
27 | } );
28 |
--------------------------------------------------------------------------------
/test/unit/position-cells.js:
--------------------------------------------------------------------------------
1 | ( function() {
2 |
3 | // position values can be off by 0.1% or 1px
4 | function isPositionApprox( value, expected ) {
5 | let isPercent = value.indexOf('%') !== -1;
6 | value = parseFloat( value );
7 | let diff = Math.abs( expected - value );
8 | return isPercent ? diff < 0.1 : diff <= 1;
9 | }
10 |
11 | // loop through cells and check position values against expecteds
12 | function checkCellPositions( flkty, expecteds ) {
13 | let isOK;
14 | for ( let i = 0; i < expecteds.length; i++ ) {
15 | let expected = expecteds[i];
16 | let cell = flkty.cells[i];
17 | let transform = cell.element.style.transform;
18 | let position = transform.replace( 'translateX(', '' ).replace( ')', '' );
19 | isOK = isPositionApprox( position, expected );
20 | if ( !isOK ) {
21 | console.error(`wrong cell position, index: ${i}.
22 | expected: ${expected}. position: ${position}`);
23 | break;
24 | }
25 | }
26 | return isOK;
27 | }
28 |
29 | QUnit.test( 'position cells', function( assert ) {
30 |
31 | let flkty = new Flickity('#position-cells');
32 |
33 | assert.ok( checkCellPositions( flkty, [ 0, 160, 108.3, 312.5, 275, 900 ] ),
34 | 'percent cell position' );
35 | // .cell { margin: 0 2%; }
36 | flkty.element.classList.add('percent-margin');
37 | flkty.positionCells();
38 | assert.ok( checkCellPositions( flkty, [ 0, 176, 121.67, 342.5, 301.67, 980 ] ),
39 | 'percent cell position with margin' );
40 | flkty.element.classList.remove('percent-margin');
41 | // pixel-based position
42 | flkty.options.percentPosition = false;
43 | flkty.positionCells();
44 | assert.ok( checkCellPositions( flkty, [ 0, 160, 260, 500, 660, 900 ] ),
45 | 'pixel cell position' );
46 | // pixel margin, { margin: 0 10px; }
47 | flkty.element.classList.add('pixel-margin');
48 | flkty.positionCells();
49 | assert.ok( checkCellPositions( flkty, [ 0, 180, 300, 560, 740, 1000 ] ),
50 | 'pixel cell position with margin' );
51 |
52 | } );
53 |
54 | } )();
55 |
--------------------------------------------------------------------------------
/test/unit/prev-next-buttons.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'prev-next-buttons', function( assert ) {
2 |
3 | let elem = document.querySelector('#prev-next-buttons');
4 | let flkty = new Flickity( elem );
5 |
6 | let prevElem = elem.querySelector('.flickity-prev-next-button.previous');
7 | let nextElem = elem.querySelector('.flickity-prev-next-button.next');
8 | assert.ok( prevElem, 'previous button in DOM' );
9 | assert.ok( nextElem, 'next button in DOM' );
10 | assert.equal( flkty.prevButton.element, prevElem,
11 | 'previous button element matches prevButton.element' );
12 | assert.equal( flkty.nextButton.element, nextElem,
13 | 'next button element matches nextButton.element' );
14 | assert.ok( prevElem.disabled, 'previous button is disabled at first index' );
15 |
16 | prevElem.focus();
17 | prevElem.click();
18 | assert.equal( flkty.selectedIndex, 0, 'selectedIndex still at 0' );
19 | nextElem.focus();
20 | nextElem.click();
21 | assert.equal( flkty.selectedIndex, 1, 'next button clicked, selectedIndex at 1' );
22 | prevElem.focus();
23 | prevElem.click();
24 | assert.equal( flkty.selectedIndex, 0,
25 | 'previous button clicked, selectedIndex back at 0' );
26 | flkty.select( 5 );
27 | assert.ok( nextElem.disabled, 'next button disabled when at last cell' );
28 |
29 | } );
30 |
--------------------------------------------------------------------------------
/test/unit/resize.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'resize', function( assert ) {
2 |
3 | let elem = document.querySelector('#resize');
4 | let flkty = new Flickity( elem, {
5 | initialIndex: 2,
6 | } );
7 | elem.style.width = '500px';
8 | flkty.resize();
9 |
10 | assert.equal( flkty.selectedIndex, 2, 'selectedIndex = 2' );
11 | assert.equal( flkty.cursorPosition, 250, 'cursorPosition = 250' );
12 | assert.equal( flkty.x + flkty.cursorPosition, -1000, 'x + cursorPosition = -1000' );
13 |
14 | } );
15 |
--------------------------------------------------------------------------------
/test/unit/select-cell.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'selectCell', function( assert ) {
2 |
3 | let gallery = document.querySelector('#select-cell');
4 | let cellElems = gallery.querySelectorAll('.cell');
5 | let flkty = new Flickity( gallery, {
6 | groupCells: true, // groups of 3
7 | } );
8 |
9 | flkty.selectCell( 3 );
10 | assert.equal( flkty.selectedIndex, 1, 'selectCell number' );
11 | flkty.selectCell( cellElems[1] );
12 | assert.equal( flkty.selectedIndex, 0, 'selectCell element' );
13 | flkty.selectCell('.select-cell__6');
14 | assert.equal( flkty.selectedIndex, 2, 'selectCell selector string' );
15 | flkty.selectCell('none');
16 | assert.equal( flkty.selectedIndex, 2, 'selectCell bad string is okay' );
17 | } );
18 |
--------------------------------------------------------------------------------
/test/unit/watch.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'watch fallback', function( assert ) {
2 |
3 | let elem = document.querySelector('#watch');
4 | let flkty = new Flickity( elem, {
5 | watchCSS: true,
6 | } );
7 |
8 | assert.ok( !flkty.isActive, 'fallback not active, watchCSS: true' );
9 | } );
10 |
--------------------------------------------------------------------------------
/test/unit/wrap-around-fill.js:
--------------------------------------------------------------------------------
1 | QUnit.test( 'wrapAround: "fill"', function( assert ) {
2 |
3 | let elem = document.querySelector('#wrap-around-fill');
4 | let flkty = new Flickity( elem, {
5 | wrapAround: 'fill',
6 | } );
7 |
8 | assert.ok( !flkty.isWrapping, 'total cell width not big enough, not wrapping' );
9 |
10 | let shortCell = elem.querySelector('.cell--wrap-around-short');
11 | shortCell.classList.remove('cell--wrap-around-short');
12 | flkty.resize();
13 | assert.ok( flkty.isWrapping, 'cell width big enough, wrapping' );
14 |
15 | } );
16 |
--------------------------------------------------------------------------------