├── .coveralls.yml ├── .eslintignore ├── .eslintrc ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── bower.json ├── circle.yml ├── dist ├── angular-flickity.js └── angular-flickity.min.js ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── flickity.directive.js ├── flickity.directive.spec.js ├── flickity.provider.js ├── flickity.service.js ├── flickity.service.spec.js ├── index.js ├── next │ ├── next.controller.js │ ├── next.controller.spec.js │ ├── next.directive.js │ └── next.directive.spec.js └── previous │ ├── previous.controller.js │ ├── previous.controller.spec.js │ ├── previous.directive.js │ └── previous.directive.spec.js └── webpack.config.js /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: 8TGRfQtRhmmGtpTZqHYkCVIfvRn8QUrD7 2 | 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | package.json 2 | bower.json 3 | dist/ 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/eslint-config-moment/index.js", 3 | "plugins": ["angular"], 4 | "env": { 5 | "browser": true 6 | }, 7 | "globals": { 8 | "angular": true 9 | }, 10 | "rules": { 11 | // Needed for angular 12 | "no-use-before-define": 0, 13 | // Needed for angular 14 | "no-unused-vars": 0, 15 | "angular/log": 0, 16 | // Disabled this since we uses classes for services 17 | // http://www.michaelbromley.co.uk/blog/350/exploring-es6-classes-in-angularjs-1-x%20nice 18 | "angular/no-service-method": 0, 19 | "max-params": 0 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | bower_components 4 | 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at ben@benjamincharity.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | NOTE: The actual Flickity library that this module depends on is not covered 4 | under this license. Please refer to http://flickity.metafizzy.co/license.html 5 | for Flickity licensing. 6 | 7 | Copyright (c) 2013-2015 Benjamin Charity 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Flickity 2 | 3 | angular-flickity 4 | 5 | [![MIT License][license_image]][license_url] [![Coverage Status][coverage_image]][coverage_url] [![NPM version][npm_version_image]][npm_url] [![CircleCI][circle_badge]][circle_link] 6 | 7 | An AngularJS module that exposes a directive and service to create and control multiple 8 | [Flickity][flickity] instances. 9 | 10 | > [:tv: **Demos and Examples**][demo_collection] 11 | 12 | [Flickity][flickity] by [Metafizzy][metafizzy] 13 | 14 | _[Comments and pull requests welcome!][issues]_ 15 | 16 | ## Contents 17 | 18 | - [Installation](#installation) 19 | - [Dependencies](#dependencies) 20 | - [Usage](#usage) 21 | - [Options](#options) 22 | - [ID](#id) 23 | - [Global Defaults](#global-defaults) 24 | - [Directives](#directives) 25 | - [`bc-flickity`](#bc-flickity) 26 | - [`bc-flickity-next`](#bc-flickity-next) 27 | - [`bc-flickity-previous`](#bc-flickity-previous) 28 | - [Services](#services) 29 | - [Initialize](#initialize) 30 | - [`create`](#create) 31 | - [Selecting Cells](#selecting-cells) 32 | - [`select`](#select) 33 | - [`previous`](#previous) 34 | - [`next`](#next) 35 | - [Sizing and Positioning](#sizing-and-positioning) 36 | - [`resize`](#resize) 37 | - [`reposition`](#reposition) 38 | - [Adding and Removing Cells](#adding-and-removing-cells) 39 | - [`prepend`](#prepend) 40 | - [`append`](#append) 41 | - [`insert`](#insert) 42 | - [`remove`](#remove) 43 | - [Utilities](#utilities) 44 | - [`destroy`](#destroy) 45 | - [`reloadCells`](#reloadcells) 46 | - [`getCellElements`](#getcellelements) 47 | - [`get`](#get) 48 | - [`getFirst`](#getfirst) 49 | - [`getByElement`](#getbyelement) 50 | - [Properties](#properties) 51 | - [`selectedIndex`](#selectedindex) 52 | - [`selectedElement`](#selectedelement) 53 | - [`cells`](#cells) 54 | - [Events](#events) 55 | - [Events and Parameters](#eventsandparameters) 56 | - [Event Naming Convention](#eventnamingconvention) 57 | - [Demos](#demos) 58 | - [Development](#development) 59 | - [About Flickity](#about-flickity) 60 | 61 | --- 62 | 63 | ## Installation 64 | 65 | #### Dependencies 66 | 67 | - [AngularJS `(^1.4.0)`][angular] 68 | - [Flickity `(^2.0.5)`][flickity_docs] 69 | - [flickity-imagesloaded `(^2.0.0)`][flickity_imagesloaded] (if using the [imagesloaded option][flickity_options]) 70 | - [flickity-bg-lazyload `(^1.0.0)`][flickity-bg-lazyload] (if using the [bgLazyLoad option][flickity_options]) 71 | 72 | 73 | #### NPM 74 | 75 | ```bash 76 | npm install flickity --save 77 | npm install flickity-imagesloaded --save # if using the imagesloaded option 78 | npm install flickity-bg-lazyload --save # if using the bg-lazyload option 79 | npm install angular-flickity --save 80 | ``` 81 | 82 | #### Bower 83 | 84 | ```bash 85 | bower install flickity --save 86 | bower install flickity-imagesloaded --save # if using the imagesloaded option 87 | bower install flickity-bg-lazyload --save # if using the bg-lazyload option 88 | bower install angular-flickity --save 89 | ``` 90 | 91 | ### Include the scripts 92 | 93 | Include `Flickity` (and `flickity-imagesloaded`/`flickity-bg-lazyload` if needed): 94 | 95 | #### Webpack 96 | 97 | ``` 98 | import Flickity from 'flickity'; 99 | import 'flickity-imagesloaded'; 100 | import 'flickity-bg-lazyload'; 101 | import 'angular-flickity'; 102 | 103 | angular.module('myProject', ['bc.Flickity']); 104 | ``` 105 | 106 | #### Manually 107 | 108 | ``` 109 | 110 | 111 | 112 | 113 | 114 | ``` 115 | 116 | 117 | ##### Note when using Flickity via bower 118 | 119 | In my experience, including Flickity through bower often doesn't work out of the box. By default, 120 | bower pulls in the unpackaged files as the Flickity `bower.json` specifies rather than packaged 121 | files which seems to be what we need. 122 | 123 | The trick is to specify which files bower should use in your own `bower.json`. 124 | 125 | ```json 126 | // inside your bower.json specify which Flickity files to use 127 | { 128 | "name": "myProject", 129 | "overrides": { 130 | "flickity": { 131 | "main": [ 132 | "dist/flickity.pkgd.js", 133 | "dist/flickity.min.css" 134 | ] 135 | } 136 | } 137 | } 138 | ``` 139 | 140 | 141 | ## Usage 142 | 143 | Include `bc.Flickity` as a dependency in your project. 144 | 145 | ```javascript 146 | angular.module('YourModule', ['bc.Flickity']); 147 | ``` 148 | 149 | Use the directive on the parent element containing your slides. 150 | 151 | ```html 152 | 153 |
157 |
158 | 159 |
160 |
161 | 162 | 163 | 167 | 168 | 169 | 173 | ``` 174 | 175 | 176 | ## Options 177 | 178 | This module supports all options for Flickity version `2.0.5`. A full list of options can be 179 | found here: [Flickity Options](http://flickity.metafizzy.co/options.html). 180 | 181 | Simply pass in an object containing any options you'd like to set. 182 | 183 | ```javascript 184 | // ES5 example 185 | angular.module('myModule') 186 | .controller('MyController', ($scope) => { 187 | 188 | $scope.myCustomOptions = { 189 | cellSelector: '.mySlideClassName', 190 | initialIndex: 3, 191 | prevNextButtons: false, 192 | }; 193 | 194 | }) 195 | ; 196 | 197 | 198 | // ES6 example 199 | export class MyController { 200 | constructor() { 201 | 202 | this.flickityOptions = { 203 | cellSelector: '.mySlideClassName', 204 | initialIndex: 3, 205 | prevNextButtons: false, 206 | }; 207 | 208 | } 209 | } 210 | ``` 211 | 212 | `my.template.html`: 213 | ```html 214 | 215 |
216 |
217 |
218 | ... 219 |
220 | ``` 221 | 222 | 223 | ## ID 224 | 225 | The `FlickityService` uses IDs to manage multiple instances of the directive. An ID is automatically 226 | created and assigned at initialization. However, at times you may need to access your instances in a 227 | programmatic way. There are two ways for IDs to be defined by your module. 228 | 229 | #### Element ID 230 | 231 | If the element containing the `bc-flickity` directive has an ID attribute, the value will 232 | be used to create the ID. 233 | 234 | ```html 235 | 236 |
240 | 241 |
242 | ``` 243 | 244 | #### Explicitly Define 245 | 246 | At times, you may not be able to use an element ID (or simply prefer not to). In those cases you can 247 | pass the ID in directly as a string using `bc-flickity-id`. 248 | 249 | ```html 250 | 251 |
255 | 256 |
257 | ``` 258 | 259 | 260 | ## Global Defaults 261 | 262 | This module exposes `FlickityConfigProvider` which can be used to set project-wide defaults for 263 | Flickity. Simply set any options here using options that match the original [Flickity 264 | options](http://flickity.metafizzy.co/options.html). 265 | 266 | ```javascript 267 | // ES6 Config Example 268 | export function config(FlickityConfigProvider) { 269 | 'ngInject'; 270 | 271 | FlickityConfigProvider.prevNextButtons = true; 272 | FlickityConfigProvider.setGallerySize = false; 273 | 274 | } 275 | ``` 276 | 277 | ```javascript 278 | // ES5 Config Example 279 | angular.module('myModule') 280 | .config((FlickityConfigProvider) => { 281 | 'ngInject'; 282 | 283 | FlickityConfigProvider.prevNextButtons = true; 284 | FlickityConfigProvider.setGallerySize = false; 285 | 286 | }) 287 | ; 288 | ``` 289 | 290 | 291 | ## Directives 292 | 293 | ### `bc-flickity` 294 | 295 | The directive `bc-flickity` creates the Flickity gallery. 296 | 297 | ```html 298 |
299 | 300 |
301 | ``` 302 | 303 | You may optionally pass an options object to the directive. User defined options will override any 304 | default options. 305 | 306 | ```html 307 |
308 | 309 |
310 | ``` 311 | 312 | > _Learn more about [`angular-flickity` options](#options) & [Flickity options 313 | > documentation][flickity_options]_ 314 | 315 | 316 | ### `bc-flickity-next` 317 | 318 | The directive `bc-flickity-next` is provided to call the `next()` method on a `Flickity` instance. 319 | 320 | ```html 321 | 322 | ``` 323 | 324 | #### Multiple Instances 325 | 326 | If you need to support multiple `Flickity` instances in a single view you can specify an instance ID 327 | that the control should be linked to. 328 | 329 | ```javascript 330 | 334 | ``` 335 | 336 | > _More on setting the ID using a [directive](#id) or [service](#initialize)._ 337 | 338 | If no ID is set, the directive will assume that only one instance exists and grab the ID from the 339 | first instance. 340 | 341 | #### Looping 342 | 343 | This directive accepts an optional parameter to control the looping. If `true` and at the last cell 344 | when clicked, Flickity will loop back to the first cell. If `false`, it will do nothing when 345 | clicked at the last cell. 346 | 347 | ```html 348 | 349 | ``` 350 | 351 | #### Disabled 352 | 353 | When the last cell is reached, the `disabled` attribute will be added to the element. The `disabled` 354 | attribute will _not_ be added if either of these conditions are met: 355 | 356 | - The associated gallery has `wrapAround` set to `true`. 357 | - The directive has `true` passed in as the optional parameter (which overrides the default 358 | options). 359 | 360 | 361 | ### `bc-flickity-previous` 362 | 363 | The directive `bc-flickity-previous` is provided to call the `previous()` method on the `Flickity` 364 | instance. 365 | 366 | ```html 367 | 368 | ``` 369 | 370 | #### Multiple Instances 371 | 372 | If you need to support multiple `Flickity` instances in a single view you can specify an instance ID 373 | that the control should be linked to. 374 | 375 | ```javascript 376 | 380 | ``` 381 | 382 | > _More on setting the ID using a [directive](#id) or [service](#initialize)._ 383 | 384 | If no ID is set, the directive will assume that only one instance exists and grab the ID from the 385 | first instance. 386 | 387 | #### Looping 388 | 389 | This directive accepts an optional parameter to control the looping. If `true` and at the first cell 390 | when clicked, Flickity will loop around to the last cell. If `false`, it will do nothing when 391 | clicked at the first cell. 392 | 393 | ```html 394 | 395 | ``` 396 | 397 | #### Disabled 398 | 399 | When at the first cell, the `disabled` attribute will be added to the element. The `disabled` 400 | attribute will _not_ be added if either of these conditions are met: 401 | 402 | - The associated gallery has `wrapAround` set to `true`. 403 | - The directive has `true` passed in as the optional parameter (which overrides the default 404 | options). 405 | 406 | 407 | - - - 408 | 409 | 410 | ## Services 411 | 412 | While you can easily use Flickity via the directive only, most Flickity methods are accessible via 413 | the `FlickityService`. 414 | 415 | > The services here follow the order of the [Flickity Documentation][flickity_api] as closely as possible 416 | in order to be immediately familiar. This shouldn't feel like learning another library (assuming 417 | you are already familiar with Flickity). 418 | 419 | > [:tv: Service demo][demo_service_select] 420 | 421 | - - - 422 | 423 | > _Don't be afraid to look at the [source code][source]. It isn't terribly complicated and fairly well 424 | > commented._ 425 | 426 | ### Initialize 427 | 428 | #### `create` 429 | 430 | This can be called to manually create a new `Flickity` instance. 431 | 432 | > [:tv: Create instance demo][demo_create_remote_docready] 433 | 434 | ```javascript 435 | FlickityService.create(element, id, options) 436 | ``` 437 | 438 | ##### Parameters 439 | 440 | - `element`: `{Element}` 441 | - A DOM element wrapped as a jQuery object. This can be done with jqLite 442 | (`angular.element(element)`) or jQuery (`$(element)`) 443 | - `id`: `{String}` _optional_ 444 | - ID for the created `Flickity` instance. If no ID is assigned, one will be created and used 445 | internally. 446 | - `options`: `{Object}` _optional_ 447 | - Options object for Flickity 448 | 449 | ##### Returns `Promise` 450 | 451 | - `instance`: `{Object}` 452 | 453 | ```javascript 454 | // Example return 455 | { 456 | id: 'foo', 457 | instance: Flickity 458 | }; 459 | ``` 460 | 461 | > **NOTE:** 462 | > Anytime you are dealing with the DOM from inside a controller (:-1:) make sure to use 463 | > document.ready. This ensures that the element you are looking for actually exists. You can also 464 | > use a $timeout but I find using document.ready more accurately represents the intention. 465 | 466 | > [:tv: Demo showing DOM issue and solution][demo_create_remote_docready] 467 | 468 | ```javascript 469 | // document.ready example 470 | angular.element($document[0]).ready(() => { 471 | // Get the element that should hold the slider 472 | const element = angular.element(document.getElementById('demo-slider')); 473 | 474 | // Initialize our Flickity instance 475 | FlickityService.create(element[0], element[0].id); 476 | }); 477 | ``` 478 | 479 | > **NOTE:** 480 | > If you are dealing with remote data, you should wrap the `.create()` call with a `$timeout`. 481 | > This ensures that the data has already been assigned to scope before the slider is initialized. 482 | 483 | > [:tv: Demo with remote data][demo_create_remote_docready] 484 | 485 | ```javascript 486 | // Remote data example 487 | $http.get('http://yourRemoteSource.com/slides.json') 488 | .then((results) => { 489 | 490 | // Expose the slides array to $scope 491 | $scope.slides = results.data; 492 | 493 | // Get the element that should hold the slider 494 | const element = angular.element(document.getElementById('demo-slider')); 495 | 496 | // NOTE: When fetching remote data, we initialize the Flickity 497 | // instance inside of a $timeout. This ensures that the slides 498 | // have already been assigned to scope before the slider is 499 | // initialized. 500 | $timeout(() => { 501 | // Initialize our Flickity instance 502 | FlickityService.create(element[0], element[0].id); 503 | }); 504 | }); 505 | ``` 506 | 507 | ### Selecting Cells 508 | 509 | #### `select` 510 | 511 | Move directly to a specific slide. 512 | 513 | > [:tv: Selecting a cell demo][demo_service_select] 514 | 515 | ```javascript 516 | FlickityService.select(id, index, isWrapped, isInstant) 517 | ``` 518 | 519 | - `id`: `{String}` 520 | - A string representing the ID of the `Flickity` instance to move. 521 | - `index`: `{Number}` 522 | - `isWrapped`: `{Bool}` _optional_ 523 | - Default: `false` 524 | - If `true` and `previous` is called when on the first slide, the slider will wrap around to show 525 | the last slide. 526 | - If `true` and `next` is called when on the last slide, the slider will wrap back to show slide 1. 527 | - `isInstant`: `{Bool}` _optional_ 528 | - Default: `false` 529 | - If `true` the slide will change instantly with no animation. 530 | 531 | ##### Returns `Promise` 532 | 533 | - `instance`: `{Object}` 534 | 535 | 536 | #### `previous` 537 | 538 | Move to the previous slide. 539 | 540 | ```javascript 541 | FlickityService.previous(id, isWrapped, isInstant) 542 | ``` 543 | 544 | ##### Parameters 545 | 546 | - `id`: `{String}` 547 | - A string representing the ID of the `Flickity` instance to move. 548 | - `isWrapped`: `{Bool}` _optional_ 549 | - Default: `false` 550 | - If `true` and `previous` is called when on the first slide, the slider will wrap around to show 551 | the last slide. 552 | - If `false` and `previous` is called when on the first slide, the slider will do nothing. 553 | - `isInstant`: `{Bool}` _optional_ 554 | - Default: `false` 555 | - If `true` the slide will change instantly with no animation. 556 | 557 | ##### Returns `Promise` 558 | 559 | - `instance`: `{Object}` 560 | 561 | 562 | #### `next` 563 | 564 | Move to the next slide. 565 | 566 | ```javascript 567 | FlickityService.next(id, isWrapped, isInstant) 568 | ``` 569 | 570 | ##### Parameters 571 | 572 | - `id`: `{String}` 573 | - A string representing the ID of the `Flickity` instance to move. 574 | - `isWrapped`: `{Bool}` _optional_ 575 | - Default: `false` 576 | - If `true` and `next` is called when on the last slide, the slider will wrap back to show slide 1. 577 | - If `false` and `next` is called when on the last slide, the slider will do nothing. 578 | - `isInstant`: `{Bool}` _optional_ 579 | - Default: `false` 580 | - If `true` the slide will change instantly with no animation. 581 | 582 | ##### Returns `Promise` 583 | 584 | - `instance`: `{Object}` 585 | 586 | 587 | #### `cellSelect` 588 | 589 | Select a slide of a cell. 590 | 591 | ```javascript 592 | FlickityService.cellSelect(id, value, isWrapped, isInstant) 593 | ``` 594 | 595 | ##### Parameters 596 | 597 | - `id`: `{String}` 598 | - A string representing the ID of the `Flickity` instance to move. 599 | - `value`: `{Integer|String}` 600 | - Zero-based index OR selector string of the cell to select. 601 | - `isWrapped`: `{Bool}` _optional_ 602 | - Default: `false` 603 | - If `true` and `previous` is called when on the first slide, the slider will wrap around to show 604 | the last slide. 605 | - If `true` and `next` is called when on the last slide, the slider will wrap back to show slide 1. 606 | - `isInstant`: `{Bool}` _optional_ 607 | - Default: `false` 608 | - If `true` the slide will change instantly with no animation. 609 | 610 | ##### Returns `Promise` 611 | 612 | - `instance`: `{Object}` 613 | 614 | 615 | 616 | ### Sizing and Positioning 617 | 618 | #### `resize` 619 | 620 | Triggers Flickity to resize the gallery and re-position cells. 621 | 622 | ```javascript 623 | FlickityService.resize(id) 624 | ``` 625 | 626 | - `id`: `{String}` 627 | - A string representing the ID of the `Flickity` instance. 628 | 629 | ##### Returns `Promise` 630 | 631 | - `instance`: `{Object}` 632 | 633 | 634 | #### `reposition` 635 | 636 | Tell Flickity to reposition cells while retaining the current index. Useful if cell sizes change 637 | after initialization. 638 | 639 | ```javascript 640 | FlickityService.reposition(id) 641 | ``` 642 | 643 | - `id`: `{String}` 644 | - A string representing the ID of the `Flickity` instance. 645 | 646 | ##### Returns `Promise` 647 | 648 | - `instance`: `{Object}` 649 | 650 | 651 | ### Adding and Removing Cells 652 | 653 | > Note: If you are trying to add cell(s) by appending to a 'slides' array and then reinitializing 654 | > the slider, take a look at this [:tv: demo][demo_inject_slide] 655 | 656 | #### `prepend` 657 | 658 | Create cells at the beginning of the gallery and prepend elements. 659 | 660 | ```javascript 661 | FlickityService.prepend(id, elements) 662 | ``` 663 | 664 | - `id`: `{String}` 665 | - A string representing the ID of the `Flickity` instance. 666 | - `elements`: `{Object|Array|Element|NodeList}` 667 | - jQuery object, Array of Elements, Element, or NodeList 668 | 669 | ##### Returns `Promise` 670 | 671 | - `instance`: `{Object}` 672 | 673 | 674 | #### `append` 675 | 676 | Create cells at the end of the gallery and append elements. 677 | 678 | ```javascript 679 | FlickityService.append(id, elements) 680 | ``` 681 | 682 | - `id`: `{String}` 683 | - A string representing the ID of the `Flickity` instance. 684 | - `elements`: `{Object|Array|Element|NodeList}` 685 | - jQuery object, Array of Elements, Element, or NodeList 686 | 687 | ##### Returns `Promise` 688 | 689 | - `instance`: `{Object}` 690 | 691 | 692 | #### `insert` 693 | 694 | Insert elements into the gallery and create cells at the desired index. 695 | 696 | ```javascript 697 | FlickityService.insert(id, elements, index) 698 | ``` 699 | 700 | - `id`: `{String}` 701 | - A string representing the ID of the `Flickity` instance. 702 | - `elements`: `{Object|Array|Element|NodeList}` 703 | - jQuery object, Array of Elements, Element, or NodeList 704 | - `index`: `{Integer}` 705 | - Zero based integer where the new slides should be inserted. 706 | 707 | ##### Returns `Promise` 708 | 709 | - `instance`: `{Object}` 710 | 711 | 712 | ### `remove` 713 | 714 | Remove cells from the gallery and remove the elements from DOM. 715 | 716 | ```javascript 717 | FlickityService.remove(id, elements) 718 | ``` 719 | 720 | - `id`: `{String}` 721 | - A string representing the ID of the `Flickity` instance. 722 | - `elements`: `{Object|Array|Element|NodeList}` 723 | - jQuery object, Array of Elements, Element, or NodeList 724 | 725 | ##### Returns `Promise` 726 | 727 | - `instance`: `{Object}` 728 | 729 | 730 | ### Utilities 731 | 732 | #### `destroy` 733 | 734 | Destroys a `Flickity` instance. 735 | 736 | ```javascript 737 | FlickityService.destroy(id) 738 | ``` 739 | 740 | ##### Parameters 741 | 742 | - `id`: `{String}` 743 | - A string representing the ID of the `Flickity` instance to be destroyed. 744 | 745 | ##### Returns `Promise` 746 | 747 | - `isDestroyed`: `{Bool}` 748 | 749 | This is very useful when your Flickity instance is created inside a controller attached to a route. 750 | Each time the route is hit, the route's controller calls to create a new Flickity instance. But if 751 | that instance already exists, it will cause an error. The correct way to handle this is to destroy 752 | the Flickity instance when the controller is being destroyed. 753 | 754 | ```javascript 755 | $scope.$on('$destroy', () => { 756 | FlickityService.destroy(instanceId); 757 | }); 758 | ``` 759 | 760 | 761 | #### `reloadCells` 762 | 763 | Re-collect all cell elements in `flickity-slider`. 764 | 765 | ```javascript 766 | FlickityService.reloadCells(id) 767 | ``` 768 | 769 | - `id`: `{String}` 770 | - A string representing the ID of the `Flickity` instance. 771 | 772 | ##### Returns `Promise` 773 | 774 | - `instance`: `{Object}` 775 | 776 | > Note: If you are trying to add cell(s) by appending to a 'slides' array and then reinitializing 777 | > the slider, take a look at this [:tv: demo][demo_inject_slide] 778 | 779 | #### `getCellElements` 780 | 781 | Get the elements of the cells. 782 | 783 | ```javascript 784 | FlickityService.getCellElements(id) 785 | ``` 786 | 787 | - `id`: `{String}` 788 | - A string representing the ID of the `Flickity` instance. 789 | 790 | ##### Returns `Promise` 791 | 792 | - `cellElements`: `{Array}` 793 | 794 | 795 | #### `get` 796 | 797 | Return a `Flickity` instance found by ID. 798 | 799 | ```javascript 800 | FlickityService.get(id) 801 | ``` 802 | 803 | - `id`: `{String}` 804 | - A string representing the ID of the `Flickity` instance. 805 | 806 | ##### Returns `Promise` 807 | 808 | - `instance`: `{Object}` 809 | 810 | 811 | #### `getFirst` 812 | 813 | Return the first `Flickity` instance. 814 | 815 | ```javascript 816 | FlickityService.getFirst() 817 | ``` 818 | 819 | ##### Returns `Promise` 820 | 821 | - `instance`: `{Object}` 822 | 823 | 824 | #### `getByElement` 825 | 826 | Find a `Flickity` instance by element or selector string. 827 | 828 | ```javascript 829 | FlickityService.getByElement(id, element) 830 | ``` 831 | 832 | - `id`: `{String}` 833 | - A string representing the ID of the `Flickity` instance. 834 | - `element`: `{Element}` 835 | - Element or selector string representing the `Flickity` instance. 836 | 837 | ##### Returns `Promise` 838 | 839 | - `instance`: `{Object}` 840 | 841 | 842 | ### Properties 843 | 844 | ### `selectedIndex` 845 | 846 | Get the index of the slide currently in view. 847 | 848 | ```javascript 849 | FlickityService.selectedIndex(id) 850 | ``` 851 | 852 | - `id`: `{String}` 853 | - A string representing the ID of the `Flickity` instance for which you need the index. 854 | 855 | ##### Returns `Promise` 856 | 857 | - `selectedIndex`: `{Number}` 858 | - The index of the currently visible slide. 859 | 860 | 861 | ### `selectedElement` 862 | 863 | Get the currently selected cell element. 864 | 865 | ```javascript 866 | FlickityService.selectedElement(id) 867 | ``` 868 | 869 | - `id`: `{String}` 870 | - A string representing the ID of the `Flickity` instance. 871 | 872 | ##### Returns `Promise` 873 | 874 | - `selectedElement`: `{Element}` 875 | 876 | 877 | ### `cells` 878 | 879 | Get all cells. 880 | 881 | > [:tv: Get all cells demo][demo_service_select] 882 | 883 | ```javascript 884 | FlickityService.cells(id) 885 | ``` 886 | 887 | - `id`: `{String}` 888 | - A string representing the ID of the `Flickity` instance. 889 | 890 | ##### Returns `Promise` 891 | 892 | - `cells`: `{Array}` 893 | 894 | 895 | - - - 896 | 897 | 898 | ## Events 899 | 900 | All events trigger an associated `$emit` on `$rootScope`. 901 | 902 | > [:tv: Events demo][demo_events] 903 | 904 | > _Learn more in the [Angular docs on `$emit`][emit]._ 905 | 906 | Each `$emit` event is named in this format: `Flickity:[instanceId]:[eventName]` 907 | 908 | So, for example, if you had declared a [custom ID](#id) of `myCustomId` on `bc-flickity` and wanted 909 | to know when the `settle` event occurs, you could listen for it like this: 910 | 911 | ```javascript 912 | // Assume the gallery has been initialized with the custom ID of `myCustomId` 913 | const settle = $rootScope.$on('Flickity:myCustomId:settle', (event, data) => { 914 | console.log('Flickity just settled!', event, data); 915 | }); 916 | ``` 917 | 918 | 919 | ### Events and Parameters 920 | 921 | > For more information on individual events, check out the [Flickity Documentation on Events][flickity_events]. 922 | 923 | ```javascript 924 | // Legend 925 | • eventName 926 | • parameter 927 | ``` 928 | 929 | - `select` 930 | - `scroll` 931 | - `progress` 932 | - `positionX` 933 | - `settle` 934 | - `dragStart` 935 | - `event` 936 | - `pointer` 937 | - `dragMove` 938 | - `event` 939 | - `pointer` 940 | - `moveVector` 941 | - `dragEnd` 942 | - `event` 943 | - `pointer` 944 | - `pointerDown` 945 | - `event` 946 | - `pointer` 947 | - `pointerMove` 948 | - `event` 949 | - `pointer` 950 | - `moveVector` 951 | - `pointerUp` 952 | - `event` 953 | - `pointer` 954 | - `staticClick` 955 | - `event` 956 | - `pointer` 957 | - `cellElement` 958 | - `cellIndex` 959 | - `lazyLoad` 960 | - `event` 961 | - `cellElement` 962 | 963 | 964 | #### Event Naming Convention 965 | 966 | **`eventName` => `Flickity:instanceId:eventName`** 967 | 968 | | Event name | `$emit` name | 969 | |------------|--------------| 970 | |`select`|`Flickity:instanceId:select`| 971 | |`scroll`|`Flickity:instanceId:scroll`| 972 | |`settle`|`Flickity:instanceId:settle`| 973 | |`dragStart`|`Flickity:instanceId:dragStart`| 974 | |`dragMove`|`Flickity:instanceId:dragMove`| 975 | |`dragEnd`|`Flickity:instanceId:dragEnd`| 976 | |`pointerDown`|`Flickity:instanceId:pointerDown`| 977 | |`pointerMove`|`Flickity:instanceId:pointerMove`| 978 | |`pointerUp`|`Flickity:instanceId:pointerUp`| 979 | |`staticClick`|`Flickity:instanceId:staticClick`| 980 | |`lazyLoad`|`Flickity:instanceId:lazyLoad`| 981 | 982 | > _Learn more about [Flickity events][flickity_events]._ 983 | 984 | 985 | **Don't forget:** 986 | 987 | The `$on` call should always be assigned to a variable. This allows it to be destroyed during the 988 | `$scope` cleanup. 989 | 990 | > _Learn more about [$destroy][destroy]._ 991 | 992 | - - - 993 | 994 | 995 | ## Demos 996 | 997 | - [All demos][demo_collection] 998 | - [Simple][demo_basic] 999 | - [Multiple instances on a page][demo_multiple_instances] 1000 | - [Using events][demo_events] 1001 | - [Using the service and selecting a cell][demo_service_select] 1002 | - [Create via the service and loading remote data][demo_create_remote_docready] 1003 | - [Inject a slide][demo_inject_slide] 1004 | - [bgLazyLoad][demo_bglazyload] 1005 | 1006 | 1007 | ## Development 1008 | 1009 | * `npm run build` 1010 | - Produces uncompressed (`.js`) and minified (`.min.js`) versions of the library under the `dist` folder. 1011 | * `npm run watch` 1012 | - Watches for changes inside `/src` and calls `npm run build` when changes are detected. 1013 | * `npm run test` 1014 | - Runs all tests. 1015 | * `npm run watch:tests` 1016 | - Watch for changes and re-run tests. 1017 | 1018 | ## About Flickity 1019 | 1020 | > _Touch, responsive, flickable galleries._ 1021 | 1022 | Made by [Metafizzy][metafizzy] who make seriously [awesome][packery], [stuff][isotope]. 1023 | 1024 | - [Flickity on Github][flickity] 1025 | - [Flickity Documentation][flickity_docs] 1026 | - [Flickity Licensing][flickity_license] 1027 | 1028 | 1029 | 1030 | [issues]: https://github.com/benjamincharity/angular-flickity/issues 1031 | [flickity_api]: http://flickity.metafizzy.co/api.html 1032 | [flickity_options]: http://flickity.metafizzy.co/options.html 1033 | [flickity_events]: http://flickity.metafizzy.co/api.html#events 1034 | [flickity]: https://github.com/metafizzy/flickity 1035 | [flickity_docs]: http://flickity.metafizzy.co 1036 | [source]: https://github.com/benjamincharity/angular-flickity/tree/master/src 1037 | [emit]: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit 1038 | [destroy]: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$destroy 1039 | [desandro]: http://desandro.com 1040 | [metafizzy]: http://metafizzy.co/ 1041 | [packery]: http://packery.metafizzy.co/ 1042 | [isotope]: http://isotope.metafizzy.co/ 1043 | [flickity_license]: http://flickity.metafizzy.co/license.html 1044 | [angular]: https://angularjs.org 1045 | [flickity_imagesloaded]: https://github.com/metafizzy/flickity-imagesloaded 1046 | [flickity-bg-lazyload]: https://github.com/metafizzy/flickity-bg-lazyload 1047 | 1048 | [demo_collection]: http://codepen.io/collection/nNzQxk/ 1049 | [demo_basic]: http://codepen.io/benjamincharity/pen/amxVaV?editors=1000 1050 | [demo_multiple_instances]: http://codepen.io/benjamincharity/pen/dpEqoj?editors=1000 1051 | [demo_events]: http://codepen.io/benjamincharity/pen/yaWxor?editors=0010 1052 | [demo_service_select]: http://codepen.io/benjamincharity/pen/KgLxRW?editors=0010 1053 | [demo_create_remote_docready]: http://codepen.io/benjamincharity/pen/NRVLEb?editors=0010 1054 | [demo_inject_slide]: http://codepen.io/benjamincharity/pen/qaGJmW?editors=0010 1055 | [demo_bglazyload]: http://codepen.io/kukac7/pen/vyXjBp 1056 | 1057 | [license_image]: http://img.shields.io/badge/license-MIT-blue.svg 1058 | [license_url]: LICENSE 1059 | [npm_url]: https://npmjs.org/package/angular-flickity 1060 | [npm_version_image]: http://img.shields.io/npm/v/angular-flickity.svg 1061 | [coverage_image]: https://coveralls.io/repos/github/benjamincharity/angular-flickity/badge.svg 1062 | [coverage_url]: https://coveralls.io/github/benjamincharity/angular-flickity 1063 | [circle_badge]: https://circleci.com/gh/benjamincharity/angular-flickity/tree/master.svg?style=svg 1064 | [circle_link]: https://circleci.com/gh/benjamincharity/angular-flickity/tree/master 1065 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-flickity", 3 | "description": "Angular integration for Flickity", 4 | "homepage": "https://github.com/benjamincharity/angular-flickity", 5 | "authors": [ 6 | "Benjamin Charity " 7 | ], 8 | "license": "MIT", 9 | "main": "dist/angular-flickity.js", 10 | "moduleType": [ 11 | "amd", 12 | "globals" 13 | ], 14 | "keywords": [ 15 | "angular", 16 | "angular-flickity", 17 | "flickity", 18 | "flickity.js", 19 | "angular.js", 20 | "swipe", 21 | "slideshow", 22 | "slider", 23 | "carousel", 24 | "touch" 25 | ], 26 | "dependencies": { 27 | "angular": ">= 1.4.0", 28 | "flickity": "^2.0.5" 29 | }, 30 | "ignore": [ 31 | "**/.*", 32 | "src", 33 | "node_modules", 34 | "bower_components", 35 | "package.json", 36 | "webpack.config.js" 37 | ], 38 | "overrides": { 39 | "flickity": { 40 | "main": [ 41 | "dist/flickity.pkgd.js", 42 | "dist/flickity.min.css" 43 | ] 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 6.1.0 4 | 5 | -------------------------------------------------------------------------------- /dist/angular-flickity.min.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n(require("Flickity")):"function"==typeof define&&define.amd?define("angular-flickity",["Flickity"],n):"object"==typeof exports?exports["angular-flickity"]=n(require("Flickity")):e["angular-flickity"]=n(e.Flickity)}(this,function(e){return function(e){function n(i){if(t[i])return t[i].exports;var c=t[i]={exports:{},id:i,loaded:!1};return e[i].call(c.exports,c,c.exports,n),c.loaded=!0,c.exports}var t={};return n.m=e,n.c=t,n.p="",n(0)}([function(e,n,t){"use strict";var i=t(1),c=t(2),r=t(4),o=t(5),s=t(7);angular.module("bc.Flickity",[]).provider("FlickityConfig",i.FlickityConfigProvider).service("FlickityService",c.FlickityService).directive("bcFlickity",r.FlickityDirective).directive("bcFlickityNext",o.FlickityNextDirective).directive("bcFlickityPrevious",s.FlickityPreviousDirective)},function(e,n){"use strict";function t(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=function(){function e(e,n){for(var t=0;t2&&void 0!==arguments[2]&&arguments[2],c=arguments.length>3&&void 0!==arguments[3]&&arguments[3];return new Promise(function(r,o){var s=t._getFlickityIndex(e);return s<0?o("Instance "+e+" not found."):(t.instances[s].instance.select(n,i,c),r(t.instances[s]))})}},{key:"selectCell",value:function(e,n){var t=this,i=arguments.length>2&&void 0!==arguments[2]&&arguments[2],c=arguments.length>3&&void 0!==arguments[3]&&arguments[3];return new Promise(function(r,o){var s=t._getFlickityIndex(e);return s<0?o("Instance "+e+" not found."):(t.instances[s].instance.selectCell(n,i,c),r(t.instances[s]))})}},{key:"selectedIndex",value:function(e){var n=this;return new Promise(function(t,i){var c=n._getFlickityIndex(e);return c<0?i("Instance "+e+" not found."):t(n.instances[c].instance.selectedIndex)})}},{key:"resize",value:function(e){var n=this;return new Promise(function(t,i){var c=n._getFlickityIndex(e);return c<0?i("Instance "+e+" not found."):(n.instances[c].instance.resize(),t(n.instances[c]))})}},{key:"reposition",value:function(e){var n=this;return new Promise(function(t,i){var c=n._getFlickityIndex(e);return c<0?i("Instance "+e+" not found."):(n.instances[c].instance.reposition(),t(n.instances[c]))})}},{key:"reloadCells",value:function(e){var n=this;return new Promise(function(t,i){var c=n._getFlickityIndex(e);return c<0?i("Instance "+e+" not found."):(n.instances[c].instance.reloadCells(),t(n.instances[c]))})}},{key:"get",value:function(e){var n=this;return new Promise(function(t,i){var c=n._getFlickityIndex(e);return c<0?i("Instance "+e+" not found."):t(n.instances[c])})}},{key:"getFirst",value:function(){var e=this;return new Promise(function(n,t){return!e.instances||e.instances.length<1?t("No instances exist."):n(e.instances[0])})}},{key:"getByElement",value:function(e){return new Promise(function(n,t){var i=s["default"].data(e);return i?n(i):t("Instance not found for "+e)})}},{key:"prepend",value:function(e,n){var t=this;return new Promise(function(i,c){var r=t._getFlickityIndex(e);return r<0?c("Instance "+e+" not found."):(t.instances[r].instance.prepend(n),i(t.instances[r]))})}},{key:"append",value:function(e,n){var t=this;return new Promise(function(i,c){var r=t._getFlickityIndex(e);return r<0?c("Instance "+e+" not found."):(t.instances[r].instance.append(n),i(t.instances[r]))})}},{key:"insert",value:function(e,n,t){var i=this;return new Promise(function(c,r){var o=i._getFlickityIndex(e);return o<0?r("Instance "+e+" not found."):(i.instances[o].instance.insert(n,t),c(i.instances[o]))})}},{key:"getCellElements",value:function(e){var n=this;return new Promise(function(t,i){var c=n._getFlickityIndex(e);return c<0?i("Instance "+e+" not found."):t(n.instances[c].instance.getCellElements())})}},{key:"remove",value:function(e,n){var t=this;return new Promise(function(i,c){var r=t._getFlickityIndex(e);return r<0?c("Instance "+e+" not found."):(t.instances[r].instance.remove(n),i(t.instances[r]))})}},{key:"selectedElement",value:function(e){var n=this;return new Promise(function(t,i){var c=n._getFlickityIndex(e);return c<0?i("Instance "+e+" not found."):t(n.instances[c].instance.selectedElement)})}},{key:"cells",value:function(e){var n=this;return new Promise(function(t,i){var c=n._getFlickityIndex(e);return c<0?i("Instance "+e+" not found."):t(n.instances[c].instance.cells)})}},{key:"_getFlickityIndex",value:function(e){var n=-1;return this.instances.length>0&&this.instances.forEach(function(t,i){t.id===e&&(n=i)}),n}},{key:"_bindEvents",value:function(e){var n=this;return new Promise(function(t,i){var c=n._getFlickityIndex(e);if(c<0)return i();var r=n.instances[c].id;return n.instances[c].instance.on("select",function(){n.$rootScope.$emit("Flickity:"+r+":select",n.instances[c])}),n.instances[c].instance.on("settle",function(){n.$rootScope.$emit("Flickity:"+r+":settle",n.instances[c])}),n.instances[c].instance.on("scroll",function(e,t){n.$rootScope.$emit("Flickity:"+r+":scroll",{progress:e,positionX:t})}),n.instances[c].instance.on("dragStart",function(e,t){n.$rootScope.$emit("Flickity:"+r+":dragStart",{event:e,pointer:t})}),n.instances[c].instance.on("dragMove",function(e,t,i){n.$rootScope.$emit("Flickity:"+r+":dragMove",{event:e,pointer:t,moveVector:i})}),n.instances[c].instance.on("dragEnd",function(e,t){n.$rootScope.$emit("Flickity:"+r+":dragEnd",{event:e,pointer:t})}),n.instances[c].instance.on("pointerDown",function(e,t){n.$rootScope.$emit("Flickity:"+r+":pointerDown",{event:e,pointer:t})}),n.instances[c].instance.on("pointerMove",function(e,t,i){n.$rootScope.$emit("Flickity:"+r+":pointerMove",{event:e,pointer:t,moveVector:i})}),n.instances[c].instance.on("pointerUp",function(e,t){n.$rootScope.$emit("Flickity:"+r+":pointerUp",{event:e,pointer:t})}),n.instances[c].instance.on("staticClick",function(e,t,i,c){n.$rootScope.$emit("Flickity:"+r+":staticClick",{event:e,pointer:t,cellElement:i,cellIndex:c})}),n.instances[c].instance.on("lazyLoad",function(e,t){n.$rootScope.$emit("Flickity:"+r+":lazyLoad",{event:e,cellElement:t})}),t(!0)})}},{key:"_findObjectById",value:function(e,n){return e.filter(function(e){return e.id===n})[0]}}]),e}()},function(n,t){n.exports=e},function(e,n){"use strict";function t(e,n,t){"ngInject";function i(e,n,i,c){var r=angular.fromJson(c.bcFlickity||{});c.options=angular.extend({},t,r),c.bcFlickityId||i.id&&(c.bcFlickityId=i.id)}function c(t,i,c,r){e(function(){n.create(i[0],r.bcFlickityId,r.options).then(function(e){r.Flickity=e.instance,r.bcFlickityId=e.id})});t.$on("$destroy",function(e){n.destroy(r.bcFlickityId)})}i.$inject=["$scope","$element","$attrs","$controller"],c.$inject=["$scope","$element","$attrs","$controller"];var r={restrict:"A",scope:{},bindToController:{bcFlickity:"@?",bcFlickityId:"@?"},compile:function(){return{pre:i,post:c}},controller:function(){},controllerAs:"vm"};return r}t.$inject=["$timeout","FlickityService","FlickityConfig"],Object.defineProperty(n,"__esModule",{value:!0}),n.FlickityDirective=t},function(e,n,t){"use strict";function i(e,n,t,i,r){"ngInject";function o(e,n,i,c){function o(e,n){c.wrapAround||e!==n?i.$set("disabled",!1):i.$set("disabled","disabled")}var s=c.flickityId,u="Flickity:"+s+":cellSelect",l="Flickity:"+s+":settle";t.$on(u,function(e,n){o(n.instance.slides.length,n.instance.selectedIndex+1)}),t.$on(l,function(e,n){o(n.instance.slides.length,n.instance.selectedIndex+1)});n.on("click",function(){r.next(c.flickityId,c.wrapAround).then(function(e){o(e.instance.selectedIndex)})})}o.$inject=["$scope","$element","$attrs","$controller"];var s={restrict:"A",scope:{},bindToController:{bcFlickityNext:"=?",bcFlickityId:"@?"},compile:function(){return{pre:o}},controller:c.NextController,controllerAs:"vm"};return s}i.$inject=["$log","$timeout","$rootScope","FlickityConfig","FlickityService"],Object.defineProperty(n,"__esModule",{value:!0}),n.FlickityNextDirective=i;var c=t(6)},function(e,n){"use strict";function t(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var i=function(){function e(e,n){for(var t=0;t" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/benjamincharity/angular-flickity.git" 15 | }, 16 | "keywords": [ 17 | "angular", 18 | "angular-flickity", 19 | "flickity", 20 | "flickity.js", 21 | "angular.js", 22 | "swipe", 23 | "slideshow", 24 | "slider", 25 | "carousel", 26 | "touch" 27 | ], 28 | "main": "dist/angular-flickity.js", 29 | "scripts": { 30 | "build": "WEBPACK_ENV=build webpack && osx-notifier --type \"pass\" --title \"Build successful\" --message \"gg\" --group \"npmBuild\"", 31 | "watch": "npm run build && onchange 'src/**/*.js' -- npm run build", 32 | "test": "karma start --singleRun=true", 33 | "watch:tests": "karma start" 34 | }, 35 | "dependencies": { 36 | "angular": "^1.4.0", 37 | "flickity": "^2.0.5" 38 | }, 39 | "devDependencies": { 40 | "angular-mocks": "^1.5.8", 41 | "babel": "6.3.13", 42 | "babel-core": "6.1.18", 43 | "babel-eslint": "4.1.3", 44 | "babel-loader": "6.1.0", 45 | "babel-plugin-add-module-exports": "0.1.2", 46 | "babel-preset-es2015": "6.3.13", 47 | "eslint": "^3.8.1", 48 | "eslint-config-moment": "^2.1.4", 49 | "eslint-loader": "^1.6.1", 50 | "eslint-plugin-angular": "^1.1.1", 51 | "jasmine": "^2.5.2", 52 | "jasmine-promises": "^0.4.1", 53 | "karma": "^1.3.0", 54 | "karma-chrome-launcher": "^2.0.0", 55 | "karma-coverage": "^1.1.1", 56 | "karma-coveralls": "^1.1.2", 57 | "karma-jasmine": "^1.0.2", 58 | "karma-phantomjs-launcher": "^1.0.2", 59 | "karma-sourcemap-loader": "^0.3.7", 60 | "karma-spec-reporter": "0.0.26", 61 | "karma-webpack": "^1.8.0", 62 | "ng-annotate-loader": "^0.1.0", 63 | "onchange": "^2.2.0", 64 | "osx-notifier": "^0.2.2", 65 | "source-map": "^0.5.3", 66 | "webpack": "1.12.9", 67 | "webpack-dev-server": "^1.14.1" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/flickity.directive.js: -------------------------------------------------------------------------------- 1 | /* global flickity */ 2 | 3 | export function FlickityDirective( 4 | $timeout, 5 | FlickityService, 6 | FlickityConfig 7 | ) { 8 | 'ngInject'; 9 | 10 | const directive = { 11 | restrict: 'A', 12 | scope: {}, 13 | bindToController: { 14 | bcFlickity: '@?', 15 | bcFlickityId: '@?', 16 | }, 17 | compile: () => { 18 | return { 19 | pre: preLinkFunction, 20 | post: postLinkFunction, 21 | }; 22 | }, 23 | controller: () => {}, 24 | controllerAs: 'vm', 25 | }; 26 | 27 | return directive; 28 | 29 | 30 | function preLinkFunction($scope, $element, $attrs, $controller) { 31 | 'ngInject'; 32 | 33 | // Get the user's options or start with an empty object 34 | const userOptions = angular.fromJson($controller.bcFlickity || {}); 35 | // Combine the user options with the default options 36 | $controller.options = angular.extend({}, FlickityConfig, userOptions); 37 | 38 | // If no ID was passed in 39 | if (!$controller.bcFlickityId) { 40 | // Use the element's ID if one exists 41 | if ($attrs.id) { 42 | $controller.bcFlickityId = $attrs.id; 43 | } 44 | } 45 | 46 | } 47 | 48 | /** 49 | * Post Link 50 | */ 51 | function postLinkFunction($scope, $element, $attrs, $controller) { 52 | 'ngInject'; 53 | 54 | // Make sure this `create()` gets picked up in the next digest cycle 55 | $timeout(() => { 56 | 57 | // Initialize Flickity 58 | FlickityService.create($element[0], $controller.bcFlickityId, $controller.options) 59 | .then((flickityInstance) => { 60 | 61 | // Expose the Flickity instance and ID 62 | $controller.Flickity = flickityInstance.instance; 63 | $controller.bcFlickityId = flickityInstance.id; 64 | 65 | }) 66 | ; 67 | 68 | }); 69 | 70 | // When the directive is being destroyed 71 | const onDestroy = $scope.$on('$destroy', (event) => { 72 | // Make sure we destroy the Flickity instance 73 | FlickityService.destroy($controller.bcFlickityId); 74 | }); 75 | 76 | } 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/flickity.directive.spec.js: -------------------------------------------------------------------------------- 1 | describe('FlickityDirective', () => { 2 | let $compile; 3 | let $rootScope; 4 | 5 | // Include the module 6 | beforeEach(angular.mock.module('bc.Flickity')); 7 | 8 | beforeEach(function() { 9 | 10 | inject(function($compile, $rootScope, $timeout, FlickityService) { 11 | this.$compile = $compile; 12 | this.$rootScope = $rootScope; 13 | this.$timeout = $timeout; 14 | this.FlickityService = FlickityService; 15 | 16 | this.$scope = this.$rootScope.$new(); 17 | this.$scope.slides = [ 18 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide1.jpg', 19 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide2.jpg', 20 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide3.jpg', 21 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide4.jpg', 22 | ]; 23 | this.$scope.extraSlide = 24 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide5.jpg'; 25 | }); 26 | 27 | }); 28 | 29 | 30 | beforeEach(function() { 31 | this.compileDirective = function(template) { 32 | this.element = this.$compile(template)(this.$scope); 33 | this.$scope.$digest(); 34 | }; 35 | }); 36 | 37 | 38 | afterEach(function() { 39 | if (this.element) { 40 | this.element.remove(); 41 | } 42 | }); 43 | 44 | describe(`on $scope.$destroy()`, () => { 45 | 46 | it(`should destroy the Flickity instance`, function(done) { 47 | const template = angular.element(` 48 |
49 |
50 | 51 |
52 |
53 | `); 54 | this.compileDirective(template); 55 | const DELAY = 200; 56 | 57 | this.$timeout(() => { 58 | this.FlickityService.getAll().then((instances) => { 59 | const actual = instances.length; 60 | const expected = 1; 61 | 62 | expect(actual).toEqual(expected); 63 | 64 | this.$scope.$destroy(); 65 | 66 | this.FlickityService.getAll().then((instances) => { 67 | const actual = instances.length; 68 | const expected = 0; 69 | 70 | expect(actual).toEqual(expected); 71 | 72 | done(); 73 | }); 74 | }); 75 | }); 76 | 77 | this.$timeout.flush(); 78 | 79 | this.$timeout.verifyNoPendingTasks(); 80 | }); 81 | 82 | }); 83 | 84 | 85 | }); 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/flickity.provider.js: -------------------------------------------------------------------------------- 1 | export class FlickityConfigProvider { 2 | 3 | constructor() { 4 | // Define Flickity defaults 5 | this.accessibility = true; 6 | this.cellAlign = 'center'; 7 | this.freeScrollFriction = .075; 8 | this.friction = .28; 9 | this.percentPosition = true; 10 | this.resize = true; 11 | this.selectedAttraction = .025; 12 | this.setGallerySize = true; 13 | } 14 | 15 | 16 | 17 | 18 | $get() { 19 | return this; 20 | } 21 | 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/flickity.service.js: -------------------------------------------------------------------------------- 1 | import flickity from 'flickity'; 2 | 3 | export class FlickityService { 4 | 5 | constructor( 6 | $timeout, $q, $rootScope, $log 7 | ) { 8 | 'ngInject'; 9 | 10 | this.$timeout = $timeout; 11 | this.$q = $q; 12 | this.$rootScope = $rootScope; 13 | this.$log = $log; 14 | 15 | this.instances = []; 16 | 17 | } 18 | 19 | 20 | 21 | 22 | /** 23 | * Create a new Flickity instance 24 | * 25 | * @param {Element} element 26 | * @param {String} id 27 | * @param {Object} options 28 | * @return {Object} instance 29 | */ 30 | create(element, id, options) { 31 | return new Promise((resolve, reject) => { 32 | // If no ID was passed in 33 | if (!id) { 34 | if (element.id) { 35 | // Use the element's ID if it exists 36 | id = element.id; 37 | } else { 38 | // Otherwise, assign a new ID 39 | id = this.instances.length + 1; 40 | } 41 | } 42 | 43 | // Check to see if the ID is already in use 44 | if (this._findObjectById(this.instances, id)) { 45 | const index = this._getFlickityIndex(id); 46 | this.$log.error(`This ID is already in use: `, this.instances[index]); 47 | 48 | reject(`This ID is already in use.`); 49 | } 50 | 51 | // Define the new instance 52 | const instance = { 53 | id: id, 54 | instance: new flickity(element, options), 55 | }; 56 | 57 | // Save this instance to the array 58 | this.instances.push(instance); 59 | 60 | // Bind to all events 61 | return this._bindEvents(id).then(() => { 62 | return resolve(instance); 63 | }); 64 | }); 65 | } 66 | 67 | 68 | /** 69 | * Destroy a Flickity instance 70 | * 71 | * @param {String} id 72 | * @return {Object} instance 73 | */ 74 | destroy(id) { 75 | return new Promise((resolve, reject) => { 76 | const flickityIndex = this._getFlickityIndex(id); 77 | 78 | if (flickityIndex < 0) { 79 | return reject(`Instance ${id} not found.`); 80 | } 81 | 82 | // Destroy the Flickity instance 83 | this.instances[flickityIndex].instance.destroy(); 84 | 85 | // Remove the instance from the array 86 | this.instances.splice(flickityIndex, 1); 87 | 88 | return resolve('Instance ' + id + ' destroyed.'); 89 | }); 90 | } 91 | 92 | 93 | /** 94 | * Return all instances 95 | * 96 | * @return {Array} instances 97 | */ 98 | getAll() { 99 | return new Promise((resolve) => { 100 | resolve(this.instances); 101 | }); 102 | } 103 | 104 | 105 | /** 106 | * Move to the next slide 107 | * 108 | * @param {string} id 109 | * @param {Bool} isWrapped 110 | * @param {Bool} isInstant 111 | * @return {Object} instance 112 | */ 113 | next(id, isWrapped, isInstant) { 114 | return new Promise((resolve, reject) => { 115 | const flickityIndex = this._getFlickityIndex(id); 116 | 117 | if (flickityIndex < 0) { 118 | return reject(`Instance ${id} not found.`); 119 | } else { 120 | // Move to the next slide 121 | this.instances[flickityIndex].instance.next(isWrapped, isInstant); 122 | 123 | // Return the instance 124 | return resolve(this.instances[flickityIndex]); 125 | } 126 | }); 127 | } 128 | 129 | 130 | /** 131 | * Move to the previous slide 132 | * 133 | * @param {string} id 134 | * @param {Bool} isWrapped 135 | * @param {Bool} isInstant 136 | * @return {Object} instance 137 | */ 138 | previous(id, isWrapped, isInstant) { 139 | return new Promise((resolve, reject) => { 140 | const flickityIndex = this._getFlickityIndex(id); 141 | 142 | if (flickityIndex < 0) { 143 | return reject(`Instance ${id} not found.`); 144 | } else { 145 | // Move to the previous slide 146 | this.instances[flickityIndex].instance.previous(isWrapped, isInstant); 147 | 148 | // Return the instance 149 | return resolve(this.instances[flickityIndex]); 150 | } 151 | }); 152 | } 153 | 154 | 155 | /** 156 | * Select a slide 157 | * 158 | * @param {String} id 159 | * @param {Integer} index 160 | * @param {Bool} isWrapped 161 | * @param {Bool} isInstant 162 | * @return {Object} instance 163 | */ 164 | select(id, index, isWrapped = false, isInstant = false) { 165 | return new Promise((resolve, reject) => { 166 | const flickityIndex = this._getFlickityIndex(id); 167 | 168 | if (flickityIndex < 0) { 169 | return reject(`Instance ${id} not found.`); 170 | } else { 171 | // Move to the selected slide 172 | this.instances[flickityIndex].instance.select(index, isWrapped, isInstant); 173 | 174 | // Return the instance 175 | return resolve(this.instances[flickityIndex]); 176 | } 177 | }); 178 | } 179 | 180 | 181 | /** 182 | * Select a slide of a cell 183 | * 184 | * @param {String} id 185 | * @param {Integer|String} value 186 | * @param {Bool} isWrapped 187 | * @param {Bool} isInstant 188 | * @return {Object} instance 189 | */ 190 | selectCell(id, value, isWrapped = false, isInstant = false) { 191 | return new Promise((resolve, reject) => { 192 | const flickityIndex = this._getFlickityIndex(id); 193 | 194 | if (flickityIndex < 0) { 195 | return reject(`Instance ${id} not found.`); 196 | } else { 197 | // Move to the selected slide 198 | this.instances[flickityIndex].instance.selectCell(value, isWrapped, isInstant); 199 | 200 | // Return the instance 201 | return resolve(this.instances[flickityIndex]); 202 | } 203 | }); 204 | } 205 | 206 | 207 | /** 208 | * Get the current slide index 209 | * 210 | * @param {String} id 211 | * @return {Integer} selectedIndex 212 | */ 213 | selectedIndex(id) { 214 | return new Promise((resolve, reject) => { 215 | const flickityIndex = this._getFlickityIndex(id); 216 | 217 | if (flickityIndex < 0) { 218 | return reject(`Instance ${id} not found.`); 219 | } else { 220 | // Return the current index 221 | return resolve(this.instances[flickityIndex].instance.selectedIndex); 222 | } 223 | }); 224 | } 225 | 226 | 227 | /** 228 | * Resize the gallery and re-position cells. 229 | * 230 | * @param {String} id 231 | * @return {Object} instance 232 | */ 233 | resize(id) { 234 | return new Promise((resolve, reject) => { 235 | const flickityIndex = this._getFlickityIndex(id); 236 | 237 | if (flickityIndex < 0) { 238 | return reject(`Instance ${id} not found.`); 239 | } else { 240 | // Trigger the resize 241 | this.instances[flickityIndex].instance.resize(); 242 | 243 | // Return the instance 244 | return resolve(this.instances[flickityIndex]); 245 | } 246 | }); 247 | } 248 | 249 | 250 | /** 251 | * Position cells at selected position. 252 | * Trigger reposition after the size of a cell has been changed. 253 | * 254 | * @param {String} id 255 | * @return {Object} instance 256 | */ 257 | reposition(id) { 258 | return new Promise((resolve, reject) => { 259 | const flickityIndex = this._getFlickityIndex(id); 260 | 261 | if (flickityIndex < 0) { 262 | return reject(`Instance ${id} not found.`); 263 | } else { 264 | // Trigger the reposition 265 | this.instances[flickityIndex].instance.reposition(); 266 | 267 | // Return the instance 268 | return resolve(this.instances[flickityIndex]); 269 | } 270 | }); 271 | } 272 | 273 | 274 | /** 275 | * Re-collect all cell elements in `flickity-slider`. 276 | * 277 | * @param {String} id 278 | * @return {Object} instance 279 | */ 280 | reloadCells(id) { 281 | return new Promise((resolve, reject) => { 282 | const flickityIndex = this._getFlickityIndex(id); 283 | 284 | if (flickityIndex < 0) { 285 | return reject(`Instance ${id} not found.`); 286 | } else { 287 | // Reload cells 288 | this.instances[flickityIndex].instance.reloadCells(); 289 | 290 | // Return the instance 291 | return resolve(this.instances[flickityIndex]); 292 | } 293 | }); 294 | } 295 | 296 | 297 | /** 298 | * Get a Flickity instance by ID 299 | * 300 | * @param {String} id 301 | * @return {Object} instance 302 | */ 303 | get(id) { 304 | return new Promise((resolve, reject) => { 305 | const flickityIndex = this._getFlickityIndex(id); 306 | 307 | if (flickityIndex < 0) { 308 | return reject(`Instance ${id} not found.`); 309 | } else { 310 | // Return the instance 311 | return resolve(this.instances[flickityIndex]); 312 | } 313 | }); 314 | } 315 | 316 | 317 | /** 318 | * Get the first Flickity instance 319 | * 320 | * @return {Object} instance 321 | */ 322 | getFirst() { 323 | return new Promise((resolve, reject) => { 324 | if (!this.instances || this.instances.length < 1) { 325 | return reject(`No instances exist.`); 326 | } else { 327 | // Return the instance 328 | return resolve(this.instances[0]); 329 | } 330 | }); 331 | } 332 | 333 | 334 | /** 335 | * Get the Flickity instance 336 | * 337 | * @param {Element} element 338 | * @return {Object} instance 339 | */ 340 | getByElement(element) { 341 | return new Promise((resolve, reject) => { 342 | const instance = flickity.data(element) 343 | 344 | if (instance) { 345 | // Return the instance 346 | return resolve(instance); 347 | } else { 348 | return reject('Instance not found for ' + element); 349 | } 350 | }); 351 | } 352 | 353 | 354 | /** 355 | * Prepend elements and create cells to the beginning of the gallery. 356 | * 357 | * @param {String} id 358 | * @param {*} element(s) - jQuery object, Array of Elements, Element, or NodeList 359 | * @return {Object} instance 360 | */ 361 | prepend(id, elements) { 362 | return new Promise((resolve, reject) => { 363 | const flickityIndex = this._getFlickityIndex(id); 364 | 365 | if (flickityIndex < 0) { 366 | return reject(`Instance ${id} not found.`); 367 | } else { 368 | // Prepend the slides 369 | this.instances[flickityIndex].instance.prepend(elements); 370 | 371 | // Return the instance 372 | return resolve(this.instances[flickityIndex]); 373 | } 374 | }); 375 | } 376 | 377 | 378 | /** 379 | * Append elements and create cells to the end of the gallery. 380 | * 381 | * @param {String} id 382 | * @param {*} element(s) - jQuery object, Array of Elements, Element, or NodeList 383 | * @return {Object} instance 384 | */ 385 | append(id, elements) { 386 | return new Promise((resolve, reject) => { 387 | const flickityIndex = this._getFlickityIndex(id); 388 | 389 | if (flickityIndex < 0) { 390 | return reject(`Instance ${id} not found.`); 391 | } else { 392 | // Append the slides 393 | this.instances[flickityIndex].instance.append(elements); 394 | 395 | // Return the instance 396 | return resolve(this.instances[flickityIndex]); 397 | } 398 | }); 399 | } 400 | 401 | 402 | /** 403 | * Insert elements into the gallery and create cells at the desired index. 404 | * 405 | * @param {String} id 406 | * @param {*} element(s) - jQuery object, Array of Elements, Element, or NodeList 407 | * @param {Integer} index - Zero based index 408 | * @return {Object} instance 409 | */ 410 | insert(id, elements, index) { 411 | return new Promise((resolve, reject) => { 412 | const flickityIndex = this._getFlickityIndex(id); 413 | 414 | if (flickityIndex < 0) { 415 | return reject(`Instance ${id} not found.`); 416 | } else { 417 | // Insert the slides 418 | this.instances[flickityIndex].instance.insert(elements, index); 419 | 420 | // Return the instance 421 | return resolve(this.instances[flickityIndex]); 422 | } 423 | }); 424 | } 425 | 426 | 427 | /** 428 | * Get the elements of the cells 429 | * 430 | * @param {String} id 431 | * @return {Array} cellElements 432 | */ 433 | getCellElements(id) { 434 | return new Promise((resolve, reject) => { 435 | const flickityIndex = this._getFlickityIndex(id); 436 | 437 | if (flickityIndex < 0) { 438 | return reject(`Instance ${id} not found.`); 439 | } else { 440 | // Return the array of cell elements 441 | return resolve(this.instances[flickityIndex].instance.getCellElements()); 442 | } 443 | }); 444 | } 445 | 446 | 447 | /** 448 | * Remove cells by element 449 | * 450 | * @param {String} id 451 | * @param {Object|Array|Element} element(s) 452 | * @return {Object} instance 453 | */ 454 | remove(id, elements) { 455 | return new Promise((resolve, reject) => { 456 | const flickityIndex = this._getFlickityIndex(id); 457 | 458 | if (flickityIndex < 0) { 459 | return reject(`Instance ${id} not found.`); 460 | } else { 461 | this.instances[flickityIndex].instance.remove(elements); 462 | 463 | // Return the instance 464 | return resolve(this.instances[flickityIndex]); 465 | } 466 | }); 467 | } 468 | 469 | 470 | /** 471 | * Get the currently selected cell element 472 | * 473 | * @param {String} id 474 | * @return {Element} selectedCellElement 475 | */ 476 | selectedElement(id) { 477 | return new Promise((resolve, reject) => { 478 | const flickityIndex = this._getFlickityIndex(id); 479 | 480 | if (flickityIndex < 0) { 481 | return reject(`Instance ${id} not found.`); 482 | } else { 483 | // Return the selected element 484 | return resolve(this.instances[flickityIndex].instance.selectedElement); 485 | } 486 | }); 487 | } 488 | 489 | 490 | /** 491 | * Get an array of all cells 492 | * 493 | * @param {String} id 494 | * @return {Array} cells 495 | */ 496 | cells(id) { 497 | return new Promise((resolve, reject) => { 498 | const flickityIndex = this._getFlickityIndex(id); 499 | 500 | if (flickityIndex < 0) { 501 | return reject(`Instance ${id} not found.`); 502 | } else { 503 | // Return the array of cells 504 | return resolve(this.instances[flickityIndex].instance.cells); 505 | } 506 | }); 507 | } 508 | 509 | 510 | 511 | // 512 | // Helper methods 513 | // 514 | 515 | 516 | /** 517 | * Find the index for a Flickity instance 518 | * 519 | * @param {String} id 520 | * @return {Integer} flickityIndex 521 | */ 522 | _getFlickityIndex(id) { 523 | let foundIndex = -1; 524 | 525 | // Verify at least one instance exists 526 | if (this.instances.length > 0) { 527 | 528 | // Check the ID of each instance 529 | this.instances.forEach((instance, index) => { 530 | 531 | // If it matches our ID, set the index 532 | if (instance.id === id) { 533 | foundIndex = index; 534 | } 535 | 536 | }); 537 | 538 | } 539 | 540 | return foundIndex; 541 | } 542 | 543 | 544 | /** 545 | * Bind all events for a new instance 546 | * 547 | * @param {String} id 548 | * @return {Bool} isFinished 549 | */ 550 | _bindEvents(id) { 551 | return new Promise((resolve, reject) => { 552 | const flickityIndex = this._getFlickityIndex(id); 553 | 554 | if (flickityIndex < 0) { 555 | return reject(); 556 | } 557 | 558 | const ID = this.instances[flickityIndex].id; 559 | 560 | this.instances[flickityIndex].instance.on('select', () => { 561 | this.$rootScope.$emit(`Flickity:${ID}:select`, this.instances[flickityIndex]); 562 | }); 563 | 564 | this.instances[flickityIndex].instance.on('settle', () => { 565 | this.$rootScope.$emit(`Flickity:${ID}:settle`, 566 | this.instances[flickityIndex]); 567 | }); 568 | 569 | this.instances[flickityIndex].instance.on('scroll', (progress, positionX) => { 570 | this.$rootScope.$emit(`Flickity:${ID}:scroll`, { 571 | progress: progress, 572 | positionX: positionX, 573 | }); 574 | }); 575 | 576 | this.instances[flickityIndex].instance.on('dragStart', (event, pointer) => { 577 | this.$rootScope.$emit(`Flickity:${ID}:dragStart`, { 578 | event: event, 579 | pointer: pointer, 580 | }); 581 | }); 582 | 583 | this.instances[flickityIndex].instance.on('dragMove', (event, pointer, moveVector) => { 584 | this.$rootScope.$emit(`Flickity:${ID}:dragMove`, { 585 | event: event, 586 | pointer: pointer, 587 | moveVector: moveVector, 588 | }); 589 | }); 590 | 591 | this.instances[flickityIndex].instance.on('dragEnd', (event, pointer) => { 592 | this.$rootScope.$emit(`Flickity:${ID}:dragEnd`, { 593 | event: event, 594 | pointer: pointer, 595 | }); 596 | }); 597 | 598 | this.instances[flickityIndex].instance.on('pointerDown', (event, pointer) => { 599 | this.$rootScope.$emit(`Flickity:${ID}:pointerDown`, { 600 | event: event, 601 | pointer: pointer, 602 | }); 603 | }); 604 | 605 | this.instances[flickityIndex].instance.on('pointerMove',(event, pointer, 606 | moveVector) => { 607 | this.$rootScope.$emit(`Flickity:${ID}:pointerMove`, { 608 | event: event, 609 | pointer: pointer, 610 | moveVector: moveVector, 611 | }); 612 | }); 613 | 614 | this.instances[flickityIndex].instance.on('pointerUp', (event, pointer) => { 615 | this.$rootScope.$emit(`Flickity:${ID}:pointerUp`, { 616 | event: event, 617 | pointer: pointer, 618 | }); 619 | }); 620 | 621 | this.instances[flickityIndex].instance.on('staticClick', (event, pointer, cellElement, 622 | cellIndex) => { 623 | this.$rootScope.$emit(`Flickity:${ID}:staticClick`, { 624 | event: event, 625 | pointer: pointer, 626 | cellElement: cellElement, 627 | cellIndex: cellIndex, 628 | }); 629 | }); 630 | 631 | this.instances[flickityIndex].instance.on('lazyLoad', (event, cellElement) => { 632 | this.$rootScope.$emit(`Flickity:${ID}:lazyLoad`, { 633 | event: event, 634 | cellElement: cellElement, 635 | }); 636 | }); 637 | 638 | return resolve(true); 639 | }); 640 | 641 | } 642 | 643 | 644 | /** 645 | * Find an object within an array by ID 646 | * 647 | * @param {Array} source 648 | * @param {String} id 649 | * @return {Object} match 650 | */ 651 | _findObjectById(source, id) { 652 | return source.filter((object) => { 653 | return object.id === id; 654 | })[0]; 655 | } 656 | 657 | } 658 | 659 | -------------------------------------------------------------------------------- /src/flickity.service.spec.js: -------------------------------------------------------------------------------- 1 | describe('FlickityService', () => { 2 | let $compile; 3 | let $rootScope; 4 | 5 | // Include the module 6 | beforeEach(angular.mock.module('bc.Flickity')); 7 | 8 | beforeEach(function() { 9 | 10 | inject(function($compile, $rootScope, FlickityService) { 11 | this.$compile = $compile; 12 | this.$rootScope = $rootScope; 13 | this.FlickityService = FlickityService; 14 | 15 | this.$scope = this.$rootScope.$new(); 16 | this.$scope.slides = [ 17 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide1.jpg', 18 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide2.jpg', 19 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide3.jpg', 20 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide4.jpg', 21 | ]; 22 | this.$scope.extraSlide = 23 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide5.jpg'; 24 | }); 25 | 26 | }); 27 | 28 | 29 | beforeEach(function() { 30 | this.compileDirective = function(template) { 31 | this.element = this.$compile(template)(this.$scope); 32 | this.$scope.$digest(); 33 | }; 34 | }); 35 | 36 | 37 | afterEach(function() { 38 | if (this.element) { 39 | this.element.remove(); 40 | } 41 | }); 42 | 43 | describe(`create()`, () => { 44 | 45 | it(`should instantiate a Flickity instance with a custom ID`, function(done) { 46 | const template = angular.element(` 47 |
48 |
49 | 50 |
51 |
52 | `); 53 | this.compileDirective(template); 54 | const customID = 'create'; 55 | 56 | this.FlickityService.create(this.element[0], customID).then((instance) => { 57 | const actual = instance.id; 58 | const expected = customID; 59 | 60 | expect(actual).toEqual(expected); 61 | done(); 62 | }); 63 | }); 64 | 65 | it(`should instantiate a Flickity instance with the element's ID`, function(done) { 66 | const template = angular.element(` 67 |
68 |
69 | 70 |
71 |
72 | `); 73 | this.compileDirective(template); 74 | 75 | this.FlickityService.create(this.element[0]).then((instance) => { 76 | const actual = instance.id; 77 | const expected = 'js_demo'; 78 | 79 | expect(actual).toEqual(expected); 80 | done(); 81 | }); 82 | }); 83 | 84 | it(`should instantiate a Flickity instance with a dynamic ID`, function(done) { 85 | const template = angular.element(` 86 |
87 |
88 | 89 |
90 |
91 | `); 92 | this.compileDirective(template); 93 | 94 | this.FlickityService.create(this.element[0]).then((instance) => { 95 | const actual = instance.id; 96 | const expected = 1; 97 | 98 | expect(actual).toEqual(expected); 99 | done(); 100 | }); 101 | }); 102 | 103 | it(`should reject creation if the ID is already in use`, function(done) { 104 | const template = angular.element(` 105 |
106 |
107 | 108 |
109 |
110 | `); 111 | this.compileDirective(template); 112 | const testId = 'createReject'; 113 | 114 | this.FlickityService.create(this.element[0], testId).then((instance) => { 115 | const actual = instance.id; 116 | const expected = testId; 117 | 118 | expect(actual).toEqual(expected); 119 | 120 | this.FlickityService.create(this.element[0], testId).catch((error) => { 121 | const actual = error; 122 | const expected = `This ID is already in use.`; 123 | 124 | expect(actual).toEqual(expected); 125 | done(); 126 | }); 127 | }); 128 | }); 129 | 130 | }); 131 | 132 | 133 | describe(`destroy()`, () => { 134 | 135 | it(`should destroy a Flickity instance`, function(done) { 136 | const template = angular.element(` 137 |
138 |
139 | 140 |
141 |
142 | `); 143 | this.compileDirective(template); 144 | const testId = 'destroy'; 145 | 146 | this.FlickityService.create(this.element[0], testId).then(() => { 147 | this.FlickityService.getAll().then((results) => { 148 | const actual = results.length; 149 | const expected = 1; 150 | 151 | // Verify an instance was created 152 | expect(actual).toEqual(expected); 153 | 154 | this.FlickityService.destroy(testId).then(() => { 155 | this.FlickityService.getAll().then((results) => { 156 | const actual = results.length; 157 | const expected = 0; 158 | 159 | // Verify the instance was removed 160 | expect(actual).toEqual(expected); 161 | done(); 162 | }); 163 | }); 164 | }); 165 | }); 166 | }); 167 | 168 | it(`should reject if destroying an instance that doesn't exist`, function(done) { 169 | const template = angular.element(` 170 |
171 |
172 | 173 |
174 |
175 | `); 176 | this.compileDirective(template); 177 | const testId = 'destroyReject'; 178 | const fakeId = 'destroyRejectFake'; 179 | 180 | this.FlickityService.create(this.element[0], testId).then(() => { 181 | this.FlickityService.getAll().then((results) => { 182 | const actual = results.length; 183 | const expected = 1; 184 | 185 | expect(actual).toEqual(expected); 186 | 187 | this.FlickityService.destroy(fakeId).catch((error) => { 188 | const actual = error; 189 | const expected = `Instance ${fakeId} not found.`; 190 | 191 | expect(actual).toEqual(expected); 192 | done(); 193 | }); 194 | }); 195 | }); 196 | }); 197 | 198 | }); 199 | 200 | 201 | describe(`getAll()`, () => { 202 | 203 | it(`should return all instances`, function(done) { 204 | const template = angular.element(` 205 |
206 |
207 | 208 |
209 |
210 | `); 211 | this.compileDirective(template); 212 | const idOne = 'getAllOne'; 213 | const idTwo = 'getAllTwo'; 214 | 215 | this.FlickityService.create(this.element[0], idOne).then(() => { 216 | this.FlickityService.getAll().then((results) => { 217 | const actual = results.length; 218 | const expected = 1; 219 | 220 | // Verify a singe instance was returned 221 | expect(actual).toEqual(expected); 222 | 223 | this.FlickityService.create(this.element[0], idTwo).then(() => { 224 | this.FlickityService.getAll().then((results) => { 225 | const actual = results.length; 226 | const expected = 2; 227 | 228 | // Verify both instances are returned 229 | expect(actual).toEqual(expected); 230 | done(); 231 | }); 232 | }); 233 | }); 234 | }); 235 | }); 236 | 237 | }); 238 | 239 | 240 | describe(`next()`, () => { 241 | 242 | it(`should move to the next slide`, function(done) { 243 | const template = angular.element(` 244 |
245 |
246 | 247 |
248 |
249 | `); 250 | this.compileDirective(template); 251 | const testId = 'next'; 252 | 253 | this.FlickityService.create(this.element[0], testId).then(() => { 254 | this.FlickityService.selectedIndex(testId).then((result) => { 255 | const actual = result; 256 | const expected = 0; 257 | 258 | // Verify the index '0' is selected 259 | expect(actual).toEqual(expected); 260 | 261 | this.FlickityService.next(testId).then(() => { 262 | this.FlickityService.selectedIndex(testId).then((result) => { 263 | const actual = result; 264 | const expected = 1; 265 | 266 | // Verify the index '1' is selected 267 | expect(actual).toEqual(expected); 268 | done(); 269 | }); 270 | }); 271 | }); 272 | }); 273 | }); 274 | 275 | it(`should wrap when moving to the next slide`, function(done) { 276 | const template = angular.element(` 277 |
278 |
279 | 280 |
281 |
282 | `); 283 | this.compileDirective(template); 284 | const testId = 'nextWrap'; 285 | const options = { 286 | initialIndex: 3, 287 | }; 288 | 289 | this.FlickityService.create(this.element[0], testId, options).then(() => { 290 | this.FlickityService.selectedIndex(testId).then((result) => { 291 | const actual = result; 292 | const expected = 3; 293 | 294 | // Verify the index '3' is selected 295 | expect(actual).toEqual(expected); 296 | 297 | this.FlickityService.next(testId, true).then(() => { 298 | this.FlickityService.selectedIndex(testId).then((result) => { 299 | const actual = result; 300 | const expected = 0; 301 | 302 | // Verify the index '0' is selected 303 | expect(actual).toEqual(expected); 304 | done(); 305 | }); 306 | }); 307 | }); 308 | }); 309 | }); 310 | 311 | }); 312 | 313 | 314 | describe(`previous()`, () => { 315 | 316 | it(`should move to the previous slide`, function(done) { 317 | const template = angular.element(` 318 |
319 |
320 | 321 |
322 |
323 | `); 324 | this.compileDirective(template); 325 | const testId = 'previous'; 326 | const options = { 327 | initialIndex: 2, 328 | }; 329 | 330 | this.FlickityService.create(this.element[0], testId, options).then(() => { 331 | this.FlickityService.selectedIndex(testId).then((result) => { 332 | const actual = result; 333 | const expected = 2; 334 | 335 | // Verify the index '0' is selected 336 | expect(actual).toEqual(expected); 337 | 338 | this.FlickityService.previous(testId).then(() => { 339 | this.FlickityService.selectedIndex(testId).then((result) => { 340 | const actual = result; 341 | const expected = 1; 342 | 343 | // Verify the index '1' is selected 344 | expect(actual).toEqual(expected); 345 | done(); 346 | }); 347 | }); 348 | }); 349 | }); 350 | }); 351 | 352 | it(`should wrap when moving to the previous slide`, function(done) { 353 | const template = angular.element(` 354 |
355 |
356 | 357 |
358 |
359 | `); 360 | this.compileDirective(template); 361 | const testId = 'previousWrap'; 362 | 363 | this.FlickityService.create(this.element[0], testId).then(() => { 364 | this.FlickityService.selectedIndex(testId).then((result) => { 365 | const actual = result; 366 | const expected = 0; 367 | 368 | // Verify the index '0' is selected 369 | expect(actual).toEqual(expected); 370 | 371 | this.FlickityService.previous(testId, true).then(() => { 372 | this.FlickityService.selectedIndex(testId).then((result) => { 373 | const actual = result; 374 | const expected = 3; 375 | 376 | // Verify the index '3' is selected 377 | expect(actual).toEqual(expected); 378 | done(); 379 | }); 380 | }); 381 | }); 382 | }); 383 | }); 384 | 385 | }); 386 | 387 | 388 | describe(`select()`, () => { 389 | 390 | it(`should select a slide`, function(done) { 391 | const template = angular.element(` 392 |
393 |
394 | 395 |
396 |
397 | `); 398 | this.compileDirective(template); 399 | const testId = 'select'; 400 | const newIndex = 2; 401 | 402 | this.FlickityService.create(this.element[0], testId).then(() => { 403 | this.FlickityService.selectedIndex(testId).then((result) => { 404 | const actual = result; 405 | const expected = 0; 406 | 407 | // Verify the index '0' is selected 408 | expect(actual).toEqual(expected); 409 | 410 | this.FlickityService.select(testId, newIndex).then(() => { 411 | this.FlickityService.selectedIndex(testId).then((result) => { 412 | const actual = result; 413 | const expected = newIndex; 414 | 415 | // Verify the index '2' is selected 416 | expect(actual).toEqual(expected); 417 | done(); 418 | }); 419 | }); 420 | }); 421 | }); 422 | }); 423 | 424 | }); 425 | 426 | 427 | describe(`selectCell()`, () => { 428 | 429 | it(`should select a slide with a selector string`, function(done) { 430 | const template = angular.element(` 431 |
432 |
436 | 437 |
438 |
439 | `); 440 | this.compileDirective(template); 441 | const testId = 'selectCell'; 442 | 443 | this.FlickityService.create(this.element[0], testId).then(() => { 444 | this.FlickityService.selectedIndex(testId).then((result) => { 445 | const actual = result; 446 | const expected = 0; 447 | 448 | // Verify the index '0' is selected 449 | expect(actual).toEqual(expected); 450 | 451 | this.FlickityService.selectCell(testId, '.slide3').then(() => { 452 | this.FlickityService.selectedIndex(testId).then((result) => { 453 | const actual = result; 454 | const expected = 3; 455 | 456 | // Verify the index '3' is selected 457 | expect(actual).toEqual(expected); 458 | done(); 459 | }); 460 | }); 461 | }); 462 | }); 463 | }); 464 | 465 | }); 466 | 467 | 468 | describe(`selectedIndex()`, () => { 469 | 470 | it(`should return the index of the current slide`, function(done) { 471 | const template = angular.element(` 472 |
473 |
474 | 475 |
476 |
477 | `); 478 | this.compileDirective(template); 479 | const testId = 'selectedIndex'; 480 | const options = { 481 | initialIndex: 1, 482 | }; 483 | 484 | this.FlickityService.create(this.element[0], testId, options).then(() => { 485 | this.FlickityService.selectedIndex(testId).then((result) => { 486 | const actual = result; 487 | const expected = 1; 488 | 489 | // Verify the index '1' is selected 490 | expect(actual).toEqual(expected); 491 | 492 | this.FlickityService.next(testId).then(() => { 493 | this.FlickityService.selectedIndex(testId).then((result) => { 494 | const actual = result; 495 | const expected = 2; 496 | 497 | // Verify the index '2' is selected 498 | expect(actual).toEqual(expected); 499 | done(); 500 | }); 501 | }); 502 | }); 503 | }); 504 | }); 505 | 506 | }); 507 | 508 | 509 | describe(`resize()`, () => { 510 | 511 | it(`should call resize() on the current instance`, function(done) { 512 | const template = angular.element(` 513 |
514 |
515 | 516 |
517 |
518 | `); 519 | this.compileDirective(template); 520 | const customID = 'resize'; 521 | 522 | this.FlickityService.create(this.element[0], customID).then((instance) => { 523 | const flickityInstance = instance.instance; 524 | const actual = instance.id; 525 | const expected = customID; 526 | 527 | expect(actual).toEqual(expected); 528 | 529 | spyOn(flickityInstance, 'resize'); 530 | 531 | this.FlickityService.resize(customID).then((result) => { 532 | expect(flickityInstance.resize).toHaveBeenCalled(); 533 | done(); 534 | }); 535 | }); 536 | 537 | }); 538 | 539 | }); 540 | 541 | 542 | describe(`reposition()`, () => { 543 | 544 | it(`should call reposition() on the current instance`, function(done) { 545 | const template = angular.element(` 546 |
547 |
548 | 549 |
550 |
551 | `); 552 | this.compileDirective(template); 553 | const customID = 'reposition'; 554 | 555 | this.FlickityService.create(this.element[0], customID).then((instance) => { 556 | const flickityInstance = instance.instance; 557 | const actual = instance.id; 558 | const expected = customID; 559 | 560 | expect(actual).toEqual(expected); 561 | 562 | spyOn(flickityInstance, 'reposition'); 563 | 564 | this.FlickityService.reposition(customID).then((result) => { 565 | expect(flickityInstance.reposition).toHaveBeenCalled(); 566 | done(); 567 | }); 568 | }); 569 | 570 | }); 571 | 572 | }); 573 | 574 | 575 | describe(`reloadCells()`, () => { 576 | 577 | it(`should call reloadCells() on the current instance`, function(done) { 578 | const template = angular.element(` 579 |
580 |
581 | 582 |
583 |
584 | `); 585 | this.compileDirective(template); 586 | const customID = 'reloadCells'; 587 | 588 | this.FlickityService.create(this.element[0], customID).then((instance) => { 589 | const flickityInstance = instance.instance; 590 | const actual = instance.id; 591 | const expected = customID; 592 | 593 | expect(actual).toEqual(expected); 594 | 595 | spyOn(flickityInstance, 'reloadCells'); 596 | 597 | this.FlickityService.reloadCells(customID).then((result) => { 598 | expect(flickityInstance.reloadCells).toHaveBeenCalled(); 599 | done(); 600 | }); 601 | }); 602 | 603 | }); 604 | 605 | }); 606 | 607 | 608 | describe(`get()`, () => { 609 | 610 | it(`should return the instance with the matching ID`, function(done) { 611 | const template = angular.element(` 612 |
613 |
614 | 615 |
616 |
617 | `); 618 | this.compileDirective(template); 619 | const customID = 'get'; 620 | 621 | this.FlickityService.create(this.element[0], customID).then((instance) => { 622 | const actual = instance.id; 623 | const expected = customID; 624 | 625 | expect(actual).toEqual(expected); 626 | 627 | this.FlickityService.get(customID).then((result) => { 628 | const actual = result.id; 629 | const expected = customID; 630 | 631 | expect(actual).toEqual(expected); 632 | done(); 633 | }); 634 | }); 635 | }); 636 | 637 | }); 638 | 639 | 640 | describe(`getFirst()`, () => { 641 | 642 | it(`should return the first instance`, function(done) { 643 | const template = angular.element(` 644 |
645 |
646 | 647 |
648 |
649 | `); 650 | this.compileDirective(template); 651 | const customID = 'getFirst'; 652 | const customIDTwo = 'getFirstSecond'; 653 | 654 | this.FlickityService.create(this.element[0], customID).then((instance) => { 655 | const actual = instance.id; 656 | const expected = customID; 657 | 658 | expect(actual).toEqual(expected); 659 | 660 | this.FlickityService.create(this.element[0], customIDTwo).then((secondInstance) => { 661 | const actual = secondInstance.id; 662 | const expected = customIDTwo; 663 | 664 | expect(actual).toEqual(expected); 665 | 666 | this.FlickityService.getFirst().then((result) => { 667 | const actual = result.id; 668 | const expected = customID; 669 | 670 | expect(actual).toEqual(expected); 671 | done(); 672 | }); 673 | }); 674 | }); 675 | }); 676 | 677 | }); 678 | 679 | 680 | describe(`getByElement()`, () => { 681 | 682 | it(`should return the instance attached to the element`, function(done) { 683 | const template = angular.element(` 684 |
685 |
686 | 687 |
688 |
689 | `); 690 | this.compileDirective(template); 691 | const customID = 'getByElement'; 692 | 693 | this.FlickityService.create(this.element[0], customID).then((created) => { 694 | this.FlickityService.getByElement(this.element[0]).then((instance) => { 695 | expect(instance).toEqual(jasmine.any(Object)); 696 | done(); 697 | }); 698 | }); 699 | }); 700 | 701 | }); 702 | 703 | 704 | describe(`prepend()`, () => { 705 | 706 | it(`should call prepend() on the current instance with the new elements`, function(done) { 707 | const template = angular.element(` 708 |
709 |
710 | 711 |
712 |
713 | `); 714 | this.compileDirective(template); 715 | 716 | const slide = angular.element(` 717 |
718 | 719 |
720 | `); 721 | 722 | this.newElement = this.$compile(slide)(this.$scope); 723 | this.$scope.$digest(); 724 | 725 | const customID = 'prepend'; 726 | 727 | this.FlickityService.create(this.element[0], customID).then((instance) => { 728 | const flickityInstance = instance.instance; 729 | const actual = instance.id; 730 | const expected = customID; 731 | 732 | expect(actual).toEqual(expected); 733 | 734 | spyOn(flickityInstance, 'prepend'); 735 | 736 | this.FlickityService.prepend(customID, this.newElement).then(() => { 737 | expect(flickityInstance.prepend.calls.argsFor(0)[0]).toEqual(this.newElement); 738 | done(); 739 | }); 740 | }); 741 | }); 742 | 743 | }); 744 | 745 | 746 | describe(`append()`, () => { 747 | 748 | it(`should call append() on the current instance with the new elements`, function(done) { 749 | const template = angular.element(` 750 |
751 |
752 | 753 |
754 |
755 | `); 756 | this.compileDirective(template); 757 | 758 | const slide = angular.element(` 759 |
760 | 761 |
762 | `); 763 | 764 | this.newElement = this.$compile(slide)(this.$scope); 765 | this.$scope.$digest(); 766 | 767 | const customID = 'append'; 768 | 769 | this.FlickityService.create(this.element[0], customID).then((instance) => { 770 | const flickityInstance = instance.instance; 771 | const actual = instance.id; 772 | const expected = customID; 773 | 774 | expect(actual).toEqual(expected); 775 | 776 | spyOn(flickityInstance, 'append'); 777 | 778 | this.FlickityService.append(customID, this.newElement).then(() => { 779 | expect(flickityInstance.append.calls.argsFor(0)[0]).toEqual(this.newElement); 780 | done(); 781 | }); 782 | }); 783 | }); 784 | 785 | }); 786 | 787 | 788 | describe(`insert()`, () => { 789 | 790 | it(`should call insert() on the current instance with the new elements`, function(done) { 791 | const template = angular.element(` 792 |
793 |
794 | 795 |
796 |
797 | `); 798 | this.compileDirective(template); 799 | 800 | const slide = angular.element(` 801 |
802 | 803 |
804 | `); 805 | 806 | this.newElement = this.$compile(slide)(this.$scope); 807 | this.$scope.$digest(); 808 | 809 | const customID = 'insert'; 810 | const insertIndex = 3; 811 | 812 | this.FlickityService.create(this.element[0], customID).then((instance) => { 813 | const flickityInstance = instance.instance; 814 | const actual = instance.id; 815 | const expected = customID; 816 | 817 | expect(actual).toEqual(expected); 818 | 819 | spyOn(flickityInstance, 'insert'); 820 | 821 | this.FlickityService.insert(customID, this.newElement, insertIndex).then(() => { 822 | expect(flickityInstance.insert.calls.argsFor(0)[0]).toEqual(this.newElement); 823 | done(); 824 | }); 825 | }); 826 | }); 827 | 828 | }); 829 | 830 | 831 | describe(`getCellElements()`, () => { 832 | 833 | it(`should call getCellElements() on the current instance`, function(done) { 834 | const template = angular.element(` 835 |
836 |
837 | 838 |
839 |
840 | `); 841 | this.compileDirective(template); 842 | const customID = 'getCellElements'; 843 | 844 | this.FlickityService.create(this.element[0], customID).then((instance) => { 845 | const flickityInstance = instance.instance; 846 | const actual = instance.id; 847 | const expected = customID; 848 | 849 | expect(actual).toEqual(expected); 850 | 851 | this.FlickityService.getCellElements(customID).then((cells) => { 852 | const actual = cells.length; 853 | const expected = 4; 854 | 855 | expect(actual).toEqual(expected); 856 | done(); 857 | }); 858 | }); 859 | }); 860 | 861 | }); 862 | 863 | 864 | describe(`remove()`, () => { 865 | 866 | it(`should call remove() on the current instance with the element`, function(done) { 867 | const template = angular.element(` 868 |
869 |
870 | 871 |
872 |
873 | `); 874 | this.compileDirective(template); 875 | 876 | const slide = angular.element(` 877 |
878 | 879 |
880 | `); 881 | 882 | this.newElement = this.$compile(slide)(this.$scope); 883 | this.$scope.$digest(); 884 | 885 | const customID = 'remove'; 886 | 887 | this.FlickityService.create(this.element[0], customID).then((instance) => { 888 | const flickityInstance = instance.instance; 889 | const actual = instance.id; 890 | const expected = customID; 891 | 892 | expect(actual).toEqual(expected); 893 | 894 | spyOn(flickityInstance, 'remove'); 895 | 896 | this.FlickityService.remove(customID, this.newElement).then(() => { 897 | expect(flickityInstance.remove.calls.argsFor(0)[0]).toEqual(this.newElement); 898 | done(); 899 | }); 900 | }); 901 | }); 902 | 903 | }); 904 | 905 | 906 | describe(`selectedElement()`, () => { 907 | 908 | it(`should return the currently selected element`, function(done) { 909 | const template = angular.element(` 910 |
911 |
912 | 913 |
914 |
915 | `); 916 | this.compileDirective(template); 917 | const customID = 'selectedElement'; 918 | 919 | this.FlickityService.create(this.element[0], customID).then((instance) => { 920 | const flickityInstance = instance.instance; 921 | const actual = instance.id; 922 | const expected = customID; 923 | 924 | expect(actual).toEqual(expected); 925 | 926 | this.FlickityService.selectedElement(customID).then((element) => { 927 | expect(element.outerHTML).toContain('slide1.jpg'); 928 | done(); 929 | }); 930 | }); 931 | }); 932 | 933 | }); 934 | 935 | 936 | describe(`cells()`, () => { 937 | 938 | it(`should return an array containing all cells`, function(done) { 939 | const template = angular.element(` 940 |
941 |
942 | 943 |
944 |
945 | `); 946 | this.compileDirective(template); 947 | const customID = 'cells'; 948 | 949 | this.FlickityService.create(this.element[0], customID).then((instance) => { 950 | const flickityInstance = instance.instance; 951 | const actual = instance.id; 952 | const expected = customID; 953 | 954 | expect(actual).toEqual(expected); 955 | 956 | this.FlickityService.cells(customID).then((cells) => { 957 | const actual = cells.length; 958 | const expected = 4; 959 | 960 | expect(actual).toEqual(expected); 961 | done(); 962 | }); 963 | }); 964 | }); 965 | 966 | }); 967 | 968 | 969 | describe(`_getFlickityIndex()`, () => { 970 | 971 | it(`should return the index for a Flickity instance`, function(done) { 972 | const template = angular.element(` 973 |
974 |
975 | 976 |
977 |
978 | `); 979 | this.compileDirective(template); 980 | const customID = '_getFlickityIndex'; 981 | const customIDTwo = '_getFlickityIndex2'; 982 | 983 | this.FlickityService.create(this.element[0], customID).then(() => { 984 | const actual = this.FlickityService._getFlickityIndex(customID); 985 | const expected = 0; 986 | 987 | expect(actual).toEqual(expected); 988 | 989 | this.FlickityService.create(this.element[0], customIDTwo).then((instance) => { 990 | const actual = this.FlickityService._getFlickityIndex(customIDTwo); 991 | const expected = 1; 992 | 993 | expect(actual).toEqual(expected); 994 | done(); 995 | }); 996 | }); 997 | }); 998 | 999 | }); 1000 | 1001 | 1002 | describe(`_findObjectById()`, () => { 1003 | 1004 | it(`should return an object with a matching ID if one exists`, function(done) { 1005 | const objectsArray = [ 1006 | { 1007 | id: 'foo', 1008 | attr: 'object1', 1009 | }, 1010 | { 1011 | id: 'bar', 1012 | attr: 'object2', 1013 | }, 1014 | { 1015 | id: 'baz', 1016 | attr: 'object3', 1017 | }, 1018 | ]; 1019 | 1020 | const object = this.FlickityService._findObjectById(objectsArray, 'foo'); 1021 | const actual = object.attr; 1022 | const expected = 'object1'; 1023 | expect(actual).toEqual(expected); 1024 | 1025 | const object2 = this.FlickityService._findObjectById(objectsArray, 'baz'); 1026 | const actual2 = object2.attr; 1027 | const expected2 = 'object3'; 1028 | expect(actual2).toEqual(expected2); 1029 | 1030 | done(); 1031 | }); 1032 | 1033 | }); 1034 | 1035 | 1036 | }); 1037 | 1038 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { FlickityConfigProvider } from './flickity.provider' 2 | import { FlickityService } from './flickity.service'; 3 | import { FlickityDirective } from './flickity.directive'; 4 | import { FlickityNextDirective } from './next/next.directive'; 5 | import { FlickityPreviousDirective } from './previous/previous.directive'; 6 | 7 | angular.module('bc.Flickity', []) 8 | .provider('FlickityConfig', FlickityConfigProvider) 9 | .service('FlickityService', FlickityService) 10 | .directive('bcFlickity', FlickityDirective) 11 | .directive('bcFlickityNext', FlickityNextDirective) 12 | .directive('bcFlickityPrevious', FlickityPreviousDirective) 13 | ; 14 | 15 | -------------------------------------------------------------------------------- /src/next/next.controller.js: -------------------------------------------------------------------------------- 1 | export class NextController { 2 | 3 | constructor( 4 | $log, $q, $timeout, 5 | FlickityConfig, FlickityService 6 | ) { 7 | 'ngInject'; 8 | 9 | this.$log = $log; 10 | this.$q = $q; 11 | this.$timeout = $timeout; 12 | this.FlickityConfig = FlickityConfig; 13 | this.FlickityService = FlickityService; 14 | 15 | 16 | this._activate(); 17 | 18 | } 19 | 20 | 21 | 22 | 23 | _activate() { 24 | // Assign wrap around or fall back to a default 25 | if (typeof this.bcFlickityNext !== 'undefined') { 26 | this.wrapAround = this.bcFlickityNext; 27 | } else if (typeof this.FlickityConfig.wrapAround !== 'undefined') { 28 | this.wrapAround = this.FlickityConfig.wrapAround; 29 | } else { 30 | this.wrapAround = false; 31 | } 32 | this.flickityId = null; 33 | 34 | // Make sure we have an ID 35 | this._setId(); 36 | } 37 | 38 | 39 | /** 40 | * Set ID to what is defined, fallback to first instance 41 | * 42 | * @return {String} flickityId 43 | */ 44 | _setId() { 45 | return new Promise((resolve, reject) => { 46 | this.$timeout(() => { 47 | 48 | if (this.bcFlickityId) { 49 | this.flickityId = this.bcFlickityId; 50 | return resolve(this.flickityId); 51 | } else { 52 | this.$timeout(() => { 53 | this.FlickityService.getFirst() 54 | .then((instance) => { 55 | this.flickityId = instance.id; 56 | return resolve(this.flickityId); 57 | }) 58 | .catch((error) => { 59 | this.$log.warn(error); 60 | return reject(error); 61 | }) 62 | ; 63 | }); 64 | } 65 | 66 | }); 67 | }); 68 | } 69 | 70 | 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/next/next.controller.spec.js: -------------------------------------------------------------------------------- 1 | describe('NextController', () => { 2 | let $compile; 3 | let $rootScope; 4 | 5 | beforeEach(angular.mock.module('bc.Flickity')); 6 | 7 | beforeEach(function() { 8 | 9 | inject(function($compile, $rootScope, $timeout, FlickityService) { 10 | this.$compile = $compile; 11 | this.$rootScope = $rootScope; 12 | this.$timeout = $timeout; 13 | this.FlickityService = FlickityService; 14 | 15 | this.$scope = this.$rootScope.$new(); 16 | this.$scope.slides = [ 17 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide1.jpg', 18 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide2.jpg', 19 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide3.jpg', 20 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide4.jpg', 21 | ]; 22 | this.$scope.flickityOptions = { 23 | prevNextButtons: false, 24 | wrapAround: false, 25 | }; 26 | }); 27 | 28 | }); 29 | 30 | beforeEach(function() { 31 | this.compileDirective = function(template) { 32 | const angularElement = angular.element(template); 33 | this.element = this.$compile(angularElement)(this.$scope); 34 | this.$scope.$digest(); 35 | }; 36 | }); 37 | 38 | afterEach(function() { 39 | if (this.element) { 40 | this.element.remove(); 41 | } 42 | }); 43 | 44 | 45 | 46 | describe(`this.flickityId`, () => { 47 | 48 | it(`should be set to the passed in ID`, function(done) { 49 | const template = ` 50 |
51 |
52 |
53 | 54 |
55 |
56 | 59 |
60 | `; 61 | this.compileDirective(template); 62 | const directiveElement = angular.element(this.element.find('button')); 63 | this.NextController = directiveElement.controller('bcFlickityNext'); 64 | 65 | // We must wrap in a $timeout since create() is also in a $timeout 66 | this.$timeout(() => { 67 | const actual = this.NextController.flickityId; 68 | const expected = 'buttonId'; 69 | 70 | expect(actual).toEqual(expected); 71 | done(); 72 | }); 73 | 74 | this.$timeout.flush(); 75 | this.$timeout.verifyNoPendingTasks(); 76 | }); 77 | 78 | it(`should be set to the ID of the first instance`, function(done) { 79 | const template = ` 80 |
81 |
82 |
83 | 84 |
85 |
86 | 89 |
90 | `; 91 | this.compileDirective(template); 92 | const directiveElement = angular.element(this.element.find('button')); 93 | const DELAY = 300; 94 | this.NextController = directiveElement.controller('bcFlickityNext'); 95 | 96 | this.$timeout(() => { 97 | this.FlickityService.getFirst().then((instance) => { 98 | const actual = this.NextController.flickityId; 99 | const expected = instance.id; 100 | 101 | expect(actual).toEqual(expected); 102 | done(); 103 | }); 104 | }, DELAY); 105 | 106 | this.$timeout.flush(); 107 | this.$timeout.verifyNoPendingTasks(); 108 | }); 109 | 110 | }); 111 | 112 | }); 113 | 114 | -------------------------------------------------------------------------------- /src/next/next.directive.js: -------------------------------------------------------------------------------- 1 | import { NextController } from './next.controller'; 2 | 3 | export function FlickityNextDirective( 4 | $log, $timeout, $rootScope, 5 | FlickityConfig, FlickityService 6 | ) { 7 | 'ngInject'; 8 | 9 | const directive = { 10 | restrict: 'A', 11 | scope: {}, 12 | bindToController: { 13 | bcFlickityNext: '=?', 14 | bcFlickityId: '@?', 15 | }, 16 | compile: () => { 17 | return { 18 | pre: preLinkFunction, 19 | }; 20 | }, 21 | controller: NextController, 22 | controllerAs: 'vm', 23 | }; 24 | 25 | return directive; 26 | 27 | 28 | 29 | 30 | /** 31 | * Pre Link 32 | */ 33 | function preLinkFunction( 34 | $scope, $element, $attrs, $controller 35 | ) { 36 | 'ngInject'; 37 | 38 | // Get the ID 39 | const ID = $controller.flickityId; 40 | 41 | // Define the broadcast names to listen for 42 | const selectEvent = `Flickity:${ID}:cellSelect`; 43 | const settleEvent = `Flickity:${ID}:settle`; 44 | 45 | // Listen 46 | const cellSelect = $rootScope.$on(selectEvent, (event, data) => { 47 | _disableButtonIfNeeded(data.instance.slides.length, data.instance.selectedIndex + 1); 48 | }); 49 | const settle = $rootScope.$on(settleEvent, (event, data) => { 50 | _disableButtonIfNeeded(data.instance.slides.length, data.instance.selectedIndex + 1); 51 | }); 52 | 53 | 54 | $element.on('click', () => { 55 | 56 | // Move to the next cell 57 | FlickityService.next($controller.flickityId, $controller.wrapAround) 58 | .then((instance) => { 59 | _disableButtonIfNeeded(instance.instance.selectedIndex); 60 | }) 61 | ; 62 | 63 | }); 64 | 65 | 66 | 67 | 68 | /** 69 | * Disable button if needed 70 | * 71 | * @param {number} index 72 | */ 73 | function _disableButtonIfNeeded(index, cellCount) { 74 | 75 | // Disable button if at the beginning and we shouldn't wrap 76 | if (!$controller.wrapAround && index === cellCount) { 77 | $attrs.$set('disabled', 'disabled'); 78 | } else { 79 | $attrs.$set('disabled', false); 80 | } 81 | } 82 | } 83 | 84 | 85 | } 86 | 87 | -------------------------------------------------------------------------------- /src/next/next.directive.spec.js: -------------------------------------------------------------------------------- 1 | describe('FlickityNextDirective', () => { 2 | let $compile; 3 | let $rootScope; 4 | 5 | // Include the module 6 | beforeEach(angular.mock.module('bc.Flickity')); 7 | 8 | beforeEach(function() { 9 | 10 | inject(function($compile, $rootScope, $timeout, FlickityService) { 11 | this.$compile = $compile; 12 | this.$rootScope = $rootScope; 13 | this.$timeout = $timeout; 14 | this.FlickityService = FlickityService; 15 | 16 | this.$scope = this.$rootScope.$new(); 17 | this.$scope.slides = [ 18 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide1.jpg', 19 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide2.jpg', 20 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide3.jpg', 21 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide4.jpg', 22 | ]; 23 | this.$scope.flickityOptions = { 24 | groupCells: 2, 25 | prevNextButtons: true, 26 | wrapAround: false, 27 | }; 28 | }); 29 | 30 | }); 31 | 32 | 33 | beforeEach(function() { 34 | this.compileDirective = function(template) { 35 | this.element = this.$compile(template)(this.$scope); 36 | this.$scope.$digest(); 37 | }; 38 | }); 39 | 40 | 41 | afterEach(function() { 42 | if (this.element) { 43 | this.element.remove(); 44 | } 45 | }); 46 | 47 | describe(`when wrapping is set to false`, () => { 48 | 49 | it(`should disable button when at the end`, function(done) { 50 | const template = angular.element(` 51 |
52 |
53 |
54 | 55 |
56 |
57 | 60 |
61 | `); 62 | this.compileDirective(template); 63 | const customID = 'js_demo'; 64 | const DELAY = 300; 65 | 66 | // We must wrap in a $timeout since create() is also in a $timeout 67 | this.$timeout(() => { 68 | Array.apply(null, Array(this.$scope.slides.length / 2 - 1)) 69 | .reduce((acc) => acc.then(() => this.FlickityService.next(customID)), Promise.resolve()) 70 | .then((data) => { 71 | const button = angular.element(data.instance.element.querySelector('button.next')); 72 | const actual = button.attr('disabled'); 73 | 74 | expect(actual).toBeTruthy(); 75 | done(); 76 | }); 77 | }, DELAY); 78 | 79 | this.$timeout.flush(); 80 | this.$timeout.verifyNoPendingTasks(); 81 | }); 82 | 83 | }); 84 | 85 | }); 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/previous/previous.controller.js: -------------------------------------------------------------------------------- 1 | export class PreviousController { 2 | 3 | constructor( 4 | $log, $q, $timeout, 5 | FlickityConfig, FlickityService 6 | ) { 7 | 'ngInject'; 8 | 9 | this.$log = $log; 10 | this.$q = $q; 11 | this.$timeout = $timeout; 12 | this.FlickityConfig = FlickityConfig; 13 | this.FlickityService = FlickityService; 14 | 15 | 16 | this._activate(); 17 | 18 | } 19 | 20 | 21 | 22 | 23 | _activate() { 24 | // Assign wrap around or fall back to a default 25 | if (typeof this.bcFlickityPrevious !== 'undefined') { 26 | this.wrapAround = this.bcFlickityPrevious; 27 | } else if (typeof this.FlickityConfig.wrapAround !== 'undefined') { 28 | this.wrapAround = this.FlickityConfig.wrapAround; 29 | } else { 30 | this.wrapAround = false; 31 | } 32 | this.flickityId = null; 33 | 34 | // Make sure we have an ID 35 | this._setId(); 36 | } 37 | 38 | 39 | /** 40 | * Set ID to what is defined, fallback to first instance 41 | * 42 | * @return {String} flickityId 43 | */ 44 | _setId() { 45 | return new Promise((resolve, reject) => { 46 | this.$timeout(() => { 47 | 48 | if (this.bcFlickityId) { 49 | this.flickityId = this.bcFlickityId; 50 | return resolve(this.flickityId); 51 | } else { 52 | this.$timeout(() => { 53 | this.FlickityService.getFirst() 54 | .then((instance) => { 55 | this.flickityId = instance.id; 56 | return resolve(this.flickityId); 57 | }) 58 | .catch((error) => { 59 | this.$log.warn(error); 60 | return reject(error); 61 | }) 62 | ; 63 | }); 64 | } 65 | 66 | }); 67 | }); 68 | } 69 | 70 | 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/previous/previous.controller.spec.js: -------------------------------------------------------------------------------- 1 | describe('PreviousController', () => { 2 | let $compile; 3 | let $rootScope; 4 | 5 | beforeEach(angular.mock.module('bc.Flickity')); 6 | 7 | beforeEach(function() { 8 | 9 | inject(function($compile, $rootScope, $timeout, FlickityService) { 10 | this.$compile = $compile; 11 | this.$rootScope = $rootScope; 12 | this.$timeout = $timeout; 13 | this.FlickityService = FlickityService; 14 | 15 | this.$scope = this.$rootScope.$new(); 16 | this.$scope.slides = [ 17 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide1.jpg', 18 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide2.jpg', 19 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide3.jpg', 20 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide4.jpg', 21 | ]; 22 | this.$scope.flickityOptions = { 23 | prevNextButtons: false, 24 | wrapAround: false, 25 | }; 26 | }); 27 | 28 | }); 29 | 30 | beforeEach(function() { 31 | this.compileDirective = function(template) { 32 | const angularElement = angular.element(template); 33 | this.element = this.$compile(angularElement)(this.$scope); 34 | this.$scope.$digest(); 35 | }; 36 | }); 37 | 38 | afterEach(function() { 39 | if (this.element) { 40 | this.element.remove(); 41 | } 42 | }); 43 | 44 | 45 | 46 | describe(`this.flickityId`, () => { 47 | 48 | it(`should be set to the passed in ID`, function(done) { 49 | const template = ` 50 |
51 |
52 |
53 | 54 |
55 |
56 | 59 |
60 | `; 61 | this.compileDirective(template); 62 | const directiveElement = angular.element(this.element.find('button')); 63 | this.PreviousController = directiveElement.controller('bcFlickityPrevious'); 64 | 65 | // We must wrap in a $timeout since create() is also in a $timeout 66 | this.$timeout(() => { 67 | const actual = this.PreviousController.flickityId; 68 | const expected = 'buttonId'; 69 | 70 | expect(actual).toEqual(expected); 71 | done(); 72 | }); 73 | 74 | this.$timeout.flush(); 75 | this.$timeout.verifyNoPendingTasks(); 76 | }); 77 | 78 | it(`should be set to the ID of the first instance`, function(done) { 79 | const template = ` 80 |
81 |
82 |
83 | 84 |
85 |
86 | 89 |
90 | `; 91 | this.compileDirective(template); 92 | const directiveElement = angular.element(this.element.find('button')); 93 | const DELAY = 300; 94 | this.PreviousController = directiveElement.controller('bcFlickityPrevious'); 95 | 96 | this.$timeout(() => { 97 | this.FlickityService.getFirst().then((instance) => { 98 | const actual = this.PreviousController.flickityId; 99 | const expected = instance.id; 100 | 101 | expect(actual).toEqual(expected); 102 | done(); 103 | }); 104 | }, DELAY); 105 | 106 | this.$timeout.flush(); 107 | this.$timeout.verifyNoPendingTasks(); 108 | }); 109 | 110 | }); 111 | 112 | }); 113 | 114 | -------------------------------------------------------------------------------- /src/previous/previous.directive.js: -------------------------------------------------------------------------------- 1 | import { PreviousController } from './previous.controller'; 2 | 3 | export function FlickityPreviousDirective( 4 | $log, $timeout, $rootScope, 5 | FlickityConfig, FlickityService 6 | ) { 7 | 'ngInject'; 8 | 9 | const directive = { 10 | restrict: 'A', 11 | scope: {}, 12 | bindToController: { 13 | bcFlickityPrevious: '=?', 14 | bcFlickityId: '@?', 15 | }, 16 | compile: () => { 17 | return { 18 | pre: preLinkFunction, 19 | }; 20 | }, 21 | controller: PreviousController, 22 | controllerAs: 'vm', 23 | }; 24 | 25 | return directive; 26 | 27 | 28 | /** 29 | * Pre Link 30 | */ 31 | function preLinkFunction( 32 | $scope, $element, $attrs, $controller 33 | ) { 34 | 'ngInject'; 35 | 36 | // Get the ID 37 | const ID = $controller.flickityId; 38 | 39 | // Define the broadcast names to listen for 40 | const selectEvent = `Flickity:${ID}:cellSelect`; 41 | const settleEvent = `Flickity:${ID}:settle`; 42 | 43 | // Listen 44 | const cellSelect = $rootScope.$on(selectEvent, (event, data) => { 45 | _disableButtonIfNeeded(data.instance.selectedIndex); 46 | }); 47 | const settle = $rootScope.$on(settleEvent, (event, data) => { 48 | _disableButtonIfNeeded(data.instance.selectedIndex); 49 | }); 50 | 51 | 52 | $element.on('click', () => { 53 | 54 | // Move to the next cell 55 | FlickityService.previous($controller.flickityId, $controller.wrapAround) 56 | .then((instance) => { 57 | _disableButtonIfNeeded(instance.instance.selectedIndex); 58 | }) 59 | ; 60 | 61 | }); 62 | 63 | 64 | 65 | 66 | /** 67 | * Disable button if needed 68 | * 69 | * @param {number} index 70 | */ 71 | function _disableButtonIfNeeded(index) { 72 | // Disable button if at the beginning and we shouldn't wrap 73 | if (!$controller.wrapAround && index === 0) { 74 | $attrs.$set('disabled', 'disabled'); 75 | } else { 76 | $attrs.$set('disabled', false); 77 | } 78 | } 79 | 80 | } 81 | 82 | 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/previous/previous.directive.spec.js: -------------------------------------------------------------------------------- 1 | describe('FlickityPreviousDirective', () => { 2 | let $compile; 3 | let $rootScope; 4 | 5 | // Include the module 6 | beforeEach(angular.mock.module('bc.Flickity')); 7 | 8 | beforeEach(function() { 9 | 10 | inject(function($compile, $rootScope, $timeout, FlickityService) { 11 | this.$compile = $compile; 12 | this.$rootScope = $rootScope; 13 | this.$timeout = $timeout; 14 | this.FlickityService = FlickityService; 15 | 16 | this.$scope = this.$rootScope.$new(); 17 | this.$scope.slides = [ 18 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide1.jpg', 19 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide2.jpg', 20 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide3.jpg', 21 | 'http://cdn.benjamincharity.com/codepen/angular-flickity/slide4.jpg', 22 | ]; 23 | this.$scope.flickityOptions = { 24 | cellSelector: '.flickity__slide', 25 | prevNextButtons: true, 26 | wrapAround: false, 27 | }; 28 | }); 29 | 30 | }); 31 | 32 | 33 | beforeEach(function() { 34 | this.compileDirective = function(template) { 35 | this.element = this.$compile(template)(this.$scope); 36 | this.$scope.$digest(); 37 | }; 38 | }); 39 | 40 | 41 | afterEach(function() { 42 | if (this.element) { 43 | this.element.remove(); 44 | } 45 | }); 46 | 47 | describe(`when wrapping is set to false`, () => { 48 | 49 | it(`should disable button when at the beginning`, function(done) { 50 | const template = angular.element(` 51 |
52 |
53 |
54 | 55 |
56 |
57 | 60 |
61 | `); 62 | this.compileDirective(template); 63 | const DELAY = 300; 64 | 65 | // We must wrap in a $timeout since create() is also in a $timeout 66 | this.$timeout(() => { 67 | const button = this.element.find('button'); 68 | const actual = button.attr('disabled'); 69 | const expected = 'disabled'; 70 | 71 | expect(actual).toEqual(expected); 72 | done(); 73 | }, DELAY); 74 | 75 | this.$timeout.flush(); 76 | this.$timeout.verifyNoPendingTasks(); 77 | }); 78 | 79 | }); 80 | 81 | }); 82 | 83 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* global process, require, __dirname */ 2 | const webpack = require('webpack'); 3 | const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; 4 | const env = process.env.WEBPACK_ENV; 5 | const path = require('path'); 6 | const libraryName = 'angular-flickity'; 7 | 8 | 9 | const config = { 10 | entry: { 11 | 'angular-flickity': './src/index.js', 12 | 'angular-flickity.min': './src/index.js', 13 | }, 14 | devtool: 'inline-source-map', 15 | output: { 16 | path: __dirname + '/dist', 17 | filename: '[name].js', 18 | library: libraryName, 19 | libraryTarget: 'umd', 20 | umdNamedDefine: true, 21 | }, 22 | externals: { 23 | 'flickity': 'Flickity', 24 | }, 25 | module: { 26 | preLoaders: [ 27 | { 28 | test: /\.js$/, 29 | loader: 'eslint-loader', 30 | include: [ 31 | path.resolve('src'), 32 | ], 33 | }, 34 | ], 35 | loaders: [ 36 | { 37 | test: /\.js$/, 38 | loaders: [ 39 | 'ng-annotate', 40 | 'babel?presets[]=es2015', 41 | ], 42 | include: [ 43 | path.resolve('src'), 44 | ], 45 | }, 46 | ], 47 | }, 48 | resolve: { 49 | root: path.resolve('./src'), 50 | extensions: ['', '.js'], 51 | }, 52 | plugins: [ 53 | new webpack.optimize.UglifyJsPlugin({ 54 | include: /\.min\.js$/, 55 | minimize: true, 56 | sourceMap: false, 57 | }), 58 | ], 59 | }; 60 | 61 | module.exports = config; 62 | 63 | --------------------------------------------------------------------------------