├── .codeclimate.yml ├── .csslintrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── bower.json ├── dist ├── fonts │ ├── ui-carousel.eot │ ├── ui-carousel.svg │ ├── ui-carousel.ttf │ └── ui-carousel.woff ├── ui-carousel.css ├── ui-carousel.js ├── ui-carousel.min.css └── ui-carousel.min.js ├── gulpfile.js ├── karma-dist-concatenated.conf.js ├── karma-dist-minified.conf.js ├── karma-src.conf.js ├── package.json ├── src ├── demo │ ├── index.pug │ ├── main.js │ └── style.scss └── ui-carousel │ ├── controllers │ └── carousel.controller.js │ ├── directives │ └── carousel.directive.js │ ├── fonts │ ├── ui-carousel.eot │ ├── ui-carousel.svg │ ├── ui-carousel.ttf │ └── ui-carousel.woff │ ├── providers │ └── carousel.provider.js │ ├── scss │ ├── _fonts.scss │ ├── base.scss │ ├── carousel.scss │ ├── mixin.scss │ ├── ui-carousel.scss │ └── variables.scss │ ├── templates │ └── carousel.template.pug │ └── uiCarousel.module.js └── test └── unit └── ui-carousel ├── configs └── test.utils.js ├── controllers └── carousel.controller.js ├── directives └── carousel.directive.js ├── providers └── carousel.provider.js └── uiCarouselSpec.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | csslint: 4 | enabled: true 5 | duplication: 6 | enabled: true 7 | config: 8 | languages: 9 | - javascript 10 | eslint: 11 | enabled: true 12 | checks: 13 | complexity: 14 | enabled: false 15 | fixme: 16 | enabled: false 17 | ratings: 18 | paths: 19 | - "**.js" 20 | exclude_paths: 21 | - dist/ 22 | - node_modules/ 23 | - bower_components/ 24 | - _gh_pages/ 25 | - .tmp/ 26 | - test/ 27 | - src/demo/ 28 | - gulpfile.js 29 | -------------------------------------------------------------------------------- /.csslintrc: -------------------------------------------------------------------------------- 1 | --exclude-exts=.min.css 2 | --ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | ecmaFeatures: 2 | modules: true 3 | jsx: true 4 | 5 | env: 6 | amd: true 7 | browser: true 8 | es6: true 9 | jquery: true 10 | node: true 11 | 12 | # http://eslint.org/docs/rules/ 13 | rules: 14 | # Possible Errors 15 | comma-dangle: [2, never] 16 | no-cond-assign: 2 17 | no-console: 0 18 | no-constant-condition: 2 19 | no-control-regex: 2 20 | no-debugger: 2 21 | no-dupe-args: 2 22 | no-dupe-keys: 2 23 | no-duplicate-case: 2 24 | no-empty: 2 25 | no-empty-character-class: 2 26 | no-ex-assign: 2 27 | no-extra-boolean-cast: 2 28 | no-extra-parens: 0 29 | no-extra-semi: 2 30 | no-func-assign: 2 31 | no-inner-declarations: [2, functions] 32 | no-invalid-regexp: 2 33 | no-irregular-whitespace: 2 34 | no-negated-in-lhs: 2 35 | no-obj-calls: 2 36 | no-regex-spaces: 2 37 | no-sparse-arrays: 2 38 | no-unexpected-multiline: 2 39 | no-unreachable: 2 40 | use-isnan: 2 41 | valid-jsdoc: 0 42 | valid-typeof: 2 43 | 44 | # Best Practices 45 | accessor-pairs: 2 46 | block-scoped-var: 0 47 | complexity: [2, 6] 48 | consistent-return: 0 49 | curly: 0 50 | default-case: 0 51 | dot-location: 0 52 | dot-notation: 0 53 | eqeqeq: 2 54 | guard-for-in: 2 55 | no-alert: 2 56 | no-caller: 2 57 | no-case-declarations: 2 58 | no-div-regex: 2 59 | no-else-return: 0 60 | no-empty-label: 2 61 | no-empty-pattern: 2 62 | no-eq-null: 2 63 | no-eval: 2 64 | no-extend-native: 2 65 | no-extra-bind: 2 66 | no-fallthrough: 2 67 | no-floating-decimal: 0 68 | no-implicit-coercion: 0 69 | no-implied-eval: 2 70 | no-invalid-this: 0 71 | no-iterator: 2 72 | no-labels: 0 73 | no-lone-blocks: 2 74 | no-loop-func: 2 75 | no-magic-number: 0 76 | no-multi-spaces: 0 77 | no-multi-str: 0 78 | no-native-reassign: 2 79 | no-new-func: 2 80 | no-new-wrappers: 2 81 | no-new: 2 82 | no-octal-escape: 2 83 | no-octal: 2 84 | no-proto: 2 85 | no-redeclare: 2 86 | no-return-assign: 2 87 | no-script-url: 2 88 | no-self-compare: 2 89 | no-sequences: 0 90 | no-throw-literal: 0 91 | no-unused-expressions: 2 92 | no-useless-call: 2 93 | no-useless-concat: 2 94 | no-void: 2 95 | no-warning-comments: 0 96 | no-with: 2 97 | radix: 2 98 | vars-on-top: 0 99 | wrap-iife: 2 100 | yoda: 0 101 | 102 | # Strict 103 | strict: 0 104 | 105 | # Variables 106 | init-declarations: 0 107 | no-catch-shadow: 2 108 | no-delete-var: 2 109 | no-label-var: 2 110 | no-shadow-restricted-names: 2 111 | no-shadow: 0 112 | no-undef-init: 2 113 | no-undef: 0 114 | no-undefined: 0 115 | no-unused-vars: 0 116 | no-use-before-define: 0 117 | 118 | # Node.js and CommonJS 119 | callback-return: 2 120 | global-require: 2 121 | handle-callback-err: 2 122 | no-mixed-requires: 0 123 | no-new-require: 0 124 | no-path-concat: 2 125 | no-process-exit: 2 126 | no-restricted-modules: 0 127 | no-sync: 0 128 | 129 | # Stylistic Issues 130 | array-bracket-spacing: 0 131 | block-spacing: 0 132 | brace-style: 0 133 | camelcase: 0 134 | comma-spacing: 0 135 | comma-style: 0 136 | computed-property-spacing: 0 137 | consistent-this: 0 138 | eol-last: 0 139 | func-names: 0 140 | func-style: 0 141 | id-length: 0 142 | id-match: 0 143 | indent: 0 144 | jsx-quotes: 0 145 | key-spacing: 0 146 | linebreak-style: 0 147 | lines-around-comment: 0 148 | max-depth: 0 149 | max-len: 0 150 | max-nested-callbacks: 0 151 | max-params: 0 152 | max-statements: [2, 30] 153 | new-cap: 0 154 | new-parens: 0 155 | newline-after-var: 0 156 | no-array-constructor: 0 157 | no-bitwise: 0 158 | no-continue: 0 159 | no-inline-comments: 0 160 | no-lonely-if: 0 161 | no-mixed-spaces-and-tabs: 0 162 | no-multiple-empty-lines: 0 163 | no-negated-condition: 0 164 | no-nested-ternary: 0 165 | no-new-object: 0 166 | no-plusplus: 0 167 | no-restricted-syntax: 0 168 | no-spaced-func: 0 169 | no-ternary: 0 170 | no-trailing-spaces: 0 171 | no-underscore-dangle: 0 172 | no-unneeded-ternary: 0 173 | object-curly-spacing: 0 174 | one-var: 0 175 | operator-assignment: 0 176 | operator-linebreak: 0 177 | padded-blocks: 0 178 | quote-props: 0 179 | quotes: 0 180 | require-jsdoc: 0 181 | semi-spacing: 0 182 | semi: 0 183 | sort-vars: 0 184 | space-after-keywords: 0 185 | space-before-blocks: 0 186 | space-before-function-paren: 0 187 | space-before-keywords: 0 188 | space-in-parens: 0 189 | space-infix-ops: 0 190 | space-return-throw-case: 0 191 | space-unary-ops: 0 192 | spaced-comment: 0 193 | wrap-regex: 0 194 | 195 | # ECMAScript 6 196 | arrow-body-style: 0 197 | arrow-parens: 0 198 | arrow-spacing: 0 199 | constructor-super: 0 200 | generator-star-spacing: 0 201 | no-arrow-condition: 0 202 | no-class-assign: 0 203 | no-const-assign: 0 204 | no-dupe-class-members: 0 205 | no-this-before-super: 0 206 | no-var: 0 207 | object-shorthand: 0 208 | prefer-arrow-callback: 0 209 | prefer-const: 0 210 | prefer-reflect: 0 211 | prefer-spread: 0 212 | prefer-template: 0 213 | require-yield: 0 214 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ._* 2 | .~lock.* 3 | .buildpath 4 | .DS_Store 5 | .idea 6 | .project 7 | .settings 8 | 9 | # Ignore node stuff 10 | node_modules/ 11 | npm-debug.log 12 | libpeerconnection.log 13 | 14 | pakmanaged.js 15 | 16 | # OS-specific 17 | .DS_Store 18 | 19 | # Bower components 20 | bower_components 21 | 22 | # Demo and Github pages 23 | /demo/ 24 | _gh-pages/ 25 | .tmp/ 26 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "es3": false, 7 | "forin": true, 8 | "freeze": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": "nofunc", 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonbsp": true, 16 | "nonew": true, 17 | "plusplus": false, 18 | "quotmark": "single", 19 | "undef": true, 20 | "unused": false, 21 | "strict": false, 22 | "maxparams": 10, 23 | "maxdepth": 5, 24 | "maxstatements": 40, 25 | "maxcomplexity": 20, 26 | "maxlen": 120, 27 | 28 | "asi": false, 29 | "boss": false, 30 | "debug": false, 31 | "eqnull": true, 32 | "esnext": false, 33 | "evil": false, 34 | "expr": false, 35 | "funcscope": false, 36 | "globalstrict": false, 37 | "iterator": false, 38 | "lastsemic": false, 39 | "laxbreak": true, 40 | "laxcomma": true, 41 | "loopfunc": true, 42 | "maxerr": false, 43 | "moz": false, 44 | "multistr": true, 45 | "notypeof": false, 46 | "proto": false, 47 | "scripturl": false, 48 | "shadow": false, 49 | "sub": true, 50 | "supernew": false, 51 | "validthis": false, 52 | "noyield": false, 53 | 54 | "browser": true, 55 | "node": true, 56 | "esversion": 6, 57 | 58 | "globals": { 59 | "angular": false, 60 | "$": false 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Any hidden files 2 | **/.* 3 | 4 | # Build related stuff 5 | /src 6 | /test 7 | /demo 8 | /_gh-pages 9 | /.tmp 10 | /bower_components 11 | /node_modules 12 | gulpfile.js 13 | pakmanaged.js 14 | karma-dist-concatenated.conf.js 15 | karma-dist-minified.conf.js 16 | karma-src.conf.js 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: ["6.7.0"] 3 | before_script: 4 | - npm install -g bower 5 | - bower install 6 | - gulp build 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 mihnsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ui-carousel ([live demo](http://mihnsen.github.io/ui-carousel/)) [![npm version](https://badge.fury.io/js/angular-ui-carousel.svg)](https://badge.fury.io/js/angular-ui-carousel) [![Bower version](https://badge.fury.io/bo/angular-ui-carousel.svg)](https://badge.fury.io/bo/angular-ui-carousel) [![Build Status](https://travis-ci.org/mihnsen/ui-carousel.svg?branch=master)](https://travis-ci.org/mihnsen/ui-carousel) [![Code Climate](https://codeclimate.com/github/mihnsen/ui-carousel/badges/gpa.svg)](https://codeclimate.com/github/mihnsen/ui-carousel) 2 | ========= 3 | 4 | A simple, lightweight module for carousel in your AngularJS app, Inspired from [http://kenwheeler.github.io/slick/](http://kenwheeler.github.io/slick/). No Jquery required. 5 | 6 | [![ui.carousel Demo](https://snag.gy/0hRlB5.jpg)](http://mihnsen.github.io/ui-carousel) 7 | 8 | IE9+ (AngularJS v1.3.x no longer supports IE8) and the latest versions of Chrome, FireFox and Safari have been tested and are supported. If you do run across any issues, please submit a [new issue](https://github.com/mihnsen/ui-carousel/issues) and I'll take a look - or better yet - submit a PR with the bug fix and I'll merge it in. 9 | 10 | You can check out basic options and demo here: [http://mihnsen.github.io/ui-carousel](http://mihnsen.github.io/ui-carousel) 11 | 12 | #### First version 13 | With first version, we provide a directive ui-carousel. Basic support like slick carousel 14 | - arrows 15 | - autoplay 16 | - autoplaySpeed 17 | - cssEase 18 | - dots 19 | - fade 20 | - infinite 21 | - initialSlide 22 | - slidesToShow 23 | - slidesToScroll 24 | - speed 25 | - onBeforeChange 26 | - onAfterChange 27 | - onInit 28 | 29 | And with angularjs it also contain 30 | - Filtering 31 | 32 | #### Comming soon 33 | With next version we will provide: 34 | 35 | - Lazy loading 36 | - Vertical 37 | - Mouse swipe event 38 | - Touch swipe event 39 | - Responsive config 40 | - Variable width 41 | - Adaptive height 42 | - rtl 43 | 44 | 45 | Implementation 46 | ============== 47 | 48 | ### Requirements 49 | 50 | AngularJS is the only dependency. Animation is achieved with pure JS, jQuery not necessary. 51 | 52 | ### Installation 53 | 54 | You can install ui-carousel with Bower. 55 | 56 | bower install angular-ui-carousel --save 57 | 58 | You can also install ui-carousel with npm. 59 | 60 | npm install angular-ui-carousel --save 61 | 62 | 63 | And as always, you can download the source files straight from this repo - they're located in the `dist` dir. Be sure to include the minified version of both js and css files. 64 | 65 | ### Usage 66 | Inject module 67 | ```javascript 68 | angular.module('App', ['ui.carousel']); 69 | ``` 70 | 71 | Directive configuration. 72 | 73 | ```javascript 74 | 82 | 83 | 84 |

{{ item + 1 }}

85 |
86 |
87 | ``` 88 | 89 | Provide Configuration: 90 | You can also using global configuration on angular setup like this: 91 | 92 | ```javascript 93 | app.run(['Carousel', (Carousel) => { 94 | Carousel.setOptions({ 95 | arrows: true, 96 | autoplay: false, 97 | autoplaySpeed: 3000, 98 | cssEase: 'ease', 99 | dots: false, 100 | 101 | easing: 'linear', 102 | fade: false, 103 | infinite: true, 104 | initialSlide: 0, 105 | 106 | slidesToShow: 1, 107 | slidesToScroll: 1, 108 | speed: 500, 109 | }); 110 | }]); 111 | ``` 112 | 113 | ### Advanced customize 114 | 115 | ```javascript 116 | 123 | 124 | 125 | 126 | 127 | {{ item.title }} 128 |

{{ item.name }}

129 |

{{ item.description }}

130 | 131 |
132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 |
148 | ``` 149 | 150 | Definitions 151 | =========== 152 | 153 | ### Settings 154 | 155 | Option | Type | Default | Description 156 | ------ | ---- | ------- | ----------- 157 | autoplay | boolean | false | Enables auto play of slides 158 | autoplaySpeed | int | 3000 | Auto play change interval 159 | cssEase | string | 'ease' | CSS3 easing 160 | dots | boolean | false | Current slide indicator dots 161 | easing | string | 'linear' | animate() fallback easing 162 | fade | boolean | false | Enables fade 163 | arrows | boolean | true | Enable Next/Prev arrows 164 | infinite | boolean | true | Infinite looping 165 | initialSlide | integer | 0 | Slide to start on 166 | slidesToShow | int | 1 | # of slides to show at a time 167 | slidesToScroll | int | 1 | # of slides to scroll at a time 168 | speed | int | 300 | Transition speed 169 | 170 | ### Callbacks 171 | 172 | #### onInit() 173 | On carousel initialized 174 | 175 | #### onBeforeChange(currentSlide, nextSlide) 176 | Fires before slide change 177 | 178 | #### onAfterChange(currentSlide) 179 | Fires after slide change 180 | 181 | ```javascript 182 | 189 | 190 | 191 |

{{ item + 1 }}

192 |
193 |
194 | ``` 195 | 196 | 197 | 198 | Development 199 | =========== 200 | 201 | If you've forked or cloned the project and would like to make any sort of adjustments, there are few items to make note of. First, your system will need to have the following bits in place: 202 | 203 | - Node & NPM 204 | - gulp 205 | - karma 206 | - Scss 207 | 208 | Second, there are a few gulp tasks that you'll be able to leverage to help validate and prepare your changes for use. 209 | 210 | You can fire off a `gulp` or `gulp build` command manually at any time to lint, minify, and setup your demo (built in the _gh-pages dir) for testing. 211 | 212 | ```console 213 | gulp (or gulp build) 214 | ``` 215 | 216 | Also, you can run `gulp dev` to lint, minify, and prep your demo for testing. Once the build is complete, it'll also fire off a `watch` so that any changes that are made to the the sass, js, and demo files will automatically trigger the build script to update your project. 217 | 218 | ```console 219 | gulp 220 | ``` 221 | 222 | To run through the configured unit tests, you can run `gulp test`. This will fire off a series of tests that check that all default options are set correctly, all configurable options are able to be set correctly, and that all methods carry out the functionality that they're supposed to. These tests should let you know if any of the updates that you've made have negatively effected any preexisting functionality. Also, when the tests complete, there will be a test coverage report generated and stored in the `coverage` directory. 223 | 224 | ```console 225 | gulp test 226 | ``` 227 | 228 | To public gh-pages you can using command bellow. A folder with name _gh-pages contain all file in your gh-pages repo will be generated. 229 | Read here to config your gh-pages: 230 | - https://help.github.com/articles/creating-project-pages-from-the-command-line/ 231 | - https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/ 232 | ```console 233 | gulp gh-pages 234 | ``` 235 | 236 | Next, you'll want to do all of your development within three locations. If you add changes anywhere else, they're likely to be overwritten during the build process. These locations are: 237 | 238 | `src/ui-carousel/*.js` - for any script modifications. 239 | 240 | `src/ui-carousel/scss/*.scss` - for any style modifications. 241 | 242 | `src/demo/*` - for any modifications to the demo. 243 | 244 | Lastly, once you've made your changes and run through the appropriate gulp tasks, your changes should be baked and ready for you to consume - located in the `dist` directory as minified js and css files. 245 | 246 | 247 | 248 | ## Authors 249 | **Minh Nguyen** 250 | 251 | + [https://twitter.com/mihnsen](https://twitter.com/mihnsen) 252 | 253 | ## Credits 254 | UI-Carousel by [mihnsen](https://github.com/mihnsen) inspired by http://kenwheeler.github.io/slick/ 255 | 256 | ## Copyright 257 | Copyright © 2016 258 | 259 | ## License 260 | UI-Carousel is under MIT license - http://www.opensource.org/licenses/mit-license.php 261 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-carousel", 3 | "version": "0.1.10", 4 | "authors": [ 5 | { 6 | "name": "mihnsen", 7 | "email": "minhnt.hut@gmail.com" 8 | } 9 | ], 10 | "description": "A simple, lightweight carousel for angularjs.", 11 | "main": [ 12 | "dist/ui-carousel.js", 13 | "dist/ui-carousel.css" 14 | ], 15 | "keywords": [ 16 | "ui-carousel", 17 | "carousel", 18 | "carousel", 19 | "angular-carousel", 20 | "angular-carousel", 21 | "angular-ui-carousel", 22 | "ng-carousel" 23 | ], 24 | "ignore": [ 25 | "src", 26 | "test", 27 | "demo", 28 | ".tmp", 29 | "_gh-pages", 30 | "gulpfile.js", 31 | "karma-*.conf.js", 32 | "**/.*" 33 | ], 34 | "license": "MIT", 35 | "homepage": "https://mihnsen.github.com/ui-carousel", 36 | "dependencies": {}, 37 | "devDependencies": { 38 | "angular-mocks": ">=1.2.0", 39 | "angular": "1.6", 40 | "angular-sanitize": ">=1.2.0", 41 | "prism": "^1.5.1" 42 | }, 43 | "resolutions": { 44 | "angular": "1.6.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /dist/fonts/ui-carousel.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihnsen/ui-carousel/2fea2a6e7e7b9d98434f49a420e261490ec87627/dist/fonts/ui-carousel.eot -------------------------------------------------------------------------------- /dist/fonts/ui-carousel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by Fontastic.me 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /dist/fonts/ui-carousel.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihnsen/ui-carousel/2fea2a6e7e7b9d98434f49a420e261490ec87627/dist/fonts/ui-carousel.ttf -------------------------------------------------------------------------------- /dist/fonts/ui-carousel.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihnsen/ui-carousel/2fea2a6e7e7b9d98434f49a420e261490ec87627/dist/fonts/ui-carousel.woff -------------------------------------------------------------------------------- /dist/ui-carousel.css: -------------------------------------------------------------------------------- 1 | .v-middle, .ui-carousel .carousel-btn { 2 | display: block; 3 | position: absolute; 4 | top: 50%; 5 | -webkit-transform: translate(0, -50%); 6 | -ms-transform: translate(0, -50%); 7 | -o-transform: translate(0, -50%); 8 | transform: translate(0, -50%); } 9 | 10 | @font-face { 11 | font-family: "ui-carousel"; 12 | src: url("fonts/ui-carousel.eot"); 13 | src: url("fonts/ui-carousel.eot?#iefix") format("embedded-opentype"), url("fonts/ui-carousel.woff") format("woff"), url("fonts/ui-carousel.ttf") format("truetype"), url("fonts/ui-carousel.svg#ui-carousel") format("svg"); 14 | font-weight: normal; 15 | font-style: normal; } 16 | 17 | [data-icon]:before { 18 | font-family: "ui-carousel" !important; 19 | content: attr(data-icon); 20 | font-style: normal !important; 21 | font-weight: normal !important; 22 | font-variant: normal !important; 23 | text-transform: none !important; 24 | speak: none; 25 | line-height: 1; 26 | -webkit-font-smoothing: antialiased; 27 | -moz-osx-font-smoothing: grayscale; } 28 | 29 | [class^="ui-icon-"]:before, 30 | [class*=" ui-icon-"]:before { 31 | font-family: "ui-carousel" !important; 32 | font-style: normal !important; 33 | font-weight: normal !important; 34 | font-variant: normal !important; 35 | text-transform: none !important; 36 | speak: none; 37 | line-height: 1; 38 | -webkit-font-smoothing: antialiased; 39 | -moz-osx-font-smoothing: grayscale; } 40 | 41 | .ui-icon-prev:before { 42 | content: "\61"; } 43 | 44 | .ui-icon-next:before { 45 | content: "\62"; } 46 | 47 | .ui-icon-dot:before { 48 | content: "\63"; } 49 | 50 | .ui-carousel { 51 | display: block; 52 | margin-bottom: 30px; } 53 | .ui-carousel .carousel-wrapper { 54 | position: relative; } 55 | .ui-carousel .track-wrapper { 56 | position: relative; 57 | display: block; 58 | overflow: hidden; 59 | margin: 0; 60 | padding: 0; } 61 | .ui-carousel .track { 62 | position: relative; 63 | display: block; 64 | float: left; } 65 | .ui-carousel .slide { 66 | float: left; 67 | height: 100%; 68 | min-height: 1px; } 69 | .ui-carousel .carousel-btn { 70 | position: absolute; 71 | z-index: 10; 72 | background-color: transparent; 73 | outline: none; 74 | border: none; 75 | font-size: 20px; 76 | opacity: .75; } 77 | .ui-carousel .carousel-btn:hover { 78 | opacity: 1; } 79 | .ui-carousel .carousel-prev .carousel-btn { 80 | left: -25px; } 81 | .ui-carousel .carousel-next .carousel-btn { 82 | right: -25px; } 83 | .ui-carousel .carousel-disable { 84 | opacity: 0.5; } 85 | .ui-carousel .carousel-disable .carousel-btn:hover { 86 | opacity: .75; } 87 | 88 | .carousel-dots { 89 | position: absolute; 90 | bottom: -30px; 91 | display: block; 92 | width: 100%; 93 | padding: 0; 94 | margin: 0; 95 | list-style: none; 96 | text-align: center; } 97 | .carousel-dots li { 98 | position: relative; 99 | display: inline-block; 100 | width: 15px; 101 | height: 15px; 102 | margin: 0 5px; 103 | padding: 0; 104 | cursor: pointer; } 105 | .carousel-dots li button { 106 | font-size: 0; 107 | line-height: 0; 108 | display: block; 109 | width: 15px; 110 | height: 15px; 111 | padding: 5px; 112 | cursor: pointer; 113 | color: transparent; 114 | border: 0; 115 | outline: none; 116 | background: transparent; } 117 | .carousel-dots li button:before { 118 | font-family: ui-carousel; 119 | font-size: 9px; 120 | line-height: 15px; 121 | position: absolute; 122 | top: 0px; 123 | left: 0px; 124 | width: 15px; 125 | height: 15px; 126 | content: "\63"; 127 | text-align: center; 128 | opacity: 0.25; 129 | color: black; 130 | -webkit-font-smoothing: antialiased; } 131 | .carousel-dots li.carousel-active button:before { 132 | opacity: .75; } 133 | -------------------------------------------------------------------------------- /dist/ui-carousel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function (angular) { 4 | // Create all modules and define dependencies to make sure they exist 5 | // and are loaded in the correct order to satisfy dependency injection 6 | // before all nested files are concatenated by Gulp 7 | 8 | // Config 9 | angular.module('ui.carousel.config', []).value('ui.carousel.config', { 10 | debug: true 11 | }); 12 | 13 | // Modules 14 | angular.module('ui.carousel.providers', []); 15 | angular.module('ui.carousel.controllers', []); 16 | angular.module('ui.carousel.directives', []); 17 | angular.module('ui.carousel', ['ui.carousel.config', 'ui.carousel.directives', 'ui.carousel.controllers', 'ui.carousel.providers']); 18 | })(angular); 19 | 'use strict'; 20 | 21 | /** 22 | * angular-ui-carousel 23 | * for example: 24 | * length = 8, show = 4, scroll = 3, current = 0 25 | * --------- 26 | * | | 27 | * |4|5|6|7|0|1|2|3|4|5|6|7|1|2|3|4 28 | * | | 29 | * --------- 30 | * rectangle is visible for users 31 | */ 32 | angular.module('ui.carousel.controllers').controller('CarouselController', ['$scope', '$element', '$timeout', '$q', 'Carousel', '$window', function ($scope, $element, $timeout, $q, Carousel, $window) { 33 | var _this = this; 34 | 35 | /** 36 | * Initial carousel 37 | * 38 | * Mirgate to angularjs 1.6 39 | * @see https://docs.angularjs.org/guide/migration#commit-bcd0d4 40 | */ 41 | this.$onInit = function () { 42 | _this.initOptions(); 43 | _this.initRanges(); 44 | _this.setProps(); 45 | _this.setupInfinite(); 46 | }; 47 | 48 | /** 49 | * Init option based on directive config 50 | */ 51 | this.initOptions = function () { 52 | _this.options = angular.extend({}, Carousel.getOptions()); 53 | 54 | // TODO customize attribute from directive 55 | if (_this.initialSlide !== undefined) { 56 | _this.options.initialSlide = _this.initialSlide; 57 | } 58 | if (_this.fade !== undefined) { 59 | _this.options.fade = _this.fade; 60 | } 61 | if (_this.autoplay !== undefined) { 62 | _this.options.autoplay = _this.autoplay; 63 | } 64 | if (_this.autoplaySpeed !== undefined) { 65 | _this.options.autoplaySpeed = _this.autoplaySpeed; 66 | } 67 | if (_this.cssEase !== undefined) { 68 | _this.options.cssEase = _this.cssEase; 69 | } 70 | if (_this.speed !== undefined) { 71 | _this.options.speed = _this.speed; 72 | } 73 | if (_this.infinite !== undefined) { 74 | _this.options.infinite = _this.infinite; 75 | } 76 | if (_this.arrows !== undefined) { 77 | _this.options.arrows = _this.arrows; 78 | } 79 | if (_this.dots !== undefined) { 80 | _this.options.dots = _this.dots; 81 | } 82 | if (_this.visiblePrev !== undefined) { 83 | _this.options.visiblePrev = _this.visiblePrev; 84 | } 85 | if (_this.visibleNext !== undefined) { 86 | _this.options.visibleNext = _this.visibleNext; 87 | } 88 | 89 | // TODO write more options for fade mode 90 | // In fade mode we have to setting slides-to-show and slides-to-scroll 91 | // to 1 slide 92 | if (_this.options.fade) { 93 | _this.options.slidesToShow = 1; 94 | _this.options.slidesToScroll = 1; 95 | } else { 96 | if (_this.show) { 97 | _this.options.slidesToShow = _this.show; 98 | } 99 | if (_this.scroll) { 100 | _this.options.slidesToScroll = _this.scroll; 101 | } 102 | } 103 | }; 104 | 105 | /** 106 | * init variables, slides, .. 107 | */ 108 | this.initRanges = function () { 109 | if (!_this.slides) { 110 | _this.slides = []; 111 | } 112 | 113 | _this.isCarouselReady = false; 114 | _this.isTrackMoving = false; 115 | _this.track = $element.find('.track'); 116 | _this.width = 1; // Fake width 117 | _this.currentSlide = _this.options.initialSlide; 118 | _this.trackStyle = {}; 119 | _this.slideStyle = {}; 120 | 121 | _this.isVisibleDots = false; 122 | _this.isVisiblePrev = _this.options.visiblePrev; 123 | _this.isVisibleNext = _this.options.visibleNext; 124 | 125 | _this.isClickablePrev = false; 126 | _this.isClickableNext = false; 127 | 128 | _this.animType = null; 129 | _this.transformType = null; 130 | _this.transitionType = null; 131 | }; 132 | 133 | /** 134 | * Init UI and carousel track 135 | */ 136 | this.initUI = function () { 137 | _this.width = $element[0].clientWidth; 138 | 139 | // Update track width first 140 | _this.initTrack(); 141 | 142 | // Then item style 143 | $timeout(function () { 144 | _this.updateItemStyle(); 145 | }, 200); 146 | }; 147 | 148 | /** 149 | * update common style for each carousel item 150 | */ 151 | this.updateItemStyle = function () { 152 | _this.itemWidth = _this.width / _this.options.slidesToShow; 153 | _this.slideStyle = { 154 | 'width': _this.itemWidth + 'px' 155 | }; 156 | }; 157 | 158 | /** 159 | * init carousel track 160 | * also make Carousel is Ready 161 | */ 162 | this.initTrack = function () { 163 | var itemWidth = _this.width / _this.options.slidesToShow; 164 | var trackWidth = itemWidth * _this.slidesInTrack.length; 165 | 166 | _this.trackStyle.width = trackWidth + 'px'; 167 | 168 | _this.slideHandler(_this.currentSlide).finally(function () { 169 | _this.isCarouselReady = true; 170 | 171 | if (!_this.options.fade) { 172 | _this.refreshTrackStyle(); 173 | } 174 | 175 | // onInit callback 176 | if (_this.onInit) { 177 | _this.onInit(); 178 | } 179 | }).catch(function () { 180 | // Catch err 181 | }); 182 | }; 183 | 184 | /** 185 | * @see https://github.com/kenwheeler/slick/blob/master/slick/slick.js#L680 186 | * 187 | * Sync slide to place it should be 188 | * for example: 189 | * - 9 total, 3 show, 3 scroll, current 1 190 | * => next index = 3 (previous index counted = 0) 191 | * 192 | * and scroll to next page: 193 | * - 6 total, 1 show, 1 scroll, current 0 => next index = 1 194 | * - 9 total, 3 show, 3 scroll, current 1 => next index = 3 195 | * - 9 total, 3 show, 3 scroll, current 3 => next index = 6 196 | * - 9 total, 3 show, 3 scroll, current 8 => next index = 3 197 | * - 8 total, 4 show, 3 scroll, current 1 => next index = 4 198 | */ 199 | this.next = function () { 200 | if (!_this.isClickableNext) { 201 | return false; 202 | } 203 | 204 | var indexOffset = _this.getIndexOffset(); 205 | var slideOffset = indexOffset === 0 ? _this.options.slidesToScroll : indexOffset; 206 | 207 | _this.slideHandler(_this.currentSlide + slideOffset).catch(function () { 208 | // Catch err 209 | }); 210 | }; 211 | 212 | /** 213 | * move to previous slide 214 | * same calculate with next 215 | * @see next function 216 | */ 217 | this.prev = function () { 218 | if (!_this.isClickablePrev) { 219 | return false; 220 | } 221 | 222 | var indexOffset = _this.getIndexOffset(); 223 | var slideOffset = indexOffset === 0 ? _this.options.slidesToScroll : _this.options.slidesToShow - indexOffset; 224 | 225 | _this.slideHandler(_this.currentSlide - slideOffset).catch(function () { 226 | // Catch err 227 | }); 228 | }; 229 | 230 | /** 231 | * Get index offset 232 | */ 233 | this.getIndexOffset = function () { 234 | var scrollOffset = _this.slides.length % _this.options.slidesToScroll !== 0; 235 | var indexOffset = scrollOffset ? 0 : (_this.slides.length - _this.currentSlide) % _this.options.slidesToScroll; 236 | 237 | return indexOffset; 238 | }; 239 | 240 | /** 241 | * move to page 242 | * @params int page 243 | * Page counter from 0 (start = 0) 244 | */ 245 | this.movePage = function (page) { 246 | var target = _this.options.slidesToScroll * page; 247 | _this.slideHandler(target).catch(function () { 248 | // Catch err 249 | }); 250 | }; 251 | 252 | /** 253 | * hanlder carousel 254 | * @description move carousel to correct page 255 | * 256 | * @params int index 257 | */ 258 | this.slideHandler = function (index) { 259 | // TODO prevent when slides not exists 260 | if (!_this.slides) { 261 | return $q.reject('Carousel not fully setup'); 262 | } 263 | 264 | // TODO Prevent when track is moving 265 | if (_this.isTrackMoving) { 266 | return $q.reject('Track is moving'); 267 | } 268 | 269 | var len = _this.slides.length; 270 | var show = _this.options.slidesToShow; 271 | 272 | if (len <= show) { 273 | _this.correctTrack(); 274 | return $q.reject('Length of slides smaller than slides to show'); 275 | } 276 | 277 | // We need target to destination 278 | // and a anim slide to translate track 279 | // 280 | // anim = animSlide (which we use to move) 281 | // target = targetSlide 282 | var anim = index; 283 | var target = null; 284 | 285 | if (anim < 0) { 286 | if (len % _this.options.slidesToScroll !== 0) { 287 | target = len - len % _this.options.slidesToScroll; 288 | } else { 289 | target = len + anim; 290 | } 291 | } else if (anim >= len) { 292 | if (len % _this.options.slidesToScroll !== 0) { 293 | target = 0; 294 | } else { 295 | target = anim - len; 296 | } 297 | } else { 298 | target = anim; 299 | } 300 | 301 | if (_this.onBeforeChange) { 302 | // @see https://docs.angularjs.org/guide/directive 303 | _this.onBeforeChange({ currentSlide: _this.currentSlide, target: target }); 304 | } 305 | 306 | // Fade handler 307 | if (_this.options.fade) { 308 | _this.currentSlide = target; 309 | 310 | // XXX 311 | // afterChange method 312 | // fire after faded 313 | // Should be revised 314 | $timeout(function () { 315 | _this.autoplayTrack(); 316 | 317 | if (_this.onAfterChange) { 318 | _this.onAfterChange({ currentSlide: _this.currentSlide }); 319 | } 320 | }, _this.options.speed); 321 | return $q.when('Handler fade'); 322 | } 323 | 324 | // No-fade handler 325 | var itemWidth = _this.width / _this.options.slidesToShow; 326 | var left = -1 * target * itemWidth; 327 | if (_this.options.infinite) { 328 | left = -1 * (anim + show) * itemWidth; 329 | } 330 | 331 | _this.isTrackMoving = true; 332 | return _this.moveTrack(left).then(function () { 333 | _this.isTrackMoving = false; 334 | _this.currentSlide = target; 335 | _this.autoplayTrack(); 336 | 337 | if (target !== anim) { 338 | _this.correctTrack(); 339 | } 340 | 341 | if (!_this.options.infinite) { 342 | if (_this.currentSlide === 0) { 343 | _this.isClickablePrev = false; 344 | _this.isClickableNext = true; 345 | } else if (_this.currentSlide === _this.slidesInTrack.length - _this.options.slidesToShow) { 346 | _this.isClickableNext = false; 347 | _this.isClickablePrev = true; 348 | } else { 349 | _this.isClickablePrev = true; 350 | _this.isClickableNext = true; 351 | } 352 | } 353 | 354 | // XXX 355 | // afterChange method 356 | // fire after 200ms wakeup and correct track 357 | // Should be revised 358 | $timeout(function () { 359 | if (_this.onAfterChange) { 360 | _this.onAfterChange({ currentSlide: _this.currentSlide }); 361 | } 362 | }, 200); 363 | }); 364 | }; 365 | 366 | /** 367 | * moveTrack 368 | * move track to left position using css3 translate 369 | * for example left: -1000px 370 | */ 371 | this.moveTrack = function (left) { 372 | var deferred = $q.defer(); 373 | if (_this.options.vertical === false) { 374 | _this.trackStyle[_this.animType] = 'translate3d(' + left + 'px, 0px, 0px)'; 375 | } else { 376 | _this.trackStyle[_this.animType] = 'translate3d(0px, ' + left + 'px, 0px)'; 377 | } 378 | 379 | $timeout(function () { 380 | deferred.resolve('Track moved'); 381 | }, _this.options.speed); 382 | 383 | return deferred.promise; 384 | }; 385 | 386 | /** 387 | * correctTrack 388 | * @description correct track after move to animSlide we have to move track 389 | * to exactly its position 390 | */ 391 | this.correctTrack = function () { 392 | if (_this.options.infinite) { 393 | (function () { 394 | var left = 0; 395 | if (_this.slides.length > _this.options.slidesToShow) { 396 | left = -1 * (_this.currentSlide + _this.options.slidesToShow) * _this.itemWidth; 397 | } 398 | 399 | // Move without anim 400 | _this.trackStyle[_this.transitionType] = _this.transformType + ' ' + 0 + 'ms ' + _this.options.cssEase; 401 | 402 | _this.isTrackMoving = true; 403 | $timeout(function () { 404 | _this.trackStyle[_this.animType] = 'translate3d(' + left + 'px, 0, 0px)'; 405 | 406 | // Revert animation 407 | $timeout(function () { 408 | _this.refreshTrackStyle(); 409 | _this.isTrackMoving = false; 410 | }, 200); 411 | }); 412 | })(); 413 | } 414 | }; 415 | 416 | /** 417 | * Refresh track style 418 | */ 419 | this.refreshTrackStyle = function () { 420 | _this.trackStyle[_this.transitionType] = _this.transformType + ' ' + _this.options.speed + 'ms ' + _this.options.cssEase; 421 | }; 422 | 423 | /** 424 | * autoplay track 425 | * @description autoplay = true 426 | */ 427 | this.autoplayTrack = function () { 428 | if (_this.options.autoplay) { 429 | if (_this.timeout) { 430 | $timeout.cancel(_this.timeout); 431 | } 432 | 433 | _this.timeout = $timeout(function () { 434 | _this.next(); 435 | 436 | $timeout.cancel(_this.timeout); 437 | _this.timeout = null; 438 | }, _this.options.autoplaySpeed); 439 | } 440 | }; 441 | 442 | this.getSlideStyle = function (index) { 443 | var style = _this.slideStyle; 444 | if (_this.options.fade) { 445 | var left = -1 * index * _this.itemWidth; 446 | var uniqueStyle = { 447 | position: 'relative', 448 | top: '0px', 449 | left: left + 'px', 450 | 'z-index': index === _this.currentSlide ? 10 : 9, 451 | opacity: index === _this.currentSlide ? 1 : 0 452 | }; 453 | 454 | if (index >= _this.currentSlide - 1 && index <= _this.currentSlide + 1) { 455 | uniqueStyle.transition = 'opacity 250ms linear'; 456 | } 457 | 458 | style = angular.extend(style, uniqueStyle); 459 | } 460 | 461 | return style; 462 | }; 463 | 464 | /** 465 | * setupInfinite 466 | * To make carouse infinite we need close number of slidesToShow elements to 467 | * previous elements and to after elements 468 | * 469 | * length = 8, show = 4, scroll = 3, current = 0 470 | * --------- 471 | * | | 472 | * |4|5|6|7|0|1|2|3|4|5|6|7|1|2|3|4 473 | * | | 474 | * --------- 475 | */ 476 | this.setupInfinite = function () { 477 | // Clone 478 | var len = _this.slides.length; 479 | var show = _this.options.slidesToShow; 480 | 481 | var tmpTrack = angular.copy(_this.slides); 482 | 483 | if (_this.options.infinite && _this.options.fade === false) { 484 | if (len > show) { 485 | var number = show; 486 | for (var i = 0; i < number; i++) { 487 | tmpTrack.push(angular.copy(_this.slides[i])); 488 | } 489 | for (var _i = len - 1; _i >= len - show; _i--) { 490 | tmpTrack.unshift(angular.copy(_this.slides[_i])); 491 | } 492 | } 493 | } 494 | 495 | _this.slidesInTrack = tmpTrack; 496 | }; 497 | 498 | /** 499 | * get number of dosts 500 | * 501 | * @return Array 502 | */ 503 | this.getDots = function () { 504 | if (!_this.slides) { 505 | return []; 506 | } 507 | 508 | var dots = Math.ceil(_this.slides.length / _this.options.slidesToScroll); 509 | 510 | var res = []; 511 | for (var i = 0; i < dots; i++) { 512 | res.push(i); 513 | } 514 | return res; 515 | }; 516 | 517 | /** 518 | * set carousel property 519 | * 520 | * - animType 521 | * - transformType 522 | * - transitionType 523 | */ 524 | this.setProps = function () { 525 | var bodyStyle = document.body.style; 526 | 527 | /* eslint-disable */ 528 | if (bodyStyle.OTransform !== undefined) { 529 | _this.animType = 'OTransform'; 530 | _this.transformType = '-o-transform'; 531 | _this.transitionType = 'OTransition'; 532 | } 533 | if (bodyStyle.MozTransform !== undefined) { 534 | _this.animType = 'MozTransform'; 535 | _this.transformType = '-moz-transform'; 536 | _this.transitionType = 'MozTransition'; 537 | } 538 | if (bodyStyle.webkitTransform !== undefined) { 539 | _this.animType = 'webkitTransform'; 540 | _this.transformType = '-webkit-transform'; 541 | _this.transitionType = 'webkitTransition'; 542 | } 543 | if (bodyStyle.msTransform !== undefined) { 544 | _this.animType = 'msTransform'; 545 | _this.transformType = '-ms-transform'; 546 | _this.transitionType = 'msTransition'; 547 | } 548 | if (bodyStyle.transform !== undefined && _this.animType !== false) { 549 | _this.animType = 'transform'; 550 | _this.transformType = 'transform'; 551 | _this.transitionType = 'transition'; 552 | } 553 | /* eslint-enable */ 554 | 555 | _this.transformsEnabled = true; 556 | }; 557 | 558 | /** 559 | * Refresh carousel 560 | */ 561 | this.refreshCarousel = function () { 562 | if (_this.slides && _this.slides.length && _this.slides.length > _this.options.slidesToShow) { 563 | _this.isVisibleDots = true; 564 | _this.isVisiblePrev = true; 565 | _this.isVisibleNext = true; 566 | _this.isClickablePrev = true; 567 | _this.isClickableNext = true; 568 | } else { 569 | _this.isVisibleDots = false; 570 | _this.isVisiblePrev = _this.options.visiblePrev || false; 571 | _this.isVisibleNext = _this.options.visibleNext || false; 572 | _this.isClickablePrev = false; 573 | _this.isClickableNext = false; 574 | } 575 | 576 | // Re-init UI 577 | _this.initUI(); 578 | }; 579 | 580 | /** 581 | * refresh model 582 | */ 583 | $scope.$watchCollection('ctrl.slides', function (slides) { 584 | if (!slides) { 585 | return; 586 | } 587 | 588 | // Init carousel 589 | if (_this.currentSlide > slides.length - 1) { 590 | _this.currentSlide = slides.length - 1; 591 | } 592 | 593 | _this.setupInfinite(); 594 | _this.refreshCarousel(); 595 | }); 596 | 597 | /** 598 | * update when resize 599 | * 600 | * @see https://github.com/mihnsen/ui-carousel/issues/10 601 | * @author tarkant 602 | */ 603 | angular.element($window).on('resize', this.refreshCarousel); 604 | 605 | /** 606 | * cleanup when done 607 | * 608 | * @see https://github.com/mihnsen/ui-carousel/issues/10 609 | * @author tarkant 610 | */ 611 | $scope.$on('$destroy', function () { 612 | angular.element($window).off('resize'); 613 | }); 614 | 615 | // Prior to v1.5, we need to call `$onInit()` manually. 616 | // (Bindings will always be pre-assigned in these versions.) 617 | if (angular.version.major === 1 && angular.version.minor < 5) { 618 | this.$onInit(); 619 | } 620 | }]); 621 | 'use strict'; 622 | 623 | angular.module('ui.carousel.directives').directive('uiCarousel', ['$compile', '$templateCache', '$sce', function ($compile, $templateCache, $sce) { 624 | 625 | return { restrict: 'AE', 626 | bindToController: true, 627 | scope: { 628 | name: '=?', 629 | slides: '=', 630 | show: '=?slidesToShow', 631 | scroll: '=?slidesToScroll', 632 | classes: '@', 633 | fade: '=?', 634 | onChange: '=?', 635 | disableArrow: '=?', 636 | autoplay: '=?', 637 | autoplaySpeed: '=?', 638 | cssEase: '=?', 639 | speed: '=?', 640 | infinite: '=?', 641 | arrows: '=?', 642 | dots: '=?', 643 | initialSlide: '=?', 644 | visibleNext: '=?', 645 | visiblePrev: '=?', 646 | 647 | // Method 648 | onBeforeChange: '&', 649 | onAfterChange: '&', 650 | onInit: '&' 651 | }, 652 | link: function link($scope, el) { 653 | var template = angular.element($templateCache.get('ui-carousel/carousel.template.html')); 654 | 655 | // dynamic injections to override the inner layers' components 656 | var injectComponentMap = { 657 | 'carousel-item': '.carousel-item', 658 | 'carousel-prev': '.carousel-prev', 659 | 'carousel-next': '.carousel-next' 660 | }; 661 | 662 | var templateInstance = template.clone(); 663 | angular.forEach(injectComponentMap, function (innerSelector, outerSelector) { 664 | var outerElement = el[0].querySelector(outerSelector); 665 | if (outerElement) { 666 | angular.element(templateInstance[0].querySelector(innerSelector)).html(outerElement.innerHTML); 667 | } 668 | }); 669 | 670 | var compiledElement = $compile(templateInstance)($scope); 671 | el.addClass('ui-carousel').html('').append(compiledElement); 672 | }, 673 | 674 | 675 | controller: 'CarouselController', 676 | controllerAs: 'ctrl' 677 | }; 678 | }]); 679 | 'use strict'; 680 | 681 | angular.module('ui.carousel.providers').provider('Carousel', function () { 682 | var _this = this; 683 | 684 | this.options = { 685 | // Init like Slick carousel 686 | // XXX Should be revised 687 | arrows: true, 688 | autoplay: false, 689 | autoplaySpeed: 3000, 690 | cssEase: 'ease', 691 | dots: false, 692 | 693 | easing: 'linear', 694 | fade: false, 695 | infinite: true, 696 | initialSlide: 0, 697 | 698 | slidesToShow: 1, 699 | slidesToScroll: 1, 700 | speed: 500, 701 | 702 | visiblePrev: false, 703 | visibleNext: false, 704 | 705 | // Not available right now 706 | draggable: true, 707 | 708 | lazyLoad: 'ondemand', 709 | 710 | swipe: true, 711 | swipeToSlide: false, 712 | touchMove: true, 713 | 714 | vertical: false, 715 | verticalSwiping: false 716 | }; 717 | this.$get = [function () { 718 | return { 719 | setOptions: function setOptions(options) { 720 | _this.options = angular.extend(_this.options, options); 721 | }, 722 | getOptions: function getOptions() { 723 | return _this.options; 724 | } 725 | }; 726 | }]; 727 | }); 728 | 'use strict'; 729 | 730 | (function (module) { 731 | try { 732 | module = angular.module('ui.carousel'); 733 | } catch (e) { 734 | module = angular.module('ui.carousel', []); 735 | } 736 | module.run(['$templateCache', function ($templateCache) { 737 | $templateCache.put('ui-carousel/carousel.template.html', ''); 738 | }]); 739 | })(); -------------------------------------------------------------------------------- /dist/ui-carousel.min.css: -------------------------------------------------------------------------------- 1 | [class*=" ui-icon-"]:before,[class^=ui-icon-]:before,[data-icon]:before{font-family:ui-carousel!important;font-style:normal!important;font-weight:400!important;font-variant:normal!important;text-transform:none!important;speak:none;line-height:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}.ui-carousel .carousel-btn,.v-middle{display:block;position:absolute;top:50%;-webkit-transform:translate(0,-50%);-ms-transform:translate(0,-50%);-o-transform:translate(0,-50%);transform:translate(0,-50%)}@font-face{font-family:ui-carousel;src:url(fonts/ui-carousel.eot);src:url(fonts/ui-carousel.eot?#iefix) format("embedded-opentype"),url(fonts/ui-carousel.woff) format("woff"),url(fonts/ui-carousel.ttf) format("truetype"),url(fonts/ui-carousel.svg#ui-carousel) format("svg");font-weight:400;font-style:normal}[data-icon]:before{content:attr(data-icon)}.ui-icon-prev:before{content:"\61"}.ui-icon-next:before{content:"\62"}.carousel-dots li button:before,.ui-icon-dot:before{content:"\63"}.ui-carousel{display:block;margin-bottom:30px}.ui-carousel .carousel-wrapper{position:relative}.ui-carousel .track-wrapper{position:relative;display:block;overflow:hidden;margin:0;padding:0}.ui-carousel .track{position:relative;display:block;float:left}.ui-carousel .slide{float:left;height:100%;min-height:1px}.ui-carousel .carousel-btn{position:absolute;z-index:10;background-color:transparent;outline:0;border:none;font-size:20px;opacity:.75}.ui-carousel .carousel-btn:hover{opacity:1}.ui-carousel .carousel-prev .carousel-btn{left:-25px}.ui-carousel .carousel-next .carousel-btn{right:-25px}.ui-carousel .carousel-disable{opacity:.5}.ui-carousel .carousel-disable .carousel-btn:hover{opacity:.75}.carousel-dots{position:absolute;bottom:-30px;display:block;width:100%;padding:0;margin:0;list-style:none;text-align:center}.carousel-dots li{position:relative;display:inline-block;width:15px;height:15px;margin:0 5px;padding:0;cursor:pointer}.carousel-dots li button{font-size:0;line-height:0;display:block;width:15px;height:15px;padding:5px;cursor:pointer;color:transparent;border:0;outline:0;background:0 0}.carousel-dots li button:before{font-family:ui-carousel;font-size:9px;line-height:15px;position:absolute;top:0;left:0;width:15px;height:15px;text-align:center;opacity:.25;color:#000;-webkit-font-smoothing:antialiased}.carousel-dots li.carousel-active button:before{opacity:.75} 2 | /*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInVpLWNhcm91c2VsLmNzcyIsIl9mb250cy5zY3NzIiwiY2Fyb3VzZWwuc2NzcyIsIm1peGluLnNjc3MiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBNkJBLDRCQURBLHlCQVpBLG1CQ2FFLFlBQWEsc0JBQ2IsV0FBWSxpQkFDWixZQUFhLGNBQ2IsYUFBYyxpQkFDZCxlQUFnQixlQUNoQixNQUFPLEtBQ1AsWUFBYSxFQUViLHdCQUF5QixVQ29FbkIsdUJBQXdCLFlDekVoQywyQkFBQSxVQUNFLFFBQVMsTUFDVCxTQUFVLFNBQ1YsSUFBSyxJQWZMLGtCQUFtQixrQkFDZixjQUFlLGtCQUNkLGFBQWMsa0JBQ1gsVUFBVyxrQkZyQnJCLFdBQ0UsWUFBYSxZQUNiLElBQUksMkJBQ0osSUFBSSxrQ0FBQSw0QkFBb0MsNEJBQ3RDLGVBQThCLDJCQUM5QixtQkFBNkIsdUNBQzdCLGNBQ0YsWUFBYSxJQUNiLFdBQVksT0RNZCxtQkNBRSxRQUFTLGdCQXdCWCxxQkFDRSxRQUFTLE1BRVgscUJBQ0UsUUFBUyxNQ2dCWCxnQ0RkQSxvQkN1RFEsUUFBUyxNQXJHakIsYUFDRSxRQUFTLE1BQ1QsY0FBZSxLQUZqQiwrQkFLSSxTQUFVLFNBTGQsNEJBUUksU0FBVSxTQUNWLFFBQVMsTUFDVCxTQUFVLE9BQ1YsT0FBUSxFQUNSLFFBQVMsRUFaYixvQkFlSSxTQUFVLFNBQ1YsUUFBUyxNQUNULE1BQU8sS0FqQlgsb0JBb0JJLE1BQU8sS0FDUCxPQUFRLEtBQ1IsV0FBWSxJQXRCaEIsMkJBeUJJLFNBQVUsU0FDVixRQUFTLEdBRVQsaUJBQWtCLFlBQ2xCLFFBQVMsRUFDVCxZQUNBLFVBQVcsS0FDWCxRQUFTLElBaENiLGlDQW1DTSxRQUFTLEVBbkNmLDBDQXlDTSxLQUFNLE1BekNaLDBDQThDTSxNQUFPLE1BOUNiLCtCQWtESSxRQUFTLEdBbERiLG1EQXNEUSxRQUFTLElBTWpCLGVBQ0UsU0FBVSxTQUNWLE9BQVEsTUFDUixRQUFTLE1BQ1QsTUFBTyxLQUNQLFFBQVMsRUFDVCxPQUFRLEVBQ1IsV0FBWSxLQUNaLFdBQVksT0FSZCxrQkFXSSxTQUFVLFNBQ1YsUUFBUyxhQUNULE1BQU8sS0FDUCxPQUFRLEtBQ1IsT0FBUSxFQUFBLElBQ1IsUUFBUyxFQUNULE9BQVEsUUFqQloseUJBb0JNLFVBQVcsRUFDWCxZQUFhLEVBQ2IsUUFBUyxNQUNULE1BQU8sS0FDUCxPQUFRLEtBQ1IsUUFBUyxJQUNULE9BQVEsUUFDUixNQUFPLFlBQ1AsT0FBUSxFQUNSLFFBQVMsRUFDVCxlQTlCTixnQ0FpQ1EsWUFBYSxZQUNiLFVBQVcsSUFDWCxZQUFhLEtBQ2IsU0FBVSxTQUNWLElBQUssRUFDTCxLQUFNLEVBQ04sTUFBTyxLQUNQLE9BQVEsS0FFUixXQUFZLE9BQ1osUUFBUyxJQUNULE1BQU8sS0FDUCx1QkFBd0IsWUE3Q2hDLGdEQW9EVSxRQUFTIiwiZmlsZSI6InVpLWNhcm91c2VsLm1pbi5jc3MiLCJzb3VyY2VzQ29udGVudCI6WyIudi1taWRkbGUsIC51aS1jYXJvdXNlbCAuY2Fyb3VzZWwtYnRuIHtcbiAgZGlzcGxheTogYmxvY2s7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiA1MCU7XG4gIC13ZWJraXQtdHJhbnNmb3JtOiB0cmFuc2xhdGUoMCwgLTUwJSk7XG4gIC1tcy10cmFuc2Zvcm06IHRyYW5zbGF0ZSgwLCAtNTAlKTtcbiAgLW8tdHJhbnNmb3JtOiB0cmFuc2xhdGUoMCwgLTUwJSk7XG4gIHRyYW5zZm9ybTogdHJhbnNsYXRlKDAsIC01MCUpOyB9XG5cbkBmb250LWZhY2Uge1xuICBmb250LWZhbWlseTogXCJ1aS1jYXJvdXNlbFwiO1xuICBzcmM6IHVybChcImZvbnRzL3VpLWNhcm91c2VsLmVvdFwiKTtcbiAgc3JjOiB1cmwoXCJmb250cy91aS1jYXJvdXNlbC5lb3Q/I2llZml4XCIpIGZvcm1hdChcImVtYmVkZGVkLW9wZW50eXBlXCIpLCB1cmwoXCJmb250cy91aS1jYXJvdXNlbC53b2ZmXCIpIGZvcm1hdChcIndvZmZcIiksIHVybChcImZvbnRzL3VpLWNhcm91c2VsLnR0ZlwiKSBmb3JtYXQoXCJ0cnVldHlwZVwiKSwgdXJsKFwiZm9udHMvdWktY2Fyb3VzZWwuc3ZnI3VpLWNhcm91c2VsXCIpIGZvcm1hdChcInN2Z1wiKTtcbiAgZm9udC13ZWlnaHQ6IG5vcm1hbDtcbiAgZm9udC1zdHlsZTogbm9ybWFsOyB9XG5cbltkYXRhLWljb25dOmJlZm9yZSB7XG4gIGZvbnQtZmFtaWx5OiBcInVpLWNhcm91c2VsXCIgIWltcG9ydGFudDtcbiAgY29udGVudDogYXR0cihkYXRhLWljb24pO1xuICBmb250LXN0eWxlOiBub3JtYWwgIWltcG9ydGFudDtcbiAgZm9udC13ZWlnaHQ6IG5vcm1hbCAhaW1wb3J0YW50O1xuICBmb250LXZhcmlhbnQ6IG5vcm1hbCAhaW1wb3J0YW50O1xuICB0ZXh0LXRyYW5zZm9ybTogbm9uZSAhaW1wb3J0YW50O1xuICBzcGVhazogbm9uZTtcbiAgbGluZS1oZWlnaHQ6IDE7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGFudGlhbGlhc2VkO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlOyB9XG5cbltjbGFzc149XCJ1aS1pY29uLVwiXTpiZWZvcmUsXG5bY2xhc3MqPVwiIHVpLWljb24tXCJdOmJlZm9yZSB7XG4gIGZvbnQtZmFtaWx5OiBcInVpLWNhcm91c2VsXCIgIWltcG9ydGFudDtcbiAgZm9udC1zdHlsZTogbm9ybWFsICFpbXBvcnRhbnQ7XG4gIGZvbnQtd2VpZ2h0OiBub3JtYWwgIWltcG9ydGFudDtcbiAgZm9udC12YXJpYW50OiBub3JtYWwgIWltcG9ydGFudDtcbiAgdGV4dC10cmFuc2Zvcm06IG5vbmUgIWltcG9ydGFudDtcbiAgc3BlYWs6IG5vbmU7XG4gIGxpbmUtaGVpZ2h0OiAxO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhbnRpYWxpYXNlZDtcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTsgfVxuXG4udWktaWNvbi1wcmV2OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFw2MVwiOyB9XG5cbi51aS1pY29uLW5leHQ6YmVmb3JlIHtcbiAgY29udGVudDogXCJcXDYyXCI7IH1cblxuLnVpLWljb24tZG90OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFw2M1wiOyB9XG5cbi51aS1jYXJvdXNlbCB7XG4gIGRpc3BsYXk6IGJsb2NrO1xuICBtYXJnaW4tYm90dG9tOiAzMHB4OyB9XG4gIC51aS1jYXJvdXNlbCAuY2Fyb3VzZWwtd3JhcHBlciB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlOyB9XG4gIC51aS1jYXJvdXNlbCAudHJhY2std3JhcHBlciB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIG92ZXJmbG93OiBoaWRkZW47XG4gICAgbWFyZ2luOiAwO1xuICAgIHBhZGRpbmc6IDA7IH1cbiAgLnVpLWNhcm91c2VsIC50cmFjayB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIGZsb2F0OiBsZWZ0OyB9XG4gIC51aS1jYXJvdXNlbCAuc2xpZGUge1xuICAgIGZsb2F0OiBsZWZ0O1xuICAgIGhlaWdodDogMTAwJTtcbiAgICBtaW4taGVpZ2h0OiAxcHg7IH1cbiAgLnVpLWNhcm91c2VsIC5jYXJvdXNlbC1idG4ge1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICB6LWluZGV4OiAxMDtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgICBvdXRsaW5lOiBub25lO1xuICAgIGJvcmRlcjogbm9uZTtcbiAgICBmb250LXNpemU6IDIwcHg7XG4gICAgb3BhY2l0eTogLjc1OyB9XG4gICAgLnVpLWNhcm91c2VsIC5jYXJvdXNlbC1idG46aG92ZXIge1xuICAgICAgb3BhY2l0eTogMTsgfVxuICAudWktY2Fyb3VzZWwgLmNhcm91c2VsLXByZXYgLmNhcm91c2VsLWJ0biB7XG4gICAgbGVmdDogLTI1cHg7IH1cbiAgLnVpLWNhcm91c2VsIC5jYXJvdXNlbC1uZXh0IC5jYXJvdXNlbC1idG4ge1xuICAgIHJpZ2h0OiAtMjVweDsgfVxuICAudWktY2Fyb3VzZWwgLmNhcm91c2VsLWRpc2FibGUge1xuICAgIG9wYWNpdHk6IDAuNTsgfVxuICAgIC51aS1jYXJvdXNlbCAuY2Fyb3VzZWwtZGlzYWJsZSAuY2Fyb3VzZWwtYnRuOmhvdmVyIHtcbiAgICAgIG9wYWNpdHk6IC43NTsgfVxuXG4uY2Fyb3VzZWwtZG90cyB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgYm90dG9tOiAtMzBweDtcbiAgZGlzcGxheTogYmxvY2s7XG4gIHdpZHRoOiAxMDAlO1xuICBwYWRkaW5nOiAwO1xuICBtYXJnaW46IDA7XG4gIGxpc3Qtc3R5bGU6IG5vbmU7XG4gIHRleHQtYWxpZ246IGNlbnRlcjsgfVxuICAuY2Fyb3VzZWwtZG90cyBsaSB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgICB3aWR0aDogMTVweDtcbiAgICBoZWlnaHQ6IDE1cHg7XG4gICAgbWFyZ2luOiAwIDVweDtcbiAgICBwYWRkaW5nOiAwO1xuICAgIGN1cnNvcjogcG9pbnRlcjsgfVxuICAgIC5jYXJvdXNlbC1kb3RzIGxpIGJ1dHRvbiB7XG4gICAgICBmb250LXNpemU6IDA7XG4gICAgICBsaW5lLWhlaWdodDogMDtcbiAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgd2lkdGg6IDE1cHg7XG4gICAgICBoZWlnaHQ6IDE1cHg7XG4gICAgICBwYWRkaW5nOiA1cHg7XG4gICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICBjb2xvcjogdHJhbnNwYXJlbnQ7XG4gICAgICBib3JkZXI6IDA7XG4gICAgICBvdXRsaW5lOiBub25lO1xuICAgICAgYmFja2dyb3VuZDogdHJhbnNwYXJlbnQ7IH1cbiAgICAgIC5jYXJvdXNlbC1kb3RzIGxpIGJ1dHRvbjpiZWZvcmUge1xuICAgICAgICBmb250LWZhbWlseTogdWktY2Fyb3VzZWw7XG4gICAgICAgIGZvbnQtc2l6ZTogOXB4O1xuICAgICAgICBsaW5lLWhlaWdodDogMTVweDtcbiAgICAgICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgICAgICB0b3A6IDBweDtcbiAgICAgICAgbGVmdDogMHB4O1xuICAgICAgICB3aWR0aDogMTVweDtcbiAgICAgICAgaGVpZ2h0OiAxNXB4O1xuICAgICAgICBjb250ZW50OiBcIlxcNjNcIjtcbiAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyO1xuICAgICAgICBvcGFjaXR5OiAwLjI1O1xuICAgICAgICBjb2xvcjogYmxhY2s7XG4gICAgICAgIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGFudGlhbGlhc2VkOyB9XG4gICAgLmNhcm91c2VsLWRvdHMgbGkuY2Fyb3VzZWwtYWN0aXZlIGJ1dHRvbjpiZWZvcmUge1xuICAgICAgb3BhY2l0eTogLjc1OyB9XG4iLCJAY2hhcnNldCBcIlVURi04XCI7XG5cbkBmb250LWZhY2Uge1xuICBmb250LWZhbWlseTogXCJ1aS1jYXJvdXNlbFwiO1xuICBzcmM6dXJsKFwiZm9udHMvdWktY2Fyb3VzZWwuZW90XCIpO1xuICBzcmM6dXJsKFwiZm9udHMvdWktY2Fyb3VzZWwuZW90PyNpZWZpeFwiKSBmb3JtYXQoXCJlbWJlZGRlZC1vcGVudHlwZVwiKSxcbiAgICB1cmwoXCJmb250cy91aS1jYXJvdXNlbC53b2ZmXCIpIGZvcm1hdChcIndvZmZcIiksXG4gICAgdXJsKFwiZm9udHMvdWktY2Fyb3VzZWwudHRmXCIpIGZvcm1hdChcInRydWV0eXBlXCIpLFxuICAgIHVybChcImZvbnRzL3VpLWNhcm91c2VsLnN2ZyN1aS1jYXJvdXNlbFwiKSBmb3JtYXQoXCJzdmdcIik7XG4gIGZvbnQtd2VpZ2h0OiBub3JtYWw7XG4gIGZvbnQtc3R5bGU6IG5vcm1hbDtcblxufVxuXG5bZGF0YS1pY29uXTpiZWZvcmUge1xuICBmb250LWZhbWlseTogXCJ1aS1jYXJvdXNlbFwiICFpbXBvcnRhbnQ7XG4gIGNvbnRlbnQ6IGF0dHIoZGF0YS1pY29uKTtcbiAgZm9udC1zdHlsZTogbm9ybWFsICFpbXBvcnRhbnQ7XG4gIGZvbnQtd2VpZ2h0OiBub3JtYWwgIWltcG9ydGFudDtcbiAgZm9udC12YXJpYW50OiBub3JtYWwgIWltcG9ydGFudDtcbiAgdGV4dC10cmFuc2Zvcm06IG5vbmUgIWltcG9ydGFudDtcbiAgc3BlYWs6IG5vbmU7XG4gIGxpbmUtaGVpZ2h0OiAxO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhbnRpYWxpYXNlZDtcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbn1cblxuW2NsYXNzXj1cInVpLWljb24tXCJdOmJlZm9yZSxcbltjbGFzcyo9XCIgdWktaWNvbi1cIl06YmVmb3JlIHtcbiAgZm9udC1mYW1pbHk6IFwidWktY2Fyb3VzZWxcIiAhaW1wb3J0YW50O1xuICBmb250LXN0eWxlOiBub3JtYWwgIWltcG9ydGFudDtcbiAgZm9udC13ZWlnaHQ6IG5vcm1hbCAhaW1wb3J0YW50O1xuICBmb250LXZhcmlhbnQ6IG5vcm1hbCAhaW1wb3J0YW50O1xuICB0ZXh0LXRyYW5zZm9ybTogbm9uZSAhaW1wb3J0YW50O1xuICBzcGVhazogbm9uZTtcbiAgbGluZS1oZWlnaHQ6IDE7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGFudGlhbGlhc2VkO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xufVxuXG4udWktaWNvbi1wcmV2OmJlZm9yZSB7XG4gIGNvbnRlbnQ6IFwiXFw2MVwiO1xufVxuLnVpLWljb24tbmV4dDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcNjJcIjtcbn1cbi51aS1pY29uLWRvdDpiZWZvcmUge1xuICBjb250ZW50OiBcIlxcNjNcIjtcbn1cbiIsIi51aS1jYXJvdXNlbCB7XG4gIGRpc3BsYXk6IGJsb2NrO1xuICBtYXJnaW4tYm90dG9tOiAzMHB4O1xuXG4gIC5jYXJvdXNlbC13cmFwcGVyIHtcbiAgICBwb3NpdGlvbjogcmVsYXRpdmU7XG4gIH1cbiAgLnRyYWNrLXdyYXBwZXIge1xuICAgIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgICBkaXNwbGF5OiBibG9jaztcbiAgICBvdmVyZmxvdzogaGlkZGVuO1xuICAgIG1hcmdpbjogMDtcbiAgICBwYWRkaW5nOiAwO1xuICB9XG4gIC50cmFjayB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIGZsb2F0OiBsZWZ0O1xuICB9XG4gIC5zbGlkZSB7XG4gICAgZmxvYXQ6IGxlZnQ7XG4gICAgaGVpZ2h0OiAxMDAlO1xuICAgIG1pbi1oZWlnaHQ6IDFweDtcbiAgfVxuICAuY2Fyb3VzZWwtYnRuIHtcbiAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gICAgei1pbmRleDogMTA7XG4gICAgQGV4dGVuZCAudi1taWRkbGU7XG4gICAgYmFja2dyb3VuZC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gICAgb3V0bGluZTogbm9uZTtcbiAgICBib3JkZXI6IG5vbmU7XG4gICAgZm9udC1zaXplOiAyMHB4O1xuICAgIG9wYWNpdHk6IC43NTtcblxuICAgICY6aG92ZXIge1xuICAgICAgb3BhY2l0eTogMTtcbiAgICB9XG4gIH1cblxuICAuY2Fyb3VzZWwtcHJldiB7XG4gICAgLmNhcm91c2VsLWJ0biB7XG4gICAgICBsZWZ0OiAtMjVweDtcbiAgICB9XG4gIH1cbiAgLmNhcm91c2VsLW5leHQge1xuICAgIC5jYXJvdXNlbC1idG4ge1xuICAgICAgcmlnaHQ6IC0yNXB4O1xuICAgIH1cbiAgfVxuICAuY2Fyb3VzZWwtZGlzYWJsZSB7XG4gICAgb3BhY2l0eTogMC41O1xuXG4gICAgLmNhcm91c2VsLWJ0biB7XG4gICAgICAmOmhvdmVyIHtcbiAgICAgICAgb3BhY2l0eTogLjc1O1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG4uY2Fyb3VzZWwtZG90cyB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgYm90dG9tOiAtMzBweDtcbiAgZGlzcGxheTogYmxvY2s7XG4gIHdpZHRoOiAxMDAlO1xuICBwYWRkaW5nOiAwO1xuICBtYXJnaW46IDA7XG4gIGxpc3Qtc3R5bGU6IG5vbmU7XG4gIHRleHQtYWxpZ246IGNlbnRlcjtcblxuICBsaSB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgICB3aWR0aDogMTVweDtcbiAgICBoZWlnaHQ6IDE1cHg7XG4gICAgbWFyZ2luOiAwIDVweDtcbiAgICBwYWRkaW5nOiAwO1xuICAgIGN1cnNvcjogcG9pbnRlcjtcblxuICAgIGJ1dHRvbiB7XG4gICAgICBmb250LXNpemU6IDA7XG4gICAgICBsaW5lLWhlaWdodDogMDtcbiAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgd2lkdGg6IDE1cHg7XG4gICAgICBoZWlnaHQ6IDE1cHg7XG4gICAgICBwYWRkaW5nOiA1cHg7XG4gICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICBjb2xvcjogdHJhbnNwYXJlbnQ7XG4gICAgICBib3JkZXI6IDA7XG4gICAgICBvdXRsaW5lOiBub25lO1xuICAgICAgYmFja2dyb3VuZDogdHJhbnNwYXJlbnQ7XG5cbiAgICAgICY6YmVmb3JlIHtcbiAgICAgICAgZm9udC1mYW1pbHk6IHVpLWNhcm91c2VsO1xuICAgICAgICBmb250LXNpemU6IDlweDtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDE1cHg7XG4gICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgICAgdG9wOiAwcHg7XG4gICAgICAgIGxlZnQ6IDBweDtcbiAgICAgICAgd2lkdGg6IDE1cHg7XG4gICAgICAgIGhlaWdodDogMTVweDtcbiAgICAgICAgY29udGVudDogXCJcXDYzXCI7XG4gICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjtcbiAgICAgICAgb3BhY2l0eTogMC4yNTtcbiAgICAgICAgY29sb3I6IGJsYWNrO1xuICAgICAgICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhbnRpYWxpYXNlZDtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAmLmNhcm91c2VsLWFjdGl2ZSB7XG4gICAgICBidXR0b24ge1xuICAgICAgICAmOmJlZm9yZSB7XG4gICAgICAgICAgb3BhY2l0eTogLjc1O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG59XG4iLCJAbWl4aW4gdHJhbnNpdGlvbi10cmFuc2Zvcm0oJGR1cmF0aW9uOiAyMDBtcywgJGVhc2luZzogbGluZWFyKSB7XG4gIC13ZWJraXQtdHJhbnNpdGlvbjogLXdlYmtpdC10cmFuc2Zvcm0gJGR1cmF0aW9uICRlYXNpbmc7XG4gIC1tb3otdHJhbnNpdGlvbjogLW1vei10cmFuc2Zvcm0gJGR1cmF0aW9uICRlYXNpbmc7XG4gIHRyYW5zaXRpb246IHRyYW5zZm9ybSAkZHVyYXRpb24gJGVhc2luZztcbn1cbkBtaXhpbiB0cmFuc2Zvcm0oJHAuLi4pIHtcbiAgLXdlYmtpdC10cmFuc2Zvcm06ICRwO1xuICAtbW96LXRyYW5zZm9ybTogJHA7XG4gIHRyYW5zZm9ybTogJHA7XG59XG5cbkBtaXhpbiB0cmFuc2l0aW9uLW11bHRpKCR2YWx1ZS4uLikge1xuICAtd2Via2l0LXRyYW5zaXRpb246ICR2YWx1ZTtcbiAgLW1vei10cmFuc2l0aW9uOiAkdmFsdWU7XG4gIC1tcy10cmFuc2l0aW9uOiAkdmFsdWU7XG4gIC1vLXRyYW5zaXRpb246ICR2YWx1ZTtcbiAgdHJhbnNpdGlvbjogJHZhbHVlO1xufVxuXG5AbWl4aW4gdHJhbnNsYXRlKCR4LCAkeSkge1xuICAtd2Via2l0LXRyYW5zZm9ybTogdHJhbnNsYXRlKCR4LCAkeSk7XG4gICAgICAtbXMtdHJhbnNmb3JtOiB0cmFuc2xhdGUoJHgsICR5KTsgLy8gSUU5IG9ubHlcbiAgICAgICAtby10cmFuc2Zvcm06IHRyYW5zbGF0ZSgkeCwgJHkpO1xuICAgICAgICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlKCR4LCAkeSk7XG59XG5AbWl4aW4gdHJhbnNpdGlvbigkdHJhbnNpdGlvbi4uLikge1xuICAtd2Via2l0LXRyYW5zaXRpb246ICR0cmFuc2l0aW9uO1xuICAgICAgIC1vLXRyYW5zaXRpb246ICR0cmFuc2l0aW9uO1xuICAgICAgICAgIHRyYW5zaXRpb246ICR0cmFuc2l0aW9uO1xufVxuXG4vLyBQb3NpdGlvblxuLnYtbWlkZGxlIHtcbiAgZGlzcGxheTogYmxvY2s7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiA1MCU7XG4gIEBpbmNsdWRlIHRyYW5zbGF0ZSgwLCAtNTAlKTtcbn1cbiJdfQ== */ 3 | -------------------------------------------------------------------------------- /dist/ui-carousel.min.js: -------------------------------------------------------------------------------- 1 | "use strict";!function(e){e.module("ui.carousel.config",[]).value("ui.carousel.config",{debug:!0}),e.module("ui.carousel.providers",[]),e.module("ui.carousel.controllers",[]),e.module("ui.carousel.directives",[]),e.module("ui.carousel",["ui.carousel.config","ui.carousel.directives","ui.carousel.controllers","ui.carousel.providers"])}(angular),angular.module("ui.carousel.controllers").controller("CarouselController",["$scope","$element","$timeout","$q","Carousel","$window",function(e,i,t,s,o,n){var r=this;this.$onInit=function(){r.initOptions(),r.initRanges(),r.setProps(),r.setupInfinite()},this.initOptions=function(){r.options=angular.extend({},o.getOptions()),void 0!==r.initialSlide&&(r.options.initialSlide=r.initialSlide),void 0!==r.fade&&(r.options.fade=r.fade),void 0!==r.autoplay&&(r.options.autoplay=r.autoplay),void 0!==r.autoplaySpeed&&(r.options.autoplaySpeed=r.autoplaySpeed),void 0!==r.cssEase&&(r.options.cssEase=r.cssEase),void 0!==r.speed&&(r.options.speed=r.speed),void 0!==r.infinite&&(r.options.infinite=r.infinite),void 0!==r.arrows&&(r.options.arrows=r.arrows),void 0!==r.dots&&(r.options.dots=r.dots),void 0!==r.visiblePrev&&(r.options.visiblePrev=r.visiblePrev),void 0!==r.visibleNext&&(r.options.visibleNext=r.visibleNext),r.options.fade?(r.options.slidesToShow=1,r.options.slidesToScroll=1):(r.show&&(r.options.slidesToShow=r.show),r.scroll&&(r.options.slidesToScroll=r.scroll))},this.initRanges=function(){r.slides||(r.slides=[]),r.isCarouselReady=!1,r.isTrackMoving=!1,r.track=i.find(".track"),r.width=1,r.currentSlide=r.options.initialSlide,r.trackStyle={},r.slideStyle={},r.isVisibleDots=!1,r.isVisiblePrev=r.options.visiblePrev,r.isVisibleNext=r.options.visibleNext,r.isClickablePrev=!1,r.isClickableNext=!1,r.animType=null,r.transformType=null,r.transitionType=null},this.initUI=function(){r.width=i[0].clientWidth,r.initTrack(),t(function(){r.updateItemStyle()},200)},this.updateItemStyle=function(){r.itemWidth=r.width/r.options.slidesToShow,r.slideStyle={width:r.itemWidth+"px"}},this.initTrack=function(){var e=r.width/r.options.slidesToShow,i=e*r.slidesInTrack.length;r.trackStyle.width=i+"px",r.slideHandler(r.currentSlide).finally(function(){r.isCarouselReady=!0,r.options.fade||r.refreshTrackStyle(),r.onInit&&r.onInit()}).catch(function(){})},this.next=function(){if(!r.isClickableNext)return!1;var e=r.getIndexOffset(),i=0===e?r.options.slidesToScroll:e;r.slideHandler(r.currentSlide+i).catch(function(){})},this.prev=function(){if(!r.isClickablePrev)return!1;var e=r.getIndexOffset(),i=0===e?r.options.slidesToScroll:r.options.slidesToShow-e;r.slideHandler(r.currentSlide-i).catch(function(){})},this.getIndexOffset=function(){var e=r.slides.length%r.options.slidesToScroll!==0,i=e?0:(r.slides.length-r.currentSlide)%r.options.slidesToScroll;return i},this.movePage=function(e){var i=r.options.slidesToScroll*e;r.slideHandler(i).catch(function(){})},this.slideHandler=function(e){if(!r.slides)return s.reject("Carousel not fully setup");if(r.isTrackMoving)return s.reject("Track is moving");var i=r.slides.length,o=r.options.slidesToShow;if(o>=i)return r.correctTrack(),s.reject("Length of slides smaller than slides to show");var n=e,l=null;if(l=0>n?i%r.options.slidesToScroll!==0?i-i%r.options.slidesToScroll:i+n:n>=i?i%r.options.slidesToScroll!==0?0:n-i:n,r.onBeforeChange&&r.onBeforeChange({currentSlide:r.currentSlide,target:l}),r.options.fade)return r.currentSlide=l,t(function(){r.autoplayTrack(),r.onAfterChange&&r.onAfterChange({currentSlide:r.currentSlide})},r.options.speed),s.when("Handler fade");var a=r.width/r.options.slidesToShow,c=-1*l*a;return r.options.infinite&&(c=-1*(n+o)*a),r.isTrackMoving=!0,r.moveTrack(c).then(function(){r.isTrackMoving=!1,r.currentSlide=l,r.autoplayTrack(),l!==n&&r.correctTrack(),r.options.infinite||(0===r.currentSlide?(r.isClickablePrev=!1,r.isClickableNext=!0):r.currentSlide===r.slidesInTrack.length-r.options.slidesToShow?(r.isClickableNext=!1,r.isClickablePrev=!0):(r.isClickablePrev=!0,r.isClickableNext=!0)),t(function(){r.onAfterChange&&r.onAfterChange({currentSlide:r.currentSlide})},200)})},this.moveTrack=function(e){var i=s.defer();return r.trackStyle[r.animType]=r.options.vertical===!1?"translate3d("+e+"px, 0px, 0px)":"translate3d(0px, "+e+"px, 0px)",t(function(){i.resolve("Track moved")},r.options.speed),i.promise},this.correctTrack=function(){r.options.infinite&&!function(){var e=0;r.slides.length>r.options.slidesToShow&&(e=-1*(r.currentSlide+r.options.slidesToShow)*r.itemWidth),r.trackStyle[r.transitionType]=r.transformType+" 0ms "+r.options.cssEase,r.isTrackMoving=!0,t(function(){r.trackStyle[r.animType]="translate3d("+e+"px, 0, 0px)",t(function(){r.refreshTrackStyle(),r.isTrackMoving=!1},200)})}()},this.refreshTrackStyle=function(){r.trackStyle[r.transitionType]=r.transformType+" "+r.options.speed+"ms "+r.options.cssEase},this.autoplayTrack=function(){r.options.autoplay&&(r.timeout&&t.cancel(r.timeout),r.timeout=t(function(){r.next(),t.cancel(r.timeout),r.timeout=null},r.options.autoplaySpeed))},this.getSlideStyle=function(e){var i=r.slideStyle;if(r.options.fade){var t=-1*e*r.itemWidth,s={position:"relative",top:"0px",left:t+"px","z-index":e===r.currentSlide?10:9,opacity:e===r.currentSlide?1:0};e>=r.currentSlide-1&&e<=r.currentSlide+1&&(s.transition="opacity 250ms linear"),i=angular.extend(i,s)}return i},this.setupInfinite=function(){var e=r.slides.length,i=r.options.slidesToShow,t=angular.copy(r.slides);if(r.options.infinite&&r.options.fade===!1&&e>i){for(var s=i,o=0;s>o;o++)t.push(angular.copy(r.slides[o]));for(var n=e-1;n>=e-i;n--)t.unshift(angular.copy(r.slides[n]))}r.slidesInTrack=t},this.getDots=function(){if(!r.slides)return[];for(var e=Math.ceil(r.slides.length/r.options.slidesToScroll),i=[],t=0;e>t;t++)i.push(t);return i},this.setProps=function(){var e=document.body.style;void 0!==e.OTransform&&(r.animType="OTransform",r.transformType="-o-transform",r.transitionType="OTransition"),void 0!==e.MozTransform&&(r.animType="MozTransform",r.transformType="-moz-transform",r.transitionType="MozTransition"),void 0!==e.webkitTransform&&(r.animType="webkitTransform",r.transformType="-webkit-transform",r.transitionType="webkitTransition"),void 0!==e.msTransform&&(r.animType="msTransform",r.transformType="-ms-transform",r.transitionType="msTransition"),void 0!==e.transform&&r.animType!==!1&&(r.animType="transform",r.transformType="transform",r.transitionType="transition"),r.transformsEnabled=!0},this.refreshCarousel=function(){r.slides&&r.slides.length&&r.slides.length>r.options.slidesToShow?(r.isVisibleDots=!0,r.isVisiblePrev=!0,r.isVisibleNext=!0,r.isClickablePrev=!0,r.isClickableNext=!0):(r.isVisibleDots=!1,r.isVisiblePrev=r.options.visiblePrev||!1,r.isVisibleNext=r.options.visibleNext||!1,r.isClickablePrev=!1,r.isClickableNext=!1),r.initUI()},e.$watchCollection("ctrl.slides",function(e){e&&(r.currentSlide>e.length-1&&(r.currentSlide=e.length-1),r.setupInfinite(),r.refreshCarousel())}),angular.element(n).on("resize",this.refreshCarousel),e.$on("$destroy",function(){angular.element(n).off("resize")}),1===angular.version.major&&angular.version.minor<5&&this.$onInit()}]),angular.module("ui.carousel.directives").directive("uiCarousel",["$compile","$templateCache","$sce",function(e,i){return{restrict:"AE",bindToController:!0,scope:{name:"=?",slides:"=",show:"=?slidesToShow",scroll:"=?slidesToScroll",classes:"@",fade:"=?",onChange:"=?",disableArrow:"=?",autoplay:"=?",autoplaySpeed:"=?",cssEase:"=?",speed:"=?",infinite:"=?",arrows:"=?",dots:"=?",initialSlide:"=?",visibleNext:"=?",visiblePrev:"=?",onBeforeChange:"&",onAfterChange:"&",onInit:"&"},link:function(t,s){var o=angular.element(i.get("ui-carousel/carousel.template.html")),n={"carousel-item":".carousel-item","carousel-prev":".carousel-prev","carousel-next":".carousel-next"},r=o.clone();angular.forEach(n,function(e,i){var t=s[0].querySelector(i);t&&angular.element(r[0].querySelector(e)).html(t.innerHTML)});var l=e(r)(t);s.addClass("ui-carousel").html("").append(l)},controller:"CarouselController",controllerAs:"ctrl"}}]),angular.module("ui.carousel.providers").provider("Carousel",function(){var e=this;this.options={arrows:!0,autoplay:!1,autoplaySpeed:3e3,cssEase:"ease",dots:!1,easing:"linear",fade:!1,infinite:!0,initialSlide:0,slidesToShow:1,slidesToScroll:1,speed:500,visiblePrev:!1,visibleNext:!1,draggable:!0,lazyLoad:"ondemand",swipe:!0,swipeToSlide:!1,touchMove:!0,vertical:!1,verticalSwiping:!1},this.$get=[function(){return{setOptions:function(i){e.options=angular.extend(e.options,i)},getOptions:function(){return e.options}}}]}),function(e){try{e=angular.module("ui.carousel")}catch(i){e=angular.module("ui.carousel",[])}e.run(["$templateCache",function(e){e.put("ui-carousel/carousel.template.html",'')}])}(); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | , karma = require('karma').server 3 | , concat = require('gulp-concat') 4 | , rename = require('gulp-rename') 5 | , path = require('path') 6 | , plumber = require('gulp-plumber') 7 | , runSequence = require('run-sequence') 8 | , jshint = require('gulp-jshint') 9 | , babel = require('gulp-babel') 10 | , sass = require('gulp-sass') 11 | , pug = require('gulp-pug') 12 | , ngHtml2Js = require('gulp-ng-html2js') 13 | , sourcemaps = require('gulp-sourcemaps') 14 | , usemin = require('gulp-usemin') 15 | , uglify = require('gulp-uglify') 16 | , minifyCss = require('gulp-minify-css') 17 | , minifyHtml = require('gulp-minify-html'); 18 | 19 | 20 | /** 21 | * File patterns 22 | **/ 23 | 24 | // Root directory 25 | var rootDirectory = path.resolve('./'); 26 | 27 | // Source directory for build process 28 | var sourceDirectory = path.join(rootDirectory, './src'); 29 | 30 | // tests 31 | var testDirectory = path.join(rootDirectory, './test/unit'); 32 | 33 | var sourceFiles = [ 34 | 35 | // Make sure module files are handled first 36 | path.join(sourceDirectory, '/**/*.module.js'), 37 | 38 | // Then add all JavaScript files 39 | path.join(sourceDirectory, 'ui-carousel/**/*.js') 40 | ]; 41 | 42 | var cssFiles = path.join(sourceDirectory, '/ui-carousel/scss/ui-carousel.scss'); 43 | var pugFiles = path.join(sourceDirectory, '/ui-carousel/templates/**/*.pug'); 44 | var demoFiles = path.join(sourceDirectory, '/demo'); 45 | 46 | var lintFiles = [ 47 | 'gulpfile.js', 48 | // Karma configuration 49 | 'karma-*.conf.js' 50 | ].concat(sourceFiles); 51 | 52 | gulp.task('build', ['pug'], function() { 53 | gulp.src(sourceFiles.concat([ './.tmp/carousel.template.js' ])) 54 | .pipe(plumber()) 55 | .pipe(babel({ 56 | presets: ['es2015'] 57 | })) 58 | .pipe(concat('ui-carousel.js')) 59 | .pipe(gulp.dest('./dist/')) 60 | .pipe(uglify()) 61 | .pipe(rename('ui-carousel.min.js')) 62 | .pipe(gulp.dest('./dist')); 63 | }); 64 | 65 | /** 66 | * Process 67 | */ 68 | gulp.task('process-all', function (done) { 69 | runSequence('pug', 'demo', 'jshint', 'build', 'test', done); 70 | }); 71 | 72 | /** 73 | * Watch task 74 | */ 75 | gulp.task('watch', function () { 76 | 77 | // Watch JavaScript files 78 | gulp.watch(sourceFiles, ['process-all']); 79 | gulp.watch(pugFiles, ['process-all']); 80 | gulp.watch(path.join(sourceDirectory, '/**/*.scss'), ['scss']); 81 | gulp.watch(path.join(sourceDirectory, '/ui-carousel/fonts/**/*'), ['other']); 82 | gulp.watch(path.join(demoFiles, '/**/*'), ['demo']); 83 | 84 | // watch test files and re-run unit tests when changed 85 | gulp.watch(path.join(testDirectory, '/**/*.js'), ['test']); 86 | }); 87 | 88 | /** 89 | * stylesheet 90 | */ 91 | gulp.task('scss', function() { 92 | return gulp.src(cssFiles) 93 | .pipe(sourcemaps.init()) 94 | .pipe(sass().on('error', sass.logError)) 95 | .pipe(gulp.dest('./dist')) 96 | .pipe(minifyCss()) 97 | .pipe(rename('ui-carousel.min.css')) 98 | .pipe(sourcemaps.write()) 99 | .pipe(gulp.dest('./dist')); 100 | }); 101 | 102 | gulp.task('other', function () { 103 | return gulp.src(sourceDirectory + '/ui-carousel/fonts/**/*') 104 | .pipe(gulp.dest('./dist/fonts')); 105 | }); 106 | 107 | gulp.task('pug', function() { 108 | return gulp.src(pugFiles) 109 | .pipe(pug({})) 110 | .pipe(gulp.dest('./.tmp')) 111 | .pipe(ngHtml2Js({ 112 | moduleName: 'ui.carousel', 113 | prefix: 'ui-carousel/' 114 | })) 115 | .pipe(gulp.dest('./.tmp')); 116 | }); 117 | 118 | gulp.task('demo', function() { 119 | gulp.src(demoFiles + '/*.pug') 120 | .pipe(pug({ pretty: true })) 121 | .pipe(gulp.dest('./demo')); 122 | 123 | gulp.src(demoFiles + '/*.scss') 124 | .pipe(sass().on('error', sass.logError)) 125 | .pipe(gulp.dest('./demo')); 126 | 127 | gulp.src(demoFiles + '/*.js') 128 | .pipe(babel({ 129 | presets: ['es2015'] 130 | })) 131 | .pipe(concat('main.js')) 132 | .pipe(gulp.dest('./demo')); 133 | }); 134 | 135 | /** 136 | * Validate source JavaScript 137 | */ 138 | gulp.task('jshint', function () { 139 | return gulp.src(lintFiles) 140 | .pipe(plumber()) 141 | .pipe(jshint()) 142 | .pipe(jshint.reporter('jshint-stylish')) 143 | .pipe(jshint.reporter('fail')); 144 | }); 145 | 146 | /** 147 | * Run test once and exit 148 | */ 149 | gulp.task('test', function (done) { 150 | karma.start({ 151 | configFile: __dirname + '/karma-src.conf.js', 152 | singleRun: true 153 | }, done); 154 | }); 155 | 156 | /** 157 | * Run test once and exit 158 | */ 159 | gulp.task('test-dist-concatenated', function (done) { 160 | karma.start({ 161 | configFile: __dirname + '/karma-dist-concatenated.conf.js', 162 | singleRun: true 163 | }, done); 164 | }); 165 | 166 | /** 167 | * Run test once and exit 168 | */ 169 | gulp.task('test-dist-minified', function (done) { 170 | karma.start({ 171 | configFile: __dirname + '/karma-dist-minified.conf.js', 172 | singleRun: true 173 | }, done); 174 | }); 175 | 176 | 177 | /** 178 | * gh-pages pubish 179 | */ 180 | gulp.task('gh-pages', ['build'], function() { 181 | gulp.src('./demo/index.html') 182 | .pipe(usemin({ 183 | css: [ minifyCss(), 'concat' ], 184 | html: [ minifyHtml({ empty: true }) ], 185 | js: [ uglify() ], 186 | })) 187 | .pipe(gulp.dest('_gh-pages/')); 188 | 189 | gulp.src('./demo/hero.png') 190 | .pipe(gulp.dest('_gh-pages/')); 191 | }); 192 | 193 | gulp.task('default', function () { 194 | runSequence('watch'); 195 | }); 196 | -------------------------------------------------------------------------------- /karma-dist-concatenated.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Aug 21 2014 10:24:39 GMT+0200 (CEST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jasmine'], 14 | 15 | plugins: [ 16 | 'karma-jasmine', 17 | 'karma-phantomjs-launcher' 18 | ], 19 | 20 | // list of files / patterns to load in the browser 21 | files: [ 22 | 'bower_components/angular/angular.js', 23 | 'bower_components/angular-sanitize/angular-sanitize.js', 24 | 'bower_components/angular-mocks/angular-mocks.js', 25 | 'dist/ui-carousel.js', 26 | 'test/unit/**/*.js' 27 | ], 28 | 29 | 30 | // list of files to exclude 31 | exclude: [ 32 | ], 33 | 34 | 35 | // preprocess matching files before serving them to the browser 36 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 37 | preprocessors: { 38 | }, 39 | 40 | 41 | // test results reporter to use 42 | // possible values: 'dots', 'progress' 43 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 44 | reporters: ['progress'], 45 | 46 | 47 | // web server port 48 | port: 9876, 49 | 50 | 51 | // enable / disable colors in the output (reporters and logs) 52 | colors: true, 53 | 54 | 55 | // level of logging 56 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 57 | logLevel: config.LOG_INFO, 58 | 59 | 60 | // enable / disable watching file and executing tests whenever any file changes 61 | autoWatch: true, 62 | 63 | 64 | // start these browsers 65 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 66 | browsers: ['PhantomJS'], 67 | 68 | 69 | // Continuous Integration mode 70 | // if true, Karma captures browsers, runs the tests and exits 71 | singleRun: false 72 | }); 73 | }; 74 | -------------------------------------------------------------------------------- /karma-dist-minified.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Aug 21 2014 10:24:39 GMT+0200 (CEST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jasmine'], 14 | 15 | plugins: [ 16 | 'karma-jasmine', 17 | 'karma-phantomjs-launcher' 18 | ], 19 | 20 | // list of files / patterns to load in the browser 21 | files: [ 22 | 'bower_components/angular/angular.js', 23 | 'bower_components/angular-sanitize/angular-sanitize.js', 24 | 'bower_components/angular-mocks/angular-mocks.js', 25 | 'dist/ui-carousel.min.js', 26 | 'test/unit/**/*.js' 27 | ], 28 | 29 | 30 | // list of files to exclude 31 | exclude: [ 32 | ], 33 | 34 | 35 | // preprocess matching files before serving them to the browser 36 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 37 | preprocessors: { 38 | }, 39 | 40 | 41 | // test results reporter to use 42 | // possible values: 'dots', 'progress' 43 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 44 | reporters: ['progress'], 45 | 46 | 47 | // web server port 48 | port: 9876, 49 | 50 | 51 | // enable / disable colors in the output (reporters and logs) 52 | colors: true, 53 | 54 | 55 | // level of logging 56 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 57 | logLevel: config.LOG_INFO, 58 | 59 | 60 | // enable / disable watching file and executing tests whenever any file changes 61 | autoWatch: true, 62 | 63 | 64 | // start these browsers 65 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 66 | browsers: ['PhantomJS'], 67 | 68 | 69 | // Continuous Integration mode 70 | // if true, Karma captures browsers, runs the tests and exits 71 | singleRun: false 72 | }); 73 | }; 74 | -------------------------------------------------------------------------------- /karma-src.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Aug 21 2014 10:24:39 GMT+0200 (CEST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jasmine'], 14 | 15 | plugins: [ 16 | 'karma-jasmine', 17 | 'karma-phantomjs-launcher', 18 | 'karma-babel-preprocessor', 19 | 'karma-spec-reporter' 20 | ], 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | 'bower_components/angular/angular.js', 25 | 'bower_components/angular-sanitize/angular-sanitize.js', 26 | 'bower_components/angular-mocks/angular-mocks.js', 27 | 'src/**/*.module.js', 28 | 'src/**/*.js', 29 | 'test/unit/**/*.js' 30 | ], 31 | 32 | usePolling: true, 33 | 34 | 35 | // list of files to exclude 36 | exclude: [ 37 | ], 38 | 39 | 40 | // preprocess matching files before serving them to the browser 41 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 42 | preprocessors: { 43 | 'src/**/*.js': ['babel'], 44 | 'test/**/*.js': ['babel'] 45 | }, 46 | 47 | babelPreprocessor: { 48 | options: { 49 | presets: ['es2015'], 50 | sourceMap: 'inline' 51 | }, 52 | filename: function(file) { 53 | return file.originalPath.replace(/\.js$/, '.es5.js'); 54 | }, 55 | sourceFileName: function(file) { 56 | return file.originalPath; 57 | } 58 | }, 59 | 60 | 61 | // test results reporter to use 62 | // possible values: 'dots', 'progress' 63 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 64 | reporters: ['progress', 'spec'], 65 | 66 | 67 | // web server port 68 | port: 9876, 69 | 70 | 71 | // enable / disable colors in the output (reporters and logs) 72 | colors: true, 73 | 74 | 75 | // level of logging 76 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 77 | logLevel: config.LOG_INFO, 78 | 79 | 80 | // enable / disable watching file and executing tests whenever any file changes 81 | autoWatch: true, 82 | 83 | 84 | // start these browsers 85 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 86 | browsers: ['PhantomJS'], 87 | 88 | 89 | // Continuous Integration mode 90 | // if true, Karma captures browsers, runs the tests and exits 91 | singleRun: false 92 | }); 93 | }; 94 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ui-carousel", 3 | "version": "0.1.10", 4 | "author": { 5 | "name": "mihnsen", 6 | "email": "minhnt.hut@gmail.com" 7 | }, 8 | "description": "A simple, lightweight carousel for angularjs", 9 | "license": "MIT License", 10 | "homepage": "https://mihnsen.github.io/ui-carousel/", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/mihnsen/ui-carousel.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/mihnsen/ui-carousel/issues" 17 | }, 18 | "scripts": { 19 | "test": "gulp test" 20 | }, 21 | "main": "dist/ui-carousel.js", 22 | "keywords": [ 23 | "ui-carousel", 24 | "carousel", 25 | "carousel", 26 | "angular-carousel", 27 | "angular-carousel", 28 | "angular-ui-carousel", 29 | "ng-carousel" 30 | ], 31 | "dependencies": { }, 32 | "devDependencies": { 33 | "babel-preset-es2015": "^6.16.0", 34 | "gulp": "^3.8.7", 35 | "gulp-babel": "^6.1.2", 36 | "gulp-concat": "^2.3.4", 37 | "gulp-jshint": "^1.8.4", 38 | "gulp-minify-css": "^1.2.4", 39 | "gulp-minify-html": "^1.0.6", 40 | "gulp-ng-html2js": "^0.2.2", 41 | "gulp-plumber": "^0.6.6", 42 | "gulp-pug": "^3.0.4", 43 | "gulp-rename": "^1.2.0", 44 | "gulp-sass": "^2.3.2", 45 | "gulp-sourcemaps": "^1.6.0", 46 | "gulp-uglify": "^0.3.1", 47 | "gulp-usemin": "^0.3.24", 48 | "jasmine-core": "^2.5.2", 49 | "jshint-stylish": "^0.4.0", 50 | "karma": "^1.3.0", 51 | "karma-babel-preprocessor": "^6.0.1", 52 | "karma-jasmine": "^1.0.2", 53 | "karma-phantomjs-launcher": "^1.0.2", 54 | "karma-spec-reporter": "0.0.26", 55 | "pug": "^2.0.0-beta6", 56 | "run-sequence": "^1.0.2" 57 | }, 58 | "engines": { 59 | "node": ">=0.8.0" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/demo/index.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(ng-app="App") 3 | head 4 | meta(charset='utf-8') 5 | meta(http-equiv='X-UA-Compatible', content='chrome=1') 6 | meta(name='description', content='UI-Carousel lightweight module') 7 | link(rel='icon', type='image/x-icon', href='https://assets-cdn.github.com/favicon.ico') 8 | 9 | link(href='https://fonts.googleapis.com/css?family=Lato:400,500,700,900', rel='stylesheet') 10 | link(href='https://fonts.googleapis.com/css?family=Pacifico', rel='stylesheet') 11 | 12 | link(rel='stylesheet', type='text/css', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.1/css/bootstrap.css') 13 | 14 | // build:css style.css 15 | link(rel='stylesheet', type='text/css', href='../bower_components/prism/themes/prism-okaidia.css') 16 | link(rel='stylesheet', type='text/css', href='../dist/ui-carousel.css') 17 | link(rel='stylesheet', type='text/css', href='style.css') 18 | // endbuild 19 | 20 | title UI-Carousel - simple lightweight carousel for angular app 21 | script. 22 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 23 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 24 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 25 | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 26 | ga('create', 'UA-85040392-2', 'auto'); 27 | ga('send', 'pageview'); 28 | 29 | body(ng-controller="CarouselDemoCtrl as DemoCtrl") 30 | header.header 31 | .container 32 | h1 UI-Carousel 33 | p A simple, lightweight carousel for AngularJS app. 34 | p 35 | | Inspired by   36 | a(href='http://kenwheeler.github.io/slick/', target='_blank') Slick carousel 37 | main.main(role='main') 38 | section.screen 39 | .container 40 | .content-box 41 | h2.title Features 42 | ul.features 43 | //li Fully responsive. Scales with its container. 44 | //li Separate settings per breakpoint 45 | //-li Uses CSS3 when available. Fully functional when not. 46 | //-li Swipe enabled. Or disabled, if you prefer. 47 | //-li Desktop mouse dragging 48 | //-li Fully accessible with arrow key navigation 49 | //-li Add, remove, filter & unfilter slides 50 | li CSS3 (IE9+ will working good, lower IE version not tested) 51 | li Infinite looping. 52 | li Autoplay, dots, arrows, callbacks, etc... 53 | .divider 54 | .content-box 55 | h2.title Single Item 56 | ui-carousel(slides="DemoCtrl.single.slides", dots="true", on-init="DemoCtrl.singleInit()", infinite="false" on-after-change="DemoCtrl.singleAfter(currentSlide)") 57 | carousel-item 58 | h3 {{ item + 1 }} 59 | pre 60 | code.language-html(prism="") 61 | | {{ DemoCtrl.single.source }} 62 | 63 | .divider 64 | .content-box 65 | h2.title Multiple Item 66 | ui-carousel( 67 | slides="DemoCtrl.multiple.slides", 68 | slides-to-show="3", 69 | slides-to-scroll="3", 70 | dots="true" 71 | ) 72 | carousel-item 73 | h3 {{ item + 1 }} 74 | pre 75 | code.language-html(prism="") 76 | | {{ DemoCtrl.multiple.source }} 77 | .divider 78 | .content-box 79 | h2.title Autoplay 80 | ui-carousel( 81 | slides="DemoCtrl.autoplay.slides", 82 | slides-to-show="3", 83 | slides-to-scroll="1", 84 | initial-slide="1", 85 | autoplay="true", 86 | autoplay-speed="2000", 87 | dots="true" 88 | ) 89 | carousel-item 90 | h3 {{ item + 1 }} 91 | pre 92 | code.language-html(prism="") 93 | | {{ DemoCtrl.autoplay.source }} 94 | .divider 95 | .content-box 96 | h2.title Fade 97 | ui-carousel( 98 | slides="DemoCtrl.fade.slides", 99 | slides-to-show="3", 100 | slides-to-scroll="2", 101 | fade="true", 102 | dots="true", 103 | ) 104 | carousel-item 105 | .image 106 | img(src="{{ item }}") 107 | pre 108 | code.language-html(prism="") 109 | | {{ DemoCtrl.fade.source }} 110 | .divider 111 | .content-box 112 | h2.title Add & remove 113 | ui-carousel( 114 | slides="DemoCtrl.add.slides", 115 | slides-to-show="3", 116 | slides-to-scroll="1", 117 | dots="true", 118 | ) 119 | carousel-item 120 | h3 {{ item + 1 }} 121 | .buttons 122 | a.button(ng-click="DemoCtrl.addItem()") Add Slide 123 | a.button(ng-click="DemoCtrl.removeItem()") Remove Slide 124 | pre 125 | code.language-html(prism="") 126 | | {{ DemoCtrl.add.source }} 127 | .divider 128 | .content-box 129 | h2.title Customzie 130 | pre 131 | code.language-html(prism="") 132 | | {{ DemoCtrl.customize }} 133 | 134 | section.screen.extra 135 | .divider 136 | h2.title Follow & Share 137 | .text-center 138 | // Place this tag where you want the button to render. 139 | a.github-button(href='https://github.com/mihnsen', aria-label='Follow @mihnsen on GitHub') Follow @mihnsen 140 | // Place this tag where you want the button to render. 141 | a.github-button(href='https://github.com/mihnsen/ui-carousel', data-icon='octicon-star', data-count-href='/mihnsen/ui-carousel/stargazers', data-count-api='/repos/mihnsen/ui-carousel#stargazers_count', data-count-aria-label='# stargazers on GitHub', aria-label='Star mihnsen/ui-carousel on GitHub') Star 142 | .divider 143 | h2.title Issues tracker 144 | .text-center 145 | // Place this tag where you want the button to render. 146 | a.github-button(href='https://github.com/mihnsen/ui-carousel/issues', data-icon='octicon-issue-opened', data-style='mega', data-count-api='/repos/mihnsen/ui-carousel#open_issues_count', data-count-aria-label='# issues on GitHub', aria-label='Issue mihnsen/ui-carousel on GitHub') Issue 147 | 148 | 149 | footer.footer 150 | .container 151 | ul.footer-links 152 | li 153 | a(href='http://github.com/mihnsen/ui-carousel', target='_blank') Github 154 | li 155 | a(href='http://ownego.com', target='_blank') About 156 | p 157 | | Designed and built with love by  158 | a(href='http://www.github.com/mihnsen', target='_blank') @mihnsen 159 | p 160 | | Code licensed  161 | a(href='https://github.com/twbs/bootstrap/blob/master/LICENSE', target='_blank') MIT 162 | a(href='https://github.com/mihnsen/ui-carousel') 163 | img(style='position: fixed; top: 0; right: 0; border: 0;', src='https://camo.githubusercontent.com/52760788cde945287fbb584134c4cbc2bc36f904/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f77686974655f6666666666662e706e67', alt='Fork me on GitHub', data-canonical-src='https://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png') 164 | 165 | script(async='', defer='', src='https://buttons.github.io/buttons.js') 166 | 167 | // build:js main.js 168 | script(src='../bower_components/angular/angular.js') 169 | script(src='../bower_components/angular-sanitize/angular-sanitize.js') 170 | script(src='../bower_components/prism/prism.js') 171 | script(type='text/javascript', src='../dist/ui-carousel.js') 172 | script(type='text/javascript', src='main.js') 173 | // endbuild 174 | -------------------------------------------------------------------------------- /src/demo/main.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('App', ['ui.carousel', 'ngSanitize']); 2 | 3 | app.run(['Carousel', (Carousel) => { 4 | Carousel.setOptions({}); 5 | }]); 6 | 7 | app.controller('CarouselDemoCtrl', ['$scope', 'Carousel', function($scope, Carousel) { 8 | 'use strict'; 9 | 10 | this.singleInit = () => { 11 | console.log('single init'); 12 | }; 13 | 14 | this.singleAfter = (currentSlide) => { 15 | console.log(currentSlide); 16 | }; 17 | 18 | this.single = { 19 | slides: [...Array(6).keys()], 20 | source: '\n' + 21 | ' \n' + 22 | '

{{ item + 1 }}

\n' + 23 | '
\n' + 24 | '
' 25 | }; 26 | this.multiple = { 27 | slides: [...Array(9).keys()], 28 | source: '\n' + 29 | ' \n' + 30 | '

{{ item + 1 }}

\n' + 31 | '
\n' + 32 | '
' 33 | }; 34 | 35 | this.autoplay = { 36 | slides: [...Array(6).keys()], 37 | source: '\n' + 38 | ' \n' + 39 | '

{{ item + 1 }}

\n' + 40 | '
\n' + 41 | '
' 42 | }; 43 | 44 | this.fade = { 45 | slides: [ 46 | 'http://lorempixel.com/560/400/sports/1', 47 | 'http://lorempixel.com/560/400/sports/2', 48 | 'http://lorempixel.com/560/400/sports/3', 49 | ], 50 | source: '\n' + 51 | ' \n' + 52 | '
\n' + 53 | '
\n' + 54 | '
' 55 | }; 56 | 57 | this.addIndex = 1; 58 | this.addItem = () => { 59 | this.add.slides.push(this.addIndex++); 60 | }; 61 | this.removeItem = () => { 62 | if (this.add.slides.length <= 1) { 63 | return; 64 | } 65 | this.add.slides.splice(-1, 1); 66 | this.addIndex--; 67 | }; 68 | this.add = { 69 | slides: [...Array(1).keys()], 70 | source: '\n' + 71 | ' \n' + 72 | '

{{ item + 1 }}

\n' + 73 | '
\n' + 74 | '
' 75 | }; 76 | 77 | this.customize= 78 | '\n' + 79 | ' \n' + 80 | ' \n' + 81 | ' \n' + 82 | ' {{ item.title }}\n' + 83 | '

{{ item.name }}

\n' + 84 | '

{{ item.description }} \n' + 85 | ' \n' + 86 | ' \n' + 87 | ' \n' + 88 | ' \n' + 89 | ' \n' + 90 | ' \n' + 91 | ' \n' + 92 | ' \n' + 93 | ' \n' + 94 | ' \n' + 95 | ' \n' + 96 | ' \n' + 97 | ' \n' + 98 | ' \n' + 99 | ' \n' + 100 | '' 101 | ; 102 | }]); 103 | 104 | app.directive('prism', [function() { 105 | return { 106 | restrict: 'A', 107 | link: function ($scope, element, attrs) { 108 | element.ready(function() { 109 | Prism.highlightElement(element[0]); 110 | }); 111 | } 112 | } 113 | }]); 114 | -------------------------------------------------------------------------------- /src/demo/style.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 16px; 3 | line-height: 20px; 4 | font-family: 'Roboto', sans-serif; 5 | background-color: #fafafa; 6 | } 7 | .container { 8 | max-width: 560px; 9 | } 10 | .header { 11 | padding: 50px 0 10px; 12 | color: #3498db; 13 | background: #fff; 14 | text-align: center; 15 | 16 | h1 { 17 | margin-bottom: 40px; 18 | } 19 | } 20 | h1 { 21 | font-weight: 400; 22 | font-family: 'Pacifico', cursive; 23 | font-size: 70px; 24 | } 25 | h2.title { 26 | font-weight: bold; 27 | font-size: 40px; 28 | font-family: 'Pacifico', cursive; 29 | margin-bottom: 40px; 30 | } 31 | 32 | .footer { 33 | font-size: 14px; 34 | padding: 30px 0; 35 | color: #99979c; 36 | background-color: #2a2730; 37 | } 38 | .footer p { 39 | margin-bottom: 0; 40 | } 41 | .footer-links { 42 | padding: 0; 43 | list-style: none; 44 | } 45 | .footer-links li { 46 | display: inline; 47 | margin-right: 15px; 48 | } 49 | .footer a { 50 | color: #fff; 51 | } 52 | .main { 53 | background-color: #3498db; 54 | color: #fff; 55 | padding: 30px 0 50px; 56 | position: relative; 57 | min-height: 500px; 58 | text-align: center; 59 | } 60 | .divider { 61 | display: block; 62 | float: none; 63 | max-width: 500px; 64 | height: 1px; 65 | margin: 40px auto; 66 | clear: both; 67 | float: none; 68 | background-color: #fff; 69 | } 70 | ul.features { 71 | padding: 0; 72 | list-style: none; 73 | 74 | li { 75 | padding: 7px 0; 76 | } 77 | } 78 | 79 | h3 { 80 | font-weight: 700; 81 | background: #fff; 82 | color: #3498db; 83 | font-size: 36px; 84 | line-height: 100px; 85 | margin: 10px; 86 | padding: 10px; 87 | position: relative; 88 | text-align: center; 89 | } 90 | .ui-carousel { 91 | margin-bottom: 50px; 92 | .image { 93 | padding: 10px; 94 | 95 | img { 96 | display: block; 97 | border: 5px solid #FFF; 98 | width: 100%; 99 | } 100 | } 101 | } 102 | pre[class*="language-"] { 103 | background-color: #fdfdfd; 104 | margin-bottom: 1em; 105 | margin-left: 10px; 106 | margin-right: 10px; 107 | border-radius: 0; 108 | } 109 | code[class*="language"] { 110 | background: black; 111 | max-height: inherit; 112 | padding: 15px; 113 | display: block; 114 | overflow: auto; 115 | text-shadow: none; 116 | } 117 | 118 | .buttons { 119 | padding: 0 20px 20px; 120 | margin-bottom: 10px; 121 | } 122 | 123 | .button { 124 | float: left; 125 | background: #fff; 126 | color: #3498db; 127 | display: block; 128 | font-size: 16px; 129 | margin: 20px auto; 130 | padding: 20px; 131 | text-align: center; 132 | text-decoration: none; 133 | width: 48%; 134 | margin-right: 2%; 135 | cursor: pointer; 136 | } 137 | 138 | .button:hover { 139 | text-decoration: none; 140 | } 141 | -------------------------------------------------------------------------------- /src/ui-carousel/controllers/carousel.controller.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-ui-carousel 3 | * for example: 4 | * length = 8, show = 4, scroll = 3, current = 0 5 | * --------- 6 | * | | 7 | * |4|5|6|7|0|1|2|3|4|5|6|7|1|2|3|4 8 | * | | 9 | * --------- 10 | * rectangle is visible for users 11 | */ 12 | angular.module('ui.carousel.controllers') 13 | .controller('CarouselController', [ 14 | '$scope', '$element', '$timeout', '$q', 'Carousel', '$window', 15 | function ($scope, $element, $timeout, $q, Carousel, $window) { 16 | 17 | /** 18 | * Initial carousel 19 | * 20 | * Mirgate to angularjs 1.6 21 | * @see https://docs.angularjs.org/guide/migration#commit-bcd0d4 22 | */ 23 | this.$onInit = () => { 24 | this.initOptions(); 25 | this.initRanges(); 26 | this.setProps(); 27 | this.setupInfinite(); 28 | }; 29 | 30 | /** 31 | * Init option based on directive config 32 | */ 33 | this.initOptions = () => { 34 | this.options = angular.extend({}, Carousel.getOptions()); 35 | 36 | // TODO customize attribute from directive 37 | if (this.initialSlide !== undefined) { 38 | this.options.initialSlide = this.initialSlide; 39 | } 40 | if (this.fade !== undefined) { 41 | this.options.fade = this.fade; 42 | } 43 | if (this.autoplay !== undefined) { 44 | this.options.autoplay = this.autoplay; 45 | } 46 | if (this.autoplaySpeed !== undefined) { 47 | this.options.autoplaySpeed = this.autoplaySpeed; 48 | } 49 | if (this.cssEase !== undefined) { 50 | this.options.cssEase = this.cssEase; 51 | } 52 | if (this.speed !== undefined) { 53 | this.options.speed = this.speed; 54 | } 55 | if (this.infinite !== undefined) { 56 | this.options.infinite = this.infinite; 57 | } 58 | if (this.arrows !== undefined) { 59 | this.options.arrows = this.arrows; 60 | } 61 | if (this.dots !== undefined) { 62 | this.options.dots = this.dots; 63 | } 64 | if (this.visiblePrev !== undefined) { 65 | this.options.visiblePrev = this.visiblePrev; 66 | } 67 | if (this.visibleNext !== undefined) { 68 | this.options.visibleNext = this.visibleNext; 69 | } 70 | 71 | // TODO write more options for fade mode 72 | // In fade mode we have to setting slides-to-show and slides-to-scroll 73 | // to 1 slide 74 | if (this.options.fade) { 75 | this.options.slidesToShow = 1; 76 | this.options.slidesToScroll = 1; 77 | } else { 78 | if (this.show) { 79 | this.options.slidesToShow = this.show; 80 | } 81 | if (this.scroll) { 82 | this.options.slidesToScroll = this.scroll; 83 | } 84 | } 85 | }; 86 | 87 | /** 88 | * init variables, slides, .. 89 | */ 90 | this.initRanges = () => { 91 | if (!this.slides) { 92 | this.slides = []; 93 | } 94 | 95 | this.isCarouselReady = false; 96 | this.isTrackMoving = false; 97 | this.track = $element.find('.track'); 98 | this.width = 1; // Fake width 99 | this.currentSlide = this.options.initialSlide; 100 | this.trackStyle = {}; 101 | this.slideStyle = {}; 102 | 103 | this.isVisibleDots = false; 104 | this.isVisiblePrev = this.options.visiblePrev; 105 | this.isVisibleNext = this.options.visibleNext; 106 | 107 | this.isClickablePrev = false; 108 | this.isClickableNext = false; 109 | 110 | this.animType = null; 111 | this.transformType = null; 112 | this.transitionType = null; 113 | }; 114 | 115 | /** 116 | * Init UI and carousel track 117 | */ 118 | this.initUI = () => { 119 | this.width = $element[0].clientWidth; 120 | 121 | // Update track width first 122 | this.initTrack(); 123 | 124 | // Then item style 125 | $timeout(() => { 126 | this.updateItemStyle(); 127 | }, 200); 128 | }; 129 | 130 | /** 131 | * update common style for each carousel item 132 | */ 133 | this.updateItemStyle = () => { 134 | this.itemWidth = this.width / this.options.slidesToShow; 135 | this.slideStyle = { 136 | 'width': this.itemWidth + 'px' 137 | }; 138 | }; 139 | 140 | /** 141 | * init carousel track 142 | * also make Carousel is Ready 143 | */ 144 | this.initTrack = () => { 145 | const itemWidth = this.width / this.options.slidesToShow; 146 | const trackWidth = itemWidth * this.slidesInTrack.length; 147 | 148 | this.trackStyle.width = trackWidth + 'px'; 149 | 150 | this 151 | .slideHandler(this.currentSlide) 152 | .finally(() => { 153 | this.isCarouselReady = true; 154 | 155 | if (!this.options.fade) { 156 | this.refreshTrackStyle(); 157 | } 158 | 159 | // onInit callback 160 | if (this.onInit) { 161 | this.onInit(); 162 | } 163 | }) 164 | .catch(() => { 165 | // Catch err 166 | }); 167 | }; 168 | 169 | /** 170 | * @see https://github.com/kenwheeler/slick/blob/master/slick/slick.js#L680 171 | * 172 | * Sync slide to place it should be 173 | * for example: 174 | * - 9 total, 3 show, 3 scroll, current 1 175 | * => next index = 3 (previous index counted = 0) 176 | * 177 | * and scroll to next page: 178 | * - 6 total, 1 show, 1 scroll, current 0 => next index = 1 179 | * - 9 total, 3 show, 3 scroll, current 1 => next index = 3 180 | * - 9 total, 3 show, 3 scroll, current 3 => next index = 6 181 | * - 9 total, 3 show, 3 scroll, current 8 => next index = 3 182 | * - 8 total, 4 show, 3 scroll, current 1 => next index = 4 183 | */ 184 | this.next = () => { 185 | if (!this.isClickableNext) { 186 | return false; 187 | } 188 | 189 | const indexOffset = this.getIndexOffset(); 190 | const slideOffset = indexOffset === 0 191 | ? this.options.slidesToScroll 192 | : indexOffset; 193 | 194 | this 195 | .slideHandler(this.currentSlide + slideOffset) 196 | .catch(() => { 197 | // Catch err 198 | }); 199 | }; 200 | 201 | /** 202 | * move to previous slide 203 | * same calculate with next 204 | * @see next function 205 | */ 206 | this.prev = () => { 207 | if (!this.isClickablePrev) { 208 | return false; 209 | } 210 | 211 | const indexOffset = this.getIndexOffset(); 212 | const slideOffset = indexOffset === 0 213 | ? this.options.slidesToScroll 214 | : this.options.slidesToShow - indexOffset; 215 | 216 | this 217 | .slideHandler(this.currentSlide - slideOffset) 218 | .catch(() => { 219 | // Catch err 220 | }); 221 | }; 222 | 223 | /** 224 | * Get index offset 225 | */ 226 | this.getIndexOffset = () => { 227 | const scrollOffset = this.slides.length % this.options.slidesToScroll !== 0; 228 | const indexOffset = scrollOffset 229 | ? 0 230 | : (this.slides.length - this.currentSlide) % this.options.slidesToScroll; 231 | 232 | return indexOffset; 233 | }; 234 | 235 | /** 236 | * move to page 237 | * @params int page 238 | * Page counter from 0 (start = 0) 239 | */ 240 | this.movePage = (page) => { 241 | const target = this.options.slidesToScroll * page; 242 | this 243 | .slideHandler(target) 244 | .catch(() => { 245 | // Catch err 246 | }); 247 | }; 248 | 249 | /** 250 | * hanlder carousel 251 | * @description move carousel to correct page 252 | * 253 | * @params int index 254 | */ 255 | this.slideHandler = (index) => { 256 | // TODO prevent when slides not exists 257 | if (!this.slides) { 258 | return $q.reject('Carousel not fully setup'); 259 | } 260 | 261 | // TODO Prevent when track is moving 262 | if (this.isTrackMoving) { 263 | return $q.reject('Track is moving'); 264 | } 265 | 266 | const len = this.slides.length; 267 | const show = this.options.slidesToShow; 268 | 269 | if (len <= show) { 270 | this.correctTrack(); 271 | return $q.reject('Length of slides smaller than slides to show'); 272 | } 273 | 274 | // We need target to destination 275 | // and a anim slide to translate track 276 | // 277 | // anim = animSlide (which we use to move) 278 | // target = targetSlide 279 | const anim = index; 280 | let target = null; 281 | 282 | if (anim < 0) { 283 | if (len % this.options.slidesToScroll !== 0) { 284 | target = len - (len % this.options.slidesToScroll); 285 | } else { 286 | target = len + anim; 287 | } 288 | } else if (anim >= len) { 289 | if (len % this.options.slidesToScroll !== 0) { 290 | target = 0; 291 | } else { 292 | target = anim - len; 293 | } 294 | } else { 295 | target = anim; 296 | } 297 | 298 | if (this.onBeforeChange) { 299 | // @see https://docs.angularjs.org/guide/directive 300 | this.onBeforeChange({ currentSlide: this.currentSlide, target: target }); 301 | } 302 | 303 | // Fade handler 304 | if (this.options.fade) { 305 | this.currentSlide = target; 306 | 307 | // XXX 308 | // afterChange method 309 | // fire after faded 310 | // Should be revised 311 | $timeout(() => { 312 | this.autoplayTrack(); 313 | 314 | if (this.onAfterChange) { 315 | this.onAfterChange({ currentSlide: this.currentSlide }); 316 | } 317 | }, this.options.speed); 318 | return $q.when('Handler fade'); 319 | } 320 | 321 | // No-fade handler 322 | const itemWidth = this.width / this.options.slidesToShow; 323 | let left = -1 * target * itemWidth; 324 | if (this.options.infinite) { 325 | left = -1 * (anim + show) * itemWidth; 326 | } 327 | 328 | this.isTrackMoving = true; 329 | return this 330 | .moveTrack(left) 331 | .then(() => { 332 | this.isTrackMoving = false; 333 | this.currentSlide = target; 334 | this.autoplayTrack(); 335 | 336 | if (target !== anim) { 337 | this.correctTrack(); 338 | } 339 | 340 | if (!this.options.infinite) { 341 | if (this.currentSlide === 0) { 342 | this.isClickablePrev = false; 343 | this.isClickableNext = true; 344 | } else if (this.currentSlide === this.slidesInTrack.length - this.options.slidesToShow) { 345 | this.isClickableNext = false; 346 | this.isClickablePrev = true; 347 | } else { 348 | this.isClickablePrev = true; 349 | this.isClickableNext = true; 350 | } 351 | } 352 | 353 | // XXX 354 | // afterChange method 355 | // fire after 200ms wakeup and correct track 356 | // Should be revised 357 | $timeout(() => { 358 | if (this.onAfterChange) { 359 | this.onAfterChange({ currentSlide: this.currentSlide }); 360 | } 361 | }, 200); 362 | }); 363 | }; 364 | 365 | 366 | /** 367 | * moveTrack 368 | * move track to left position using css3 translate 369 | * for example left: -1000px 370 | */ 371 | this.moveTrack = (left) => { 372 | const deferred = $q.defer(); 373 | if (this.options.vertical === false) { 374 | this.trackStyle[this.animType] = 'translate3d(' + left + 'px, 0px, 0px)'; 375 | } else { 376 | this.trackStyle[this.animType] = 'translate3d(0px, ' + left + 'px, 0px)'; 377 | } 378 | 379 | $timeout(() => { 380 | deferred.resolve('Track moved'); 381 | }, this.options.speed); 382 | 383 | return deferred.promise; 384 | }; 385 | 386 | /** 387 | * correctTrack 388 | * @description correct track after move to animSlide we have to move track 389 | * to exactly its position 390 | */ 391 | this.correctTrack = () => { 392 | if (this.options.infinite) { 393 | let left = 0; 394 | if ( this.slides.length > this.options.slidesToShow ) { 395 | left = -1 * (this.currentSlide + this.options.slidesToShow) * this.itemWidth; 396 | } 397 | 398 | // Move without anim 399 | this.trackStyle[this.transitionType] = 400 | this.transformType + ' ' + 0 + 'ms ' + this.options.cssEase; 401 | 402 | this.isTrackMoving = true; 403 | $timeout(() => { 404 | this.trackStyle[this.animType] = 'translate3d(' + left + 'px, 0, 0px)'; 405 | 406 | // Revert animation 407 | $timeout(() => { 408 | this.refreshTrackStyle(); 409 | this.isTrackMoving = false; 410 | }, 200); 411 | }); 412 | } 413 | }; 414 | 415 | /** 416 | * Refresh track style 417 | */ 418 | this.refreshTrackStyle = () => { 419 | this.trackStyle[this.transitionType] = 420 | this.transformType + ' ' + this.options.speed + 'ms ' + this.options.cssEase; 421 | }; 422 | 423 | /** 424 | * autoplay track 425 | * @description autoplay = true 426 | */ 427 | this.autoplayTrack = () => { 428 | if (this.options.autoplay) { 429 | if (this.timeout) { 430 | $timeout.cancel(this.timeout); 431 | } 432 | 433 | this.timeout = $timeout(() => { 434 | this.next(); 435 | 436 | $timeout.cancel(this.timeout); 437 | this.timeout = null; 438 | }, this.options.autoplaySpeed); 439 | } 440 | }; 441 | 442 | this.getSlideStyle = index => { 443 | let style = this.slideStyle; 444 | if (this.options.fade) { 445 | const left = -1 * index * this.itemWidth; 446 | const uniqueStyle = { 447 | position: 'relative', 448 | top: '0px', 449 | left: left + 'px', 450 | 'z-index': index === this.currentSlide? 10 : 9, 451 | opacity: index === this.currentSlide? 1 : 0 452 | }; 453 | 454 | if (index >= this.currentSlide - 1 && index <= this.currentSlide + 1) { 455 | uniqueStyle.transition = 'opacity 250ms linear'; 456 | } 457 | 458 | style = angular.extend(style, uniqueStyle); 459 | } 460 | 461 | return style; 462 | }; 463 | 464 | 465 | /** 466 | * setupInfinite 467 | * To make carouse infinite we need close number of slidesToShow elements to 468 | * previous elements and to after elements 469 | * 470 | * length = 8, show = 4, scroll = 3, current = 0 471 | * --------- 472 | * | | 473 | * |4|5|6|7|0|1|2|3|4|5|6|7|1|2|3|4 474 | * | | 475 | * --------- 476 | */ 477 | this.setupInfinite = () => { 478 | // Clone 479 | const len = this.slides.length; 480 | const show = this.options.slidesToShow; 481 | 482 | let tmpTrack = angular.copy(this.slides); 483 | 484 | if (this.options.infinite && this.options.fade === false) { 485 | if (len > show) { 486 | const number = show; 487 | for (let i = 0; i < number; i++) { 488 | tmpTrack.push(angular.copy(this.slides[i])); 489 | } 490 | for (let i = len -1; i >= len - show; i--) { 491 | tmpTrack.unshift(angular.copy(this.slides[i])); 492 | } 493 | } 494 | } 495 | 496 | this.slidesInTrack = tmpTrack; 497 | }; 498 | 499 | /** 500 | * get number of dosts 501 | * 502 | * @return Array 503 | */ 504 | this.getDots = () => { 505 | if (!this.slides) { 506 | return []; 507 | } 508 | 509 | const dots = Math.ceil(this.slides.length / this.options.slidesToScroll); 510 | 511 | let res = []; 512 | for (let i = 0; i < dots; i++) { 513 | res.push(i); 514 | } 515 | return res; 516 | }; 517 | 518 | /** 519 | * set carousel property 520 | * 521 | * - animType 522 | * - transformType 523 | * - transitionType 524 | */ 525 | this.setProps = () => { 526 | const bodyStyle = document.body.style; 527 | 528 | /* eslint-disable */ 529 | if (bodyStyle.OTransform !== undefined) { 530 | this.animType = 'OTransform'; 531 | this.transformType = '-o-transform'; 532 | this.transitionType = 'OTransition'; 533 | } 534 | if (bodyStyle.MozTransform !== undefined) { 535 | this.animType = 'MozTransform'; 536 | this.transformType = '-moz-transform'; 537 | this.transitionType = 'MozTransition'; 538 | } 539 | if (bodyStyle.webkitTransform !== undefined) { 540 | this.animType = 'webkitTransform'; 541 | this.transformType = '-webkit-transform'; 542 | this.transitionType = 'webkitTransition'; 543 | } 544 | if (bodyStyle.msTransform !== undefined) { 545 | this.animType = 'msTransform'; 546 | this.transformType = '-ms-transform'; 547 | this.transitionType = 'msTransition'; 548 | } 549 | if (bodyStyle.transform !== undefined && this.animType !== false) { 550 | this.animType = 'transform'; 551 | this.transformType = 'transform'; 552 | this.transitionType = 'transition'; 553 | } 554 | /* eslint-enable */ 555 | 556 | this.transformsEnabled = true; 557 | }; 558 | 559 | /** 560 | * Refresh carousel 561 | */ 562 | this.refreshCarousel = () => { 563 | if (this.slides && this.slides.length && this.slides.length > this.options.slidesToShow) { 564 | this.isVisibleDots = true; 565 | this.isVisiblePrev = true; 566 | this.isVisibleNext = true; 567 | this.isClickablePrev = true; 568 | this.isClickableNext = true; 569 | } else { 570 | this.isVisibleDots = false; 571 | this.isVisiblePrev = this.options.visiblePrev || false; 572 | this.isVisibleNext = this.options.visibleNext || false; 573 | this.isClickablePrev = false; 574 | this.isClickableNext = false; 575 | } 576 | 577 | // Re-init UI 578 | this.initUI(); 579 | }; 580 | 581 | /** 582 | * refresh model 583 | */ 584 | $scope.$watchCollection('ctrl.slides', slides => { 585 | if (!slides) { 586 | return; 587 | } 588 | 589 | // Init carousel 590 | if (this.currentSlide > slides.length - 1) { 591 | this.currentSlide = slides.length - 1; 592 | } 593 | 594 | this.setupInfinite(); 595 | this.refreshCarousel(); 596 | }); 597 | 598 | /** 599 | * update when resize 600 | * 601 | * @see https://github.com/mihnsen/ui-carousel/issues/10 602 | * @author tarkant 603 | */ 604 | angular.element($window).on('resize', this.refreshCarousel); 605 | 606 | /** 607 | * cleanup when done 608 | * 609 | * @see https://github.com/mihnsen/ui-carousel/issues/10 610 | * @author tarkant 611 | */ 612 | $scope.$on('$destroy', function () { 613 | angular.element($window).off('resize'); 614 | }); 615 | 616 | // Prior to v1.5, we need to call `$onInit()` manually. 617 | // (Bindings will always be pre-assigned in these versions.) 618 | if (angular.version.major === 1 && angular.version.minor < 5) { 619 | this.$onInit(); 620 | } 621 | }]); 622 | -------------------------------------------------------------------------------- /src/ui-carousel/directives/carousel.directive.js: -------------------------------------------------------------------------------- 1 | angular.module('ui.carousel.directives') 2 | .directive('uiCarousel', ['$compile', '$templateCache', '$sce', 3 | function($compile, $templateCache, $sce) { 4 | 5 | return { restrict: 'AE', 6 | bindToController: true, 7 | scope: { 8 | name: '=?', 9 | slides: '=', 10 | show: '=?slidesToShow', 11 | scroll: '=?slidesToScroll', 12 | classes: '@', 13 | fade: '=?', 14 | onChange: '=?', 15 | disableArrow: '=?', 16 | autoplay: '=?', 17 | autoplaySpeed: '=?', 18 | cssEase: '=?', 19 | speed: '=?', 20 | infinite: '=?', 21 | arrows: '=?', 22 | dots: '=?', 23 | initialSlide: '=?', 24 | visibleNext: '=?', 25 | visiblePrev: '=?', 26 | 27 | // Method 28 | onBeforeChange: '&', 29 | onAfterChange: '&', 30 | onInit: '&', 31 | }, 32 | link($scope, el) { 33 | const template = angular.element( 34 | $templateCache.get('ui-carousel/carousel.template.html') 35 | ); 36 | 37 | // dynamic injections to override the inner layers' components 38 | const injectComponentMap = { 39 | 'carousel-item': '.carousel-item', 40 | 'carousel-prev': '.carousel-prev', 41 | 'carousel-next': '.carousel-next', 42 | }; 43 | 44 | const templateInstance = template.clone(); 45 | angular.forEach(injectComponentMap, (innerSelector, outerSelector) => { 46 | const outerElement = el[0].querySelector(outerSelector); 47 | if (outerElement) { 48 | angular 49 | .element(templateInstance[0].querySelector(innerSelector)) 50 | .html(outerElement.innerHTML); 51 | } 52 | }); 53 | 54 | const compiledElement = $compile(templateInstance)($scope); 55 | el.addClass('ui-carousel').html('').append(compiledElement); 56 | }, 57 | 58 | controller: 'CarouselController', 59 | controllerAs: 'ctrl' 60 | }; 61 | }]); 62 | -------------------------------------------------------------------------------- /src/ui-carousel/fonts/ui-carousel.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihnsen/ui-carousel/2fea2a6e7e7b9d98434f49a420e261490ec87627/src/ui-carousel/fonts/ui-carousel.eot -------------------------------------------------------------------------------- /src/ui-carousel/fonts/ui-carousel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by Fontastic.me 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/ui-carousel/fonts/ui-carousel.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihnsen/ui-carousel/2fea2a6e7e7b9d98434f49a420e261490ec87627/src/ui-carousel/fonts/ui-carousel.ttf -------------------------------------------------------------------------------- /src/ui-carousel/fonts/ui-carousel.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mihnsen/ui-carousel/2fea2a6e7e7b9d98434f49a420e261490ec87627/src/ui-carousel/fonts/ui-carousel.woff -------------------------------------------------------------------------------- /src/ui-carousel/providers/carousel.provider.js: -------------------------------------------------------------------------------- 1 | angular.module('ui.carousel.providers').provider('Carousel', function() { 2 | this.options = { 3 | // Init like Slick carousel 4 | // XXX Should be revised 5 | arrows: true, 6 | autoplay: false, 7 | autoplaySpeed: 3000, 8 | cssEase: 'ease', 9 | dots: false, 10 | 11 | easing: 'linear', 12 | fade: false, 13 | infinite: true, 14 | initialSlide: 0, 15 | 16 | slidesToShow: 1, 17 | slidesToScroll: 1, 18 | speed: 500, 19 | 20 | visiblePrev: false, 21 | visibleNext: false, 22 | 23 | // Not available right now 24 | draggable: true, 25 | 26 | lazyLoad: 'ondemand', 27 | 28 | swipe: true, 29 | swipeToSlide: false, 30 | touchMove: true, 31 | 32 | vertical: false, 33 | verticalSwiping: false 34 | }; 35 | this.$get = [() => { 36 | return { 37 | setOptions: options => { 38 | this.options = angular.extend(this.options, options); 39 | }, 40 | getOptions: () => { 41 | return this.options; 42 | } 43 | }; 44 | }]; 45 | }); 46 | -------------------------------------------------------------------------------- /src/ui-carousel/scss/_fonts.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @font-face { 4 | font-family: "ui-carousel"; 5 | src:url("fonts/ui-carousel.eot"); 6 | src:url("fonts/ui-carousel.eot?#iefix") format("embedded-opentype"), 7 | url("fonts/ui-carousel.woff") format("woff"), 8 | url("fonts/ui-carousel.ttf") format("truetype"), 9 | url("fonts/ui-carousel.svg#ui-carousel") format("svg"); 10 | font-weight: normal; 11 | font-style: normal; 12 | 13 | } 14 | 15 | [data-icon]:before { 16 | font-family: "ui-carousel" !important; 17 | content: attr(data-icon); 18 | font-style: normal !important; 19 | font-weight: normal !important; 20 | font-variant: normal !important; 21 | text-transform: none !important; 22 | speak: none; 23 | line-height: 1; 24 | -webkit-font-smoothing: antialiased; 25 | -moz-osx-font-smoothing: grayscale; 26 | } 27 | 28 | [class^="ui-icon-"]:before, 29 | [class*=" ui-icon-"]:before { 30 | font-family: "ui-carousel" !important; 31 | font-style: normal !important; 32 | font-weight: normal !important; 33 | font-variant: normal !important; 34 | text-transform: none !important; 35 | speak: none; 36 | line-height: 1; 37 | -webkit-font-smoothing: antialiased; 38 | -moz-osx-font-smoothing: grayscale; 39 | } 40 | 41 | .ui-icon-prev:before { 42 | content: "\61"; 43 | } 44 | .ui-icon-next:before { 45 | content: "\62"; 46 | } 47 | .ui-icon-dot:before { 48 | content: "\63"; 49 | } 50 | -------------------------------------------------------------------------------- /src/ui-carousel/scss/base.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/ui-carousel/scss/carousel.scss: -------------------------------------------------------------------------------- 1 | .ui-carousel { 2 | display: block; 3 | margin-bottom: 30px; 4 | 5 | .carousel-wrapper { 6 | position: relative; 7 | } 8 | .track-wrapper { 9 | position: relative; 10 | display: block; 11 | overflow: hidden; 12 | margin: 0; 13 | padding: 0; 14 | } 15 | .track { 16 | position: relative; 17 | display: block; 18 | float: left; 19 | } 20 | .slide { 21 | float: left; 22 | height: 100%; 23 | min-height: 1px; 24 | } 25 | .carousel-btn { 26 | position: absolute; 27 | z-index: 10; 28 | @extend .v-middle; 29 | background-color: transparent; 30 | outline: none; 31 | border: none; 32 | font-size: 20px; 33 | opacity: .75; 34 | 35 | &:hover { 36 | opacity: 1; 37 | } 38 | } 39 | 40 | .carousel-prev { 41 | .carousel-btn { 42 | left: -25px; 43 | } 44 | } 45 | .carousel-next { 46 | .carousel-btn { 47 | right: -25px; 48 | } 49 | } 50 | .carousel-disable { 51 | opacity: 0.5; 52 | 53 | .carousel-btn { 54 | &:hover { 55 | opacity: .75; 56 | } 57 | } 58 | } 59 | } 60 | 61 | .carousel-dots { 62 | position: absolute; 63 | bottom: -30px; 64 | display: block; 65 | width: 100%; 66 | padding: 0; 67 | margin: 0; 68 | list-style: none; 69 | text-align: center; 70 | 71 | li { 72 | position: relative; 73 | display: inline-block; 74 | width: 15px; 75 | height: 15px; 76 | margin: 0 5px; 77 | padding: 0; 78 | cursor: pointer; 79 | 80 | button { 81 | font-size: 0; 82 | line-height: 0; 83 | display: block; 84 | width: 15px; 85 | height: 15px; 86 | padding: 5px; 87 | cursor: pointer; 88 | color: transparent; 89 | border: 0; 90 | outline: none; 91 | background: transparent; 92 | 93 | &:before { 94 | font-family: ui-carousel; 95 | font-size: 9px; 96 | line-height: 15px; 97 | position: absolute; 98 | top: 0px; 99 | left: 0px; 100 | width: 15px; 101 | height: 15px; 102 | content: "\63"; 103 | text-align: center; 104 | opacity: 0.25; 105 | color: black; 106 | -webkit-font-smoothing: antialiased; 107 | } 108 | } 109 | 110 | &.carousel-active { 111 | button { 112 | &:before { 113 | opacity: .75; 114 | } 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/ui-carousel/scss/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin transition-transform($duration: 200ms, $easing: linear) { 2 | -webkit-transition: -webkit-transform $duration $easing; 3 | -moz-transition: -moz-transform $duration $easing; 4 | transition: transform $duration $easing; 5 | } 6 | @mixin transform($p...) { 7 | -webkit-transform: $p; 8 | -moz-transform: $p; 9 | transform: $p; 10 | } 11 | 12 | @mixin transition-multi($value...) { 13 | -webkit-transition: $value; 14 | -moz-transition: $value; 15 | -ms-transition: $value; 16 | -o-transition: $value; 17 | transition: $value; 18 | } 19 | 20 | @mixin translate($x, $y) { 21 | -webkit-transform: translate($x, $y); 22 | -ms-transform: translate($x, $y); // IE9 only 23 | -o-transform: translate($x, $y); 24 | transform: translate($x, $y); 25 | } 26 | @mixin transition($transition...) { 27 | -webkit-transition: $transition; 28 | -o-transition: $transition; 29 | transition: $transition; 30 | } 31 | 32 | // Position 33 | .v-middle { 34 | display: block; 35 | position: absolute; 36 | top: 50%; 37 | @include translate(0, -50%); 38 | } 39 | -------------------------------------------------------------------------------- /src/ui-carousel/scss/ui-carousel.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | @import 'mixin'; 3 | @import 'fonts'; 4 | @import 'base'; 5 | @import 'carousel'; 6 | -------------------------------------------------------------------------------- /src/ui-carousel/scss/variables.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/ui-carousel/templates/carousel.template.pug: -------------------------------------------------------------------------------- 1 | .carousel-wrapper(ng-show="ctrl.isCarouselReady") 2 | .track-wrapper 3 | .track(ng-style="ctrl.trackStyle") 4 | .slide( 5 | ng-repeat="item in ctrl.slidesInTrack track by $index", 6 | ng-style="ctrl.getSlideStyle($index)", 7 | ) 8 | .carousel-item 9 | 10 | .carousel-prev( 11 | ng-if="!ctrl.disableArrow", 12 | ng-show="ctrl.isVisiblePrev && ctrl.options.arrows", 13 | ng-class="{'carousel-disable': !ctrl.isClickablePrev}", 14 | ng-click="ctrl.prev()" 15 | ) 16 | button.carousel-btn 17 | i.ui-icon-prev 18 | .carousel-next( 19 | ng-if="!ctrl.disableArrow", 20 | ng-show="ctrl.isVisibleNext && ctrl.options.arrows", 21 | ng-class="{'carousel-disable': !ctrl.isClickableNext}", 22 | ng-click="ctrl.next()" 23 | ) 24 | button.carousel-btn 25 | i.ui-icon-next 26 | ul.carousel-dots(ng-show="ctrl.isVisibleDots && ctrl.options.dots") 27 | li( 28 | ng-repeat="dot in ctrl.getDots()", 29 | ng-class="{ 'carousel-active': dot == ctrl.currentSlide/ctrl.options.slidesToScroll }", 30 | ng-click="ctrl.movePage(dot)" 31 | ) 32 | button {{ dot }} 33 | -------------------------------------------------------------------------------- /src/ui-carousel/uiCarousel.module.js: -------------------------------------------------------------------------------- 1 | (function (angular) { 2 | // Create all modules and define dependencies to make sure they exist 3 | // and are loaded in the correct order to satisfy dependency injection 4 | // before all nested files are concatenated by Gulp 5 | 6 | // Config 7 | angular.module('ui.carousel.config', []) 8 | .value('ui.carousel.config', { 9 | debug: true 10 | }); 11 | 12 | // Modules 13 | angular.module('ui.carousel.providers', []); 14 | angular.module('ui.carousel.controllers', []); 15 | angular.module('ui.carousel.directives', []); 16 | angular.module('ui.carousel', [ 17 | 'ui.carousel.config', 18 | 'ui.carousel.directives', 19 | 'ui.carousel.controllers', 20 | 'ui.carousel.providers' 21 | ]); 22 | })(angular); 23 | -------------------------------------------------------------------------------- /test/unit/ui-carousel/configs/test.utils.js: -------------------------------------------------------------------------------- 1 | var TestUtil = { 2 | compile: function(html, scope) { 3 | var container; 4 | 5 | inject(function($compile, $rootScope) { 6 | if (!scope) { 7 | scope = $rootScope.$new(); 8 | } else if (scope && !scope.$new) { 9 | scope = angular.extend($rootScope.$new(), scope); 10 | } 11 | 12 | const el = angular.element(html); 13 | 14 | container = $compile(el)(scope); 15 | $rootScope.$apply(); 16 | $rootScope.$digest(); 17 | }); 18 | 19 | return container; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /test/unit/ui-carousel/controllers/carousel.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('ui.carousel.controller.CarouselController', function() { 4 | let $scope; 5 | let $rootScope; 6 | let $element; 7 | let $timeout; 8 | let $q; 9 | let CarouselCtrl; 10 | let Carousel; 11 | 12 | beforeEach(angular.mock.module('ui.carousel.controllers', 'ui.carousel.providers')); 13 | 14 | beforeEach(inject((_$rootScope_, $controller, _$timeout_, _$compile_, _$q_, _Carousel_) => { 15 | $scope = _$rootScope_.$new(); 16 | $rootScope = _$rootScope_; 17 | $timeout = _$timeout_; 18 | $q = _$q_; 19 | 20 | Carousel = _Carousel_; 21 | $element = _$compile_('

')($scope); 22 | 23 | CarouselCtrl = $controller('CarouselController', { 24 | $scope, 25 | $element, 26 | $timeout, 27 | $q, 28 | Carousel 29 | }, { 30 | show: 1, 31 | scroll: 1, 32 | autoplay: true 33 | }); 34 | })); 35 | 36 | describe('$onInit()', () => { 37 | beforeEach(() => { 38 | spyOn(CarouselCtrl, 'initOptions'); 39 | spyOn(CarouselCtrl, 'initRanges'); 40 | spyOn(CarouselCtrl, 'setProps'); 41 | spyOn(CarouselCtrl, 'setupInfinite'); 42 | }); 43 | 44 | it('should be init sequence correctly', () => { 45 | CarouselCtrl.$onInit(); 46 | 47 | expect(CarouselCtrl.initOptions).toHaveBeenCalled(); 48 | expect(CarouselCtrl.initRanges).toHaveBeenCalled(); 49 | expect(CarouselCtrl.setProps).toHaveBeenCalled(); 50 | expect(CarouselCtrl.setupInfinite).toHaveBeenCalled(); 51 | }); 52 | }); 53 | 54 | describe('initOptions', () => { 55 | beforeEach(() => { 56 | spyOn(Carousel, 'getOptions'); 57 | }); 58 | 59 | it('should get options from providers, config', () => { 60 | CarouselCtrl.initOptions(); 61 | expect(Carousel.getOptions).toHaveBeenCalled(); 62 | }); 63 | 64 | it('should get config from directive', () => { 65 | CarouselCtrl.autoplay = true; 66 | CarouselCtrl.fade = false; 67 | CarouselCtrl.show = 4; 68 | CarouselCtrl.scroll = 3; 69 | CarouselCtrl.initialSlide = 10; 70 | CarouselCtrl.initOptions(); 71 | 72 | expect(CarouselCtrl.options.initialSlide).toEqual(10); 73 | expect(CarouselCtrl.options.fade).toBe(false); 74 | expect(CarouselCtrl.options.autoplay).toBe(true); 75 | expect(CarouselCtrl.options.slidesToShow).toEqual(4); 76 | expect(CarouselCtrl.options.slidesToScroll).toEqual(3); 77 | }); 78 | 79 | it('should set slides to show and scroll to be 1 when fade = true', () => { 80 | CarouselCtrl.fade = true; 81 | CarouselCtrl.show = 4; 82 | CarouselCtrl.scroll = 3; 83 | CarouselCtrl.initOptions(); 84 | 85 | expect(CarouselCtrl.options.slidesToShow).toEqual(1); 86 | expect(CarouselCtrl.options.slidesToScroll).toEqual(1); 87 | }); 88 | }); 89 | 90 | describe('initRanges()', () => { 91 | beforeEach(() => { 92 | CarouselCtrl.$onInit(); 93 | }); 94 | 95 | it('should init variables, slides correctly', () => { 96 | CarouselCtrl.slides = [1, 2, 3, 4, 5, 6]; 97 | CarouselCtrl.initRanges(); 98 | 99 | expect(CarouselCtrl.isCarouselReady).toBe(false); 100 | expect(CarouselCtrl.isTrackMoving).toBe(false); 101 | expect(CarouselCtrl.track).toBeDefined(); 102 | expect(CarouselCtrl.currentSlide).toBeDefined(); 103 | expect(CarouselCtrl.trackStyle).toBeDefined(); 104 | expect(CarouselCtrl.slideStyle).toBeDefined(); 105 | expect(CarouselCtrl.isVisibleDots).toBeDefined(); 106 | expect(CarouselCtrl.isVisibleNext).toBeDefined(); 107 | expect(CarouselCtrl.isVisiblePrev).toBeDefined(); 108 | expect(CarouselCtrl.animType).toBeDefined(); 109 | expect(CarouselCtrl.transformType).toBeDefined(); 110 | expect(CarouselCtrl.transitionType).toBeDefined(); 111 | }); 112 | }); 113 | 114 | describe('initUI()', () => { 115 | beforeEach(() => { 116 | spyOn(CarouselCtrl, 'initTrack'); 117 | spyOn(CarouselCtrl, 'updateItemStyle'); 118 | 119 | CarouselCtrl.$onInit(); 120 | }); 121 | 122 | it('should calculate track width', () => { 123 | CarouselCtrl.initUI(); 124 | $timeout.flush(); 125 | 126 | expect(CarouselCtrl.initTrack).toHaveBeenCalled(); 127 | expect(CarouselCtrl.updateItemStyle).toHaveBeenCalled(); 128 | }); 129 | }); 130 | 131 | describe('initTrack()', () => { 132 | beforeEach(() => { 133 | CarouselCtrl.$onInit(); 134 | }); 135 | 136 | it('should calculate track width and prevent transition when fade = true', () => { 137 | CarouselCtrl.slidesInTrack = [2, 0, 1, 2, 0]; 138 | CarouselCtrl.width = 1; 139 | CarouselCtrl.options.fade = true; 140 | CarouselCtrl.options.slidesToShow = 2; 141 | CarouselCtrl.initTrack(); 142 | 143 | expect(CarouselCtrl.trackStyle.width).toEqual('2.5px'); 144 | 145 | $scope.$apply(); 146 | expect(CarouselCtrl.isCarouselReady).toBe(true); 147 | expect(CarouselCtrl.trackStyle).toEqual({ 148 | 'width': '2.5px', 149 | 'webkitTransition': '-webkit-transform 0ms ease', 150 | }); 151 | }); 152 | it('should calculate track width and init track transition', () => { 153 | CarouselCtrl.slidesInTrack = [2, 0, 1, 2, 0]; 154 | CarouselCtrl.width = 1; 155 | CarouselCtrl.options.fade = false; 156 | CarouselCtrl.options.speed = 5000; 157 | CarouselCtrl.options.slidesToShow = 2; 158 | CarouselCtrl.initTrack(); 159 | 160 | expect(CarouselCtrl.trackStyle.width).toEqual('2.5px'); 161 | 162 | $scope.$apply(); 163 | expect(CarouselCtrl.isCarouselReady).toBe(true); 164 | expect(CarouselCtrl.trackStyle).toEqual({ 165 | 'width': '2.5px', 166 | 'webkitTransition': '-webkit-transform 5000ms ease' 167 | }); 168 | }); 169 | }); 170 | 171 | describe('next()', () => { 172 | /* 173 | * - 6 total, 1 show, 1 scroll, current 0 => next index = 1 174 | * - 9 total, 3 show, 3 scroll, current 1 => next index = 3 175 | * - 9 total, 3 show, 3 scroll, current 3 => next index = 6 176 | * - 9 total, 3 show, 3 scroll, current 8 => next index = 3 177 | * - 8 total, 4 show, 3 scroll, current 1 => next index = 4 178 | */ 179 | beforeEach(() => { 180 | CarouselCtrl.slideHandler = jasmine.createSpy('slideHandler').and.callFake(() => { 181 | return $q.resolve(true); 182 | }); 183 | CarouselCtrl.$onInit(); 184 | CarouselCtrl.isClickableNext = true; 185 | }); 186 | 187 | it('should works well with normal case', () => { 188 | CarouselCtrl.slides = [...Array(5)]; 189 | CarouselCtrl.options.slidesToScroll = 1; 190 | CarouselCtrl.currentSlide = 0; 191 | CarouselCtrl.next(); 192 | 193 | expect(CarouselCtrl.slideHandler).toHaveBeenCalledWith(1); 194 | }); 195 | 196 | it('should works well with infinite case and sync to page index first', () => { 197 | CarouselCtrl.slides = [...Array(9)]; 198 | CarouselCtrl.options.slidesToScroll = 3; 199 | CarouselCtrl.currentSlide = 1; 200 | CarouselCtrl.next(); 201 | 202 | expect(CarouselCtrl.slideHandler).toHaveBeenCalledWith(3); 203 | }); 204 | 205 | it('should works well with infinite case and scroll to next page', () => { 206 | CarouselCtrl.slides = [...Array(9)]; 207 | CarouselCtrl.options.slidesToScroll = 3; 208 | CarouselCtrl.currentSlide = 3; 209 | CarouselCtrl.next(); 210 | 211 | expect(CarouselCtrl.slideHandler).toHaveBeenCalledWith(6); 212 | }); 213 | 214 | it('should works well with infinite case and scroll to next page', () => { 215 | CarouselCtrl.slides = [...Array(8)]; 216 | CarouselCtrl.options.slidesToScroll = 3; 217 | CarouselCtrl.currentSlide = 1; 218 | CarouselCtrl.next(); 219 | 220 | expect(CarouselCtrl.slideHandler).toHaveBeenCalledWith(4); 221 | }); 222 | }); 223 | 224 | describe('prev()', () => { 225 | // Same with next() 226 | beforeEach(() => { 227 | CarouselCtrl.slideHandler = jasmine.createSpy('slideHandler').and.callFake(() => { 228 | return $q.resolve(true); 229 | }); 230 | CarouselCtrl.$onInit(); 231 | CarouselCtrl.isClickablePrev = true; 232 | }); 233 | 234 | it('should works well with infinite case and sync to page index first', () => { 235 | CarouselCtrl.slides = [...Array(9)]; 236 | CarouselCtrl.options.slidesToShow = 3; 237 | CarouselCtrl.options.slidesToScroll = 3; 238 | CarouselCtrl.currentSlide = 1; 239 | CarouselCtrl.prev(); 240 | 241 | expect(CarouselCtrl.slideHandler).toHaveBeenCalledWith(0); 242 | }); 243 | }); 244 | 245 | describe('movePage()', () => { 246 | beforeEach(() => { 247 | CarouselCtrl.slideHandler = jasmine.createSpy('slideHandler').and.callFake(() => { 248 | return $q.resolve(true); 249 | }); 250 | CarouselCtrl.$onInit(); 251 | }); 252 | 253 | it('should be work well', () => { 254 | CarouselCtrl.options.slidesToScroll = 1; 255 | CarouselCtrl.currentSlide = 5; 256 | expect(CarouselCtrl.currentSlide).toEqual(5); 257 | 258 | CarouselCtrl.movePage(3); 259 | expect(CarouselCtrl.slideHandler).toHaveBeenCalledWith(3); 260 | }); 261 | 262 | it('should be work with multiple slides to scroll', () => { 263 | CarouselCtrl.options.slidesToScroll = 3; 264 | CarouselCtrl.options.currentSlide = 5; 265 | CarouselCtrl.options.fade = false; 266 | 267 | CarouselCtrl.movePage(3); 268 | expect(CarouselCtrl.slideHandler).toHaveBeenCalledWith(9); 269 | }); 270 | }); 271 | 272 | describe('slideHandler()', () => { 273 | beforeEach(() => { 274 | spyOn(CarouselCtrl, 'autoplayTrack'); 275 | spyOn(CarouselCtrl, 'correctTrack'); 276 | CarouselCtrl.slides = [...Array(6)]; 277 | CarouselCtrl.$onInit(); 278 | }); 279 | 280 | it('should reject if track is moving', (done) => { 281 | CarouselCtrl.isTrackMoving = true; 282 | let expectMsg = ''; 283 | CarouselCtrl 284 | .slideHandler(1) 285 | .catch(msg => expectMsg = msg) 286 | .finally(done); 287 | 288 | $scope.$apply(); 289 | expect(expectMsg).toEqual('Track is moving'); 290 | }); 291 | 292 | it('should reject when length of slides is lower than slides to show', (done) => { 293 | CarouselCtrl.options.slidesToShow = 7; 294 | let expectMsg = ''; 295 | CarouselCtrl 296 | .slideHandler(1) 297 | .catch(msg => expectMsg = msg) 298 | .finally(done); 299 | 300 | $scope.$apply(); 301 | expect(expectMsg).toEqual('Length of slides smaller than slides to show'); 302 | }); 303 | 304 | it('should move to correct index when fade = true', (done) => { 305 | CarouselCtrl.fade = true; 306 | CarouselCtrl.initOptions(); 307 | let expectMsg = ''; 308 | CarouselCtrl 309 | .slideHandler(6) 310 | .then(msg => expectMsg = msg) 311 | .finally(done); 312 | 313 | $scope.$apply(); 314 | expect(expectMsg).toEqual('Handler fade'); 315 | expect(CarouselCtrl.currentSlide).toEqual(0); 316 | }); 317 | 318 | it('should move track to correct position', (done) => { 319 | // XXX 320 | // see http://paulsalaets.com/posts/q-promise-chains-need-digest-cycle-to-go 321 | // Should be revised 322 | spyOn(CarouselCtrl, 'moveTrack').and.callFake(() => { 323 | return $q((resolve, reject) => { 324 | resolve(); 325 | }); 326 | }); 327 | 328 | CarouselCtrl.fade = false; 329 | CarouselCtrl.infinite = true; 330 | CarouselCtrl.$onInit(); 331 | CarouselCtrl.updateItemStyle(); 332 | 333 | CarouselCtrl.slideHandler(6).finally(done); 334 | 335 | $scope.$apply(); 336 | //expect(CarouselCtrl.isTrackMoving).toBe(false); 337 | expect(CarouselCtrl.currentSlide).toBe(0); 338 | expect(CarouselCtrl.autoplayTrack).toHaveBeenCalled(); 339 | expect(CarouselCtrl.correctTrack).toHaveBeenCalled(); 340 | expect(CarouselCtrl.moveTrack).toHaveBeenCalledWith(-7); 341 | expect(CarouselCtrl.currentSlide).toEqual(0); 342 | }); 343 | }); 344 | 345 | describe('moveTrack()', () => { 346 | beforeEach(() => { 347 | CarouselCtrl.$onInit(); 348 | }); 349 | 350 | it('should move track and resolve after speed timeout', (done) => { 351 | CarouselCtrl 352 | .moveTrack(100) 353 | .then(msg => { 354 | expect(msg).toEqual('Track moved'); 355 | }) 356 | .finally(done); 357 | 358 | $timeout.flush(CarouselCtrl.options.speed); 359 | }); 360 | 361 | it('should update track style', () => { 362 | CarouselCtrl.moveTrack(100); 363 | 364 | expect(CarouselCtrl.trackStyle).toEqual(jasmine.objectContaining({ 365 | webkitTransform: "translate3d(100px, 0px, 0px)" 366 | })); 367 | }); 368 | }); 369 | 370 | describe('correctTrack()', () => { 371 | beforeEach(() => { 372 | CarouselCtrl.slides = [...Array(6)]; 373 | CarouselCtrl.$onInit(); 374 | }); 375 | 376 | it('should be working only infinite mode', () => { 377 | CarouselCtrl.isTrackMoving = false; 378 | CarouselCtrl.options.infinite = false; 379 | 380 | CarouselCtrl.correctTrack(); 381 | expect(CarouselCtrl.isTrackMoving).toBe(false); 382 | expect(CarouselCtrl.trackStyle.webkitTransform).not.toBeDefined(); 383 | }); 384 | 385 | it('should move to correct track position and revert track style', () => { 386 | CarouselCtrl.currentSlide = 1; 387 | CarouselCtrl.options.speed = 200; 388 | CarouselCtrl.options.cssEase = 'linear'; 389 | 390 | CarouselCtrl.correctTrack(); 391 | expect(CarouselCtrl.isTrackMoving).toBe(true); 392 | 393 | $scope.$apply(); 394 | expect(CarouselCtrl.trackStyle).toEqual(jasmine.objectContaining({ 395 | webkitTransition: '-webkit-transform 0ms linear', 396 | })); 397 | 398 | $timeout.flush(200); 399 | expect(CarouselCtrl.isTrackMoving).toBe(false); 400 | expect(CarouselCtrl.trackStyle).toEqual(jasmine.objectContaining({ 401 | webkitTransition: '-webkit-transform 200ms linear', 402 | })); 403 | }); 404 | }); 405 | 406 | describe('autoplayTrack()', () => { 407 | beforeEach(() => { 408 | spyOn(CarouselCtrl, 'next'); 409 | CarouselCtrl.slides = [...Array(6)]; 410 | CarouselCtrl.$onInit(); 411 | }); 412 | 413 | it('should autoplay when enabled in options', () => { 414 | CarouselCtrl.options.autoplay = false; 415 | CarouselCtrl.autoplayTrack(); 416 | expect(CarouselCtrl.timeout).not.toBeDefined(); 417 | }); 418 | 419 | it('should autoplay', () => { 420 | CarouselCtrl.options.autoplay = true; 421 | CarouselCtrl.options.autoplaySpeed = 3000; 422 | CarouselCtrl.next.calls.reset(); 423 | CarouselCtrl.autoplayTrack(); 424 | 425 | expect(CarouselCtrl.timeout).toBeDefined(); 426 | $timeout.flush(3000); 427 | expect(CarouselCtrl.next.calls.count()).toEqual(1); 428 | }); 429 | }); 430 | 431 | describe('getSlideStyle', () => { 432 | beforeEach(() => { 433 | CarouselCtrl.slides = [...Array(6)]; 434 | CarouselCtrl.$onInit(); 435 | }); 436 | 437 | it('should be get correct style', () => { 438 | CarouselCtrl.updateItemStyle(); 439 | 440 | CarouselCtrl.options.fade = false; 441 | expect(CarouselCtrl.getSlideStyle(1000)).toEqual({ 442 | 'width': '1px' 443 | }); 444 | 445 | CarouselCtrl.options.fade = true; 446 | expect(CarouselCtrl.getSlideStyle(5)).toEqual({ 447 | 'width': '1px', 448 | 'position': 'relative', 449 | 'top': '0px', 450 | 'z-index': 9, 451 | 'left': '-5px', 452 | 'opacity': 0 453 | }); 454 | }); 455 | 456 | it('should be get correct style', () => { 457 | CarouselCtrl.width = 100; 458 | CarouselCtrl.currentSlide = 5; 459 | CarouselCtrl.options.slidesToShow = 2; 460 | 461 | CarouselCtrl.updateItemStyle(); 462 | CarouselCtrl.options.fade = false; 463 | expect(CarouselCtrl.slideStyle).toEqual({ 464 | 'width': '50px' 465 | }); 466 | 467 | CarouselCtrl.options.fade = true; 468 | expect(CarouselCtrl.getSlideStyle(5)).toEqual({ 469 | 'width': '50px', 470 | 'position': 'relative', 471 | 'top': '0px', 472 | 'z-index': 10, 473 | 'left': '-250px', 474 | 'opacity': 1, 475 | 'transition': 'opacity 250ms linear' 476 | }); 477 | }); 478 | }); 479 | 480 | //TODO write more tests case for carousel 481 | }); 482 | -------------------------------------------------------------------------------- /test/unit/ui-carousel/directives/carousel.directive.js: -------------------------------------------------------------------------------- 1 | describe('ui-carousel', () => { 2 | let $rootScope; 3 | let $compile; 4 | let scope; 5 | 6 | beforeEach(module('ui.carousel.directives')); 7 | 8 | // inject services 9 | beforeEach(inject((_$rootScope_, _$compile_) => { 10 | $rootScope = _$rootScope_; 11 | $compile = _$compile_; 12 | 13 | scope = $rootScope.$new(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /test/unit/ui-carousel/providers/carousel.provider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('ui.carousel.providers.Carousel', function() { 4 | var Carousel; 5 | 6 | // load the module 7 | beforeEach(angular.mock.module('ui.carousel.providers', function($provide) { 8 | })); 9 | 10 | beforeEach(inject(function(_Carousel_) { 11 | Carousel = _Carousel_; 12 | })); 13 | 14 | it('should be defined', function() { 15 | expect(Carousel).toBeDefined(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/unit/ui-carousel/uiCarouselSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('ui.carousel', () => { 4 | var module; 5 | var dependencies = []; 6 | 7 | var hasModule = (module) => { 8 | return dependencies.indexOf(module) >= 0; 9 | }; 10 | 11 | beforeEach(() => { 12 | // Get module 13 | module = angular.module('ui.carousel'); 14 | dependencies = module.requires; 15 | }); 16 | 17 | it('should load config module', () => { 18 | expect(hasModule('ui.carousel.config')).toBeDefined(); 19 | }); 20 | }); 21 | --------------------------------------------------------------------------------